-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
391 compare student speech to slides text #400
Conversation
nltk.download('punkt') | ||
nltk.download('stopwords') | ||
russian_stop_words = stopwords.words('russian') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Оберните эти три строки в функцию и вызывайте там, где нужны russian_stop_words
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Это действительно необходимо? Если обернуть это в функцию, то скачивание punkt и stopwords будет происходить каждый раз, когда будет вызываться функция normalize_text
В общем случае это будет 2 * n вызовов, где n -- число слайдов презентации
# Функция нормализации текста | ||
def normalize_text(text: list) -> list: | ||
table = str.maketrans("", "", string.punctuation) | ||
morph = pymorphy2.MorphAnalyzer() | ||
|
||
# Замена знаков препинания на пустые строки, конвертация в нижний регистр и обрезание пробелов по краям | ||
text = list(map(lambda x: x.translate(table).lower().strip(), text)) | ||
# Замена цифр и слов не на русском языке на пустые строки | ||
text = list(map(lambda x: re.sub(r'[^А-яёЁ\s]', '', x), text)) | ||
# Удаление пустых строк | ||
text = list(filter(lambda x: x.isalpha(), text)) | ||
# Приведение слов к нормальной форме | ||
text = list(map(lambda x: morph.normal_forms(x)[0], text)) | ||
# Очистка от стоп-слов | ||
text = list(filter(lambda x: x not in RussianStopwords().words, text)) | ||
return text | ||
|
||
|
||
def delete_punctuation(text: str) -> str: | ||
return text.translate(str.maketrans('', '', string.punctuation + "\t\n\r\v\f")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Вынесите в утилиты - кажется, такие функции могут нам пригодиться и в других местах / критериях
for skip_slide in self.parameters['skip_slides']: | ||
if skip_slide.lower() in delete_punctuation(current_slide_text).lower(): | ||
logger.info(f"Слайд №{current_slide_index + 1} пропущен") | ||
skip = True | ||
break | ||
if skip: | ||
continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Вынесите в отдельный фильтрующий метод, возвращающий true/false - заодно так избавимся от танцев со skip
def get_ngrams(text, n): | ||
tokens = word_tokenize(text.lower()) | ||
n_grams = ngrams(tokens, n) | ||
return [' '.join(gram) for gram in n_grams] | ||
|
||
def calculate_similarity(text1, text2, n_values, weights=None): | ||
similarities = [] | ||
for n in n_values: | ||
ngrams_text1 = get_ngrams(text1, n) | ||
ngrams_text2 = get_ngrams(text2, n) | ||
|
||
counter_text1 = Counter(ngrams_text1) | ||
counter_text2 = Counter(ngrams_text2) | ||
|
||
intersection = set(ngrams_text1) & set(ngrams_text2) | ||
|
||
if len(ngrams_text1) == 0 or len(ngrams_text2) == 0: | ||
similarities.append(0.000) | ||
else: | ||
similarity = sum( | ||
min(counter_text1[ngram], counter_text2[ngram]) for ngram in intersection) / max( | ||
len(ngrams_text1), len(ngrams_text2)) | ||
similarities.append(similarity) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Вынесите в отдельные методы (возможно, статические) - так их проще искать/отслеживать/модифицировать, нежели в качестве вложенных функций
- Кажется, get_ngrams можно сделать lambda-функцией (занимает меньше места и лаконичнее, а используется только в calculate_similarity)
word2vec = [] | ||
n_grams = [] | ||
|
||
for current_slide_index in range(len(audio.audio_slides)): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Дальше по методу очень часто повторяется операция " ".join(x)
для current_slide_speech/current_slide_text - возможно, стоит сделать это один раз в начале?
n_values = [2, 3, 4] # Список значений n для анализа | ||
weights = [0.34, 0.33, 0.33] # Веса для каждой метрики (если нужно) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Задаваться вопросом "а откуда куда зачем такие числа" - не буду, как и в целом вдаваться в логику анализа (тут оставляю на вас), но такие параметры, кажется, стоит вынести в поля объекта (и, возможно, сделать параметрами - вдруг метрики или список значений захотим поменять или завести несколько версий критерия?)
app/db_versioning/versions.py
Outdated
9: 'PrimitivePack' | ||
9: 'PrimitivePack', | ||
10: 'ComparisonPack' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
тут не трогайте -- это столетнее старье, когда мы перевели наборы с числовых на строковые ID
requirements.txt
Outdated
scikit-learn | ||
gensim |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Установите версии
# TF-IDF | ||
if len(current_slide_text) == 0 or len(current_slide_speech) == 0: | ||
tf_idf.append(0.000) | ||
else: | ||
corpus = [" ".join(current_slide_speech), " ".join(current_slide_text)] | ||
vectorizer = TfidfVectorizer() | ||
X = vectorizer.fit_transform(corpus) | ||
cosine_sim = cosine_similarity(X[0], X[1]) | ||
similarity = cosine_sim[0][0] | ||
tf_idf.append(round(similarity, 3)) | ||
|
||
# word2vec | ||
tokens_speech = word_tokenize(" ".join(current_slide_speech)) | ||
tokens_slide = word_tokenize(" ".join(current_slide_text)) | ||
|
||
if len(current_slide_speech) == 0 or len(current_slide_text) == 0: | ||
word2vec.append(0.000) | ||
else: | ||
sentences = [tokens_speech, tokens_slide] | ||
model = Word2Vec(sentences, min_count=1) | ||
similarity = model.wv.n_similarity(tokens_speech, tokens_slide) | ||
word2vec.append(round(similarity, 3)) | ||
|
||
# n-grams |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
очень хочется вынести отдельные методы сравнения в отдельные соответствующие функции/методы - предлагаю выделить для этого даже целый модуль-директорию (или как минимум файл)
return cls._instances[cls] | ||
|
||
|
||
class RussianStopwords(metaclass=Singleton): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Поправил работу с nltk.download - вынес с стартовый модуль и сделал volume между контейнерами, использующими nltk (чтобы каждый из них не загружал нужные словари каждый в себя)
Изменения будут проверены в рамках #406 |
No description provided.