基于词向量的英汉翻译——“火炬上的深度学习"下第一次作业

在这个作业中,你需要半独立地完成一个英文到中文的单词翻译器

本文件是集智AI学园http://campus.swarma.org 出品的“火炬上的深度学习”第VI课的配套源代码


In [1]:
# 加载必要的程序包
# PyTorch的程序包
import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

# 数值运算和绘图的程序包
import numpy as np
import matplotlib.pyplot as plt
import matplotlib


# 加载机器学习的软件包,主要为了词向量的二维可视化
from sklearn.decomposition import PCA

#加载Word2Vec的软件包
import gensim as gensim
from gensim.models import Word2Vec
from gensim.models.keyedvectors import KeyedVectors
from gensim.models.word2vec import LineSentence

#加载正则表达式处理的包
import re

#在Notebook界面能够直接显示图形
%matplotlib inline

第一步:加载词向量

首先,让我们加载别人已经在大型语料库上训练好的词向量


In [2]:
# 加载中文词向量,下载地址为:链接:http://pan.baidu.com/s/1gePQAun 密码:kvtg
# 该中文词向量库是由尹相志提供,训练语料来源为:微博、人民日报、上海热线、汽车之家等,包含1366130个词向量
word_vectors = KeyedVectors.load_word2vec_format('vectors.bin', binary=True, unicode_errors='ignore')
len(word_vectors.vocab)


Out[2]:
1366130

In [3]:
# 加载中文的词向量,下载地址为:http://nlp.stanford.edu/data/glove.6B.zip,解压后将glove.6B.100d.txt文件拷贝到与本notebook
# 文件一致的文件夹洗面。
f = open('glove.6B.100d.txt', 'r')
i = 1

# 将英文的词向量都存入如下的字典中
word_vectors_en = {}
with open('glove.6B.100d.txt') as f:
    for line in f:
        numbers = line.split()
        word = numbers[0]
        vectors = np.array([float(i) for i in numbers[1 : ]])
        word_vectors_en[word] = vectors
        i += 1
print(len(word_vectors_en))


400000

第二步:可视化同一组意思词在两种不同语言的词向量中的相互位置关系


In [4]:
# 中文的一二三四五列表
cn_list = {'一', '二', '三', '四', '五', '六', '七', '八', '九', '零'}
# 阿拉伯数字的12345列表
en_list = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}
# 英文数字的列表
en_list = {'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'zero'}

# 对应词向量都存入到列表中
cn_vectors = []  #中文的词向量列表
en_vectors = []  #英文的词向量列表
for w in cn_list:
    cn_vectors.append(word_vectors[w])
for w in en_list:
    en_vectors.append(word_vectors_en[w])

# 将这些词向量统一转化为矩阵
cn_vectors = np.array(cn_vectors)
en_vectors = np.array(en_vectors)

In [5]:
# 降维实现可视化
X_reduced = PCA(n_components=2).fit_transform(cn_vectors)
Y_reduced = PCA(n_components = 2).fit_transform(en_vectors)

# 绘制所有单词向量的二维空间投影
f, (ax1, ax2) = plt.subplots(1, 2, figsize = (10, 8))
ax1.plot(X_reduced[:, 0], X_reduced[:, 1], 'o')
ax2.plot(Y_reduced[:, 0], Y_reduced[:, 1], 'o')
zhfont1 = matplotlib.font_manager.FontProperties(fname='/home/fuyang/.fonts/YaHei.Consolas.1.11b.ttf', size=16)
for i, w in enumerate(cn_list):
    ax1.text(X_reduced[i, 0], X_reduced[i, 1], w, fontproperties = zhfont1, alpha = 1)
for i, w in enumerate(en_list):
    ax2.text(Y_reduced[i, 0], Y_reduced[i, 1], w, alpha = 1)


结论:可以看出,中文的一、二、等数字彼此之间的关系与英文的数字彼此之间的关系很类似

第三步:训练一个神经网络,输入一个英文单词的词向量,输出一个中文的词向量,并翻译为中文

首先,读入一个已经建立好的词典(dictionary.txt)。本词典是老师调用百度翻译的API,自动将一篇英文小说中的词汇逐个翻译为中文而得来的

我们一个个地载入词典,并查找对应的中文词向量,如果找得到,则放入original_words中,做为正式的训练集


In [6]:
original_words = []
with open('dictionary.txt', 'r') as f:
    dataset = []
    for line in f:
        itm = line.split('\t')
        eng = itm[0]
        chn = itm[1].strip()
        if eng in word_vectors_en and chn in word_vectors:
            data = word_vectors_en[eng]
            target = word_vectors[chn]
            # 将中英文词对做成数据集
            dataset.append([data, target])
            original_words.append([eng, chn])
print(len(dataset)) # 共有4962个单词做为总的数据集合


4962

In [7]:
# 建立训练集、测试集和校验集
# 训练集用来训练神经网络,更改网络的参数;校验集用来判断网络模型是否过拟合:当校验集的损失数值超过训练集的时候,即为过拟合
# 测试集用来检验模型的好坏
indx = np.random.permutation(range(len(dataset)))
dataset = [dataset[i] for i in indx]
original_words = [original_words[i] for i in indx]
train_size = 500
train_data = dataset[train_size:]
valid_data = dataset[train_size // 2 : train_size]
test_data = dataset[: train_size // 2]
test_words = original_words[: train_size // 2]

In [8]:
print(len(train_data), len(valid_data), len(test_data))


4462 250 250

In [18]:
# 开始训练一个多层神经网络,将一个100维度的英文向量映射为200维度的中文词向量,隐含层节点为30

input_size = 100
output_size = 200
hidden_size = 30

# 新建一个神经网络,包含一个隐含层
model = nn.Sequential(nn.Linear(input_size, hidden_size),
                     nn.Tanh(),
                     nn.Linear(hidden_size, output_size)
                     )
print(model)

# 构造损失函数
criterion = torch.nn.MSELoss()

# 构造优化器
optimizer = torch.optim.Adam(model.parameters(), lr = 0.0001)

# 总的循环周期
num_epoch = 100


#开始训练500次,每次对所有的数据都做循环
results = []
for epoch in range(num_epoch):
    train_loss = []
    for data in train_data:
        # 读入数据
        x = Variable(torch.FloatTensor(data[0])).unsqueeze(0)
        y = Variable(torch.FloatTensor(data[1])).unsqueeze(0)
        # 模型预测
        output = model(x)
        
        # 反向传播算法训练
        optimizer.zero_grad()
        loss = criterion(output, y)
        train_loss.append(loss.data.numpy()[0])
        loss.backward()
        optimizer.step()
    # 在校验集上测试一下效果
    valid_loss = []
    for data in valid_data:
        x = Variable(torch.FloatTensor(data[0])).unsqueeze(0)
        y = Variable(torch.FloatTensor(data[1])).unsqueeze(0)
        output = model(x)
        loss = criterion(output, y)
        valid_loss.append(loss.data.numpy()[0])
    results.append([np.mean(train_loss), np.mean(valid_loss)])
    print('{}轮,训练Loss: {:.2f}, 校验Loss: {:.2f}'.format(epoch, np.mean(train_loss), np.mean(valid_loss)))


Sequential (
  (0): Linear (100 -> 30)
  (1): Tanh ()
  (2): Linear (30 -> 200)
)
0轮,训练Loss: 8.73, 校验Loss: 8.56
1轮,训练Loss: 8.30, 校验Loss: 8.26
2轮,训练Loss: 8.07, 校验Loss: 8.10
3轮,训练Loss: 7.93, 校验Loss: 7.99
4轮,训练Loss: 7.82, 校验Loss: 7.91
5轮,训练Loss: 7.74, 校验Loss: 7.83
6轮,训练Loss: 7.67, 校验Loss: 7.77
7轮,训练Loss: 7.61, 校验Loss: 7.72
8轮,训练Loss: 7.56, 校验Loss: 7.68
9轮,训练Loss: 7.52, 校验Loss: 7.64
10轮,训练Loss: 7.48, 校验Loss: 7.61
11轮,训练Loss: 7.45, 校验Loss: 7.58
12轮,训练Loss: 7.42, 校验Loss: 7.56
13轮,训练Loss: 7.39, 校验Loss: 7.53
14轮,训练Loss: 7.36, 校验Loss: 7.51
15轮,训练Loss: 7.34, 校验Loss: 7.49
16轮,训练Loss: 7.32, 校验Loss: 7.47
17轮,训练Loss: 7.30, 校验Loss: 7.45
18轮,训练Loss: 7.28, 校验Loss: 7.44
19轮,训练Loss: 7.27, 校验Loss: 7.42
20轮,训练Loss: 7.25, 校验Loss: 7.41
21轮,训练Loss: 7.24, 校验Loss: 7.39
22轮,训练Loss: 7.22, 校验Loss: 7.38
23轮,训练Loss: 7.21, 校验Loss: 7.37
24轮,训练Loss: 7.20, 校验Loss: 7.36
25轮,训练Loss: 7.19, 校验Loss: 7.35
26轮,训练Loss: 7.18, 校验Loss: 7.34
27轮,训练Loss: 7.17, 校验Loss: 7.33
28轮,训练Loss: 7.16, 校验Loss: 7.33
29轮,训练Loss: 7.15, 校验Loss: 7.32
30轮,训练Loss: 7.14, 校验Loss: 7.31
31轮,训练Loss: 7.14, 校验Loss: 7.31
32轮,训练Loss: 7.13, 校验Loss: 7.30
33轮,训练Loss: 7.12, 校验Loss: 7.29
34轮,训练Loss: 7.11, 校验Loss: 7.29
35轮,训练Loss: 7.11, 校验Loss: 7.28
36轮,训练Loss: 7.10, 校验Loss: 7.28
37轮,训练Loss: 7.10, 校验Loss: 7.28
38轮,训练Loss: 7.09, 校验Loss: 7.27
39轮,训练Loss: 7.09, 校验Loss: 7.27
40轮,训练Loss: 7.08, 校验Loss: 7.26
41轮,训练Loss: 7.08, 校验Loss: 7.26
42轮,训练Loss: 7.07, 校验Loss: 7.26
43轮,训练Loss: 7.07, 校验Loss: 7.25
44轮,训练Loss: 7.07, 校验Loss: 7.25
45轮,训练Loss: 7.06, 校验Loss: 7.25
46轮,训练Loss: 7.06, 校验Loss: 7.25
47轮,训练Loss: 7.05, 校验Loss: 7.24
48轮,训练Loss: 7.05, 校验Loss: 7.24
49轮,训练Loss: 7.05, 校验Loss: 7.24
50轮,训练Loss: 7.05, 校验Loss: 7.24
51轮,训练Loss: 7.04, 校验Loss: 7.24
52轮,训练Loss: 7.04, 校验Loss: 7.23
53轮,训练Loss: 7.04, 校验Loss: 7.23
54轮,训练Loss: 7.04, 校验Loss: 7.23
55轮,训练Loss: 7.03, 校验Loss: 7.23
56轮,训练Loss: 7.03, 校验Loss: 7.23
57轮,训练Loss: 7.03, 校验Loss: 7.23
58轮,训练Loss: 7.03, 校验Loss: 7.23
59轮,训练Loss: 7.02, 校验Loss: 7.22
60轮,训练Loss: 7.02, 校验Loss: 7.22
61轮,训练Loss: 7.02, 校验Loss: 7.22
62轮,训练Loss: 7.02, 校验Loss: 7.22
63轮,训练Loss: 7.02, 校验Loss: 7.22
64轮,训练Loss: 7.02, 校验Loss: 7.22
65轮,训练Loss: 7.01, 校验Loss: 7.22
66轮,训练Loss: 7.01, 校验Loss: 7.22
67轮,训练Loss: 7.01, 校验Loss: 7.22
68轮,训练Loss: 7.01, 校验Loss: 7.22
69轮,训练Loss: 7.01, 校验Loss: 7.22
70轮,训练Loss: 7.01, 校验Loss: 7.22
71轮,训练Loss: 7.01, 校验Loss: 7.21
72轮,训练Loss: 7.00, 校验Loss: 7.21
73轮,训练Loss: 7.00, 校验Loss: 7.21
74轮,训练Loss: 7.00, 校验Loss: 7.21
75轮,训练Loss: 7.00, 校验Loss: 7.21
76轮,训练Loss: 7.00, 校验Loss: 7.21
77轮,训练Loss: 7.00, 校验Loss: 7.21
78轮,训练Loss: 7.00, 校验Loss: 7.21
79轮,训练Loss: 7.00, 校验Loss: 7.21
80轮,训练Loss: 7.00, 校验Loss: 7.21
81轮,训练Loss: 6.99, 校验Loss: 7.21
82轮,训练Loss: 6.99, 校验Loss: 7.21
83轮,训练Loss: 6.99, 校验Loss: 7.21
84轮,训练Loss: 6.99, 校验Loss: 7.21
85轮,训练Loss: 6.99, 校验Loss: 7.21
86轮,训练Loss: 6.99, 校验Loss: 7.21
87轮,训练Loss: 6.99, 校验Loss: 7.21
88轮,训练Loss: 6.99, 校验Loss: 7.21
89轮,训练Loss: 6.99, 校验Loss: 7.21
90轮,训练Loss: 6.99, 校验Loss: 7.21
91轮,训练Loss: 6.99, 校验Loss: 7.21
92轮,训练Loss: 6.99, 校验Loss: 7.21
93轮,训练Loss: 6.98, 校验Loss: 7.21
94轮,训练Loss: 6.98, 校验Loss: 7.21
95轮,训练Loss: 6.98, 校验Loss: 7.21
96轮,训练Loss: 6.98, 校验Loss: 7.21
97轮,训练Loss: 6.98, 校验Loss: 7.20
98轮,训练Loss: 6.98, 校验Loss: 7.20
99轮,训练Loss: 6.98, 校验Loss: 7.20

In [19]:
# 绘制图形
a = [i[0] for i in results]
b = [i[1] for i in results]
plt.plot(a, 'o', label = 'Training Loss')
plt.plot(b, 's', label = 'Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss Function')
plt.legend()


Out[19]:
<matplotlib.legend.Legend at 0x7f6edcaba630>

In [20]:
# 在测试集上验证准确度
# 检验标准有两个:一个是直接用预测的词和标准答案做全词匹配;另一个是做单字的匹配
exact_same = 0  #全词匹配数量
one_same = 0 #单字匹配数量
results = []
for i, data in enumerate(test_data):
    x = Variable(torch.FloatTensor(data[0])).unsqueeze(0)
    # 给出模型的输出
    output = model(x)
    output = output.squeeze().data.numpy()
    # 从中文词向量中找到与输出向量最相似的向量
    most_similar = word_vectors.wv.similar_by_vector(output, 1)
    # 将标准答案中的词与最相似的向量所对应的词打印出来
    results.append([original_words[i][1], most_similar[0][0]])
    
    # 全词匹配
    if original_words[i][1] == most_similar[0][0]:
        exact_same += 1
    # 某一个字匹配
    if list(set(list(original_words[i][1])) & set(list(most_similar[0][0]))) != []:
        one_same += 1
    
print("精确匹配率:{:.2f}".format(1.0 * exact_same / len(test_data)))
print('一字匹配率:{:.2f}'.format(1.0 * one_same / len(test_data)))
print(results)


精确匹配率:0.08
一字匹配率:0.18
[['猛烈', '反抗'], ['飞行', '探测器'], ['恶意', '兴奋'], ['终止', '告知'], ['周围', '不远处'], ['开朗', '温暖'], ['吃', '吃'], ['亲爱的', '我'], ['支持', '和'], ['犹豫', '为难'], ['蔬菜', '水果'], ['拉', '卡住'], ['战栗', '颤抖'], ['为', '提供'], ['决定', '合理'], ['踢', '打'], ['菜', '水果'], ['稻草人', '兔子'], ['问题', '应该'], ['聊', '闲聊'], ['舞者', '舞者'], ['奥尔良', '夜晚'], ['百胜', '哇'], ['412', '15'], ['苏格兰', '圣乔治'], ['然后', '然后'], ['烟', '烟雾'], ['敲', '倒'], ['任命', '聘请'], ['质量', '生物体'], ['行星', '行星'], ['寺', '岩洞'], ['你', '疼'], ['运动', '重复'], ['侦探', '特工'], ['玩', '他'], ['塑料', '布料'], ['咧嘴一笑', '大喊'], ['礼貌', '礼貌'], ['均匀', '着装'], ['警告', '指责'], ['脚尖', '停下来'], ['见鬼', '我'], ['未知', '灭绝'], ['孩子', '妈妈'], ['插图', '图示'], ['送', '俯身'], ['拉尔夫', '米歇尔'], ['轨道', '连接'], ['数据库', '数据库'], ['霍格尔', 'stanislaw'], ['印第安纳波利斯', '布伦德尔'], ['什么', '运动员'], ['晚上', '夜晚'], ['高度', '长度'], ['放缓', '导致'], ['检验', '检查'], ['毒', '毒药'], ['巴伐利亚', '西欧'], ['记录', '序列号'], ['宣布', '推翻'], ['请求', '告知'], ['功率', '控制'], ['开始', '结束'], ['霜', '巧克力'], ['脸', '拉扯'], ['线索', '知道'], ['嘲笑', '斥责'], ['感动', '惊恐'], ['认为', '想'], ['烂', '有时候'], ['圈', '斜'], ['标志', '贴纸'], ['紫色', '青色'], ['油墨', '纸张'], ['窃听', '关掉'], ['微弱', '颤动'], ['恨', '因为'], ['咩咩', '叫喊'], ['促使', '指责'], ['赋', '快乐'], ['崩溃', '雷击'], ['徽章', '徽章'], ['过去', '反抗'], ['人', '我'], ['少数', '听说'], ['调整', '计算'], ['泰勒曼', '音乐'], ['组成', '专用'], ['顺口', '说'], ['管理', '解决'], ['毛钱', '碗'], ['解除', '关闭'], ['拖', '车把'], ['女', '妇女'], ['行动', '行为'], ['建设', '工程'], ['普雷斯顿', '亚历山大'], ['块', '底板'], ['生活', '苦难'], ['柜', '工作台'], ['原', '原型'], ['拼盘', '盒'], ['特权', '权利'], ['藏', '躲'], ['从容', '停下来'], ['盎司', '伊甸园'], ['田园', '孤寂'], ['直立', '后背'], ['系列', '怪兽'], ['否则', '因此'], ['耻辱', '背叛'], ['评分', '走位'], ['不', '恼怒'], ['大小', '大小'], ['洗劫', '丢弃'], ['停', '挡板'], ['额头', '后背'], ['重复', '标记'], ['优势', '灵活性'], ['相比', '平均'], ['论文', '包装袋'], ['理解', '理解'], ['两侧', '一字排开'], ['选美', '舞团'], ['磨', '粘'], ['状态', '国家'], ['气球', '降落伞'], ['留声机', '噗'], ['部分', '荒谬'], ['啤酒', '冰淇淋'], ['激怒', '愤慨'], ['一旦', '因为'], ['他们', '自己'], ['法国', '法国'], ['马力', '重量'], ['吞没', '坍塌'], ['入侵', '阻止'], ['刷', '擦'], ['能', '需要'], ['食人', '恶魔'], ['拉里', '彼得'], ['争论', '认为'], ['茶杯', '牛角'], ['实例', '文本'], ['吸收', '膨胀'], ['拨号', '语音'], ['沙漠', '沙漠'], ['制造商', '组件'], ['地方', '小木屋'], ['衣柜', '衣服'], ['开始', '休整'], ['收拢', '勾'], ['金发', '粉红色'], ['39', '15'], ['恢复', '移除'], ['1814', '11'], ['文件', '文档'], ['派', '豆子'], ['锁定', '卡住'], ['沿', '沿着'], ['头脑', '恐惧'], ['阿拉伯', '敌视'], ['鬣蜥', '蜥蜴'], ['订购', '告知'], ['假定', '换句话说'], ['后台', '洗手间'], ['是', '或者说'], ['填补', '装好'], ['遗传', '生物体'], ['王子', '太太'], ['强度', '强度'], ['显示', '动画'], ['僵硬', '粗'], ['活着', '死去'], ['大家', '我'], ['方向', '站位'], ['墓', '骸骨'], ['食用', '浆果'], ['呆', '休息'], ['提高', '有效地'], ['姜', '菠萝'], ['怀疑', '也许'], ['标签', '钩子'], ['爬', '翻滚'], ['500', '50'], ['禁止', '强迫'], ['引导', '内容'], ['皇后', '海滩'], ['二十', '20'], ['梦', '梦想'], ['布鲁斯', '里德'], ['膨化', '粘'], ['帕洛米诺', '使用'], ['炒', '铲子'], ['闪耀', '光亮'], ['昆虫', '昆虫'], ['捆', '形状'], ['法术', '回去'], ['海滩', '海滩'], ['需要', '需要'], ['明星', '魔术师'], ['表面', '孔洞'], ['用户', '终端设备'], ['经理', '取得联系'], ['表示', '说明'], ['神话', '奇异'], ['出租车', '开关'], ['涂鸦', '文字'], ['火灾', '破坏'], ['侧', '边沿'], ['徽章', '肩章'], ['惊', '听到'], ['项目', '物品'], ['神秘', '外星人'], ['教', '想'], ['好', '我'], ['迷惑', '害怕'], ['违约', '退回'], ['蟑螂', '蜥蜴'], ['是', '所以'], ['石', '山岭'], ['轻微', '轻微'], ['组成', '精练'], ['拖鞋', '铆钉'], ['动摇', '不安'], ['唐纳', '强烈抗议'], ['炒', '豆子'], ['叶', '草叶'], ['主题', '主题'], ['马戏团', '丛林'], ['粗鲁', '害怕'], ['烂', '豆子'], ['手', '双手'], ['工具', '工具'], ['随时', '如果'], ['叶片', '肋'], ['糟', '可能'], ['冰雹', '咆哮'], ['弯曲', '边沿'], ['暂停', '转过身'], ['电极', '电极'], ['咀嚼', '吃'], ['草莓', '蜂蜜'], ['保护', '保护'], ['假定', '也就是说'], ['新', '猜测'], ['允许', '使用'], ['赫斯顿', '杰克'], ['模仿', '它']]

In [ ]: