欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > 中文分词-基于机器学习

中文分词-基于机器学习

2024/10/25 7:35:04 来源:https://blog.csdn.net/m0_57922605/article/details/140307950  浏览:    关键词:中文分词-基于机器学习

文章目录

  • 前言
  • 生成网络层
  • jieba切分与标注
  • 数据的搭建
  • 模型训练与评估

前言

之前介绍了几种简单的中文分词方式,但是这几种分词方式都太依赖词库了。有没有不依赖词库的方式,重新思考,如果想要对一句话进行分词,我们需要什么知道什么?对于每一个字,我们想知道它是不是一个词的边界。
在这里插入图片描述
蓝色表示不是词边界,红色表示是词边界。问题转化为:对于句子中的每一个字,进行二分类判断,正类表示这句话中,它是词边界,负类表示它不是词边界标注数据、训练模型,使模型可以完成上述判断,那么这个模型,可以称为一个分词模型。这次分词实战和上一次深度学习实战之简单文本分类的区别是上一次每个样本有个标签,这次是每个样本中的每个字都有标签。

对于这类问题我们就需要已经切分好的数据,为了便于生成数据,我们使用jieba分词的结果作为训练数据,看看是否可以得到一个效果接近的神经网络模型。

生成网络层

这个实践和前一次相同,深度学习实战之简单文本分类,所以网络层与之前类似,不懂的可以参考这篇文章,

import torch
import torch.nn as nn
import jieba
import numpy as np
import random
import json
from torch.utils.data import DataLoader
class TorchModel(nn.Module):def __init__(self, input_dim, hidden_size, num_rnn_layers, vocab):super(TorchModel, self).__init__()self.embedding = nn.Embedding(len(vocab) + 1, input_dim, padding_idx=0) #shape=(vocab_size, dim)self.rnn_layer = nn.RNN(input_size=input_dim,hidden_size=hidden_size,batch_first=True,num_layers=num_rnn_layers,)self.classify = nn.Linear(hidden_size, 2)  # w = hidden_size * 2self.loss_func = nn.CrossEntropyLoss(ignore_index=-100)#当输入真实标签,返回loss值;无真实标签,返回预测值def forward(self, x, y=None):x = self.embedding(x)  #input shape: (batch_size, sen_len), output shape:(batch_size, sen_len, input_dim)x, _ = self.rnn_layer(x)  #output shape:(batch_size, sen_len, hidden_size)  y_pred = self.classify(x)   #output shape:(batch_size, sen_len, 2) -> y_pred.view(-1, 2) (batch_size*sen_len, 2)if y is not None:return self.loss_func(y_pred.reshape(-1, 2), y.view(-1))else:return y_pred

这里的 nn.CrossEntropyLoss(ignore_index=-100)中ignore_idex=-100是忽视补齐的样本不作损失函数计算,而RNN中的num_layers是为了多几层RNN,就类似线性层的 y y y值作为新的 x x x进入新的线性层。self.loss_func(y_pred.reshape(-1, 2), y.view(-1))中是为了保证每个字的标注都放进去了训练。

jieba切分与标注

def sequence_to_label(sentence):words = jieba.lcut(sentence)label = [0] * len(sentence)pointer = 0for word in words:pointer += len(word)label[pointer - 1] = 1return label

jieba切分好后,给对应分词长度的位置标记为1,如s=‘abcd’,切分为[ab,cd],则知道在第二个len(’ab‘)位置标记为1

数据的搭建

class Dataset:def __init__(self, corpus_path, vocab, max_length):self.vocab = vocabself.corpus_path = corpus_pathself.max_length = max_lengthself.load()def load(self):self.data = []with open(self.corpus_path, encoding="utf8") as f:for line in f:sequence = sentence_to_sequence(line, self.vocab)label = sequence_to_label(line)sequence, label = self.padding(sequence, label)sequence = torch.LongTensor(sequence)label = torch.LongTensor(label)self.data.append([sequence, label])#使用部分数据做展示,使用全部数据训练时间会相应变长if len(self.data) > 10000:break#将文本截断或补齐到固定长度def padding(self, sequence, label):sequence = sequence[:self.max_length]sequence += [0] * (self.max_length - len(sequence))label = label[:self.max_length]label += [-100] * (self.max_length - len(label))#对于补齐的样本标记为-100return sequence, labeldef __len__(self):return len(self.data)def __getitem__(self, item):return self.data[item]#文本转化为数字序列,为embedding做准备
def sentence_to_sequence(sentence, vocab):sequence = [vocab.get(char, vocab['unk']) for char in sentence]return sequence
#加载字表
def build_vocab(vocab_path):vocab = {}with open(vocab_path, "r", encoding="utf8") as f:for index, line in enumerate(f):char = line.strip()vocab[char] = index + 1   #每个字对应一个序号vocab['unk'] = len(vocab) + 1return vocab#建立数据集
def build_dataset(corpus_path, vocab, max_length, batch_size):dataset = Dataset(corpus_path, vocab, max_length) #diy __len__ __getitem__data_loader = DataLoader(dataset, shuffle=True, batch_size=batch_size) #torch打乱数据集分批训练,不需要之前的手动分批训练了return data_loader

模型训练与评估

def main():epoch_num = 5        #训练轮数batch_size = 20       #每次训练样本个数char_dim = 50         #每个字的维度hidden_size = 100     #隐含层维度num_rnn_layers = 1    #rnn层数max_length = 20       #样本最大长度learning_rate = 1e-3  #学习率vocab_path = "chars.txt"  #字表文件路径corpus_path = "../corpus.txt"  #语料文件路径vocab = build_vocab(vocab_path)       #建立字表data_loader = build_dataset(corpus_path, vocab, max_length, batch_size)  #建立数据集model = TorchModel(char_dim, hidden_size, num_rnn_layers, vocab)   #建立模型optim = torch.optim.Adam(model.parameters(), lr=learning_rate)     #建立优化器#训练开始for epoch in range(epoch_num):model.train()watch_loss = []for x, y in data_loader:optim.zero_grad()    #梯度归零loss = model.forward(x, y)   #计算lossloss.backward()      #计算梯度optim.step()         #更新权重watch_loss.append(loss.item())print("=========\n第%d轮平均loss:%f" % (epoch + 1, np.mean(watch_loss)))#保存模型torch.save(model.state_dict(), "model.pth")return#最终预测
def predict(model_path, vocab_path, input_strings):#配置保持和训练时一致char_dim = 50  # 每个字的维度hidden_size = 100  # 隐含层维度num_rnn_layers = 1  # rnn层数vocab = build_vocab(vocab_path)       #建立字表model = TorchModel(char_dim, hidden_size, num_rnn_layers, vocab)   #建立模型model.load_state_dict(torch.load(model_path))   #加载训练好的模型权重model.eval()for input_string in input_strings:#逐条预测x = sentence_to_sequence(input_string, vocab)with torch.no_grad():result = model.forward(torch.LongTensor([x]))[0]result = torch.argmax(result, dim=-1)  #预测出的01序列#在预测为1的地方切分,将切分后文本打印出来for index, p in enumerate(result):if p == 1:print(input_string[index], end=" ")else:print(input_string[index], end="")print()if __name__ == "__main__":main()input_strings = ["同时国内有望出台新汽车刺激方案","沪胶后市有望延续强势","经过两个交易日的强势调整后","昨日上海天然橡胶期货价格再度大幅上扬"]predict("model.pth", "chars.txt", input_strings)

这部分与上次内容几乎一样,看结果:
在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com