ATE api update 2

This commit is contained in:
Kikimanox
2022-11-11 15:45:00 +01:00
parent 7b7214905a
commit e9345ee887
4 changed files with 150 additions and 100 deletions
+12 -15
View File
@@ -3,26 +3,23 @@ FROM python:3.8
EXPOSE 5000 EXPOSE 5000
# Creates a non-root user with an explicit UID and adds permission to access the /app folder
# For more info, please refer to https://aka.ms/vscode-docker-python-configure-containers
RUN adduser -u 5678 --disabled-password --gecos "" appuser
USER appuser
# Keeps Python from generating .pyc files in the container # Keeps Python from generating .pyc files in the container
ENV PYTHONDONTWRITEBYTECODE=1
# Turns off buffering for easier container logging # Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1 ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
# RUN apk add --no-cache gcc musl-dev PATH="/home/appuser/.local/bin:${PATH}"
RUN pip install gunicorn
# Install pip requirements # Install pip requirements
COPY requirements.txt . COPY requirements.txt .
RUN python -m pip install -r requirements.txt RUN --mount=type=cache,target=/home/appuser/.cache/pip,uid=5678 python -m pip install --user -r requirements.txt
RUN python -c "import classla; classla.download('sl')"
WORKDIR /app # WORKDIR /app
COPY . /app COPY . /app
# Creates a non-root user with an explicit UID and adds permission to access the /app folder
# For more info, please refer to https://aka.ms/vscode-docker-python-configure-containers
RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
USER appuser
# During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug # During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "main:app"] CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--timeout", "128", "app.main:app"]
+129 -79
View File
@@ -1,108 +1,158 @@
import argparse
import csv
import os import os
import classla import string
classla.download('sl', logging_level='WARNING')
import classla
from lemmagen3 import Lemmatizer from lemmagen3 import Lemmatizer
def lem_adj(gender, wrd): # classla.download("sl", logging_level="WARNING")
lem = Lemmatizer() classla_nlp_pipeline = classla.Pipeline(
if gender == 'm': lang="sl",
lem.load_model(os.path.join('./model/lemmagen_models/kanon-adj-male.bin')) processors="tokenize,pos,lemma,depparse",
elif gender == 'f': tokenize_pretokenized=True,
lem.load_model(os.path.join('./model/lemmagen_models/kanon-adj-female.bin')) logging_level="WARNING",
elif gender == 'n': download_method=None
lem.load_model(os.path.join('./model/lemmagen_models/kanon-adj-neutral.bin')) )
form = lem.lemmatize(wrd) def _resolve_lemmagen_model_loc(model_name):
return form basedir = os.path.dirname(__file__)
return os.path.join(basedir, "/app/model/lemmagen_models", model_name)
_canon_lemmatizer = Lemmatizer()
_canon_lemmatizer.load_model(_resolve_lemmagen_model_loc("kanon.bin"))
canon_lemma = _canon_lemmatizer.lemmatize
def process_nlp_pipeline(lang, text): ADJ_LEMMATIZER_LOC_MAP = {
nlp = classla.Pipeline(lang=lang, processors='tokenize,pos,lemma', tokenize_pretokenized=True, logging_level='WARNING') ("m", "s"): _resolve_lemmagen_model_loc("kanon-adj-male.bin"),
doc = nlp(text) ("m", "p"): _resolve_lemmagen_model_loc("kanon-adj-male-plural.bin"),
return doc ("f", "s"): _resolve_lemmagen_model_loc("kanon-adj-female.bin"),
("f", "p"): _resolve_lemmagen_model_loc("kanon-adj-female-plural.bin"),
("n", "s"): _resolve_lemmagen_model_loc("kanon-adj-neutral.bin"),
("n", "p"): _resolve_lemmagen_model_loc("kanon-adj-neutral-plural.bin"),
}
_ADJ_LEMMATIZER_CACHE = {}
def lem_adj(gender, number, wrd):
lem_key = (gender, number)
if lem_key not in _ADJ_LEMMATIZER_CACHE:
assert lem_key in ADJ_LEMMATIZER_LOC_MAP
lemmatizer_model_loc = ADJ_LEMMATIZER_LOC_MAP[lem_key]
lemmatizer = Lemmatizer()
lemmatizer.load_model(lemmatizer_model_loc)
_ADJ_LEMMATIZER_CACHE[lem_key] = lemmatizer
lemmatizer = _ADJ_LEMMATIZER_CACHE[lem_key]
return lemmatizer.lemmatize(wrd)
def get_adj_msd(head, word): def get_adj_msd(head, word):
feats = head.feats feats = head.feats
feats_dict = {} feats_dict = {}
feats = feats.strip().split('|') feats = feats.strip().split("|")
for f in feats: for f in feats:
f = f.strip().split('=') f = f.strip().split("=")
feats_dict[f[0]] = f[1] feats_dict[f[0]] = f[1]
gender = feats_dict['Gender'] gender = feats_dict["Gender"]
#print(gender) if gender == "Masc" and len(word.xpos) == 6:
#gender = gender.strip().split('=')[1] msd = word.xpos[:-1] + "ny"
if gender == 'Masc' and len(word.xpos) == 6: elif gender == "Masc" and len(word.xpos) == 7:
msd = word.xpos[:-1]+'ny' msd = word.xpos[:-1] + "y"
elif gender == 'Masc' and len(word.xpos) == 7: elif gender == "Fem":
msd = word.xpos[:-1]+'y' msd = word.xpos[:-1] + "n"
elif gender == 'Fem': elif gender == "Neut":
msd = word.xpos[:-1]+'n' msd = word.xpos[:-1] + "n"
elif gender == 'Neut':
msd = word.xpos[:-1]+'n'
else: else:
msd = None # msd = None
msd = "qqqqqq" # hacky but it means that adverbs are just copied over to the canonical form
return msd return msd
def _is_single_acronym(term):
def subfinder(mylist, pattern): # (single word, all uppercase and length less than 5 characters)
matches = []
for i in range(len(mylist)):
if mylist[i].text.lower() == pattern[0] and [t.text.lower() for t in mylist[i:i+len(pattern)]] == pattern:
matches.append(mylist[i:i+len(pattern)])
return matches
def find_canon(term):
head = None
pre = []
post = []
for word in term.words:
if word.upos == 'NOUN' or word.upos == 'PROPN':
head = word
break
if head is None:
if len(term.words) == 1: if len(term.words) == 1:
head2 = term.words[0] word = term.words[0].text
lem = Lemmatizer() return len(word) < 5 and word.isupper()
lem.load_model(os.path.join('./model/lemmagen_models/kanon.bin')) return False
head_form = lem.lemmatize(head2.text.lower())
return head_form
else:
return ' '.join([w.text for w in term.words]) # just return the input because we do not cover such case
else:
for word in term.words:
if word.id < head.id:
pre.append(word)
elif word.id > head.id:
post.append(word)
def _join_term_words(term):
return " ".join([w.text for w in term.words])
def _process_pre(pre, head, gender, number):
canon = [] canon = []
for el in pre: for el in pre:
msd = get_adj_msd(head, el) msd = get_adj_msd(head, el)
if msd is None: if msd[0] == "A":
canon.append(el.lemma.lower()) form = lem_adj(gender, number, el.text.lower())
canon.append(form)
else: else:
if msd[0] == 'A' and msd[3] == 'm': canon.append(el.lemma.lower())
form = lem_adj('m', el.text.lower()) return canon
canon.append(form)
elif msd[0] == 'A' and msd[3] == 'f':
form = lem_adj('f', el.text.lower())
canon.append(form)
elif msd[0] == 'A' and msd[3] == 'n':
form = lem_adj('n', el.text.lower())
canon.append(form)
lem = Lemmatizer() def find_canon(term):
lem.load_model(os.path.join('./model/lemmagen_models/kanon.bin')) if _is_single_acronym(term):
head_form = lem.lemmatize(head.text.lower()) return term.words[0].text
head = None
pre = []
post = []
for word in term.words:
if word.head == 0:
head = word
elif head is None:
pre.append(word)
else:
post.append(word)
## special case where all words are proper nouns and each word is canonized independently
if all(w.upos == "PROPN" for w in term.words):
canon_name = [canon_lemma(w.text) for w in term.words]
return " ".join(canon_name)
if head is None:
if len(term.words) == 1:
head2 = term.words[0]
return canon_lemma(head2.text.lower())
else:
# just return the input because we do not cover such case
return _join_term_words(term)
if head.upos == "VERB": # if the term is not a noun phrase
# just return the input because we do not cover such case
return _join_term_words(term)
if head.upos == "ADJ":
if len(term.words) == 1: # for single word adjectives, return male form
return lem_adj("m", "s", term.words[0].text.lower())
else:
# just return the input because we do not cover such case
return _join_term_words(term)
gender = head.xpos[2]
number = head.xpos[3]
ending = head.lemma[-1]
if gender == "f" and number == "p" and ending in "ie": # sani, hlače
canon = _process_pre(pre, head, gender, number)
canon.append(head.lemma)
elif gender == "m" and number == "p" and ending == "i": # možgani
canon = _process_pre(pre, head, gender, number)
canon.append(head.lemma)
elif gender == "n" and number == "p" and ending == "a": # vrata
canon = _process_pre(pre, head, gender, number)
canon.append(head.lemma)
else:
canon = _process_pre(pre, head, gender, "s")
head_form = canon_lemma(head.text.lower())
canon.append(head_form) canon.append(head_form)
for el in post: for el in post:
canon.append(el.text) canon.append(el.text)
return ' '.join(canon) return " ".join(canon)
def process(forms): def process(forms):
text = '\n'.join(forms) text = "\n".join(forms)
doc = process_nlp_pipeline('sl', text) doc = classla_nlp_pipeline(text)
return [find_canon(sent) for sent in doc.sentences] canonical_forms = []
for term in doc.sentences:
try:
canonical_form = find_canon(term)
except Exception:
canonical_form = _join_term_words(term)
canonical_forms.append(canonical_form)
return canonical_forms
+6 -4
View File
@@ -3,8 +3,9 @@ import torch
import torch.nn.functional as F import torch.nn.functional as F
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
from utils import * from .utils import *
from canonical_utils import * from .canonical_utils import *
from flask import Flask, request, jsonify from flask import Flask, request, jsonify
from transformers import AutoTokenizer, AutoModelForTokenClassification from transformers import AutoTokenizer, AutoModelForTokenClassification
@@ -12,12 +13,13 @@ from transformers import AutoTokenizer, AutoModelForTokenClassification
app = Flask(__name__) app = Flask(__name__)
label_list=["n", "B-T", "T"] label_list=["n", "B-T", "T"]
tokenizer = AutoTokenizer.from_pretrained('./model/term_extractor/') tokenizer = AutoTokenizer.from_pretrained('/app/model/term_extractor/')
model = AutoModelForTokenClassification.from_pretrained('./model/term_extractor/', num_labels=len(label_list)).to(device) model = AutoModelForTokenClassification.from_pretrained('/app/model/term_extractor/', num_labels=len(label_list)).to(device)
@app.route('/predict',methods=['POST']) @app.route('/predict',methods=['POST'])
def predict(): def predict():
frame = read_conll(request.files['file']) frame = read_conll(request.files['file'])
# print(frame)
sequences = [' '.join(x) for x in frame.word] sequences = [' '.join(x) for x in frame.word]
lemma, pos, msd = frame.lemma, frame.pos, frame.msd lemma, pos, msd = frame.lemma, frame.pos, frame.msd
preds = [] preds = []
+2 -1
View File
@@ -3,5 +3,6 @@ Flask==2.2.2
lemmagen3==3.3.2 lemmagen3==3.3.2
numpy==1.23.0 numpy==1.23.0
pandas==1.3.5 pandas==1.3.5
torch torch==1.12.0
transformers==4.20.1 transformers==4.20.1
gunicorn==20.1.0