语言模型与新闻的内容预测

  • 从国内主流新闻网站(如腾讯、新浪、网易等)的科技频道抓取新闻内容
  • 对新闻数据进行预处理(分句、分词、去噪)
  • 使用预处理后的新闻数据训练两个语言模型
    • 简单的 n-gram 语言模型(n 取 2 或者 3)
    • 基于 RNN/LSTM/GRU 的语言模型
  • 在测试集检验训练好的模型的性能

实验环境

本地环境

所用机器型号为 VAIO Z Flip 2016。

  • Intel(R) Core(TM) i7-6567U CPU @3.30GHZ 3.31GHz
  • 8.00GB RAM
  • Windows 10, 64-bit (Build 17763) 10.0.17763
  • Visual Studio Code 1.39.2
    • Python 2019.10.41019:九月底发布的 VSCode Python 插件支持在编辑器窗口内原生运行 juyter nootbook 了,非常赞!
    • Remote - WSL 0.39.9:配合 WSL,在 Windows 上获得 Linux 接近原生环境的体验。
  • Windows Subsystem for Linux [Ubuntu 18.04.2 LTS]:WSL 是以软件的形式运行在 Windows 下的 Linux 子系统,是近些年微软推出来的新工具,可以在 Windows 系统上原生运行 Linux。
    • Python 3.7.4 64-bit (‘anaconda3’:virtualenv):安装在 WSL 中。
      • requests==2.20.0
      • beautifulsoup4==4.8.1
      • jieba==0.39
      • torch==1.1.0

集群环境

借用了超算队(祖传的)一个 K80 节点。

  • CPU 是双路 intel Xeon e5-2660v3 处理器
  • 256GB RAM
  • GPU 每节点为两张 NVIDIA K80
  • CUDA/8.0
  • Python/3.6.4-anaconda2
  • PyTorch/0.5a
$ yhcontrol show node gn21
NodeName=gn21 Arch=x86_64 CoresPerSocket=10
   CPUAlloc=0 CPUErr=0 CPUTot=20 CPULoad=1.84 Features=(null)
   Gres=(null)
   NodeAddr=gn21 NodeHostName=gn21
   OS=Linux RealMemory=256000 AllocMem=0 Sockets=2 Boards=1
   State=IDLE ThreadsPerCore=1 TmpDisk=0 Weight=1
   BootTime=2019-08-05T09:29:10 SlurmdStartTime=2019-08-05T09:29:34
   CurrentWatts=0 LowestJoules=0 ConsumedJoules=0
   ExtSensorsJoules=n/s ExtSensorsWatts=0 ExtSensorsTemp=n/s
$ nvidia-smi
Wed Nov 13 20:41:55 2019
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 390.30                 Driver Version: 390.30                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla K80           Off  | 00000000:04:00.0 Off |                    0 |
| N/A   51C    P0    59W / 149W |      0MiB / 11441MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   1  Tesla K80           Off  | 00000000:05:00.0 Off |                    0 |
| N/A   40C    P0    71W / 149W |      0MiB / 11441MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   2  Tesla K80           Off  | 00000000:84:00.0 Off |                    0 |
| N/A   50C    P0    60W / 149W |      0MiB / 11441MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   3  Tesla K80           Off  | 00000000:85:00.0 Off |                    0 |
| N/A   39C    P0    71W / 149W |      0MiB / 11441MiB |     98%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

实验过程

spider.py爬虫,获得训练数据

从国内主流新闻网站(如腾讯、新浪、网易等)的科技频道抓取新闻内容。

要求新闻语言为中文,发布日期为 2019 年 1 月 1 日以后,数量至少为 1000 条。

可以考虑尝试用 BeautifulSoup 进行 html 内容分析。

腾讯滚动新闻获得新闻 url,然后解析新闻正文保存到本地。

# coding=UTF-8
import os
import requests
import re
from bs4 import BeautifulSoup

newsList = []


def finNextPage(newsUrl):
    url = newsUrl.split(".htm")
    url = url[0] + '_2.htm'
    result = requests.get(url)
    if (result.status_code == 200):
        return url


def getListPage(pageUrl):
    reslistnew = requests.get(pageUrl)
    reslistnew.encoding = 'gbk'
    souplistnew = BeautifulSoup(reslistnew.text, 'html.parser')
    for news in souplistnew.select('li'):
        if len(news.select('.pub_time')) > 0:
            newsUrl = news.select('a')[0].attrs['href']
            # getNewsDetail(newsUrl)
            result = re.search('http(.*?)html', newsUrl)
            if result is None:
                # getNewsDetail(newsUrl)
                newsList.append(newsUrl)


# 判断当天新闻是否存在下一页
for month in range(1, 13):
    for day in range(1, 32):
        try:
            mon = str(month)
            da = str(day)
            if(month < 10):
                mon = '0'+mon
            if(day < 10):
                da = '0'+da
            url = 'http://tech.qq.com/l/2019{mon}/scroll_{da}.htm'.format(
                mon=mon, da=da)
            nextUrl = finNextPage(url)
            if nextUrl:
                getListPage(nextUrl)
            getListPage(url)
        except:
            print("Error: "+url)
    print(str(month)+" "+str(len(newsList)))
# 根据上一步获得的Url抓取新闻
if not os.path.exists("吴坎_17341163_新闻数据/"):
    os.mkdir("吴坎_17341163_新闻数据/")
for i in range(len(newsList)):
    try:
        newsUrl = newsList[i]
        resd = requests.get(newsUrl)
        resd.encoding = 'gbk'
        soupd = BeautifulSoup(resd.text, 'html.parser')
        news = {}
        Cnt_Main_Article = soupd.select('.Cnt-Main-Article-QQ')
        Main_P_QQ = soupd.select('Main-P-QQ')
        if soupd.select('.rv-middle'):
            news['content'] = soupd.select('h1')[0].text
        else:
            news['title'] = soupd.select('h1')[0].text
            if Cnt_Main_Article:
                news['content'] = Cnt_Main_Article[0].text
            elif Main_P_QQ:
                news['content'] = '空'
            else:
                news['content'] = '空'
        f = open("吴坎_17341163_新闻数据/"+str(i+1)+".txt", 'w', encoding='utf-8')
        f.write(news['content'])
        f.close()
    except:
        print("Error: "+newsList[i])

jiebaCut.py

对新闻数据进行预处理(分句、分词、去噪),可以使用已有的一些中文分词工具

根据前一次作业的经验,这里仍然使用jieba进行分词。

我分句的逻辑比较简单,直接根据常见的终止标点符号((。|!|\!|\.|?|\?))进行分句;而数据清洗过程更加粗暴,这里直接根据字符的 UTF 编码筛掉其中非中文汉字的部分。

# coding=UTF-8
import io
import os
import re
import jieba

if __name__ == '__main__':
    if not os.path.exists("吴坎_17341163_预处理结果/"):
        os.mkdir("吴坎_17341163_预处理结果/")
    for filename in os.listdir("吴坎_17341163_新闻数据/"):
        f = io.open("吴坎_17341163_新闻数据/"+filename, 'r', encoding='utf-8')
        sentences = re.split("(。|!|\!|\.|?|\?)", f.read())
        f.close()
        f = io.open("吴坎_17341163_预处理结果/"+filename, 'w', encoding='utf-8')
        for s in sentences:
            for w in jieba.cut(s):
                is_all_chinese = True
                for _char in w:
                    if not '\u4e00' <= _char <= '\u9fa5':
                        is_all_chinese = False
                        break
                if is_all_chinese:
                    f.write(w+" ")
            f.write("\n")
        f.close()

wordsList.py完成建立词表的工作

# coding=UTF-8
import io
import os


def getWordsList():
    sentence_set = []
    word_set = set()
    for filename in os.listdir("吴坎_17341163_预处理结果/"):
        if filename != "wordsList.txt":
            with io.open("吴坎_17341163_预处理结果/"+filename, 'r', encoding='utf-8')as f:
                for sentence in f.readlines():
                    if not sentence.isspace():
                        sentence_set.append(sentence)
                        word_set.update(sentence.split())

    # 构建词典,注意:word_to_ix用于对字词进行编号,ix_to_word用于将模型的输出转化为字词
    # 'EOS': end of sentence
    # 'SOS': start of sentencex
    # 'UNK': unknown token
    word_to_ix = {'SOS': 0, 'EOS': 1, 'UNK': 2}
    ix_to_word = {0: 'SOS', 1: 'EOS', 2: 'UNK'}
    for word in word_set:
        if word not in word_to_ix:
            word_to_ix[word] = len(word_to_ix)
            ix_to_word[len(ix_to_word)] = word
    return sentence_set, word_to_ix, ix_to_word


if __name__ == '__main__':
    _, _, ix_to_word = getWordsList()
    with io.open('吴坎_17341163_预处理结果/wordsList.txt', 'w', encoding='UTF-8') as f:  # 保存词典
        for vocab in ix_to_word.items():
            f.write(str(vocab[0])+' '+str(vocab[1])+'\n')

使用预处理后的新闻数据训练两个语言模型,并在测试集检验训练好的模型的性能

其一为简单的 n-gram 语言模型(n 取 2 或者 3);其二为基于 RNN/LSTM/GRU 的语言模型。词典大小、网络和训练的超参数等可以自己指定。

我们提供的测试数据是在同一来源的新闻中随机抽取的 100 个句子,其中每个句子有一个词被挖空,要求预测出这些词。预测的结果好坏仅作为最终的分数的参考,重点在实验过程。

在 n-gram 模型中,去掉停止词能节省资源、提高效率。可参考https://github.com/goto456/stopwords

显卡资源不足时,可考虑采用梯度累积的方法进行训练。

没接触过深度学习的同学可以先做 Pytorch 官网的 tutorials,有个初步了解。

这里借用了(没人用)的 K80 节点(V100 集群上的调度队列要等到第二天…

N-Gram.py

参考了PyTorch 搭建 N-gram 模型实现单词预测。这个模型是在本地使用 CPU 跑的,因此使用的样本集合非常小。

# coding=UTF-8
import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.nn.functional as F
import torch.optim as optim
from wordsList import getWordsList
import io

CONTEXT_SIZE = 2
EMBEDDING_DIM = 10
EPOCH = 7
Sentence_Num = 31


class NgramModel(nn.Module):
    def __init__(self, vocb_size, context_size, n_dim):
        # super(NgramModel, self)._init_()
        super().__init__()
        self.n_word = vocb_size
        self.embedding = nn.Embedding(self.n_word, n_dim)
        self.linear1 = nn.Linear(context_size*n_dim, 128)
        self.linear2 = nn.Linear(128, self.n_word)

    def forward(self, x):
        # the first step: transmit words and achieve word embedding. eg. transmit two words, and then achieve (2, 100)
        emb = self.embedding(x)
        # the second step: word wmbedding unfold to (1,200)
        emb = emb.view(1, -1)
        # the third step: transmit to linear model, and then use relu, at last, transmit to linear model again
        out = self.linear1(emb)
        out = F.relu(out)
        out = self.linear2(out)
        # the output dim of last step is the number of words, wo can view as a classification problem
        # if we want to predict the max probability of the words, finally we need use log softmax
        log_prob = F.log_softmax(out)
        return log_prob


def getNgram(trigram, word2id, id2word):
    ngrammodel = NgramModel(len(word2id), CONTEXT_SIZE, 100)
    criterion = nn.NLLLoss()
    optimizer = optim.SGD(ngrammodel.parameters(), lr=1e-3)
    for epoch in range(EPOCH):
        print('epoch: {}'.format(epoch+1))
        print('*'*10)
        running_loss = 0
        for data in trigram:
            # we use 'word' to represent the two words forward the predict word, we use 'label' to represent the predict word
            word, label = data                          # attention
            word = Variable(torch.LongTensor([word2id[e] for e in word]))
            label = Variable(torch.LongTensor([word2id[label]]))
            # forward
            out = ngrammodel(word)
            loss = criterion(out, label)
            running_loss += loss.data.item()
            # backward
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
        print('loss: {:.6f}'.format(running_loss/len(word2id)))
    return ngrammodel


# predict
if __name__ == '__main__':
    sentence_set, word2id, id2word = getWordsList()
    trigram = []
    for j in range(Sentence_Num):
        test_sentence = sentence_set[j].split()
        test_sentence.insert(0, 'SOS')
        test_sentence.append('EOS')
        for i in range(len(test_sentence)-2):
            trigram.append(
                ((test_sentence[i], test_sentence[i+1]), test_sentence[i+2]))

    ngrammodel = torch.load('吴坎_17341163_预测结果/ngrammodel.pkl')
    #ngrammodel = getNgram(trigram, word2id, id2word)
    torch.save(ngrammodel, '吴坎_17341163_预测结果/ngrammodel.pkl')

    fout = io.open("吴坎_17341163_预测结果/prediction.ngram.txt",
                   "w", encoding="utf-8")
    with io.open("吴坎_17341163_预测结果/questions.1.txt", "r", encoding="utf-8") as f:
        for sentence in f.readlines():
            wordss = sentence.split()
            wordss.insert(0, 'SOS')
            wordss.append('EOS')
            for j in range(len(wordss)):
                if j+6 >= len(wordss):
                    word = wordss[j+2], wordss[j+3]
                    break
                if wordss[j+5] == "[MASK]":
                    word = wordss[j+3], wordss[j+4]
                    break
            word = Variable(torch.LongTensor([word2id[i] for i in word]))
            out = ngrammodel(word)
            _, predict_label = torch.max(out, 1)
            fout.write(id2word[out.max(1)[1].item()]+"\n")
    fout.close()
screen.log

python N-Gram.py > screen.log的运行记录。可以看到这里训练迭代几乎没有什么效果,可以和下面搭建的 GRU 进行对比。

epoch: 1
**********
/home/wuk/N-Gram.py:36: UserWarning: Implicit dimension choice for log_softmax has been deprecated. Change the call to include dim=X as an argument.
  log_prob = F.log_softmax(out)
loss: 0.041276
epoch: 2
**********
loss: 0.040883
epoch: 3
**********
loss: 0.040478
epoch: 4
**********
loss: 0.040048
epoch: 5
**********
loss: 0.039582
epoch: 6
**********
loss: 0.039063
epoch: 7
**********
loss: 0.039042

real	42m35.763s
user	27m15.786s
sys	9m6.868s
prediction.ngram.txt

只预测成功一个第十行的手机

冻胀
偷税
告别
无所不用其极
尊重知识
低污染
迪信通
陆霆
几抹
手机
分中心
报名表
小花招
伯乐
中央社
宜家
锥切
涌入
闻所未闻
成都市委
隆重开幕
出位
审计长
试点工作
战上
说理
巴伐利亚州
商救
怨言
杰所称
八种
五六万
上过
足坛
稍早
掏
环月
确确实实
干洗
等值
项城
政治立场
永存
交表
张宏伟
李玉琢
外事活动
砍价
五金模具
评
银
正畸
过问
星等
文华
爱好者
瘫痪病人
恩施
生产工具
涌入
积极向上
影踪
聚才
实操性
热区
模拟化
越选越
分配程序
马云能
严
展于
职业经理
费德林
格雷戈
两位数
公认
十份
警方正
专业人士
今天上午
已到
摸摸底
分中心
字数
建造成
缓存数据
尚柿
不愧
映像管
官方版
美国在线
宜贷
掣肘
张宏伟
偷工减料
大班
积极向上
壁障
越选越
结核病

GRU.py

参考了[实现] 利用 Seq2Seq 预测句子后续字词 (Pytorch)

# coding=UTF-8
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
from torch.utils.data import DataLoader, Dataset
import numpy
import io
from wordsList import getWordsList

# Set Hyper Parameters
sentence_set, word_to_ix, ix_to_word = getWordsList()
Sentence_Num = 300
Embedding_Dim = len(word_to_ix)
LR = 0.005
EPOCH = 31
BATCH_SIZE = 1


class MyDataset(Dataset):
    def __init__(self, words, labels, transform=None, target_transform=None):
        self.words = words
        self.labels = labels
        self.transform = transform
        self.target_transform = target_transform

    def __getitem__(self, index):
        words, labels = self.words[index], self.labels[index]
        if self.transform is not None:
            words = [self.transform(word) for word in words]
        if self.target_transform is not None:
            labels = self.target_transform(labels)
        return words, labels

    def __len__(self):
        return len(self.labels)


def bulid_one_hot(word, word_dict):
    if word in word_dict:
        return torch.LongTensor([word_dict[word]])
    return torch.LongTensor([word_dict['UNK']])


def getTrainDataSetLoader():
    # transforms.ToTensor(), transforms.ToTensor()
    train_words, train_labels, trans, target_trans = [], [], None, None

    for i in range(Sentence_Num):
        words = sentence_set[i].split()
        words.insert(0, 'SOS')
        words.append('EOS')
        words = [bulid_one_hot(word, word_to_ix) for word in words]

        for j in range(len(words)):
            if j+6 >= len(words):
                break
            train_words.append(words[j:j+5])
            train_labels.append(words[j+5])

    return DataLoader(dataset=MyDataset(train_words, train_labels, trans, target_trans), batch_size=BATCH_SIZE)


def getTestDataSetLoader():
    test_words, test_labels, trans, target_trans = [], [], None, None
    # transforms.ToTensor(), transforms.ToTensor()
    fquestion = io.open("吴坎_17341163_预测结果/questions.1.txt",
                        "r", encoding="utf-8")
    fanswer = io.open("吴坎_17341163_预测结果/answer.txt", "r", encoding="utf-8")
    for sentence in fquestion.readlines():
        ans = fanswer.readline().split()[0]
        test_labels.append(bulid_one_hot(ans, word_to_ix))
        wordss = sentence.split()
        wordss.insert(0, 'SOS')
        wordss.append('EOS')
        words = [bulid_one_hot(word, word_to_ix) for word in wordss]
        for j in range(len(words)):
            if j+6 >= len(words):
                test_words.append(words[j-1:j+4])
                break
            if wordss[j+5] == "[MASK]":
                test_words.append(words[j:j+5])
                break
    fquestion.close()
    fanswer.close()
    return DataLoader(dataset=MyDataset(test_words, test_labels, trans, target_trans), batch_size=BATCH_SIZE)


class Encoder(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(Encoder, self).__init__()
        self.hidden_size = hidden_size
        self.embedding = nn.Embedding(
            input_size, hidden_size)     # 将one-hot向量embedding为词向量
        # GRU的hidden layer的size与词向量的size一样,并非必须
        self.gru = nn.GRU(hidden_size, hidden_size)

    def forward(self, input, hidden):
        # RNN的输入格式为 (seq_len, batch, input_size)
        embedded = self.embedding(input).view(1, 1, -1)
        output = embedded
        output, hidden = self.gru(output, hidden)
        return output, hidden

    def initHidden(self):
        # 初始化Encoder的隐状态
        return torch.zeros(1, 1, self.hidden_size)


class Decoder(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(Decoder, self).__init__()
        self.hidden_size = hidden_size
        self.embedding = nn.Embedding(input_size, hidden_size)
        self.gru = nn.GRU(hidden_size, hidden_size)
        self.out = nn.Linear(hidden_size, output_size)

    def forward(self, input, hidden):
        output = self.embedding(input).view(1, 1, -1)
        output = F.relu(output)
        output, hidden = self.gru(output, hidden)
        output = self.out(output[0])
        return output, hidden

    def initHidden(self):
        return torch.zeros(1, 1, self.hidden_size)


class Seq2Seq(nn.Module):  # Bulid Seq2Seq Model
    def __init__(self, encoder, decoder):
        super(Seq2Seq, self).__init__()
        self.encoder = encoder
        self.decoder = decoder

    def forward(self, inputs):
        encoder_hidden = self.encoder.initHidden()
        if torch.cuda.is_available():
            encoder_hidden = encoder_hidden.cuda()

        # encode
        for word in inputs:
            encoder_out, encoder_hidden = self.encoder(word, encoder_hidden)

        # decode
        decoder_hidden = encoder_hidden
        pred, decoder_hidden = self.decoder(inputs[-1], decoder_hidden)

        return pred


def getSeq2Seq():
    encoder = Encoder(Embedding_Dim, 1000)
    decoder = Decoder(Embedding_Dim, 1000, Embedding_Dim)

    if torch.cuda.is_available():
        encoder = encoder.cuda()
        decoder = decoder.cuda()
    seq2seq = Seq2Seq(encoder, decoder)
    if torch.cuda.is_available():
        seq2seq = seq2seq.cuda()

    # Bulid loss function and optimizer

    loss_func = nn.CrossEntropyLoss()
    #encoder_optimizer = optim.SGD(encoder.parameters(), lr=LR, momentum=0.9)
    #decoder_optimizer = optim.SGD(decoder.parameters(), lr=LR, momentum=0.9)
    seq2seq_optimizer = optim.SGD(seq2seq.parameters(), lr=LR, momentum=0.9)
    # Train Seq2Seq Model

    train_loader = getTrainDataSetLoader()

    for epoch in range(EPOCH):
        loss_sum = 0
        for step, (inputs, labels) in enumerate(train_loader):
            # encoder_hidden = encoder.initHidden()
            label = torch.LongTensor((1,))
            label[0] = int(labels.data.numpy()[0])

            if torch.cuda.is_available():
                inputs = [word.cuda() for word in inputs]
                label = label.cuda()
                # encoder_hidden = encoder_hidden.cuda()

            # forward
            pred = seq2seq(inputs)
            loss = loss_func(pred, label)

            # backward
            seq2seq_optimizer.zero_grad()
            loss.backward()
            seq2seq_optimizer.step()

            '''
            for word in inputs:
                encoder_out, encoder_hidden = encoder(word, encoder_hidden)
            decoder_hidden = encoder_hidden
            decoder_out, decoder_hidden = decoder(inputs[-1], decoder_hidden)

            loss = loss_func(decoder_out,label)

            #backward
            encoder_optimizer.zero_grad()
            decoder_optimizer.zero_grad()
            loss.backward()
            encoder_optimizer.step()
            decoder_optimizer.step()
            '''

            loss_sum += loss.item()
        print('Epoch: %2d train loss: %.4f' % (epoch, loss_sum))
    return seq2seq


if __name__ == '__main__':
    #seq2seq = torch.load('吴坎_17341163_预测结果/seq2seq.pkl')
    seq2seq = getSeq2Seq()
    '''
    torch.save(encoder.state_dict(),'吴坎_17341163_预测结果/encoder_params.pkl')
    torch.save(decoder.state_dict(),'吴坎_17341163_预测结果/decoder_params.pkl')

    torch.save(encoder,'吴坎_17341163_预测结果/encoder.pkl')
    torch.save(decoder,'吴坎_17341163_预测结果/decoder.pkl')
    torch.save(seq2seq.state_dict(), '吴坎_17341163_预测结果/seq2seq_params.pkl')
    '''
    torch.save(seq2seq, '吴坎_17341163_预测结果/seq2seq.pkl')
    test_loader = getTestDataSetLoader()
    with io.open("吴坎_17341163_预测结果/prediction.txt", "w", encoding="utf-8") as f:
        for step, (inputs, labels) in enumerate(test_loader):
            # encoder_hidden = encoder.initHidden()
            label = torch.LongTensor((1,))
            label[0] = int(labels.data.numpy()[0])

            if torch.cuda.is_available():
                inputs = [word.cuda() for word in inputs]
                label = label.cuda()
                # encoder_hidden = encoder_hidden.cuda()

            decoder_output = seq2seq(inputs)

            '''
            # forward
            for word in inputs:
                encoder_out, encoder_hidden = encoder(word, encoder_hidden)
            decoder_hidden = encoder_hidden
            decoder_out, decoder_hidden = decoder(inputs[-1], decoder_hidden)
            '''

            #ans = ix_to_word[int(labels.data.numpy()[0])]
            pred = ix_to_word[numpy.argmax(decoder_output.cpu().data.numpy())]
            f.write(pred+"\n")
slurm-89265.out

上述 GRU 代码的运行记录。可以看到,十次迭代之后损失函数就不怎么变化了。

Epoch:  0 train loss: 5104.7179
Epoch:  1 train loss: 2278.6186
Epoch:  2 train loss: 1007.3043
Epoch:  3 train loss: 576.0494
Epoch:  4 train loss: 376.3167
Epoch:  5 train loss: 229.3300
Epoch:  6 train loss: 113.6827
Epoch:  7 train loss: 40.9807
Epoch:  8 train loss: 36.9288
Epoch:  9 train loss: 3.0620
Epoch: 10 train loss: 0.8962
Epoch: 11 train loss: 0.7600
Epoch: 12 train loss: 0.6700
Epoch: 13 train loss: 0.6037
Epoch: 14 train loss: 0.5522
Epoch: 15 train loss: 0.5106
Epoch: 16 train loss: 0.4762
Epoch: 17 train loss: 0.4471
Epoch: 18 train loss: 0.4221
Epoch: 19 train loss: 0.4003
Epoch: 20 train loss: 0.3811
Epoch: 21 train loss: 0.3640
Epoch: 22 train loss: 0.3487
Epoch: 23 train loss: 0.3349
Epoch: 24 train loss: 0.3224
Epoch: 25 train loss: 0.3109
Epoch: 26 train loss: 0.3004
Epoch: 27 train loss: 0.2908
Epoch: 28 train loss: 0.2818
Epoch: 29 train loss: 0.2735
Epoch: 30 train loss: 0.2657

real	61m35.763s
user	39m15.786s
sys	11m6.868s
prediction.txt

GRU 的预测结果,成功预测了十六行的「门店」、二十七行的「中心」、九十五行的「产品」。

三年
门店
了
基础
的
涨跌
集团
的
模式
的
便利
的
至
提升
做
门店
便利
以来
便利
基础
他们
分布
实现
的
至
便利
中心
便利
模式
加速
实现
鲜度
提升
最大
便利
做
鲜食
医院
医院
模式
系统
医院
数据
新
的
鲜食
模式
基础
程度
实现
上
的
便利
做
便利
布局
门店
了
了
的
市值
经营
模式
鲜食
科学
首批
不同
鲜食
上
实现
新
个
实现
了
城市
的
门店
模式
又
这
全
上
的
这
上
上
个
以来
华北
基础
新
鲜食
传统
城市
产品
以来
市值
控
自动
上

question.txt

测试集。

一直以来,有不少家长误认为,在开车时将孩子放在儿童安全[MASK]上是最为安全的方式。
在这个汽车技术飞速发展的时代,[MASK]已经成为主要的交通工具。
2020年开始,拜腾将接受来自[MASK]和北美地区的预定,并将于2021年进入欧美市场。
背面则采用全新的[MASK]材质,更加坚固不易破碎,玻璃本身也是哑光质感而不是光滑的。
该店员透露,此魅族专卖店在2年前就开始不赚钱,[MASK]一直下滑。
体验人员在驴妈妈平台体验预订景点门票时,发现[MASK]门票均绑定了内馆门票、导览、讲解等收费项目,没有单卖的故宫门票供选择,涉嫌强制消费。
荷兰车辆管理局是欧洲负责向车辆和汽车零部件企业[MASK]许可证的机构之一。
而相比于国内的支付领域发展,跨境支付的前景也更加[MASK],PayPal进入中国后,很可能发挥其独特优势,在跨境支付领域发力。
我们致力于以开放的态度参与[MASK],而且不附带任何条件。我们的拼车服务可能会取消。
9月20日,工业和信息化部部长苗圩在国新办新闻发布会上表示,现在中国市场上商业发布的5G手机有11款,大部分[MASK]都是非独立组网。
研究者在策略网络上使用了循环[MASK]的架构。他们使用有着 ReLU 激活函数的全连接层作为输入层,并使用一个单层 LSTM 层进行处理。
总而言之,从大环境上来说,目前仍然是处于教育用户的阶段,加上扫地[MASK]品类的下限很低,市场还没有形成一个足够清晰的认知。
TOF技术虽然在手机应用上占据了大量的市场,但大多数[MASK]由于比较「鸡肋」而难以支撑其进一步发展。
中国是世界第二大经济体,居民人均可支配收入持续[MASK],消费产品的构成也逐渐向发达国家趋同。
在谈到与特斯拉的竞争时,小鹏汽车高管表示,小鹏没有照搬特斯拉的系统,但特斯拉系统中很好的部分会去[MASK],与此同时特斯拉踩过坑我们可以避免。
便利蜂以「数字驱动」的创新模式实现了对大规模[MASK]的全直营管理。
报道称,接下来几周,巴西境内亚马孙雨林的偏远西北部及西部地区会有更多降雨,但东部区域仍维持非常[MASK]的状态.
这一事件也让新西兰[MASK]意识到,加强国内的防范和应对能力是遏制恐怖主义、极端主义信息传播不可或缺的一环。
在三大运营商积极展开5G部署工作的同时,各大手机[MASK]也在积极布局5G产品,说到这里就不能不提到OPPO了。
据彭博社报道,由于业绩不佳,动视暴雪计划在下周二宣布规模达数百人的[MASK],有知情人士透露这是公司内部重组计划的一部分,旨在集中职能和提升利润。
关于手机像素一直都是热门话题,也是很多用户购机的主要[MASK]之一。
苹果突然上线了AirPods Pro,而接下来[MASK]还有多款新品上市,即将到来的是Mac Pro。
虽然新设计与现在的航天服很像,比如它们都装饰有[MASK]的红蓝色条纹,但是新航天服仍有许多新特点。
快手方面表示,靠恶意炒作吸引粉丝关注本质上伤害了快手记录与分享的初心,也伤害了社区公平,必须严厉[MASK], 否则会败坏整个生态,最终受伤的是平台。
易而言之,在鼓励的同时,需要加大力度打击国内大量涉嫌[MASK]、诈骗的所谓币圈、链圈。
当下获取简历最主要方式是通过企业账号发布[MASK]信息,等待求职者投递简历,再将简历提取出来售卖或直接利用。
安徽合肥是目前国家批准建设的三大综合性国家科学[MASK]之一,拥有全球首个全超导托卡马克EAST核聚变实验装置,以及认知智能国家重点实验室、类脑智能技术及应用国家工程实验室等重点科研机构。
众说纷纭的一个重要原因,是以目前[MASK]的技术水平,观测海王星外天体还很困难。
月夜结束后,受阳光照射作用,当两器太阳翼帆板输出功率达到唤醒阈值时,两器就能按照预定[MASK]将一些关键设备通电开机,恢复与地面的通讯联系。
学生禁带手机进校园的规定落实之初,被抓到玩手机的学生会由[MASK]谈话,没收的手机交给家长。
数字自动化程度提高的同时,也意味着人力勘探需求的降低,这也有助于减少碳排放与资源[MASK],更好地实现成本控制。
中国联通董事长王晓初会上表示中国联通今年计划[MASK]的5万个5G基站,已经大部分开通,开通了2.8万个基站。
滴滴出行董事长兼CEO程维称:自动驾驶将大幅提升出行安全、效率,助力城市更智能可持续[MASK]。
1947年,第一个双极结型晶体管诞生于贝尔实验室,引领了人类社会进入[MASK]的新时代。
数字化货币发展可以分为两个阶段:第一个[MASK]是以比特币为代表的数字化货币。
而针对目前存在的注销难,注销过程中出现需要用户去[MASK]开具证明或手持身份证拍照等繁琐程序。
随后李国庆微博发长文回应,称俞渝对我私生活做出的[MASK]和诬蔑,我只想在这里回应一句话:等着收律师函吧。
台积电董事长刘德音表示,他最近与美国[MASK]讨论了在美新建工厂事宜。
不过,一个现象也特别值得关注:有些厂商开始[MASK]机器人背后的人,在近几年兴起的机器人教育培训市场掘金。
对大多数人来说,再花钱购买另一项订阅[MASK]可能听起来并不是很有吸引力,但苹果押注其初创公司云集的内容,即使是最节俭的客户也会被说服。
家用投影仪现在基本已经无人不知无人不晓,不过还是会有很多朋友们出于节约[MASK]考虑,在选择第一台投影仪时买了那些千元以下的「廉价机型」。
去年,万豪酒店集团就被曝最多有5亿客人信息[MASK],拥有超过1亿会员数据的华住也同样面临风险。
我认为,华为公司提供了技术,我们保证遵守网络安全和GDPR隐私保护体系。但是网络最终是控制在主权国家手里的,主权国家通过[MASK]来管理和控制网络,因此这不是华为能做到与做不到的问题。
此次石头科技在日本上线的第一款产品「 Roborock S6」,同时拥有扫地和[MASK]两种功能。
截止发稿,特朗普总统在Twitch上已经拥有了9000个关注,约有8700名[MASK]正在观看他正在召开的集会。
同时,在实验观测结果的[MASK]上,该工作又对之前的理论模型进行了修正和完善。
所谓「开源」,就是将软件的源代码发布到某一社区,允许所有的社区[MASK]对其进行修正、改进和创新,并将其成果与社区内的所有成员共享。
此外,如果你仍然在使用最便宜的数据套餐,那么你不应该期望你的数字生活会出现任何的[MASK]。
持续的科技繁荣背后的信念是,在拥有足够[MASK]的巨大市场中,精明的企业家可以设计出自己的方式,即使是应对最具挑战性的问题。
据悉,酒水作为天猫超市重点类目,[MASK]的销售占比达到一半以上。此前,天猫超市与茅台、五粮液、洋河股份、泸州老窖、郎酒等白酒名企均已建立深度合作。
《报告》表示,2017年《中华人民共和国网络安全法》的正式实施,以及相关配套法规的陆续出台,为此后开展的网络[MASK]工作提供了切实的法律保障。
小程序轻便易操作的产品特性以及强大的连接能力,能帮助线下零售业更高效地连接线上与线下流量、拓展服务半径和品类、增强渠道影响力,已成为[MASK]的「必备工具」之一。
9月10日晚间消息,阿里20周年年会今日举行,马云登台发言正式[MASK]卸任阿里巴巴董事局主席。
消费者普遍接受了这种涨价。只有30%的受访者表示,他们正在减少使用共享单车服务,或者干脆停止使用。53%的受访者表示价格上涨对他们没有影响,或者不知道[MASK]上涨了。
然而,若卫视为了挽救业绩,一味向广告商低头,将节目内容更多地倾向于广告商,必将在一定程度上牺牲用户体验,进而陷入[MASK]流失、收视下降、广告商离场、业绩持续下滑的恶性循环。
对苹果今年这三款新手机的[MASK]续航时间,华尔街日报专栏作家乔安娜斯特恩似乎都特别满意。
虽然现在谈明年的iPhone还有些早,但是苹果早已行动起来。从产业链传出的最新消息称,苹果iPhone明年将配备120Hz屏幕,提高[MASK]的刷新率。
虽然燃油汽车业务有不错的表现,但新能源[MASK]仍是考验比亚迪盈利的重中之重,获得的巨额补贴是原因之一。
特别是近年来,京东、小米、奇虎360、滴滴、美团、旷视、商汤、驭势科技、寒武纪、地平线等一批新生代科技[MASK]快速崛起,在国际竞争中崭露头角,使中关村企业家精神代代相传、发扬光大。
城市交通,杭州有条穿城而过的高架路全长22公里,城市大脑接入后,出行时间平均节省了4.6分钟,大概是10%的时间;萧山区104个路口信号灯自动调控,车辆通过速度提升了15%,平均节省[MASK]3分钟。
消息人士此前告诉外媒称,诺依曼有权以高达9.7亿美元的[MASK]出售其在该公司的股份,作为软银将从投资者和员工手中购买高达30亿美元的WeWork股份的要约的一部分。
电器店在整个家乐福数万平米的卖场中,占用的[MASK]并不大。
波士顿咨询公司则在一份报告中指出,电动化、自动驾驶汽车和共享出行创造的利润[MASK],预计将从2017年的1%将增长至2035年的40%。
在开放政策和营商环境优化方面,中国政府对于外资企业在华投资和发展新产业等提供的支持[MASK]受到欢迎。
从政策上来讲,一方面,在[MASK]层面需要制定规范制度,比如什么样的车可以做自动驾驶测试;自动驾驶将来运营的时候,需要遵循哪些规则;怎么解决安全问题等。
年前9个月,在中国,实际卖给真实消费者电动车大概只有十几万辆,与特斯拉在美国前9月[MASK]14万辆差距不大。
中国载人航天工程网发布消息,谴责造谣诽谤杨利伟的言论,将依法严肃追究有关当事人的[MASK]责任。
本次「月面微型生态圈」实验位置距离地球38万公里,比离地300多公里的国际空间站遥[MASK]得多,所处月面环境也比国际空间站内部复杂得多。
而钱不够、能满足超前消费的需求是90后用户选择消费金融产品的首要[MASK]。
近日,一加[MASK]了7T系列新品手机。
渲染图显示,新款AirPods除了此前曝光的白色和黑色版本外,还新增了全新的灰色和金色[MASK]。
全通教育发布公告,受宏观经济环境、上市公司及标的资产经营情况、重组政策变化以及股票二级市场价格波动等[MASK]影响,交易双方未能就本次重组方案所涉交易定价、业绩承诺与补偿安排等要素达成最终共识,公司决定终止筹划本次重大资产重组事项,并签订有关终止协议。
全通教育发布公告,受宏观经济环境、上市公司及标的资产经营情况、重组政策变化以及股票二级市场价格波动等因素[MASK],交易双方未能就本次重组方案所涉交易定价、业绩承诺与补偿安排等要素达成最终共识,公司决定终止筹划本次重大资产重组事项,并签订有关终止协议。
苏宁金服相关负责人在接受[MASK]采访时表示,《办法》出台后,公司也将积极配合监管机构开展相关工作。
今年7月初,谷歌地图为印度用户增加了三项新[MASK],帮助他们发现当地体验,并获得个性化的推荐,以获得更好的用餐体验,其中包括「优惠」部分,帮助他们找到优惠券,并在11个城市的餐厅认领。
有超过六成被调查者表示可以接受公开透明收取适当服务费用,但前提是要让消费者充分知情,并且让[MASK]自主选择。
但是收集数据、处理数据的效率非常低。但是现在用了机器学习的方法之后,能极大提高[MASK],提升判断管道事件的及时性和准确性。
全球儿童死亡数量已从1950年的1960万例[MASK]至2017年的540万例。
实质上卢伟冰的这个点评最重要的点在于宣传小米的价值观,那就是花同样的钱买到更好的产品,而不是花更多的钱买到体验一般的[MASK]。
安信证券研报指出,随着资本市场对人工智能认知的不断深入,市场对[MASK]的投资日趋成熟和理性,投融资频次在2018年以来有所放缓,但投资金额持续增加。
5G和高速光纤宽带的发展,使用户对网络的[MASK]日益增长,也间接对Starlink的网络容量提出了更高的要求。
上海移动的工作人员也向记者表示,暂时没有听说停止销售该类套餐。目前三大运营商中,只有中国电信明确将停售达量限速[MASK]。
自从提速降费的政策实施以后,中国移动的业绩开始承压,出现了鲜见的营收、利润双跌。中国移动此前披露的2019年半年显示,公司上半年[MASK]3894亿元,同比下降0.6%,股东应占利润561亿元,同比下降14.6%。
就像几乎所有人类生产活动一样,锂离子电池的生产也对[MASK]造成了一定影响,但也为环境带来了巨大的益处。有了锂离子电池,研究人员得以发明更清洁的能源技术和电动汽车,从而有力减少了温室气体和颗粒物的排放。
第二个门槛就是[MASK]上的门槛了。云计算的创业对于资金的要求会比较高,很多没有资金实力的创业者也就要想轻松介入到云计算和大数据相关领域创业会相当难,这就需要政府以及资本市场来加大对于云计算、大数据创业的扶持力度。
「网络安全形势发生了突变,我们需要携手构建网络[MASK]命运共同体。」360集团董事长兼CEO周鸿祎说。
苹果正在把自己重塑为一家以服务为基础的公司,并且推出了流媒体视频、视频游戏和信用卡等[MASK]。
从财务数据来看,2016年优刻得出现[MASK],但2017年起扭亏为盈,此后保持较快增长。
除了欧盟、美国之外,包括韩国、日本在内的其他[MASK]地区的政府,也曾经或者正在对苹果进行各种反垄断调查。
这一数字也解释了软银在公开市场投资英伟达股票遭遇的重大损失。软银记录的[MASK]为2995亿日元,约合27亿美元。
双方称,将以签署战略合作协议为契机,持续推进5G领域的实质性业务合作,在5G、AI、云计算、物联网等新技术领域,开展更广泛的交流和[MASK]。
从来没有一个产业,像中国互联网产业一样,发展的[MASK]如此惊人。
互联网和人工智能技术为全世界的各个国家都带来巨变。在这一方面,中国做得非常优秀,中国将互联网与[MASK]技术结合得「炉火纯青」,这些经验值得各个国家学习。
总之,5G将开启一个新智能机时代,而创新将是新智能机时代的第一发展动力,期待国产品牌在新[MASK]时代大有作为。
面对山河日下的情况,漫步者推出旗下定位于高端无线便携音响的[MASK],聚焦打造售价、利润率更高的高端产品线,以期提高利润率。
工业和信息化部无线电管理局组织召开了无线电管理宣传工作座谈会,代表们就[MASK]管理宣传工作开展情况、存在的问题和困难、下一步工作思路等进行了热烈讨论,并就如何进一步提升无线电管理宣传的效率和效果提出了意见和建议。
中国公司开发的应用今年第一季度在美国共计实现了6.748亿美元的[MASK],同比增长67%,估计占今年第一季度前100强应用总营收的22%。
腾讯安全平台部作为专注腾讯企业内部安全的团队,也把十余年腾讯自身安全最佳实践对外开放赋能,并逐步把[MASK]能力向腾讯云上开放,做好产业互联时代的数字化安全助手。
华为nova5在电商平台就拍网最新一期的竞拍活动中以低至199元的[MASK]成交,不仅创下了该机上市以来的价格新低,也是智能机价格史上的里程碑。
携程的目标是三年内成为亚洲最大的国际旅游企业,五年内成为全球最大的国际旅游企业,十年内成为最具价值和最受尊敬的在线旅游[MASK]。

answer.txt

测试答案。

座椅
汽车
欧洲
玻璃
利润
故宫
颁发
广阔
对话
手机
神经网络
机器人
应用
增长
学习
门店
干燥
政府
厂商
裁员
需求
他们
鲜艳
打击
传销
招聘
中心
人类
程序
班主任
浪费
建设
发展
信息技术
阶段
公安局
诽谤
商务部
关注
服务
成本
泄露
运营商
拖地
观众
基础
成员
改善
资本
白酒
安全
零售业
宣布
价格
用户
电池
屏幕
汽车
企业
时间
价格
面积
占比
政策
法律
销量
法律
远
原因
发布
版本
因素
影响
媒体
功能
消费者
效率
降
产品
人工智能
需求
套餐
营收
环境
资金
安全
服务
亏损
国家
损失
合作
速度
人工智能
智能机
产品
无线电
营收
安全
价格
企业

总结与思考(遇到的困难及采用的解决方法、后续改进方向等)

数据来源

这里选择了腾讯网的科技频道作为数据来源。这里遇到一个问题,他的科技频道首页是用 js 脚本动态从服务器中抓取新闻的,不能直接解析出新闻的 url。认识的有些同学是模拟浏览操作向服务器发包获得新闻链接,不过我在这里选择了更简单的做法:从腾讯滚动新闻直接抓取信息,其链接有很明显的规律,html 解析起来也不是那么麻烦。

爬取数据时连接被中断

爬的时候多用一用try:语句块跳过异常(因为数据量很大,直接跳过一两条也不会有什么影响)

PyTorch

之前没有做过机器学习相关的知识,翻了不少博客和官方文档来学习。好在在 PyTorch 中搭建神经网络模型比较容易上手,只需定义好相关变量,以及对应的前向传播过程,其他的过程都可以自动生成。

计算资源

我自己使用的机器没有独立显卡,在本地训练只好使用 CPU 进行计算,因此只能使用使用非常少的输入数据(不到 100 条句子),对应的准确率基本上都是 0。换用集群进行计算之后训练的速度确实大大加快,也就是能够塞更多的训练数据。听说大公司训练出的成熟对话模型的基本上都用了上 GB 级别的语料集合进行训练,那么看来做机器学习确实很吃计算资源。后面自己有空、V100 集群也空闲的时候考虑换更强的显卡再跑一下看看效果。

预测结果

两个模型预测的成功率都只有个位数,最大的问题所在其实是训练集不够大,大部分要预测的单词都没有在选择的一百句上下的样本集的词表里面出现过。用训练的样本集进行检测的时候,n-gram 搭建的模型可以达到 30%左右的准确率,GRU 的准确率也有 12%左右。不过,应对实际问题的时候显然这样还远远不够,用样本集测试的结果也不能让人信服。我觉得,其实这两个模型都是按照既定的统计方法进行预测,不能算作真正的人工智能,也不能真的取代人工(不过训练数据量上去了也许可以应付一些小众的专业领域),还需要更好的语言模型。尤其是 n-gram,虽然有一点统计上极大似然的道理(并且在训练集上的效果明显优于 GRU),但是不理解句子语义的极大似然很可能就是断章取义的行为,也不能很好的预测新出现的词汇。

再看一下预测出来的四个词「手机」、「门店」、「中心」、「产品」,都是名词;我这里没有用常用停用词去剪枝,后续也可以考虑使用一些常用的中文停用词剪枝,应该能缩短训练的时间。

模型数据的保存与加载

训练时经常要涉及到参数的调整,如果每次都要重新训练数个小时来检验是否有问题的话时间和精力上都是难以接受的。这里我参考Pytorch 保存模型与加载模型,学会了使用torch.save(model,path),torch.load(path)来保存训练好的模型。