简单文本情感分类入门
自己最近在B站上看了pytorch入门视频,在此写下自己第一个博客,第一个文本情感分类,写的不是很好,希望大家别喷就行。
一、准备数据集
1.数据集。
我这里选用的是aclImdb数据集,下载链接是:http://ai.stanford.edu/~amaas/data/sentiment/。
2.DataSet类的构建。
这里的 ImDataset继承了torch.DataSet类,重写了父类的__getitem__、 __init__和 len。 定义了一个tokenlize(concent)方法,将传进的文本句子进行去除嘈杂符号。ImDataset类的作用是在后续需要使用DataLoader类的时候只需要使用get_data_loader(train=True)方法就即可完成对于dataloader的对象的返回。
代码如下(示例):
"""
完成数据集的准备
"""
from torch.utils.data import Dataset as dataset, DataLoader
import os
import re
import torch
from lib import ws,max_len
def tokenlize(concent):
"""
对于与传进的数据进行整理,将数据内的嘈杂信息去除。
:param concent:
:return:
"""
concent = re.sub("<.*?>", " ", concent)
fileters = ['\t', '\\n', '\x96', '\x97', '#', '$', '&', '\(', '\)', '\*', '\+', '\-', '\.', '\=', '\?', '\=', '\.',
'<', '>', ':']
concent = re.sub("|".join(fileters), " ", concent)
concent = re.sub("\'s ", " is ", concent)
concent = re.sub("\'m ", " am ", concent)
concent = re.sub("\'re ", " are ", concent)
concent = re.sub("\'ve ", " have ", concent)
token = [i.strip().lower() for i in concent.split()]
return token
class ImDataset(dataset):
def __init__(self, train=True):
self.train_data_path = r"E:\dataset\aclImdb\train"
self.test_data_path = r"E:\dataset\aclImdb\test"
self.data_path = r""
if train:
self.data_path = self.train_data_path
else:
self.data_path = self.test_data_path
temp_data_path = [os.path.join(self.data_path, "pos"), os.path.join(self.data_path, "neg")]
self.total_file_path = []
for path in temp_data_path:
file_name_list = os.listdir(path)
file_path_list = [os.path.join(path, i) for i in file_name_list]
self.total_file_path.extend(file_path_list)
def __getitem__(self, index):
file__path = self.total_file_path[index]
label_str = file__path.split("\\")[-2]
label = 0 if label_str == "neg" else 1
concent = open(file__path, 'r',encoding="utf-8").read()
concent = tokenlize(concent)
return concent, label
def __len__(self):
return len(self.total_file_path)
def collate_fn(batch):
texts, labels = list(zip(*batch))
content = [ws.transform(i,max_len=max_len) for i in texts]
content = torch.LongTensor(content)
labels = torch.LongTensor(labels)
return content, labels
def get_data_loader(train=True):
datas = ImDataset(train)
dataloader = DataLoader(dataset=datas, batch_size=256, shuffle=True, collate_fn=collate_fn)
return dataloader
if __name__ == "__main__":
for idx, (input, target) in enumerate(get_data_loader()):
print(idx)
print(input)
print(target)
break
示例:ImDataSet类完成之后,在之后需要使用的时候只需调用get_data_loader(train=True)方法函数即可返回DataLoader对象。
二、文本序列化
1.文本序列化考虑的因素:
1.1如何将词语和字典之间对应 1.2不同的词语出现的次数不同,是否需要将高频或者低频词与进行过滤,以及词语总数是否进行限制。 1.3的到数字序列之后如何进行转化, 1.4不同句子长度不同,每一个batch的句子如和打造成长度相同的句子。 1.5新词语没有出现在词典中怎么办。
2.解决办法:
构建 Word2Sequence类,分别实现如下的方法: 1.init:python构造方法 2.fit:用于一个句子的统计词频,并分别将其词语和频率记录在self.dict字典种 3.build_vocab:构建词汇字典,构建的过程中解决1.2和1.4的问题。 4.transform:将一个句子转化为数字字典。 5.inverse_transform:将数字序列转化为一个句子 6.len:返回统计的词语的多少,即一个句子的长度。 代码如下(示例):
""""
实现的是构建词典
实现方法将句子转化为数字序列序列转化为句子
"""
class Word2Sequence:
UNK_TAG = "UNK"
PAD_TAG = "PAD"
UNK = 0
PAD = 1
def __init__(self):
self.dict = {
self.UNK_TAG: self.UNK,
self.PAD: self.PAD
}
self.count = {}
def fit(self, sentence):
""""
把每一个句子放到dict中
sentence: [word1,word2.....]
"""
for word in sentence:
self.count[word] = self.count.get(word, 0) + 1
def build_vocab(self, min=5, max=None, max_featuers=None):
"""
生成词典
:param min:最小出现的次数
:param max:最大出现的次数
:param max_featuers:一共保留多少个词语
:return:
"""
self.count = {word: value for word, value in self.count.items() if value > min}
if max is not None:
self.count = {word: value for word, value in self.count.items() if value <= max}
if max_featuers is not None:
temp = sorted(self.count.items(), key=lambda x: x[-1], reverse=True)[:max_featuers]
self.count = temp
for word in self.count:
self.dict[word] = len(self.dict)
self.inverse_dict = dict(zip(self.dict.values(), self.dict.keys()))
def transform(self, sentence, max_len=None):
"""
把句子转化为序列
:param sentence:【word1,word2....】
:param max_len: 对句子进行填充和裁剪
:return: 数字序列
"""
if max_len is not None:
if max_len > len(sentence):
sentence = sentence + [self.PAD_TAG] * (max_len - len(sentence))
if max_len < len(sentence):
sentence = sentence[:max_len]
return [self.dict.get(word, self.UNK) for word in sentence]
def inverse_transform(self, indices):
"""
把序列转化为一个句子
:param indices:数字序列
:return:句子
"""
return [self.inverse_dict.get(num) for num in indices]
def __len__(self):
return len(self.dict)
三、文本句子序列化。
1.将本读取到的文本序列对象存储在本地磁盘,之后在需要的时候就可以直接使用。
from word_sequence import Word2Sequence
import pickle
import os
from DataSet import tokenlize
from tqdm import tqdm
if __name__ == "__main__":
ws = Word2Sequence()
path = r"E:\dataset\aclImdb\train"
temp_data_path = [os.path.join(path, "pos"), os.path.join(path, "neg")]
for data_path in temp_data_path:
file_paths = [os.path.join(data_path, file_name) for file_name in os.listdir(data_path)]
for file_path in tqdm(file_paths):
sencente = tokenlize(open(file_path,encoding="utf-8").read())
ws.fit(sencente)
ws.build_vocab(min=10,max_featuers=10000)
pickle.dump(ws, open("E:\Python code\情感分类\model\ws.pkl", "wb"))
这里使用tqdm模块的作用是显示序列化的进度
三、模型、训练以及总结。
1.模型的构建:
直接构建模型类然后继承torch.nn.Moudel类,重写父类构造方法,重写向前计算的方法,即可。 目前还没有学习RNN神经网络所以这里在使用embedding,之后直接使用的全连接层代替RNN来,所以训练的效果肯定不够好。 直接上代码:
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
self.embedding = nn.Embedding(len(ws), 100)
self.fc = nn.Linear(max_len * 100, 2)
def forward(self, input):
"""
:param input: [batch_size,max_len]
:return: [batch_size,max_len,100]
"""
x = self.embedding(input)
x = x.view([-1, max_len * 100])
out = self.fc(x)
return F.log_softmax(out,dim=-1)
2.模型的训练:
我这里是直接定义了一个方法进行训练,当然在训练之前肯定要实例化一个自己的模型对象,以及一个优化器类对象。最后只实验了一下代码能不能跑。
import torch.nn as nn
import torch.nn.functional as F
from lib import ws, max_len
from torch.optim import Adam
from DataSet import get_data_loader
model = MyModel()
optimizer = Adam(model.parameters(), 0.001)
def train(epoch):
for idx, (input, target) in enumerate(get_data_loader()):
optimizer.zero_grad()
output = model(input)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
print(loss.item())
if __name__ == '__main__':
for i in range(1):
train(i)
2.总结:
这是我第一次使用pytorch来写自己的第一个文本情感分类模型,也是我第一次写博客(不喜欢希望别喷就行),当然训练出效果不是很理想(因为没有使用RNN来实现),在次记录我的学习经历,希望自己可以越来越强(向大佬膜拜)。
|