1.BP神经网络
BP神经网络可以分为两个部分,BP和神经网络,BP是 Back Propagation 的简写 ,意思是反向传播。而神经网络,可以说是一类相对复杂的计算网络。 正向传播就是让信息从输入层进入网络,依次经过每一层的计算,得到最终输出层结果的过程。反向传播的信息是误差,也就是输出层的结果与输入信息x对应的真实结果之间的差距。 通过一次正向传播,和一次反向传播,我们就可以将网络的参数更新一次,所谓训练网络,就是让正向传播和反向传播不断的往复进行,不断地更新网络的参数,最终使网络能够逼近真实的关系。 理论上,只要网络的层数足够深,节点数足够多,可以逼近任何一个函数关系。但是这比较考验你的电脑性能,事实上,利用 BP 网络,能够处理的数据其实还是有限的,但是这并不影响 Bp 网络是一种高明的策略。 结合实现中文分词的模型解释: (1)随机设下一个起始点,并固定下来,确保每一次运行时都有同一个起始点。 (2)加载模型需要的参数。 加载字表。字表的生成:对训练文本去重复,按照字频从高到低排序,并编号。 加载训练数据:未进行分词的句子。 加载标签:通过SBME来标识已进行过分词的训练数据。 加载学习率;轮次;一个字用多少维度的向量来表示;(原模型只有一个隐藏层,此处增加一层)第一个隐藏层的输出大小;第二个隐藏层的输出大小。 (3)加载模型 (4)模型预测 (5)评价预测准确率
2.模型具体参数以及主要部分代码实现
(1)模型具体参数 学习率——0.000015 轮次——5->8->10(效果不再有显著提升) 输出维度——4 (对每一个字预测SBME中的一个标签) 每一个字的向量维度——16 第一层输出维度——32 第一层输出维度——64 (2)模型实现部分 1.一个输入层:输入维度——字表大小,输出维度——字的向量维度16; 两个隐藏层: 2.第一层隐藏层:输入维度——字的向量维度16,输出维度——32; 3.第二层隐藏层:输入维度——32,输出维度——64; 4.做非线性变换,维度不变; 5.一个输出层:输入维度——64,输出维度——4; 6.使用softmax()函数,将输出数据归一化,概率最大的数的下标即为预测值。
3.模型评价结果
(1)Accuracy,中文为准确率,指的是“预测正确的样本数÷样本数总数”。计算公式为: (2)Precision,中文为精确率或者精度,指的是在我们预测为True的样本里面,有多少确实为True的。在信息检索领域,precision也被称为“查准率”。其公式为: (3)Recall,中文是召回率,指的是,实际上为True的样本有多少被我们挑出来了。在信息检索领域,recall也被称为“查全率”。其公式为: (4)Accuracy和Recall的调和指标:F1 Score
4.正式开始
先放上代码的框架:
1.训练样本 train.txt(文章末有链接)
可以看到是已经分好词的文本,一共有5000行。接下来我们就利用这个训练样本来训练我们的分词模型,目标是提取中文分词的规律,就像我们找一个数列的规律,不过这一次我们借助计算机的脑子。
2.训练样本的预处理
import pickle as pkl
import time
def build_train_data(file_path):
with open(file_path,'r',encoding='utf-8') as f:
lines = f.readlines()
phrase_expel = []
for i in lines:
t1 = i.replace('“ ','')
t2 = t1.replace('\n','')
t3 = t2.replace(' ','')
phrase_expel.append(t3)
with open('../data/generate_pkl/train_data.pkl', 'wb') as f:
pkl.dump(phrase_expel, f)
def build_target(file_path):
with open(file_path,'r',encoding='utf-8') as f:
tmp = f.readlines()
t = []
for i in tmp:
t1 = i.replace('“ ','')
t2 = t1.replace('\n','')
t.append(t2)
sum_list = []
for i in t:
sum_ = ''
for j in i.split():
if len(j) == 1:
sum_ += 'S'
continue
else:
sum_ += 'B'
for k in range(1, len(j)):
if k == len(j) - 1:
sum_ += 'E'
else:
sum_ += 'M'
sum_list.append(sum_)
with open('../data/generate_pkl/target.pkl', 'wb') as f:
pkl.dump(sum_list, f)
def build_vocab_dict(file_path):
vocab_dic = {}
with open(file_path, 'rb') as f:
z = pkl.load(f)
for line in z:
for hang in line:
vocab_dic[hang] = vocab_dic.get(hang, 0) + 1
vocab_dic_sorted = sorted(vocab_dic.items(), key=lambda x: x[1], reverse=True)
vocab_dic2 = {word_count[0]: idx for idx, word_count in enumerate(vocab_dic_sorted)}
with open('../data/generate_pkl/vocab.pkl', 'wb') as f:
pkl.dump(vocab_dic2, f)
if __name__ == '__main__':
build_train_data('../data/train.txt')
time.sleep(5)
build_target('../data/train.txt')
build_vocab_dict('../data/generate_pkl/train_data.pkl')
运行后得到如下三个pkl文件:
3.简单的BP神经网络构建,由于这里比较简洁,就没有单独在一个文件夹models里面放这个模型,而是直接写在main里面了。
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import pickle as pkl
from tqdm import tqdm
class Config(object):
def __init__(self):
self.vocab = pkl.load(open('../data/generate_pkl/vocab.pkl', 'rb'))
self.train_data = pkl.load(open('../data/generate_pkl/train_data.pkl', 'rb'))
self.target = pkl.load(open('../data/generate_pkl/target.pkl', 'rb'))
self.learning_rate = 0.000015
self.epoch = 5
self.output_size = 4
self.embed_dim = 16
self.hout1 = 32
self.hout2 = 64
class Model(nn.Module):
def __init__(self, output_size, vocab_size, embed_dim,hout1,hout2):
super(Model, self).__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.hid_layer1 = nn.Linear(embed_dim, hout1)
self.hid_layer2 = nn.Linear(hout1, hout2)
self.out_layer = nn.Linear(hout2, output_size)
def forward(self, in_layer):
emd = self.embedding(in_layer)
h_out1 = self.hid_layer1(emd)
h_out2 = self.hid_layer2(h_out1)
out_ = F.relu(h_out2)
out_ = self.out_layer(out_)
out_ = F.softmax(out_, dim=1)
return out_
def model_eval(model_out, true_label):
confusion_matrix = torch.zeros([2, 2], dtype=torch.long)
predict_label = torch.argmax(model_out, 1)
accuracy = []
precision = []
recall = []
f_1 = []
for l in range(4):
tp_num, fp_num, fn_num, tn_num = 0, 0, 0, 0
for p, t in zip(predict_label, true_label):
if p == t and t == l:
tp_num += 1
if p == l and t != l:
fp_num += 1
if p != l and p != t:
fn_num += 1
if p != l and p == t:
tn_num += 1
accuracy.append((tp_num + tn_num) / (tp_num + tn_num + fp_num + fn_num))
try:
prec = tp_num / (tp_num + fp_num)
except:
prec = 0.0
try:
rec = tp_num / (tp_num + fn_num)
except:
rec = 0
precision.append(prec)
recall.append(rec)
if prec == 0 and rec == 0:
f_1.append(0)
else:
f_1.append((2 * prec * rec) / (prec + rec))
ave_acc = torch.tensor(accuracy, dtype=torch.float).mean()
ave_prec = torch.tensor(precision, dtype=torch.float).mean()
ave_rec = torch.tensor(recall, dtype=torch.float).mean()
ave_f1 = torch.tensor(f_1, dtype=torch.float).mean()
return ave_acc, ave_prec, ave_rec, ave_f1
def test_(model_):
text = '这是一个最好的时代。'
hang_ = []
for wd in text:
hang_.append(Config().vocab[wd])
test_tensor = torch.tensor(hang_, dtype=torch.long)
res = model_(test_tensor)
res = res.detach().numpy()
[print(np.argmax(r)) for r in res]
print(res)
if __name__ == '__main__':
torch.manual_seed(1)
config = Config()
voc_size = len(config.vocab)
train_data_list = []
for lin in config.train_data:
hang = []
for word in lin:
hang.append(config.vocab[word])
train_data_list.append(torch.tensor(hang, dtype=torch.long))
target_dict = {'B': 0,
'M': 1,
'E': 2,
'S': 3}
target_list = []
for lin in config.target:
hang = []
for tag in lin:
hang.append(target_dict[tag])
target_list.append(torch.tensor(hang, dtype=torch.long))
model = Model(config.output_size, voc_size, config.embed_dim,config.hout1,config.hout2)
losses = []
acc = []
rec = []
prec = []
f1 = []
optimizer = torch.optim.SGD(model.parameters(), lr=config.learning_rate)
loss_f = nn.CrossEntropyLoss()
for i in tqdm(range(config.epoch)):
for j, k in enumerate(train_data_list):
optimizer.zero_grad()
out = model(k)
loss = loss_f(out, target_list[j])
loss.backward()
optimizer.step()
acc_, prec_, rec_, f1_ = model_eval(out, target_list[j])
acc.append(acc_.item())
prec.append(prec_.item())
rec.append(rec_.item())
f1.append(f1_.item())
losses.append(loss.item())
torch.save(model, './cut.bin')
print('\nacc: ' + str(torch.tensor(acc).mean().item()) + '\tprec: ' + str(torch.tensor(prec).mean().item())
+'\trec: ' + str(torch.tensor(rec).mean().item()) + '\tf1: ' + str(torch.tensor(f1).mean().item()))
test_(model)
4.输出结果:
对应的标识为EEEESESES。即分词为 这 |是 |一|个 |最好| 的| 时| 代 。不是完全正确的,甚至有很明显的缺陷,效果不好。 保存的模型在modules/文件夹中——cut.bin。 资源: train.txt: 链接:https://pan.baidu.com/s/1VcYQW4it5XtDwApL1r59_Q 提取码:2933
|