Kaggle关于IMDB情感分类

过去了17天,按照学习计划来说,已经严重超时了。
主要的问题是在数据预处理部分。
Kaggle的IMDB情感分析任务其实很简单,train文件用于训练,test文件用于测试。

步骤

  1. 整合train和test(就是说所有语料库)生成词袋或词向量模型。也可以下载已有的word2vec或是glove词向量模型。
  2. 生成的词向量模型就是将每一个词向量化,方便后面的计算。依据生成的词向量模型对train和test语料进行向量化。
  3. 把向量化的train数据和标签输入分类模型中进行预测,完成模型训练。
  4. 评估模型,并对test进行预测。

具体实现

1. 数据集

数据集是tsv格式数据,说白了是分成了5类:

0 - negative
1 - somewhat negative
2 - neutral
3 - somewhat positive
4 - positive

我们先读取一下看看。TSV文件和CSV的文件的区别是:前者使用\t作为分隔符,后者使用,作为分隔符。

可以看到总共156060条记录,其中打2分的数据最多,说明大家都还是很中庸啊。

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()
image.png
train_data.info()

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()
image.png

以上是数据的描述性统计结果,当然,还可以做一下可视化,这些参考kaggle的泰坦尼克号任务操作。

2. 构建word2vec

接下来是要对语料库向量化,这其实就是一种对文本特征的抽取。
文中提到了两种方式:BOW(词袋)和Word2Vec,相比而言,Word2Vec所包含的信息更多,而且gensim库已经打包好了,在这里使用word2vec。

在2017年新的特征抽取算法transformer将会横扫一切……

在完成词向量抽取后,将进行分类器的训练。
在这里,我们将train和test数据集进行合并,构造出一个维度为200的词向量模型,使用gensim库进行构建。

# 合并test和train的数据,用于训练词向量模型
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来训练模型

主要参数介绍如下:

  1. sentences:我们要分析的语料,可以是一个列表,或者从文件中遍历读出(word2vec.LineSentence(filename) )。

  2. size:词向量的维度,默认值是100。这个维度的取值一般与我们的语料的大小相关,如果是不大的语料,比如小于100M的文本语料,则使用默认值一般就可以了。如果是超大的语料,建议增大维度。

  3. window:即词向量上下文最大距离,window越大,则和某一词较远的词也会产生上下文关系。默认值为5,在实际使用中,可以根据实际的需求来动态调整这个window的大小。如果是小语料则这个值可以设的更小。对于一般的语料这个值推荐在[5;10]之间。

  4. sg:即我们的word2vec两个模型的选择了。如果是0, 则是CBOW模型;是1则是Skip-Gram模型;默认是0即CBOW模型。

  5. hs:即我们的word2vec两个解法的选择了。如果是0, 则是Negative Sampling;是1的话并且负采样个数negative大于0, 则是Hierarchical Softmax。默认是0即Negative Sampling。

  6. negative:即使用Negative Sampling时负采样的个数,默认是5。推荐在[3,10]之间。这个参数在我们的算法原理篇中标记为neg。

  7. cbow_mean:仅用于CBOW在做投影的时候,为0,则算法中的xw为上下文的词向量之和,为1则为上下文的词向量的平均值。在我们的原理篇中,是按照词向量的平均值来描述的。个人比较喜欢用平均值来表示xw,默认值也是1,不推荐修改默认值。

  8. min_count:需要计算词向量的最小词频。这个值可以去掉一些很生僻的低频词,默认是5。如果是小语料,可以调低这个值。

  9. iter:随机梯度下降法中迭代的最大次数,默认是5。对于大语料,可以增大这个值。

  10. alpha:在随机梯度下降法中迭代的初始步长。算法原理篇中标记为η,默认是0.025。

  11. min_alpha: 由于算法支持在迭代的过程中逐渐减小步长,min_alpha给出了最小的迭代步。

from nltk.corpus import stopwords
StopWords = stopwords.words('english')

import logging
import gensim
from gensim.models import word2vec
# 设置输出日志
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) 

搞定之后运行模型,还可以看看效果

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的生成就已经搞定了。要感谢google的colab,替我节省了大量的时间,我用笔记本跑了4个小时的模型,在colab上只用了几分钟……
接下来是将语料库进行向量化。我在这里卡了有一周,因为在生成word2vec的时候,会将频率低于2的低频词干掉,但是在语料库数据向量化的时候会遇到低频词未登记(UNK)的情况,怎么解决,查了很多材料。后来才发现,我特么傻了,在转换的时候直接写判断过滤掉不就行了……

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是输入的已经分好词的语料库文本
# model是之前生成的word2vec模型
# num_features是word2vec模型中每个词维度大小,这里是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 )

现在好了吧,可以送进去训练了吧。
但是……又遇到问题了,在使用sklearn训练的时候报错

ValueError: Input contains NaN, infinity or a value too large for dtype('float32').

原因是我们在做语料库向量化处理的时候有一些无意义的评论,所包含的内容都在停用词之中,向量化之后就变成了空值,所以,向量化之后还需要对数据值进行空值检验,将其中为空的向量指定一个缺省值。我在这里省事就指定为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"] )

训练后进行预测,输出预测结果

# 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来处理,接下来就是构建LSTM作为分类器。

你可能感兴趣的:(Kaggle关于IMDB情感分类)