sentiment analysis with chinese and english
本项目相关文章说明转到http://slg.charmculture.com/article/5a60a16080a23b7b1c9df3f9
开始之前,你需要对深度学习原理有比较深刻的了解,lstm的原理,调参优化细节,keras基本知识的掌握。Python 版本 3.6.5
1. 准备语料
本次收集的语料不是太多。中文大概2w多条(淘宝评论),英文1w多条(电影评论),以后有时间会继续补充语料。其中有5%作为验证集,10%为测试集合。文件已经切分好,但是因为需要训练所有数据集,所以这个提前切分到不同文件夹并没有什么作用(后面讲到会重新合并再切分)。
2.选择语言版本,分别设置训练集、测试集、验证集和维度
因为后面的程序要训练中文或英文,所以在这里提前选择语言版本和不同的语言版本训练相关的参数。
# 选择语言中文还是英文
languageType = ''
while (languageType != 'c' and languageType != 'e'):
languageType = input("Please enter a train type(chinese enter lower: c , english enter lower: e): ")
max_length = '' #一句话最大长度
load_path = '' #文件加载路径
language = '' #语言类型
tr_num = 17000 #训练集
va_num = 2000 #训练集
if languageType == 'c':
max_length = 100
load_path = 'data/chinese'
language = 'chinese'
tr_num = 17000
va_num = 2000
elif languageType == 'e':
max_length = 40
load_path = 'data/english'
language = 'english'
tr_num = 8000
va_num = 600
3.加载数据集
这里把中文和英文放在不同的文件夹下,利用 pandas
中的read_csv()
读取数据集合并到一起,如果这里本来就是一个整的数据集。则直接读取就好。
# 获取csv文件:内容放到数组里面 分别是训练集、验证集、测试集,最后合并到一起
def sst_binary(data_dir='data/chinese'):
tr_data = pd.read_csv(os.path.join(data_dir, 'train_binary_sent.csv'))
va_data = pd.read_csv(os.path.join(data_dir, 'valid_binary_sent.csv'))
te_data = pd.read_csv(os.path.join(data_dir, 'test_binary_sent.csv'))
all_data = tr_data.append(va_data).append(te_data)
return all_data
4.数据预处理
这一步是针对中文和英文的特点来处理掉对分析无用的词提升精度。比如停用词、标点符号、特殊字符、转义字符等等。因为语料比较少,这个程序还没有针对这一块做处理。
5.将词语转化为向量
这里是最核心的地方,深度学习在训练数据的时候要求输入的数据是一个向量,这样才能进行矩阵运算,也是多层感知器的输入。所以如果直接将一组句子是无法识别的。所以最重要的一步就是将词语转化为词向量,可是如何才能得到向量呢?
这里用到的是词嵌入的方法,大概步骤是:
- 中文最小统计粒度是词,所以要先切词(
jieba
)将一句话按照词语切分开来而非字。 - 将所有词放在一起,统计每个词出现的次数按照重大到小的排序,然后加上索引。
- 将句子中的词语全部替换成相应的索引,这样一个句子中的每个词语就用一个数字去表示了。
- 调用keras model第一层
Embedding()
,该层会利用词嵌入将句子数字数组转化为词向量。
需要注意的是,jieba分词虽然是分中文的,但是也可以处理英文(英文是按照空格切分的),这样可以得到比较统一的数组shape。
#定义模型
class Model(object):
def __init__(self, sentence_max_length=100):
sentence_max_length = sentence_max_length #截断词数 cut texts after this number of words (among top max_features most common words)
sentence_drop_length = 5 #出现次数少于该值的词扔掉。这是最简单的降维方法
#将每个句子里的词转化成词频索引值
def transform(data):
#如果是中文调用结巴分词
xs = data['sentence'].apply(lambda s: list(jieba.cut(s)))
#将所有词放到一个数组中
word_all = []
for i in xs:
word_all.extend(i)
#统计词频并排序建索引
global word_frequency, word_set
word_frequency = pd.Series(word_all).value_counts() #统计词频,从大到小排序
word_frequency = word_frequency[word_frequency >=
sentence_drop_length] #出现次数小于5的丢弃
word_frequency[:] = list(range(
1,
len(word_frequency) + 1)) #将词频排序的结果加索引
word_frequency[''] = 0 #添加空字符串用来补全,之前丢弃的后面的找不到的会用0代替
word_set = set(
word_frequency.index) #经过处理之后的所有词的数组集合,并且去掉可能存在的重复元素
#将词语替换成按照所有训练集词频排序后的索引
xt = xs.apply(lambda s: word2num(s, sentence_max_length))
xt = np.array(list(xt))
yt = np.array(list(data['label'])).reshape(
(-1, 1)) #此处用来调整标签形状n行1列 (-1是模糊控制即有不定多少行,1是1列)
#当前训练集合词的索引长度
wi = len(word_frequency)
return xt, yt, wi
self.transform = transform
6.keras 训练数据集
这一部分就交给keras处理了,具体用法可以参见keras中文文档,可以自定义一些参数,比如训练轮数、激活函数、加入验证集等等。当然核心的还是lstm了,相对于RNN,在训练长文本有更好的效果。训练完了之后可以选择保存模型。方便下次直接调用。
#将词转化为数字向量 即一个句子里的每个词都有用上面生成的索引值代替
def word2num(s, sentence_max_length):
s = [i for i in s if i in word_set]
s = s[:sentence_max_length] + [''] * max(0, sentence_max_length - len(s))
return list(word_frequency[s])
# krea 训练数据集
def model_train(x, y, wi, language, sentence_max_length=100, tr_num=17000, va_num=2000):
global model
model = Sequential()
model.add(Embedding(wi, 256, input_length=sentence_max_length))
model.add(LSTM(128))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(
loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(
x[:tr_num],
y[:tr_num],
batch_size=128,
nb_epoch=30,
validation_data=(x[tr_num:tr_num + va_num], y[tr_num:tr_num + va_num]))
score = model.evaluate(
x[tr_num + va_num:], y[tr_num + va_num:], batch_size=128)
model.save('model_' + language + '.h5')
return score[1]
#加载已经训练好的模型
def model_load(language):
global model
model = load_model('model_' + language + '.h5')
7.预测单个句子
预测单个句子依然需要将这个句子分词,然后将词转化为数字,所以还是用到训练模型时用到的处理方式。
#单个句子的预测函数
def model_predict(s, sentence_max_length=100):
s = np.array(word2num(list(jieba.cut(s)), sentence_max_length))
s = s.reshape((1, s.shape[0]))
return model.predict_classes(s, verbose=0)[0][0]
好了,大功告成,我们已经可以直接测试训练的结果了。