Многие последующие задачи НЛП (классификация текста, анализ настроений, вывод о намерениях и т. д.) основаны на первом шаге — преобразовании текстовых строк в векторы признаков предложений.
Есть два способа получить векторы предложений в НЛП:
(1) Вектор предложения получается путем постобработки вектора слов;
(2) Получить вектор предложения напрямую
GloVe не будет представлен здесь
1. Постобработка вектора слов для получения вектора предложения
Все мы знаем, что предложения состоят из слов, а технология вектора слов просто преобразует одно слово в вектор фиксированной размерности.
Так как же получить вектор предложения, состоящего из нескольких слов?
В этой статье будут представлены следующие неконтролируемые методы генерации векторов предложений из векторов слов, а именно: метод накопления, метод среднего, метод взвешенного среднего TF-IDF и метод встраивания SIF.
1.1 Метод накопления
Накопление — самый простой способ получить векторы предложений
Допустим, есть такой текст:я очень счастлив
НЛП, обрабатывающий фрагмент текста, сначала должен сегментировать фрагмент текста и удалить стоп-слова.После удаления стоп-слов приведенный выше текст может получить следующее расстояние до слова:
["Я очень счастлив"]
В этой статье используется модель Word2vec в gensim Python для получения векторов слов, и могут быть получены следующие векторы слов из приведенных выше слов (для более четкой визуализации для демонстрации используются 5-мерные векторы слов)
i: [[-0,46499524 -2,8825798 1,1845024 -1,6874554 -0,05758076]]
Очень: [[-2,26874 0,99428487 -0,9092457 -0,67786723 4,244918 ]]
счастливый: [[-1,0627153 -0,7416505 0,41102988 -0,39201248 0,6933297 ]]
Метод накопления заключается в наложении векторов слов всех непрерывных слов в предложении.Если в предложении есть n непрерывных слов, векторы слов предложения получаются следующими способами:
Vsentence = Vword1 + Vword2 + ... + Vwordn
По этому методу его можно получить»я очень счастливВектор предложения ":
Vsentence = V1+ Vn2+ V3
Вектор предложения: [[-3,79645047 -2,62994546 0,68628651 -2,75733513 4,8806668 ]]
Анализ: не может выразить общую семантику предложения. Когда добавление нескольких векторов слов равно добавлению других слов, выражаемые значения различны.
Общий код:
import numpy as npfrom gensim.models.word2vec import Word2Vecimport jiebafrom sklearn.model_selection import train_test_splitdef readgh(path): res=[] label=[] f=open(path, "r", encoding='utf-8-sig') for line in f: if int(line.split(' ', 1)[0])==-1 : label.append([1,0]) else: label.append([0,1]) res.append(line.split(' ',1)[1]) return res,labeldef divide(result,label): x_train, x_test, y_train, y_test = train_test_split(result,label, test_size=0.3, random_state=666) return x_train, x_test, y_train, y_testdef fenci1(data,stopword): result = [] for text in data: word_list = ' '.join(jieba.cut(text)).split(" ") result.append(list(filter(lambda x : x not in stopword, word_list))) return resultdef buildWordVector(sentence,size,w2v_model): vec = np.zeros(size).reshape((1,size)) count = 0. for word in sentence: try: vec += w2v_model[word].reshape((1,size)) count += 1 except KeyError: continue if count != 0: vec /= count return vecdef buildWordVector1(sentence,size,w2v_model): vec =np.zeros(size).reshape((1,size)) data_vec=np.zeros(size).reshape((1,size)) for word in sentence: print(word) vec=w2v_model[word].reshape((1,size)) print(vec) data_vec +=vec print("句子向量:") print(data_vec) return data_vec#计算词向量def get_train_vecs(x_train,x_test): n_dim = 5 #Initialize model and build vocab w2v_model = Word2Vec(size = n_dim,min_count = 10) w2v_model.build_vocab(x_train) #在训练集训练词向量模型 w2v_model.train(x_train,total_examples = w2v_model.corpus_count,epochs = w2v_model.iter) #生成训练集词向量 train_vecs = np.concatenate([buildWordVector(line,n_dim,w2v_model) for line in x_train]) #保存训练集词向量文件 print("Train word_vector shape:",train_vecs.shape) #在测试集训练词向量模型 w2v_model.train(x_test,total_examples = w2v_model.corpus_count,epochs = w2v_model.iter) #生成测试集词向量 test_vecs = np.concatenate([buildWordVector(line,n_dim,w2v_model) for line in x_test]) #保存测试集词向量文件 print("Test word_vector shape:",test_vecs.shape) #保存词向量模型 w2v_model.save('D:\\学习资料\\项目\\邮件网关\\zh_cnn_text_classify-master\\test_model.pkl') return train_vecs,test_vecstext=["我很开心"]with open('D:\\学习资料\\项目\\情感分析\\stopwords.txt', encoding='utf8') as f: stopword = f.read().splitlines()result = fenci1(text, stopword)model=Word2Vec.load("D:\\学习资料\\项目\\邮件网关\\zh_cnn_text_classify-master\\test_model.pkl")test_vec=test_vecs = np.concatenate([buildWordVector1(line,5,model) for line in result])print(test_vec)
Метод накопления:
def buildWordVector1(sentence,size,w2v_model): vec =np.zeros(size).reshape((1,size)) data_vec=np.zeros(size).reshape((1,size)) for word in sentence: print(word) vec=w2v_model[word].reshape((1,size)) print(vec) data_vec +=vec print("句子向量:") print(data_vec) return data_vec
1.2 Средний метод
Метод усреднения аналогичен методу накопления, он также должен сложить все векторы непрерывных слов в предложении, но в конце наложенный вектор нужно разделить на количество непрерывных слов.Вектор слова предложения получается следующими способами:
Vsentence = (Vword1 + Vword2 + ... + Vwordn) / n
Вектор предложения:[[-1.26548349 -0.87664849 0.22876217 -0.91911171 1.62688893]]
Код:
def buildWordVector(sentence,size,w2v_model): vec = np.zeros(size).reshape((1,size)) count = 0. for word in sentence: try: vec += w2v_model[word].reshape((1,size)) count += 1 except KeyError: continue if count != 0: vec /= count return vec
1.3 Средневзвешенный метод TF-IDF
Метод взвешенного среднего TF-IDF должен использовать технологию TF-IDF, которая является широко используемой технологией обработки текста. Модель TF-IDF часто используется для оценки важности слова в документе и часто используется в области технологий поиска и поиска информации. Значение TF-IDF слова пропорционально его частоте в документе и обратно пропорционально его частоте в корпусе. TF-IDF получается путем умножения частоты термина TF (частота термина) и обратной частоты документа IDF (обратная частота документа).
Метод взвешивания TF-IDF должен не только получить вектор слов для каждого непрерывного слова в предложении, но также должен получить значение TFIDF для каждого непрерывного слова в предложении. Часть TF каждого непрерывного слова легко вычислить.Часть IDF зависит от того, какой корпус использует пользователь.Если это для поиска запроса, то корпус, соответствующий части IDF, представляет собой все предложения запроса;если это сделать самоподобная кластеризация текста, то корпус, соответствующий части IDF, представляет собой целое предложение, подлежащее классификации. Затем вектор взвешенного предложения TF-IDF получается следующими средствами_:_
Vsentence = TFIDFword1*Vword1+TFIDFword2 *Vword2 +... +TFIDFwordn *Vwordn
Если предположить, что фраза «Я очень счастлив» означает поиск запроса, то корпус, соответствующий вычислению IT-IDF, состоит из всех предложений запроса. Если всего имеется 100 предложений-запросов, то 60 предложений-запросов содержат слово I, 65 предложений-запросов содержат слово очень и 7 предложений-запросов содержат слово счастливый. Тогда количество TF-IDF для каждого непрерывного слова в этом предложении будет следующим:
Я: 1/(1+1+1) * log(100/(1+60)
очень: 1/(1+1+1) * log(100/(1+65)
счастливый: 1/(1+1+1) * log(100/(1+7)
Таким образом, взвешенный вектор данных IT-IDF для этого предложения:
Vsentence = TFIDF*V+ TFIDF*V + … + TFIDFg*V
Код TF-IDF:
s1_words=['今天','上','NLP','课程']s2_words=['今天','的','课程','有','意思']s3_words=['数据','课程','也','有','意思']data_set = [s1_words,s2_words,s3_words]from collections import Counterfrom collections import defaultdictword_dict = ['今天','上','NLP','的','课程','有','意思','数据','也']N=len(data_set) #文档总数In_doc=defaultdict(int) #表示含有该单词的文档数目for word in word_dict: for doc in data_set: In_doc[word]+=1tfidfs_all=[]for doc in data_set: n=len(doc) #文档中的单词总数 cont=Counter(doc) tfidf=[] for word in word_dict: if word not in cont: tfidf.append(0) else: tf=cont[word]/n idf=In_doc[word]/N tfidf.append(tf*idf) tfidfs_all.append(tfidf)print(tfidfs_all)
1.4 Метод внедрения SIF
Метод средневзвешенного значения ISF аналогичен методу средневзвешенного значения TF-IDF.Этот метод может хорошо получить вектор предложения всего предложения в соответствии с каждым вектором слова. Метод встраивания SIF требует использования анализа основных компонентов и оценки вероятности каждого слова.Конкретная операция метода встраивания SIF заключается в следующем:
Прежде всего, на вход всего алгоритма входят: (1) вектор слов каждого слова (2) все предложение в корпусе (3) регулируемый параметр a (4) оценочная вероятность каждого слова
Результат всего алгоритма:
вектор предложения
Конкретные шаги алгоритма:
(1) Получить начальный вектор предложения
Пройдите каждое предложение в корпусе, предполагая, что текущее предложение равно s, и получите начальный вектор предложения текущего предложения s по следующей формуле расчета:
То есть процесс взвешенного усреднения, каждый вектор слов умножается на коэффициент a/(a+p(w) и затем накладывается, а конечный вектор суперпозиции представляет собой количество слов в предложении s. Для регулируемого параметра a , автор использует в статье 0,001 и 0,0001. 2. P(w) — униграммная вероятность слова во всем корпусе, то есть частота слов слова w равна сумме частот слов всех слов в корпусе.
(2) Расчет главного компонента
Выполните анализ основных компонентов для всех векторов предварительных предложений, чтобы вычислить первый главный компонент u всех векторов предварительных предложений.
(3) Получить вектор целевого предложения
Посредством следующего вычисления исходный вектор предложений подвергается вторичной обработке для получения целевого вектора предложений.
Я просто грубо прогнал код, который в основном содержит английские предложения и слова. Я изучу его позже, если у меня будет возможность реального боя. Здесь я буду лениться...
2 Получите вектор предложения напрямую
Один из самых больших недостатков описанного выше метода заключается в том, что он игнорирует влияние порядка в середине слов на все предложение.
Обычно используемые методы прямого получения векторов предложений — Doc2Vec и Bert.
2.1 doc2vec
Doc2vec — это усовершенствование, основанное на Word2vec, которое учитывает не только семантику между словами, но и порядок слов. Модель Doc2Vec Doc2Vec имеет две модели, а именно:
Модель распределенной памяти векторов абзацев (PV-DM: модель распределенной памяти векторов абзацев)
PV-DBOW: версия вектора абзаца с распределенным пакетом слов Модель PV-DM предсказывает вероятность появления слова с учетом контекста и вектора документа.
Конкретные принципы см. в других дополнительных материалах, которые здесь не представлены.
Модель DM Doc2vec очень похожа на CBOW Word2vec, а модель DBOW очень похожа на Skip-gram Word2vec. Doc2Vec обучает векторы одинаковой длины для абзацев разной длины; векторы слов разных абзацев не являются общими; векторы слов, обученные в обучающем наборе, имеют одинаковое значение и могут использоваться совместно.
Код:
#coding:utf-8import jiebaimport sysimport gensimimport sklearnimport numpy as npfrom gensim.models.doc2vec import Doc2Vec, LabeledSentenceTaggededDocument = gensim.models.doc2vec.TaggedDocument#进行中文分词def cut_files(path): f = open(path, "r", encoding='utf-8-sig') res=[] text=[] for line in f: res.append(line.split(' ', 1)[1]) for line in res: curLine =' '.join(list(jieba.cut(line))) text.append(curLine) return text#读取分词后的数据并打标记,放到x_train供后续索引,但是这样的话占用很大内存(这种小数据量使用)def get_datasest(data): x_train = [] for i, text in enumerate(data): word_list = text.split(' ') l = len(word_list) word_list[l - 1] = word_list[l - 1].strip() document = TaggededDocument(word_list, tags=[i]) x_train.append(document) return x_train#模型训练def train(x_train, size=50): model_dm = Doc2Vec(x_train, min_count=5, window=3, size=size, sample=1e-3, negative=5, workers=4) model_dm.train(x_train, total_examples=model_dm.corpus_count, epochs=70) model_dm.save('model_dm_doc2vec') return model_dm#实例def test(): model_dm = Doc2Vec.load("model_dm_doc2vec") test_text = ['我不想上班','我喜欢你'] text=[] for line in test_text: curLine =' '.join(list(jieba.cut(line))) text.append(curLine) inferred_vector_dm = model_dm.infer_vector(text) sims = model_dm.docvecs.most_similar([inferred_vector_dm], topn=10) return simsif __name__ == '__main__': path="D:\\学习资料\\项目\\情感分析\\neg.txt" path1="D:\\学习资料\\项目\\情感分析\\pos.txt" text1=cut_files(path) text2=cut_files(path1) text=text1+text2 x_train=get_datasest(text) model_dm = train(x_train) sims = test() for count, sim in sims: sentence = x_train[count] words = '' for word in sentence[0]: words = words + word + ' ' print (words, sim, len(sentence[0]))
2.2 Bert
Самый большой недостаток использования векторов слов для постобработки векторов предложений с помощью различных методов заключается в том, что они не могут понять семантику контекста.Одно и то же слово может иметь разные значения в разных контекстах, но оно будет представлено как одно и то же слово. что отрицательно влияет на семантическое вычисление многозначных предложений. Векторы предложений, сгенерированные Бертом, не имеют вышеуказанных дефектов. Векторы одного и того же слова в разных контекстах различны, а многозначные слова генерируют разные векторы из-за разных контекстов. Таким образом, векторы предложений, созданные Бертом, действительно можно использовать в семантических вычислениях. Преимущество BERT для генерации векторов предложений заключается не только в том, что он может понимать смысл предложений, но также устраняет ошибку, вызванную взвешиванием векторов слов.
Код:
from bert_serving.client import BertClientimport numpy as npdef main(): bc = BertClient() doc_vecs = bc.encode(['今天天空很蓝,阳光明媚', '今天天气好晴朗', '现在天气如何', '自然语言处理', '机器学习任务']) print(len(doc_vecs[0]))if __name__ == '__main__': main()
Теперь общий метод bert в основном заключается в том, чтобы загрузить китайскую модель на github для вызова, а затем получить вектор слова.
BERT использует модель трансформатора и использует часть преобразователя, содержащую кодировщик. Так как же это решает проблему, состоящую в том, что языковые модели могут использовать информацию только в одном направлении? Ответ заключается в том, что его предобучение представляет собой не обычную языковую модель, а модель языка маски.
Модель BERT требует фиксированной длины последовательности.например 128. Если этого недостаточно, заполнение будет выполнено позже, в противном случае лишний Token будет перехвачен, чтобы гарантировать, что вход представляет собой последовательность Token фиксированной длины.
У меня будет время изучить берта позже