常常做中文自然语言处理的第一步就是将句子级文章级文本进行分词。
但中文分词常常让我们哭笑不得
乒乓球/拍/卖了
乒乓/球拍/卖了
这样的分词常常让我损失句子的一些特征
还有刘群老师的自然语言理解太难了系列话题
给大家展示一下有意思的地方
难度:※※ 两颗星
来到杨过曾经生活过的地方,小龙女动情地说:“我也想过过过儿过过的生活。”
来到儿子等校车的地方,邓超对孙俪说:“我也想等等等等等过的那辆车。”
赵敏说:我也想控忌忌己不想无忌。
你也想犯范范范玮琪犯过的错吗
对叙打击是一次性行为?
自然语言理解太难了
那如果我们不分词结果如何呢
以字为单位,不分词,将每个句子截断为200字(不够则补空字符串),然后将句子以“字-one hot”的矩阵形式输入到LSTM模型中进行学习分类;
# -*- coding:utf-8 -*-
'''
python 3.6
one hot测试
'''
import numpy as np
import pandas as pd
from tqdm import tqdm
maxlen = 200 # 截断字数
min_count = 20 # 出现次数少于该值的字扔掉。这是最简单的降维方法
tqdm.pandas(desc='onehot')
def init():
pos = pd.read_excel('./data/pos.xls', header=None)
pos['label'] = 1
neg = pd.read_excel('./data/neg.xls', header=None)
neg['label'] = 0
all_ = pos.append(neg, ignore_index=True)
return all_[0:100]
# 获得文字字典abc
def getabc(all_):
content = ''.join(all_[0])
abc = pd.Series(list(content)).value_counts()
abc = abc[abc >= min_count]
abc[:] = list(range(len(abc)))
word_set = set(abc.index)
return abc, word_set
def doc2num(s, maxlen, abc, word_set):
s = [i for i in s if i in word_set]
s = s[:maxlen]
return list(abc[s])
# 文字根据abc字典转数字
def transf(all_, abc, word_set):
all_['doc2num'] = all_[0].apply(lambda s: doc2num(s, maxlen, abc, word_set))
# 手动打乱数据
# 当然也可以把这部分加入到生成器中
idx = list(range(len(all_)))
np.random.shuffle(idx)
all_ = all_.loc[idx]
def getxy(all_):
# 按keras的输入要求来生成数据
x = np.array(list(all_['doc2num']))
y = np.array(list(all_['label']))
y = y.reshape((-1, 1)) # 调整标签形状
return x, y
all_ = init()
abc, word_set = getabc(all_)
transf(all_, abc, word_set)
x, y = getxy(all_)
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout
from keras.layers import LSTM
import sys
import math
sys.setrecursionlimit(10000) # 增大堆栈最大深度(递归深度),据说默认为1000,报错
# 建立模型
model = Sequential()
model.add(LSTM(128, input_shape=(maxlen, len(abc))))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(loss='binary_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
# 单个one hot矩阵的大小是maxlen*len(abc)的,非常消耗内存
# 为了方便低内存的PC进行测试,这里使用了生成器的方式来生成one hot矩阵
# 仅在调用时才生成one hot矩阵
# 可以通过减少batch_size来降低内存使用,但会相应地增加一定的训练时间
batch_size = 128
train_num = len(y) - 2000
# 不足则补全0行
gen_matrix = lambda z: np.vstack((np_utils.to_categorical(z, len(abc)), np.zeros((maxlen - len(z), len(abc)))))
def data_generator(data, labels, batch_size):
batches = [list(range(batch_size * i, min(len(data), batch_size * (i + 1)))) for i in
range(math.floor(len(data) / batch_size) + 1)]
while True:
for i in batches:
xx = np.zeros((maxlen, len(abc)))
xx, yy = np.array(list(map(gen_matrix, data[i]))), labels[i]
yield (xx, yy)
model.fit_generator(data_generator(x[:train_num], y[:train_num], batch_size),
samples_per_epoch=math.ceil(train_num / batch_size), nb_epoch=100)
model.evaluate_generator(data_generator(x[train_num:], y[train_num:], batch_size), val_samples=len(x[train_num:]))
def predict_one(s): # 单个句子的预测函数
s = gen_matrix(doc2num(s, maxlen, abc, word_set))
s = s.reshape((1, s.shape[0], s.shape[1]))
return model.predict_classes(s, verbose=0)[0][0]
参考苏老师:
https://spaces.ac.cn/archives/3863