File size: 6,940 Bytes
615deca
 
 
 
59e20bd
 
 
 
615deca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66e47ee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615deca
66e47ee
 
 
615deca
 
 
66e47ee
 
 
615deca
66e47ee
 
 
 
 
615deca
 
 
 
 
 
 
 
 
59e20bd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615deca
59e20bd
615deca
 
 
dae1bd3
 
 
 
 
615deca
dae1bd3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dcc3a83
 
 
 
 
 
 
 
 
 
 
 
dae1bd3
 
 
dcc3a83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dae1bd3
 
 
 
 
dcc3a83
 
 
 
 
 
dae1bd3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
import streamlit as st
import spacy
import pandas as pd
import datetime
import gc
import subprocess
import json
from spacy.util import registry

st.set_page_config(page_title="Parsing Demo", layout="wide")
st.sidebar.header("Parsing Demo")

default_text = """Ita fac, mi Lucili; vindica te tibi, et tempus, quod adhuc aut auferebatur aut subripiebatur aut excidebat, collige et serva."""


def format_morph(morph):
    morph = morph.to_dict()
    if morph:
        return ", ".join([f"{k}={v}" for k, v in morph.items()])
    else:
        return ""


def analyze_text(text):
    doc = nlp(text)
    rows = []
    token_count = 0
    for sent_idx, sent in enumerate(doc.sents):
        sent_id = f"s{sent_idx + 1}"
        sent_start = sent.start
        for token_idx, token in enumerate(sent):
            if token_count >= 500:
                break
            token_id = token_idx + 1
            if token.head == token:
                head = 0
            else:
                head = token.head.i - sent_start + 1
            rows.append(
                (
                    sent_id,
                    token_id,
                    token.text,
                    token.lemma_,
                    token.pos_,
                    token.tag_,
                    format_morph(token.morph),
                    head,
                    token.dep_,
                    token.ent_type_,
                )
            )
            token_count += 1
        if token_count >= 500:
            break
    df = pd.DataFrame(
        rows,
        columns=[
            "sent_id",
            "token_id",
            "form",
            "lemma",
            "upos",
            "xpos",
            "feats",
            "head",
            "deprel",
            "ent_type",
        ],
    )
    return df


st.title("LatinCy Text Analyzer")

# Using object notation
first_model = "la_core_web_lg"
first_model_version = spacy.info(first_model)["version"]


# Function to unload the current model
@st.cache_resource
def load_model(model_name):
    gc.collect()  # Attempt to free memory
    nlp = spacy.load(model_name)
    try:
        if "trf_vectors" in nlp.pipe_names:  # Disable trf_vectors if it exists
            nlp.disable_pipe("trf_vectors")
    except Exception as e:
        st.warning(f"Failed to disable 'trf_vectors': {e}")
    return nlp


# Function to load model in a subprocess to avoid conflicts
def load_model_in_subprocess(model_name):
    script = (
        "import spacy, json; "
        'nlp = spacy.load("' + model_name + '"); '
        "print(json.dumps(nlp.meta))"
    )
    result = subprocess.run(["python", "-c", script], capture_output=True, text=True)
    if result.returncode != 0:
        raise RuntimeError(f"Failed to load model {model_name}: {result.stderr}")
    return json.loads(result.stdout)


model_name = "la_core_web_lg"  # Hardcoded to use only the lg model
nlp = spacy.load(model_name)  # Directly load the lg model

st.write(f"Loaded model: {model_name} (v{spacy.info(model_name)['version']})")

df = None

tab1, tab2 = st.tabs(["Analyze", "About"])

with tab1:
    text = st.text_area(
        "Enter some text to analyze (max 500 tokens)", value=default_text, height=200
    )
    if st.button("Analyze"):
        df = analyze_text(text)
        sent_count = df["sent_id"].nunique()
        st.text(f"Analyzed {len(df)} tokens in {sent_count} sentences with {model_name} model.")
        st.dataframe(df, width=1200, hide_index=True)

        @st.cache_data
        def convert_df(df):
            return df.to_csv(index=False, sep="\t").encode("utf-8")

        csv = convert_df(df)

        def create_timestamp():
            return datetime.datetime.now().strftime("%Y%m%d%H%M%S")

        # nb: clicking this button resets app! Open streamlit issue, as of 4.15.2023; cf. https://github.com/streamlit/streamlit/issues/4382
        st.markdown("*NB: Clicking the download button will reset the app after download!*")
        st.download_button(
            "Press to Download",
            csv,
            f"latincy-analysis-{create_timestamp()}.tsv",
            "text/csv",
            key="download-csv",
        )

with tab2:
    st.markdown("""
    ## About

    This demo produces a **Universal Dependencies (UD)** style tabular
    analysis of Latin text, showing the full linguistic annotation that
    LatinCy predicts for each token.

    ### What Is UD Parsing?

    [Universal Dependencies](https://universaldependencies.org/) is a
    framework for consistent grammatical annotation across languages.
    A UD parse assigns every token its part of speech, morphological
    features, and a labeled syntactic dependency linking it to its
    grammatical head. The result is a complete, machine-readable
    description of sentence structure.

    ### Output Columns

    The table follows the standard
    [CoNLL-U format](https://universaldependencies.org/format.html):

    | Column | CoNLL-U Field | Description |
    |--------|---------------|-------------|
    | **sent_id** | β€” | Sentence identifier (added by this demo) |
    | **token_id** | ID | Position of the token within its sentence (1-indexed) |
    | **form** | FORM | The surface form exactly as it appears in the text |
    | **lemma** | LEMMA | Dictionary headword (*e.g.* *tempus* for *tempora*) |
    | **upos** | UPOS | Universal part-of-speech tag (NOUN, VERB, ADJ, ADV, etc.) |
    | **xpos** | XPOS | Language-specific POS tag from the Latin tagset |
    | **feats** | FEATS | Morphological features: Case, Number, Gender, Tense, Mood, Voice, etc. |
    | **head** | HEAD | Index of the syntactic head token (0 = sentence root) |
    | **deprel** | DEPREL | Dependency relation to head (*nsubj*, *obj*, *obl*, *advmod*, *amod*, etc.) |
    | **ent_type** | β€” | Named entity type, if any (PER, LOC, NORP) |

    ### Model Details

    - **Model:** `la_core_web_lg` β€” LatinCy v3.8.0
    - **Training data:** Harmonized annotations from 6 UD Latin treebanks
      (Perseus, PROIEL, ITTB, LLCT, UDante, CIRCSE)
    - **Framework:** [spaCy](https://spacy.io/) v3

    ### Accuracy (v3.8.0, lg model)

    | Component | Score |
    |-----------|-------|
    | **POS (UPOS)** | 97.5% |
    | **Morphology** | 94.1% |
    | **Dependency UAS** | 89.1% |
    | **Dependency LAS** | 85.0% |
    | **Sentence segmentation** | 99.7% F1 |

    Scores are macro-averaged across the six treebank test sets.

    ### Notes

    - Output is limited to 500 tokens
    - TSV export follows [CoNLL-U format](https://universaldependencies.org/format.html)

    ### References

    - [Universal Dependencies](https://universaldependencies.org/) β€” annotation guidelines and treebank data
    - [UD Latin treebanks](https://universaldependencies.org/la/index.html) β€” all Latin UD resources
    - [GitHub: diyclassics/latincy](https://github.com/diyclassics/latincy) β€” LatinCy source and documentation
    """)