前言
作为一个OIer,原本以为入门一些语言和框架不是一件难事,然而真正去实践了才知道python以及一系列库的函数和库实在是用处繁多,每次看到一句话就要在网上查很久它的用处,并且描述的语言通常有点晦涩,尤其是对于只是自学了一下python的我来说,在这里通过举一个例子来简单实现一份深度学习的代码,并且用并不严谨但易懂的语言解释,同时记录一下实验结果和感想。
门槛要求,基本学习过神经网络的原理(指上了不明所以的AI导论的课),对于实现方法有所困惑(指什么都不会写)
问题
这个任务来自THU人工智能导论第二次大作业,将一个已经完成了分词的中文句子进行分析,将它分类为正面或负面的情感
下发文件包括:
-
train.txt,test.txt,validation.txt,每个文件代表一组数据,每个文件内由若干行组成
- 每行开头是0/1表示负面或正面,之后是分词后的一句话,用空格分词
- train.txt有接近20000句,validation.txt有接近5000句,test.txt有接近400句
-
wiki_word2vec_50.bin,词向量包,输入后可以得到每个词的长度为50的向量,可以用gensim导入,详见代码
词向量:我们要判别两个词语是否相似,可以通过训练出词向量,对于每个词用向量表示,如果两个向量差越小,就代表着两个词越接近。
wiki_word2vec_50.bin里包含了十几万个词语,虽然并不完整,但只有少部分的词语没有,大体上并不影响训练的结果
当然,也可以有更长的词向量,能够更加精确地描述一个词语的信息
方法
-
考虑使用CNN模型 -
对于一个句子,大小为
L
×
50
L\times 50
L×50(50是词向量大小),有若干个大小为
k
×
50
k\times 50
k×50的卷积核,对于一个卷积核可以将句子变成
(
L
?
k
+
1
)
×
1
(L-k+1)\times 1
(L?k+1)×1的矩阵,再进行
m
a
x
_
p
o
o
l
i
n
g
max\_pooling
max_pooling(最大池化,就是将这个矩阵变成一个值,为矩阵中的最大值)以及激活函数
r
e
l
u
relu
relu(对0取max),这样就能得到一个大小为卷积核个数
c
c
c的向量,最后进行
c
→
2
c\to 2
c→2的一个全连接,以及softmax激活函数得到
0
,
1
0,1
0,1的概率 -
好的,你已经知道CNN是什么了,你可以轻松写出代码了!() -
然而,如果你不想在BP算法的时候手算梯度,你应该至少要会一种深度学习框架,通俗的说就是一个你能想到的常见的操作都帮你封装好了的模板库,你只需要import一下它就可以帮你写好代码(bushi)! -
我用的是pytorch
Pytorch
一个流行的模板库(深度学习框架),里面包含了非常多的函数,如果你不想将pytorch官方的手册看一遍,你最好找一份朴素的实现从最简单的开始做起
在这里,仅仅介绍在本次实验之前你需要知道的关于Pytorch的知识
- Pytorch中已经封装好的操作都是对于其自带的变量类型
torch.tensor 来做的,tensor是一个类似numpy的数组,你可以通过tensor.ones(),tensor.zeros(),tensor.randn() 来新建一个 tensor,你甚至可以将一个numpy或list直接变成tensor - 如何构建神经网络?让我们用代码来一点点解释!
实现
import torch
import gensim
import math
import torchvision
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader,Dataset
train_input_name = 'train_shuffle.txt'
test_input_name = 'test.txt'
all_input_name = 'validation.txt'
print("Loading word2vec...")
model = gensim.models.KeyedVectors.load_word2vec_format('wiki_word2vec_50.bin', binary=True)
words = model.key_to_index.keys()
train_batch_size = 64
test_batch_size = 1000
vec_size = 50
learning_rate = 0.005
n_epoch = 100000
momentum = 0.5
random_seed = 114514
torch.manual_seed(random_seed)
limit_size = 1e9
def readdata(name):
file = open(name,'r',encoding='utf-8')
data = file.readlines()
List = []
Target = []
cnt = 0
log_cnt = 1000
for line in data:
L = line[2:].split(' ')
L[len(L)-1] = L[len(L)-1].strip()
sentence = torch.zeros(len(L),vec_size,dtype=torch.float)
for i in range(len(L)) :
if L[i] in words:
sentence[i:] =torch.tensor(model.get_vector(L[i]))
List.append(sentence),Target.append(int(line[0]))
if cnt % log_cnt == 0 :
print("Reading Sentence of {}".format(name),cnt,)
cnt += 1
if cnt >= limit_size :
break
return List,torch.tensor(Target)
class Mydataset(Dataset):
def __init__(self, List, Target):
self.List = List
self.Target = Target
def __len__(self):
return len(self.List)
def __getitem__(self, index):
return self.List[index], self.Target[index]
def my_collate(batch):
data = [item[0] for item in batch]
target = [item[1] for item in batch]
target = torch.tensor(target)
return [data, target]
train_x, train_y = readdata(train_input_name)
train_ds = Mydataset(train_x, train_y)
train_dataloader = DataLoader(train_ds, batch_size = train_batch_size, shuffle = True, collate_fn = my_collate)
test_x, test_y = readdata(test_input_name)
test_ds = Mydataset(test_x, test_y)
test_dataloader = DataLoader(test_ds, batch_size = test_batch_size, shuffle = True, collate_fn= my_collate)
all_x, all_y = readdata(all_input_name)
all_ds = Mydataset(all_x, all_y)
all_dataloader = DataLoader(all_ds, batch_size = test_batch_size, shuffle = True, collate_fn= my_collate)
ConvList = []
for i in range(100):
ConvList.append(3)
for i in range(100):
ConvList.append(4)
for i in range(100):
ConvList.append(5)
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv = []
for size in ConvList :
self.conv.append(nn.Conv2d(1, 1, kernel_size=(size, vec_size)))
self.fc2 = nn.Linear(len(ConvList), 2)
def forward(self, x):
y = torch.zeros(x.size(0),1,len(ConvList))
for i in range(len(ConvList)):
z = (self.conv[i])(x)
z = F.relu(F.max_pool2d(z, kernel_size=(z.size(2),1)))
z = z.view(-1)
y[:,0,i] = z
y = F.softmax(self.fc2(y),dim=2)
return y.view(-1,2)
network = Net()
optimizer = optim.SGD(network.parameters(), lr=learning_rate, momentum=momentum)
network_state_dict = torch.load('model.pth')
network.load_state_dict(network_state_dict)
optimizer_state_dict = torch.load('optimizer.pth')
optimizer.load_state_dict(optimizer_state_dict)
def expand(batch):
max_len = 0
for sen in batch :
max_len =max(max_len, sen.size(0))
out = torch.zeros(len(batch), 1, max_len, vec_size)
for i in range(len(batch)):
for j in range(batch[i].size(0)):
out[i, 0, j] = batch[i][j]
return out
def loss_fun(pred, tar):
loss = 0
for i in range(tar.size(0)):
loss -= torch.log(pred[i][tar[i]])
return loss
log_interval = 30
def train(epoch):
print('----Epoch {} Start---'.format(epoch))
loss_sum = 0
for batch_idx, (x, y) in enumerate(train_dataloader):
network.train()
optimizer.zero_grad()
x = expand(x)
pred = network(x)
loss = loss_fun(pred, y)/train_batch_size
loss.backward()
optimizer.step()
if batch_idx % log_interval == 0:
print('Train Epoch: {} [{:.0f}%]'.format(epoch, 100.0 * batch_idx / len(train_dataloader)))
torch.save(network.state_dict(), './model.pth')
torch.save(optimizer.state_dict(), './optimizer.pth')
test(test_dataloader)
torch.save(network.state_dict(), './model.pth')
torch.save(optimizer.state_dict(), './optimizer.pth')
def Accurate(pred, y):
cnt = 0
for i in range(y.size(0)):
if pred[i][0]>pred[i][1]:
k = 0
else:
k = 1
if k == y[i]:
cnt += 1
return cnt
def test(test_dataloader):
network.eval()
loss_sum = 0
AC = 0
Total = 0
with torch.no_grad():
for batch_idx, (x, y) in enumerate(test_dataloader):
x = expand(x)
pred = network(x)
loss_sum += loss_fun(pred, y).item()
AC += Accurate(pred, y)
Total += y.size(0)
print("Test Output : {} AC rate : {}%".format(loss_sum,100.0*AC/Total))
test(test_dataloader)
for epoch in range(n_epoch):
train(epoch)
torch.save(network.state_dict(), './model/model{}.pth'.format(epoch))
torch.save(optimizer.state_dict(), './model/optimizer{}.pth'.format(epoch))
print('Result of Epoch ',epoch)
test(all_dataloader)
print('\n')
结论
感想
- 这是我第一次训练神经网络,从连python都不会到自己写出来(当然少不了查找资料),花费了一个星期的时间,虽然连续开十几个标签页递归式查找资料真的很痛苦,但是当你看到屏幕上不断跳动的Loss Output在不断变低时的喜悦是难以言表的。
- 当然,作为菜鸟在这个方面还有很多不会的东西,上文里甚至可能会有写错了的东西,但不管怎么样这都是一个不断进步的过程。
- 与此同时也是一个我对于人工智能怯魅的过程,其实也没有那么神奇嘛,都是计算机一点点统计出来、算出来的,不过也常常难免为这种统计的有效性而惊讶。
|