過去了17天蹦疑,按照學(xué)習(xí)計(jì)劃來說,已經(jīng)嚴(yán)重超時(shí)了萨驶。
主要的問題是在數(shù)據(jù)預(yù)處理部分歉摧。
Kaggle的IMDB情感分析任務(wù)其實(shí)很簡(jiǎn)單,train文件用于訓(xùn)練腔呜,test文件用于測(cè)試叁温。
步驟
- 整合train和test(就是說所有語料庫(kù))生成詞袋或詞向量模型。也可以下載已有的word2vec或是glove詞向量模型核畴。
- 生成的詞向量模型就是將每一個(gè)詞向量化膝但,方便后面的計(jì)算。依據(jù)生成的詞向量模型對(duì)train和test語料進(jìn)行向量化谤草。
- 把向量化的train數(shù)據(jù)和標(biāo)簽輸入分類模型中進(jìn)行預(yù)測(cè)跟束,完成模型訓(xùn)練。
- 評(píng)估模型丑孩,并對(duì)test進(jìn)行預(yù)測(cè)冀宴。
具體實(shí)現(xiàn)
1. 數(shù)據(jù)集
數(shù)據(jù)集是tsv格式數(shù)據(jù),說白了是分成了5類:
0 - negative
1 - somewhat negative
2 - neutral
3 - somewhat positive
4 - positive
我們先讀取一下看看温学。TSV文件和CSV的文件的區(qū)別是:前者使用\t作為分隔符略贮,后者使用,作為分隔符。
可以看到總共156060條記錄仗岖,其中打2分的數(shù)據(jù)最多逃延,說明大家都還是很中庸啊。
import pandas as pd
train_data=pd.read_csv(r'C:\Users\jwc19\Desktop\sentiment-analysis-on-movie-reviews\train.tsv',sep='\t',header=0)
test_data=pd.read_csv(r'C:\Users\jwc19\Desktop\sentiment-analysis-on-movie-reviews\test.tsv',sep='\t',header=0)
train_data.head()
train_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 156060 entries, 0 to 156059
Data columns (total 4 columns):
PhraseId 156060 non-null int64
SentenceId 156060 non-null int64
Phrase 156060 non-null object
Sentiment 156060 non-null int64
dtypes: int64(3), object(1)
memory usage: 4.8+ MB
train_data.describe()
以上是數(shù)據(jù)的描述性統(tǒng)計(jì)結(jié)果箩帚,當(dāng)然真友,還可以做一下可視化,這些參考kaggle的泰坦尼克號(hào)任務(wù)操作紧帕。
2. 構(gòu)建word2vec
接下來是要對(duì)語料庫(kù)向量化盔然,這其實(shí)就是一種對(duì)文本特征的抽取。
文中提到了兩種方式:BOW(詞袋)和Word2Vec是嗜,相比而言愈案,Word2Vec所包含的信息更多,而且gensim庫(kù)已經(jīng)打包好了鹅搪,在這里使用word2vec站绪。
在2017年新的特征抽取算法transformer將會(huì)橫掃一切……
在完成詞向量抽取后,將進(jìn)行分類器的訓(xùn)練丽柿。
在這里恢准,我們將train和test數(shù)據(jù)集進(jìn)行合并魂挂,構(gòu)造出一個(gè)維度為200的詞向量模型,使用gensim庫(kù)進(jìn)行構(gòu)建馁筐。
# 合并test和train的數(shù)據(jù)涂召,用于訓(xùn)練詞向量模型
newDf = pd.concat([test_data["Phrase"], train_data["Phrase"]], axis=0)
newDf.describe()
newDf.to_csv(r"C:\Users\jwc19\Desktop\sentiment-analysis-on-movie-reviews\wordEmbdiing.txt", index=False)
在這里使用gensim的word2vec api來訓(xùn)練模型
主要參數(shù)介紹如下:
sentences:我們要分析的語料,可以是一個(gè)列表敏沉,或者從文件中遍歷讀出(word2vec.LineSentence(filename) )果正。
size:詞向量的維度,默認(rèn)值是100盟迟。這個(gè)維度的取值一般與我們的語料的大小相關(guān)秋泳,如果是不大的語料,比如小于100M的文本語料攒菠,則使用默認(rèn)值一般就可以了迫皱。如果是超大的語料,建議增大維度要尔。
window:即詞向量上下文最大距離舍杜,window越大,則和某一詞較遠(yuǎn)的詞也會(huì)產(chǎn)生上下文關(guān)系赵辕。默認(rèn)值為5既绩,在實(shí)際使用中,可以根據(jù)實(shí)際的需求來動(dòng)態(tài)調(diào)整這個(gè)window的大小还惠。如果是小語料則這個(gè)值可以設(shè)的更小饲握。對(duì)于一般的語料這個(gè)值推薦在[5;10]之間蚕键。
sg:即我們的word2vec兩個(gè)模型的選擇了救欧。如果是0, 則是CBOW模型锣光;是1則是Skip-Gram模型笆怠;默認(rèn)是0即CBOW模型。
hs:即我們的word2vec兩個(gè)解法的選擇了誊爹。如果是0蹬刷, 則是Negative Sampling;是1的話并且負(fù)采樣個(gè)數(shù)negative大于0频丘, 則是Hierarchical Softmax办成。默認(rèn)是0即Negative Sampling。
negative:即使用Negative Sampling時(shí)負(fù)采樣的個(gè)數(shù)搂漠,默認(rèn)是5迂卢。推薦在[3,10]之間。這個(gè)參數(shù)在我們的算法原理篇中標(biāo)記為neg。
cbow_mean:僅用于CBOW在做投影的時(shí)候而克,為0靶壮,則算法中的xw為上下文的詞向量之和,為1則為上下文的詞向量的平均值拍摇。在我們的原理篇中亮钦,是按照詞向量的平均值來描述的。個(gè)人比較喜歡用平均值來表示xw,默認(rèn)值也是1,不推薦修改默認(rèn)值充活。
min_count:需要計(jì)算詞向量的最小詞頻。這個(gè)值可以去掉一些很生僻的低頻詞蜡娶,默認(rèn)是5混卵。如果是小語料,可以調(diào)低這個(gè)值窖张。
iter:隨機(jī)梯度下降法中迭代的最大次數(shù)幕随,默認(rèn)是5。對(duì)于大語料宿接,可以增大這個(gè)值赘淮。
alpha:在隨機(jī)梯度下降法中迭代的初始步長(zhǎng)。算法原理篇中標(biāo)記為η睦霎,默認(rèn)是0.025梢卸。
min_alpha: 由于算法支持在迭代的過程中逐漸減小步長(zhǎng),min_alpha給出了最小的迭代步副女。
from nltk.corpus import stopwords
StopWords = stopwords.words('english')
import logging
import gensim
from gensim.models import word2vec
# 設(shè)置輸出日志
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
sentences=word2vec.LineSentence(r"C:\Users\jwc19\Desktop\sentiment-analysis-on-movie-reviews\wordEmbdiing.txt")
sentences=list(sentences)
for idx,sentence in enumerate(sentences):
sentence = [w for w in sentence if w not in StopWords]
sentences[idx]=sentence
print(type(sentences))
model=gensim.models.Word2Vec(sentences,size=200, min_count=2,sg=1,iter=2)
model.wv.save_word2vec_format("./word2Vec02" + ".bin", binary=True)
搞定之后運(yùn)行模型蛤高,還可以看看效果
model=gensim.models.KeyedVectors.load_word2vec_format("word2Vec.bin",binary=True)
model["flower"]
array([-0.2378106 , -0.00272736, 0.31298155, 0.03572753, -0.32978794,
0.5877859 , 0.24954697, 0.10183911, 0.30661255, 0.280979 ,
0.04722883, -0.01303975, 0.08539272, 0.04781984, -0.17838825,
0.13571365, -0.07219279, 0.04345001, -0.10493791, 0.05438785,
0.33817822, 0.15342301, -0.01376961, 0.5400121 , 0.41749138,
0.0906916 , 0.04341062, -0.15571249, -0.17380357, -0.1934123 ,
-0.02405222, -0.22066571, -0.14180358, -0.09150579, -0.2944634 ,
0.07576216, -0.1660684 , 0.20156585, 0.1215609 , 0.5412449 ,
-0.1711439 , 0.3214155 , -0.3667486 , -0.18460636, -0.15220495,
-0.07949002, 0.22074243, -0.04971108, -0.09505122, 0.29928744,
0.03575212, -0.13769385, 0.18068919, 0.31546128, 0.10954601,
0.18582347, -0.04675937, -0.03966061, 0.20546672, -0.04146346,
-0.0196472 , 0.0578943 , 0.20681728, -0.04692319, -0.1698708 ,
-0.09567603, -0.11117092, 0.30465436, -0.04794674, -0.06839596,
-0.02868674, -0.20524485, 0.0295146 , -0.01159863, -0.15453497,
0.48093846, -0.3897168 , 0.02332748, 0.0439175 , 0.23415217,
-0.06639539, -0.03457333, -0.2735414 , 0.03905383, 0.038656 ,
0.23679397, -0.33047786, 0.31122783, 0.00199052, -0.30952674,
-0.10884447, -0.40330866, 0.25768963, -0.16997696, 0.12618165,
-0.08964632, -0.01782297, 0.12821278, 0.00424662, -0.11926408,
0.04985361, -0.16177899, -0.06548072, -0.018849 , 0.07923622,
-0.00496559, -0.0372107 , 0.05142358, -0.3297481 , 0.23669559,
0.16632096, 0.12055472, 0.36679494, 0.11643603, 0.05669545,
-0.26389235, -0.06538889, 0.09600764, -0.15645082, -0.00284773,
0.12941402, 0.08277974, 0.09082151, -0.12873018, 0.13429202,
-0.00188877, -0.10478543, 0.20682792, -0.18579291, -0.18376978,
-0.15438502, 0.6078415 , -0.05618986, -0.00372298, -0.34480548,
-0.00986845, 0.20730568, -0.28601113, 0.08377945, 0.2517998 ,
0.08157796, 0.24523894, -0.34019017, 0.10557748, 0.02105924,
0.03729287, -0.52203006, 0.1191924 , -0.32391408, -0.25671792,
-0.24574052, 0.21722569, 0.05409996, -0.1944298 , 0.05195828,
-0.30965397, 0.31671712, 0.23532335, 0.34292328, -0.04460131,
-0.24952726, 0.1692848 , -0.2680034 , -0.20551267, 0.31070685,
-0.01980814, 0.24538256, 0.11438795, -0.52290195, -0.25548056,
-0.12335302, -0.32273138, -0.15207022, 0.03945559, -0.02261233,
-0.11034735, -0.27235347, -0.17029978, -0.37533283, -0.0962036 ,
-0.21412134, -0.04120854, 0.12733105, -0.22446166, -0.26129523,
-0.01468701, 0.24803281, 0.0242933 , 0.12278723, -0.06079411,
0.14851114, 0.04741063, -0.16954847, -0.1654084 , 0.3050954 ,
0.0125294 , -0.03766926, 0.06326802, -0.11463621, -0.02890763],
dtype=float32)
到這里,word2vec的生成就已經(jīng)搞定了碑幅。要感謝google的colab戴陡,替我節(jié)省了大量的時(shí)間,我用筆記本跑了4個(gè)小時(shí)的模型沟涨,在colab上只用了幾分鐘……
接下來是將語料庫(kù)進(jìn)行向量化恤批。我在這里卡了有一周,因?yàn)樵谏蓋ord2vec的時(shí)候裹赴,會(huì)將頻率低于2的低頻詞干掉喜庞,但是在語料庫(kù)數(shù)據(jù)向量化的時(shí)候會(huì)遇到低頻詞未登記(UNK)的情況,怎么解決篮昧,查了很多材料赋荆。后來才發(fā)現(xiàn),我特么傻了懊昨,在轉(zhuǎn)換的時(shí)候直接寫判斷過濾掉不就行了……
import logging
import gensim
from gensim.models import word2vec
model=gensim.models.KeyedVectors.load_word2vec_format("./sample_data/word2Vec03.bin",binary=True)
index2word=model.index2word
print(len(index2word))
index2word_set=set(model.index2word)
print(len(index2word_set))
print(model)
# text是輸入的已經(jīng)分好詞的語料庫(kù)文本
# model是之前生成的word2vec模型
# num_features是word2vec模型中每個(gè)詞維度大小窄潭,這里是200
def word2vec(text,model,num_features):
featureVec = np.zeros((200,),dtype="float32")
nwords=0
for word in text:
if word in index2word_set:
nwords+=1
featureVec=np.add(featureVec,model[word])
featureVec = np.divide(featureVec,nwords)
return featureVec
# print(word2vec(token))
def getAvgFeatureVecs(phrases,model,num_features):
counter=0
phraseFeatureVecs = np.zeros((len(phrases),num_features),dtype="float32")
for phrase in phrases:
if counter % 2000==0:
print("Phrase %d of %d" % (counter, len(phrases)))
phraseFeatureVecs[counter]=word2vec(phrase, model, num_features)
counter = counter+1
return phraseFeatureVecs
from nltk.corpus import stopwords
import re
def phrase_to_wordlist(phrase, remove_stopwords=False):
phrase_text = re.sub("[^a-zA-Z]"," ", phrase)
words = phrase_text.lower().split()
if remove_stopwords:
stops = set(stopwords.words("english"))
words = [w for w in words if not w in stops]
return(words)
clean_train_phrases = []
for phrase in train_data["Phrase"]:
clean_train_phrases.append( phrase_to_wordlist( phrase, remove_stopwords=True ))
num_features=200
trainDataVecs = getAvgFeatureVecs( clean_train_phrases, model, num_features )
clean_test_phrases = []
for phrase in test_data["Phrase"]:
clean_test_phrases.append( phrase_to_wordlist( phrase, remove_stopwords=True ))
num_features=200
testDataVecs = getAvgFeatureVecs( clean_test_phrases, model, num_features )
現(xiàn)在好了吧,可以送進(jìn)去訓(xùn)練了吧。
但是……又遇到問題了嫉你,在使用sklearn訓(xùn)練的時(shí)候報(bào)錯(cuò)
ValueError: Input contains NaN, infinity or a value too large for dtype('float32').
原因是我們?cè)谧稣Z料庫(kù)向量化處理的時(shí)候有一些無意義的評(píng)論月帝,所包含的內(nèi)容都在停用詞之中,向量化之后就變成了空值幽污,所以嚷辅,向量化之后還需要對(duì)數(shù)據(jù)值進(jìn)行空值檢驗(yàn),將其中為空的向量指定一個(gè)缺省值距误。我在這里省事就指定為0了
trainDataVecs[np.isnan(trainDataVecs)] = 0
testDataVecs[np.isnan(testDataVecs)] = 0
from sklearn.ensemble import RandomForestClassifier
forest = RandomForestClassifier( n_estimators = 100 )
print ("Fitting a random forest to labeled training data...")
forest = forest.fit( trainDataVecs, train["Sentiment"] )
訓(xùn)練后進(jìn)行預(yù)測(cè)簸搞,輸出預(yù)測(cè)結(jié)果
# Test & extract results
result = forest.predict( testDataVecs )
# Write the test results
output = pd.DataFrame( data={"id":test["PhraseId"], "sentiment":result} )
output.to_csv( "Word2Vec_AverageVectors.csv", index=False, quoting=3 )
到這里大致就完成了,但是准潭,我們希望使用RNN來處理趁俊,接下來就是構(gòu)建LSTM作為分類器。