欢迎来到百丽百科
百丽百科
当前位置:网站首页 > 网络 > BDCI2017《人机大战》竞赛总结

BDCI2017《人机大战》竞赛总结

日期:2023-10-07 03:43

更新:
天哪,第三名开源了:https://www.zs-baili.com/fuliucansheng/360


更新:
比赛第一名经验分享:https://www.zs-baili.com/p/33243415


0。前言:这将是一篇又长又臭的日记

明年我要找工作。看到我还是这么坏人,我心里很着急。我一直在考虑找一些比赛来做,这样到时候我的简历就不会是一片空白。但他们总是以“学得不够、能力不够”为借口一拖再拖。
我决定参加的时候是11月初。当时国内主要的比赛有BDCI 2017系列赛、京东金融的比赛、天池以及11月底的滴滴算法。
我看了滴滴的问题,发现需要数学建模,这一直是我头疼的问题;京东的“猪脸”题(噱头威力MAX),考虑到初赛12月初才结束,可以晚几天做。
另外BDCI是CCF组织的,总觉得CCF更靠谱(误解1)。看完竞赛题,360的《真假文本分类》似乎是最容易上手的(错觉2),就这样吧。
这篇文章简单讲一下比赛经历,然后总结一下我尝试过的方法。大佬们答辩完毕后,如果有人开源了,我想再次转载一下(更新:本文发表之前,还没有开源)。


书面更新:
我非常惊讶地在 GitHub 上发现了两个仓库。本以为大佬们这么厉害,写出的代码比作者整洁多了,但是低头一看,他们的排名并不比作者高。 (羞死我了。
即便如此,他们的代码比作者的更有价值。说实话,当时我无法解释分数。我尝试了几次模型,但没有重现。不知道为什么,有什么问题吗?因为这段时间很忙,就没有再想了。
所以这个结果有点侥幸。如果TOP5打不开的话以后源码学习一下这两个方法就好了。OK


仓库地址:t https://www.zs-baili.com/a550461053/bdci2017-360
https://www.zs-baili.com/kongliang2015/bdci_360

360

360

360

360

360

补充一下,a550461053 稍后还有事要做。我看了一下他的计划,如果他继续做的话,应该能进入前15名,至少在我前面。


第一场比赛没有取得什么好成绩(初赛21分,复赛17分),方法也比较刁钻,所以本文没有经验,只有教训。
由于是我自己总结的废话,所以这篇文章可能会非常非常长,而且没有营养。请读者不要指望从本文中看到太多“高质量代码教学”之类的枯燥信息。

正文之前,先说几点我感受较深的。

  • 一直觉得自己不行,总想等到“学得更好、更深”才来参加比赛。这种想法基本上是错误的。 早学比晚学好,学好比不学好;本科学好比研究生好,先读研究生比再读好。 实践经验非常重要。
  • 队友至关重要。这次都是我一个人做的,现在回想起来,似乎没有什么有意义的成果,但那些日子确实很累。队友可以事半功倍。他们可以讨论、分工、互相鼓励、支持。作者认识的人太少了。虽然我有一个很棒的同学,但我又不好意思打扰别人,所以暂时找不到合适的同伙。我看了看前十的队伍,都至少有三个人,而且大部分都已经满员了。
    当然,如果你实力足够,请忽略这一点。
  • 留出足够的硬盘空间。 我使用的主机是祖传服务器,配置合理。但是我安装系统的时候没有仔细考虑。我的硬盘只装了128G SSD。好家伙,在初赛结束时,大约有三十或四十千兆字节消失了。大部分数据是中间数据,例如分词文件、np 张量、网络模型档案等。看模型的参数量级,我有几个模型超过1G。一般来说,如果后期做多模型融合,至少要留出三十个模型的空间。比如半决赛第一队一共出了80个模型……
  • 前面说过,早打比晚打好。这是因为游戏会给你一个“半现实”的应用场景,让你折腾、做题。当你去做的时候,你会发现很多操作可能和你所学的理论关系不大,甚至与你所学的知识无关。这可以让人多少体会到工业界和学术界之间的巨大差距。有时你可能会突然做不到一半。这个时候,你可能需要回到理论轨道,重新思考问题出在哪里。这个迭代的过程就是能力升级的过程。当然,这种理论与实践的偏差,其实说明了作者不善于学习,并不是理论有问题。
  • 所以最好同时具备扎实的基础和优秀的实践能力。前者是指导思想,后者是有力保证。
  • 说起这个差距,我突然明白了Andrew Ng.在他的课程中的教学理念......

1。问:仔细看问题是个好习惯

大赛官方链接:http://www.zs-baili.com/#/competitions/276/intro。
题目大意是区分“机器书写”和“真实”文本。为简洁起见,以下简称为“假文本”和“真实文本”。

就像考试一样,参加此类比赛首先要做的就是看题。重点包括数据格式、提交格式、评审标准、规则,甚至比赛日程——还真有人不看截止日期就参与。如果您有任何不清楚的地方,请尽快询问客服。通常会开设比赛微信和QQ群。
第二件事是看数据
比赛页面上写得很清楚数据格式“#ID\t#TITLE\t#CONTENT\t#LABEL”,样本之间用换行符“\n”分隔。
这样读取数据的脚本就很容易写了:读取文件全部内容直接使用.readlines(),使用.strip().split('\ t' 位于样本内部)


1.1 浏览数据

2e4e1c09ec9f1d9fda4fde58929d28f6 雁栖加强社会消防技术服务机构监督管理进一步规范社会消防技术服务机构管理,创建法制化实践 ,还有更多无资质执业的情况,也存在无资质执业的情况。雁栖大队组织开展社会消防技术服务机构专项检查,督促社会消防技术服务机构充分履行工作职能,不断提高建筑消防设施设备完好率,整合各方面力量全力以赴保持全地区火灾形势持续稳定。雁栖大队党委高度重视,立足辖区成立了专项巡视领导小组。重点对辖区从事建筑消防设施维护检测的单位资质、办公场所、设备、人员等进行一一现场检查,对社会单位的执业情况进行核查。一一组织对辖区3家有建筑消防设施的单位进行检查和维护,对照年度消防设施检查消防技术服务活动和许可资质的一致性,核对检查报告、维护报告和——现场检查工程项目与社会单位情况的一致性,检查维护记录是否能够真实反映自动消防设施的安装和维护状况,检查消防技术服务机构的履职情况。雁栖大队把消防技术服务机构的监督管理作为日常消防工作的重要组成部分。结合建筑消防设施“三化”建设,消防技术服务质量实行“三同”,重点落实“三必检”(一是检查消防技术服务一致性)严格查处未取得资质、挂靠资质、冒用他人名义或者超越资质范围从事社会消防技术服务活动的违法行为;其次检查工程验收报告各项目和社会单位,维护报告与实地实际情况的一致性,核实报告的真实性,严肃查处恶意检测、遗漏维护或检测结果不准确等违法行为,并下发《维护报告》。文件虚假、不实;三是检查消防技术服务机构的履职情况,对不合格人员的执业行为进行严肃查处,直至逐案降低或者取消其资格、资格,督促社会消防消防技术服务机构规范执业行为,提高服务质量,加强对消防技术服务机构的监督管理,充分发挥社会消防专业力量,大力推进消防工作社会化管理,提高社会能力以预防和控制火灾。专项整治前,社会服务机构责任意识不强,工作不深入。也有一些没有专业资格的人执业的情况。也会有单位检查时刚刚维护的问题。对于检查合格的单位,设施存在问题,单位领导评价不高。经过一段时间的专项整治,社会单位反映,消防机构的服务水平有了很大提高,能够认真履行职责,待曝光,为单位隐患整改提供技术。支持。雁栖队严格按照兵团《社会消防技术服务机构监督管理办法》、《社会消防技术服务行业信用评价管理办法》的要求,加强社会消防技术服务机构服务质量和诚信体系建设,公布违法行为、行政处罚等不良行为信息对监督管理中发现的通过报刊等曝光的,对不合格的社会消防技术服务机构,要求支队暂停使用其消防行业自律管理制度。 检查工程项目和社会单位检测报告,雁栖大队消防技术服务机构开展专项检查工作正在进行,对7家消防技术服务机构进行现场检查,对1家消防进行行政处罚技术服务机构。负
ba211cf520cd51623b87b6939e16053c 一名工人滑倒。贵州省长顺消防局成功救出该工人。图为救援现场。 7月12日17时21分,贵州省长顺县消防局接到群众报警称:长顺县朝顺洗车场旁,一名工人受伤。一名建筑工地上的工人在工作时不慎跌入房间。由于房间周围没有门窗,工人无法获救。消防队被请求救援。接到报警后,消防官兵迅速出动救援。 17时28分,官兵赶到现场,发现一名工人在干活时失足,跌入没有门窗的房间。他的腰部受了伤,无法正常活动。根据现场情况,消防官兵立即展开救援。两名官兵下屋使用多功能担架对被困人员进行固定保护,随后配合周围群众使用绳索将被困人员救出。经过约一个小时的紧张救援,18时32分,被困人员被成功救出并转移给现场医护人员。险情处理后,消防官兵立即撤离现场。积极

训练集(train.tsv)的前两个样本打印在这里。您不妨手动检查一下问题出在哪里。
(我查了一些资料,群里老大也有一些截图,看来“正例”基本上都是从网上爬取的各种新闻和广告。360 看来,爬取得到的文本是“真文本”,而算法生成或者通过某种手段制作的文本是“假文本”。这会导致一些非常有趣的情况,稍后我会举几个例子。)

第一篇材料乍一看似乎很“真实”,因为它符合大多数人心目中的公告模板。但仔细观察就会发现很多问题。
光是前两句话,凭着我脑子里残留的一点中学语文知识,我就发现了以下几个问题:

  • 开头缺少一个连接词。如果按照正常语法和上下文补全的话,“further”前面应该有一个“for”。
  • 重复“或多或少”这句话。 (这个和RNN做文本生成时出现的bug很像。)
  • “检查项目”这半句话和后面的“大队怎么样了?”之间的联系显然有问题。

那么从第一个负样本中可以得到什么信息呢?

  • “明显的语法错误”可能是一个重要特征
  • “机械重复”应该是一个重要特征(但在复赛时被新数据集黑掉了)
  • “段落意义的连续性”可以作为判断的重要依据

这三个规则并不难理解。仔细想一想,人们在判断一段文字的真假时,也会隐含地想到这样的一些规则。

1.2 初步想法

回到主题。在写处理脚本之前,先扔一个randomGuess进去,游戏评级为F1,即

score=2*prec ision*re callp recisio n+rec all. 分数 = 2 * 精确度 * 召回率 + 召回率。

不难想象,如果的正负样本数量相似且随机排列在测试集上,你不妨假设测试集服从 p= 0.5 p = 01 的 0.5 0 − 1 分布,因此将阈值设置为0.5 要做 通过随机猜测,您可以获得接近 0.5 的分数。需要注意的是,在数据挖掘比赛中,如果测试集排列有规律,这就是“数据泄露”。这样的问题在常规比赛中不应该出现。
我尝试了一下,得分为0.44。重复测试的结果相似。看来正负样本可能略有不平衡。
我对训练集做了一些统计,发现正负数的比例约为36:64。把这个比例带进来,和测试集上的分数反馈没有太大区别。
通过这样的猜测、尝试和分析,我们可以得出以下结论:

  • 正负样本不平衡,数量比例约为36:64。
  • 测试集和训练集的样本比例接近。

这两个结论有一定的参考价值,因为比赛的主要评判标准是分数,创意和具体的算法评测要到决赛才有用。这就决定了参赛者的核心目标就是“拿分”。一些根据分数计算公式稍微提高分数的方法属于竞赛的一部分,不属于作弊行为。

在看到官方的基线之前,笔者很困惑,不知道从哪里开始。从我之前做的小比赛来看,仔细想想,两人的套路还是挺不一样的。但有了官方的baseline和一些参考资料[2],笔者觉得也不是不可能做到。上面提到了TextCNN,接下来的时间就花在训练这个网络上。文献[2]中提到的其他方法也都尝试过,但效果都不好。可能是因为模型没有完全理解,超参数没有正确给出。
TextCNN 代码非常简单。网上到处都有使用tensorflow、pytorch、keras、mxnet等各种框架编写的开源程序。另外看山北也有开源资料,但笔者觉得那些东西对于没做过看山北的人来说价值不大。它们太复杂了。也许他们可以激发想法。最好看一些有意义的博文、GayHub开源等。 [3]

1.3 玩具代码

如果你还没有被上面乱七八糟的内容搞糊涂的话,你可能还记得作者写了一条后来被黑的规则。
当深度网络生成模型出现bug时,可能会重复输出一段文本。该功能可以直接在测试集上使用。所以当时群里有人通过了一个非官方的baseline,我尝试的时候居然得到了超过0.5的分数(我忘了是0.61还是) 0.67,笑死。


书面更新:
后来我在开头提到的第一个仓库中找到了这个基线,现在点击了。感谢dalao的分享。


# -*- 编码:utf-8 -*-
"""
创建于 10 月 14 日星期六 20:56:14 2017@author: qiaosj
”“”
trainfile="./evaluation_public.tsv"
with(打开(trainfile))as f:lines = [line.strip().split('\t') inf.readlines()]导入pandasaspd
df=pd.DataFrame()
id=[]
计数=[]
r=[]
对于行:ids.append(行[0])if len( line)==2:line.append('')dtl=行[2].replace('','').re地点(',', '。').替换('、','。').分割( '。')pool=[]t=0长度=0foriindtl:if len(i) < 7:继续长度+=1如果不是在 池:pool.append(i)else:t+=1tcount.append(t)df['id']=ids
df['空间']=tcount
df['标签']='负'
df['标签'][(df['空格']==0)]='正' 结果=df[['id','标签']]
www.zs-baili.com_csv('./test.csv',index=False,header=False) 

len(i)的阈值设定成7了,因为试了几次希望这个数比较高。

2。官方基线实现:用基线居然能进前四十……

赛后回顾,这三周里其实没做多少事。主要就是两个:实现官方基线和textCNN的调参。
官方基线还是挺友好的,关键代码都给了,文学这个小白实现很快实现出来。具体实现可以分为四个部分:

  • 分词
  • wordvec
  • 用fasttext做监督学习
  • 使用学得的模型做分类,并按要求初始化提交文件

下面分别做几点简单说明。

2.1分词

官方用了jieba做分词,文学试了thulac,得分有微小的提升。但不知道是不是用法不对,速度相差很大,jieba大约需要二十、三十分钟才能完成一次,而thulac则需要两三个小时。这与他们官方页面上的描述有很大不同。怀疑是API调用存在瓶颈,因为像360官方代码一样,分词是逐个样本完成的。
这里扯个题外话。理论上可以利用不同的分词结果进行模型融合。然而,即使是这一点小小的改进潜力也没有放过。有点像“抓住燕子嘴,砍铁针头”。 ,无论是在游戏后期,还是在工程实现上,既没有趣味性,也没有意义,只是一种竞争手段。

分词代码示例:

#训练数据处理
 打开(trainfile,'r',编码='ut f-8') as f , \open(Traintext, 'w', 编码='utf-8')   as 输出:for  line in f.readlines():l_ar = line.strip().分割('\ t' )if len(l_ar) != 4:继续 # 忽略格式有问题的样本 id = l_ar[0]标题 = l_ar[1]内容 = l_ar[2]标签 = l_ar[3]seg_title = jieba.cut(标题)seg_content = jieba.cut(内容)r = " " 。 join(seg_title) + " " + " ".join(seg_content)输出。写入() “__标签__” + 标签 + " " + r + '\n')#测试数据处理
打开(testFile,'r',编码=' utf-8') as tf, \打开(testSave,'w',编码='utf-8') astS , \打开(idSave,'w',编码='utf-8'as  iS:对于  line in tf.readlines():line_seg = line.strip().split ('\t' ) id = line_seg [0] title = line_seg [1] content = line_seg [2] seg_title = jieba.cut (标题) seg_Content = jieba.cut (内容) r =  " .join(seg_title) + " " + " ".join(seg_content)print(id, 文件= iS)print(r, 文件=tS)


2.2 训练词向量

官方没有给出的是word2vec网上有一些使用gensim生成词向量的操作。作者使用的是Google官方开源的C++版本。据说word2vec的超参数设置非常深。我只尝试了几组设置,结果都差不多。很难说哪个更合理。建议对这方面不熟悉的朋友先尝试一些默认的参数组合。
另外,比赛要求不能使用外部数据,不能使用外部数据训练的词向量。语料库只能从训练集和测试集中选取。所以考虑到训练词向量的初衷——“为文本的单热表示找到一个合理的分布式表示”——那么我们想要学习的应该是“真实文本“形式”的表示。这样,选择训练集中的正样本作为训练语料就合理了。
但是问题又来了。理想情况下,word2vec的训练样本越大越好,但是“训练集中的正例”相对于待处理的数据来说太少了(前面提到过,正样本只占约36%),所以训练出来的词向量肯定不够好。例如,作者训练的词向量约有38万个单词,而训练集和测试集总共不到250万个单词。
相比之下,看山北提供了谷歌词向量的转码版本,这是用TB级语料库训练的词向量。
还有dalaos在群里讨论过。对于这个问题word2vec确实不是很重要。排名第一的队伍根本就没啥用。也就是说,就这个问题而言,不需要太担心词向量的训练。

训练词向量命令示例:

./wordvec -train segName.txt -输出输出.bin -cbow 0 -尺寸 100 -窗口 5  -负 0 -hs 1 -样本 1e-4 -线程 40  -二进制 1 -iter 30

投稿分数和官方分数相差无几。

2.3 FastText

FastText是Facebook给出的文本分析快速实现解决方案。它将一些经典的 NLP 思想与浅层网络结合起来。优点是速度非常快,比深度网络快十倍甚至一百倍。 ,但可以获得可接受的精度。
FastText 是用 C++11 实现的。还是git clone然后编译,还有更多参数可以调整。据说仅使用fasttext就可以得到0.80(预赛前十五名)。不幸的是没有(捂脸。

训练和预测样例:

lr=0.1
纪元=100
暗淡=200=5000000
model_name=model_ai
输入=./res/somefile.txt
w2v=./res/w2vBig.bin./fast文本监督 -输入 $input -输出 $model_name -pretrainedVectors  $w2v -lr lr -纪元 $纪元 -wordNgrams 3 -桶  $桶-暗淡$暗淡 -损失 hs -minCount 1 -线程  $预测=./fttesttxt
./fasttext 预测 $model_name".bin" $预测 > ./预测.txt


2.4 编辑剪辑

这一步是因为 FastText 的输出跟提交格式不一样。在做分词的时候,另外把测试集数据的id保存了一份,这样拿到上面的predict.txt之后就可以直接组装提交文件了:

导入熊猫as pd
提交_csv = '结果.csv'
label_predict = '预测.txt'ids = []
标签=[]
 打开(idSave,'r',编码=' utf-8') as is: foriniS.readlines():ids.append() 线[:-1])open(label_predict,'r',编码='utf-8' ) as lP:对于  line in lP.readlines():labels.append(line) .分割('__' )[2][:-1])打印((ids[1]))
df = pd.DataFrame()
df['id'] = id
df['标签'] = 标签
www.zs-baili.com_csv(submit_csv, index=False, header=False)

需要说明的是,数据竞赛的提交是一个非常关键的环节。最好在每次提交之前检查一下。
常规比赛中,每天的提交数量有限。这是为了防止有人注册大量账户并多次提交来猜测正确答案——有些人可能还记得某年百度在国际大赛中遇到的麻烦。该丑闻导致当时的部门负责人辞职。
DataFountain(本次比赛主办方)拥有众多平台槽位。
想到这里我就很生气,一定要挑出来批评。

刚报名时,连续提交3次都收到“逻辑错误”。我查了一下官方网站,没有发现这个错误的解释。当我在一千多人的群里询问时,没有人回答,官方客服也不见踪影(后来发现除了上网发通知外,官方客服还出现好几次)那天,我就愣住了。
连续错投后当天无法提交,气得我差点退出。过了好久,才有好心人说道:“你的正负样本比例可能不对。”这时我才发现,我检查了一下,发现预测的阳性样本只有10%,而根据最初的猜测,大概有35%左右,所以我写了一个脚本来检查结果,以防万一。


3。 TextCNN实现与参数调整:一个炼金新手的地狱之旅

这是最耗时的部分。赛后反思,我觉得光靠这样的思考和尝试还是学不到什么东西。没有理论指导,沉迷于尝试各种超参数,即使最终调优出一个神奇的网络,其实也是毫无意义的。
代码参考[2]修改。为了节省时间,请直接使用Keras

3.1 准备工作

现在手头上的材料有:

? 词向量模型(.bin)

虽然深度学习是一种“端到端”的方法,但仍然无法将文本直接输入到网络中。 one-hot映射是按照[2]的方法完成的,因为工具在Keras中是现成的。 one-hot 不仅具有 [0 0 ... 0 1 0 ... 0] 的形式 - 这对于文本来说太昂贵 - 而且您还可以使用 dict保存所有单词,记录扫描语料时的词频,排序,用序号来表示单词,从而形成单词到整数的映射。这种方法也称为多项式表示,对应0-1的二项式表示。
比如一句话:“日志里的字太多了,我不想写了。” (人工)切分之后是“日志里的字太多了,不想写了”。这里没有重复的单词,所以可以直接按顺序添加到词典中:

{'日志'1'字'2, '这么多':3'啊'4'不想'5'写' :6'。 '7}

这样原句就可以表示为一个整数向量:[1, 2, 3, 4, 5, 6, 7]
同样,“我不想写日志”经过分词和映射后变成了[5,6,1]。顺便说一句,有一个问题。每条素材的字数几乎不一样,但是后面输入网络的数据维度肯定是一样的,这就需要一些统一。一般是直接缩短/加到长度上,即手动设置一个长度。如果比它长,则丢弃下面的。如果较短,请在末尾使用0 进行填充。


3.1.1 数据统计和简单可视化

那么问题来了,多少长度合适呢?起初我猜测并随机选择了一个数字。 1000 是一个很长的时间了,对吧?可惜没有道理。后来做了简单的统计和可视化,结果是这样的:

这是正样本的长度分布。

这是负样本的长度分布。

不难看出来,正负样本在长度上好像稍微有点区别。这里为了图形的比例,只显示了频次前 80% 的长度,有一些极端的文本特别地“右”。
有意思的是,负样本这边,大概3000词是一道坎,有一个奇怪的陡降。这与 RNN 在生成长文本上的缺陷似乎有一些关系。
不过在后面的模型中把长度降到了200,影响也不是很大,所以……
这里的画图只是个辅助工具而已,心里有数就行了。

3.2 数据处理

接下来做一些处理。为了偷懒直接用了 Keras 的套件:

from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequencestokenizer = Tokenizer(MAX_NB_WORDS)
tokenizer.fit_on_texts(all_data)
word_index = tokenizer.word_index
sequences = tokenizer.texts_to_sequences(all_data)

这里的 all_data 是训练和测试文本组合出一个巨大 list,因为两边要一起处理。MAX_NB_WORDS 是个限制 texts_to_sequences 的参数,word_index 不妨用 json 保存起来备用。
这段代码的意思是,先统计一下语料中的信息然后把分词数据转换为多项式的 one-hotword_index 按词频存储了一个形如上方 {'日志': 1, '字': 2, '好多': 3, '啊': 4, '不想': 5, '写': 6, '。': 7} 的巨大词典,值大于 MAX_NB_WORDS 的词会被写 0

print('Found %d unique tokens.' % len(word_index))

Found 2729771 unique tokens.
一共找到了二百七十多万的分词结果。然后是截短和填充:

train_data=pad_sequences(sequences[:trainLength], maxlen=MAX_SEQUENCE_LENGTH)
test_data=pad_sequences(sequences[trainLength:], maxlen=MAX_SEQUENCE_LENGTH)

pad_sequences 返回的是 np.array,当前的 np.array 直接占用内存,笔者内存不够,所以直接在这里把它们分开了。MAX_SEQUENCE_LENGTH 是控制数据长度的参数,这里取了1000。其实后来发现完全可以更短,长度也不是关键。

最后把标签表示成 one-hot 的形式。这里要用在网络的输出端,所以就是 0-1 了。

from keras.utils import to_categorical
labels = to_categorical(np.asarray(train_label))

在读取数据集做分词的时候就已经把原来的 POSITIVENEGATIVE 保存为 01 了。这里做的不过是把整数变为向量而已。
现在输入网络的数据已经有了。在训练网络之前,还要打乱并分出训练、验证集。这里用的是9:1,但其实对于几十万级的数据,笔者觉得可能99:1就够了,注意分之前打乱一下就行。

VALIDATION_SPLIT = 0.1valIdx = int((trainLength)*(1-VALIDATION_SPLIT))
idx = np.random.permutation(trainLength)
train_data = train_data[idx]
labels = labels[idx]x_train = train_data[:valIdx]
y_train = labels[:valIdx]
x_val = train_data[valIdx:]
y_val = labels[valIdx:]x_test = test_data

为了方便后续使用,最好把这些 Numpy Array 存一下:

npSaveFile='./nptemp.npz'
np.savez(npSaveFile, x_train=x_train, y_train=y_train, x_val=x_val, y_val=y_val, x_test=x_test)

这样,之后使用的时候只需要:

import numpy as np
npSaveFile='./nptemp.npz'
data = np.load(npSaveFile)x_train = data['x_train']
y_train = data['y_train']
x_val = data['x_val']
y_val = data['y_val']
x_test = data['x_test']


3.3 网络结构

这部分内容请批判性地阅读。在没看到 TOP5 开源的情况下,笔者对以下所有参数的设定没有任何把握。
另外可能有的内容前后对不上。没办法,笔者把大量的中间数据给删了,复现起来非常麻烦就懒得做了。很多数字是从遗留的 Notebook 直接拿过来的。

3.3.1 嵌入层

最开始使用的是预训练词向量textCNN 的结构,这部分依然参考了[2]:

VECTOR_DIR='./data/w2v.bin'
EMBEDDING_DIM = 200import json
with open('wordidx.json', 'r') as wi:word_index = json.load(wi)import gensim
from keras.utils import plot_model
w2v_model=gensim.models.KeyedVectors.load_word2vec_format(VECTOR_DIR, binary=True)
embedding_matrix=np.zeros((len(word_index)+1, EMBEDDING_DIM))
not_in_model=0
in_model=0
for word, i in word_index.items():if word in w2v_model:in_model += 1embedding_matrix[i] = np.asarray(w2v_model[word][:EMBEDDING_DIM], dtype='float32')else:not_in_model += 1print(str(not_in_model) + ' words not in w2v model')

这段代码建立了一个 len(word_index) * EMBEDDING_DIM 维的空矩阵 embedding_matrix,从训练好的词向量中取出权值填充到 embedding_matrix 中。这个映射矩阵将作为网络最前端的嵌入层使用。
嵌入层(Embedding Layer)的作用在 Keras 的文档里有很简明的解释:把整数序列(如[20, 46])映射为实数([0.3, 0.46])的网络层。
这是因为输入序列长这样子:

array([  4104,    276,    146,  19639,      2,  24848,    339,  74067,4,  17770,      4,   9485,     18,      3,   2407,    875,17825,   2407,  90875,     55,    285,      1,  11636,   5094,...2,   9571,      1,    886,   2510,   2603,   6264,      3], dtype=int32)

不同于维度受限的图像数据,用整数表达文本序列是一种比较低效的方法。为了更紧凑地表示输入序列的信息,就需要有一个合适的映射关系。word2vec 训练出的词向量就是一类比较常用的工具。

然后就可以形成网络的嵌入层了:

from keras.layers import Embedding
embedding_layer = Embedding(len(word_index)+1, EMBEDDING_DIM, weights=[embedding_matrix], input_length=MAX_SEQUENCE_LENGTH, trainable=False)

可以看到,上面的映射矩阵作为权重填入了嵌入层中。trainable 参数将控制网络在训练过程中的行为,False 选项表示在训练网络时,本层权重不作改变。

3.3.2 textCNN

关于这个话题,网上有很多非常好的资料,这里就不列举了。
Keras 搭 textCNN 是非常简单的:

from keras.layers import Embedding
from keras.layers import Dense, Input, Flatten
from keras.layers import Conv1D, MaxPooling1D, Embedding, Concatenate, Dropout, BatchNormalization
from keras.models import Model
convs = []
filter_sizes = [3,4,5]sequence_input = Input(shape=(MAX_SEQUENCE_LENGTH,), dtype='int32')
embedded_sequences = embedding_layer(sequence_input)
for fsz in filter_sizes:l_conv = Conv1D(128, fsz,activation='selu', padding='same')(embedded_sequences)l_pool = MaxPooling1D(5)(l_conv)convs.append(l_pool)
l_merge = Concatenate()(convs)
l_drop1 = Dropout(0.5)(l_merge)
l_cov1= Conv1D(256, 5, activation='selu', padding='same')(l_drop1)
l_pool1 = MaxPooling1D(5)(l_cov1)
l_flat = Flatten()(l_pool1)
l_dense = Dense(64, activation='selu')(l_flat)
preds = Dense(2, activation='softmax')(l_dense)
model = Model(sequence_input, preds)
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['acc'])
model.summary()

.summary() 可以非常体贴地打印出网络的结构信息。你可以看到网络的结构、每一层的参数数量、总的参数量。比如:

Layer (type)                    Output Shape         Param #     Connected to
==================================================================================================
input_4 (InputLayer)            (None, 1000)         0
__________________________________________________________________________________________________
embedding_1 (Embedding)         (None, 1000, 100)    272977200   input_4[0][0]
__________________________________________________________________________________________________
conv1d_16 (Conv1D)              (None, 1000, 128)    38528       embedding_1[3][0]
__________________________________________________________________________________________________
conv1d_17 (Conv1D)              (None, 1000, 128)    51328       embedding_1[3][0]
__________________________________________________________________________________________________
conv1d_18 (Conv1D)              (None, 1000, 128)    64128       embedding_1[3][0]
__________________________________________________________________________________________________
max_pooling1d_16 (MaxPooling1D) (None, 200, 128)     0           conv1d_16[0][0]
__________________________________________________________________________________________________
max_pooling1d_17 (MaxPooling1D) (None, 200, 128)     0           conv1d_17[0][0]
__________________________________________________________________________________________________
max_pooling1d_18 (MaxPooling1D) (None, 200, 128)     0           conv1d_18[0][0]
__________________________________________________________________________________________________
concatenate_4 (Concatenate)     (None, 200, 384)     0           max_pooling1d_16[0][0]           max_pooling1d_17[0][0]           max_pooling1d_18[0][0]
__________________________________________________________________________________________________
dropout_7 (Dropout)             (None, 200, 384)     0           concatenate_4[0][0]
__________________________________________________________________________________________________
conv1d_19 (Conv1D)              (None, 200, 256)     491776      dropout_7[0][0]
__________________________________________________________________________________________________
max_pooling1d_19 (MaxPooling1D) (None, 40, 256)      0           conv1d_19[0][0]
__________________________________________________________________________________________________
conv1d_20 (Conv1D)              (None, 40, 256)      327936      max_pooling1d_19[0][0]
__________________________________________________________________________________________________
max_pooling1d_20 (MaxPooling1D) (None, 8, 256)       0           conv1d_20[0][0]
__________________________________________________________________________________________________
dropout_8 (Dropout)             (None, 8, 256)       0           max_pooling1d_20[0][0]
__________________________________________________________________________________________________
flatten_4 (Flatten)             (None, 2048)         0           dropout_8[0][0]
__________________________________________________________________________________________________
dense_7 (Dense)                 (None, 64)           131136      flatten_4[0][0]
__________________________________________________________________________________________________
dense_8 (Dense) (None, 2) 130 dense_7[0][0] ================================================================================================== Total params: 274,082,162 Trainable params: 1,104,962 Non-trainable params: 272,977,200 __________________________________________________________________________________________________

这个结构是最开始用的一个结构,可以看到参数总量达到了两亿七千万,可训练参数有一百一十万。深度网络的超参数选择暂时是个缺乏可靠理论指导的问题,多数情况下只能凭借经验来尝试。Andrej Karpathy 在讲 charRNN 的时候提到一些经验,大体上是参数不要超过样本数太多。
笔者自己在调试的时候也发现了这个问题。样本一共有五十万,那么网络可调参数的上限设在一百万左右即可。作为参考,多次实验中可调参数量在一百二十万左右时,网络就发生了过拟合。

现在数据也有了、模型也有了,可以开始训练了。训练的代码很简单,只要一行:

www.zs-baili.com(x_train, y_train, epochs=1, validatio_data=(x_val, y_val), batch_size=batch_size)


3.3.3 Batch Size 、网络结构与参数量

搞深度学习,内存还是越大越好。显存没办法了,TITAN V 也不过12G显存。内存和显存有限,所以在做大量数据的处理时就容易内存不足。比如上面两亿七千万的参数量,按 float32 计算就是十亿字节,超过1GB内存占用。笔者使用了 GPU, 所以这部分参数会在显存里做计算。
好歹也是几十万的样本,肯定不能做 Batch Training。Mini-batch 的数量就要注意了。另外网上没有找到说明,印象中 trainable=False 之后这一层参数是不在 tensorflow 的计算图里的,也就是说不会占用显存空间。
所以你可能看到一些炼丹家们时不时地就打开计算器算一下。他们是在算显存和内存够不够用呢。

那么问题来了,不够用的话怎么办?

  • 早期的 AlexNet 网络做得比较大,一个显卡只有 3G 显存,所以他们当时很多工作是在尝试两块显卡训练网络。现在的深度学习框架有的可以做比较细化的分配,比如 tensorflow 里可以指定使用哪块显卡。这是一个办法。
  • 网络结构也可以调整。上面的网络 untrainable 的部分冗余太多了,接近三个亿的参数(虽然绝大多数冻结了)你是想干什么?在这个网络里,大部分参数其实就是嵌入层的参数,是两百七十万单词乘一百维词向量的结果。但训练好的词向量其实只存了大概三十八万个词,还有二百三十万个词的位置上是空的。这样显然有问题。
  • 再就是 batch_size。上面直接写了 .fit,但如果没记错的话当时是爆了显存的。后来就改成了 .fit_generator,不过这就需要你手写一个 batch_generator,也不难。Pytorch 里的 DataLoader 机制就把这一步明确化了。

3.3.4 细枝末节

截一个初赛的训练结果:

Epoch 1/1
878/878 [==============================] - 239s 272ms/step - loss: 0.2722 - acc: 0.8789 - val_loss: 0.4216 - val_acc: 0.8248

当时能出来这么一个模型还是挺高兴的。不过这前面还有几个 epoch,因为网络容易过拟合,所以只能一轮一轮地尝试。后来用了 Keras 里的 earlyStoppingcheckpointer把这个过程自动化了,但是再也没出来这么好的拟合结果(哭了。

模型训练完最好保存一下。上面自动保存的机制可以用,手动保存也很简单:

www.zs-baili.com('fit_cnn.h5')

读取只要:

from keras.models import load_model
model = load_model('cnn.h5')

预测也只需要一行代码:

predict=model.predict(x_test, batch_size=1024)

还记得之前的样本比例问题吗?网络输出的是(0,1)区间内的实数值,在划阈值写标签之前最好检查一下:

np.sum(predict, axis=0)/predict.shape[0]

这里保存的结果是

array([ 0.71660437, 0.28345084])

还凑合。阈值这里取了0.4,也是考虑到样本比例的偏差。不过主要是打完输出文件之后再检查发现 0.4 给出来的预测比例更好:

sizepre = predict.shape[0]
for i in range(sizepre):if predict[i][3] > 0.4:label='POSITIVE'else:label='NEGATIVE'predict_labels.append(label)

3.4 弃用词向量

0.73 混入了复赛就琢磨着怎么继续做下去。初赛模型到了复赛只有大概 0.67,复赛数据的难度是有提高的,不信待会截图给你看(手动滑稽)。考虑了很多办法,也试了 LSTM。但复赛刚开始那段时间真是黑暗,好像所有的方法都失效了。不管 textCNN 还是 LSTM 都拟合不了模型,具体表现就是 loss 下降到某处突然起飞,或者直接一开始就起飞,准确率最后稳定在 60% 左右。这样预测出来的模型就是全负,因为在拟合不出模型的情况下,网络会自动给出一个“无可奈何的解”,即全部预测为比例高的那一方。
当时真的百思不得其解啊,因为参数量绝对是够的。
之前刚好拜读过 ICLR 2017 的最佳论文《Understanding Deep Learning Requires Rethinking Generalization》,里面有一条结论就是只要结构没有问题的情况,把参数设得足够大,网络是可以随意拟合模型的。
笔者最初用的还是词向量嵌入+ textCNN,嵌入层锁死,只改变网络中的参数,活动参数一百多万。train 不动。
后来直接把嵌入层打开,这下哦买噶der,两亿参数该够了吧?还是在欠拟合。

这特N的什么龟龟!!!

现在觉得可能还是学习率太大。前面一直用的是 adam 的默认学习率(lr=0.01),训练得很好就没动。还是网络跑得不够多,之前做的东西, learning rate 基本都用默认参数。但很多实际问题上,尤其是在第一次接触某一类问题的时候,可能确实需要先做个随机网格搜索来找一个合适的初始值。
不过当时没想这么多,只是怀疑是不是网络结构不行。上面给的代码中间改过好几个版本。最初用的是[2]里的 textCNN,它是把原版在后面加了两个卷积层。因为笔者菜嘛,也不知道到底该怎么改,就按照这个思路加加减减,最后发现没什么卵用(捂脸。这才在最后改回了原版的 textCNN。
那么问题后来是怎么解决的呢?

从头开始 train。词向量也不要了,语料长度继续截短,参数量控制一下。中间笔者又读了一下那篇讲 textCNN 调参的文章[4],对最优 kernel_size 做了一下搜索。改后的代码样例如下:

from keras.layers import Input, Conv1D, MaxPooling1D, Dropout, Flatten, Dense,ZeroPadding1D, Concatenate, Embedding, GlobalMaxPooling1D, BatchNormalization
from keras.models import Model
from keras.layers.advanced_activations import PReLUconvs = []
filter_sizes = [2,2,2,3,3,4,5]sequence_input = Input(shape=(MAX_SEQUENCE_LENGTH,), dtype='int32')
embedding_layer = Embedding(20000+1, EMBEDDING_DIM, input_length=MAX_SEQUENCE_LENGTH, trainable=True)
embedded_sequences = embedding_layer(sequence_input)
for fsz in filter_sizes:l_conv = Conv1D(100, fsz, padding='same', activation='selu')(embedded_sequences)l_pool = GlobalMaxPooling1D()(l_conv)convs.append(l_pool)
l_merge = Concatenate()(convs)
l_drop1 = Dropout(0.2)(l_merge)
l_dense2 = Dense(64, activation='selu')(l_drop1)
l_bn2 = BatchNormalization()(l_dense2)
l_drop3 = Dropout(0.0)(l_bn2)
preds = Dense(2, activation='softmax')(l_drop3)model = Model(sequence_input, preds)
model.summary()

大半个月过去,很多细节现在已经记不清了。网络外超参数包括输入长度可能是 200 或者 500,嵌入层试过 5000*200200000*100,用不用预训练好的词向量这些。总的来说,主要发现包括:

  • 预训练词向量对本题用处不大
  • 词空间(嵌入层尺寸)也不需要太大
  • kernel_size 从2到5,拟合速度都比较快,1不行,大于5之后效果也欠佳
  • textCNN 原始结构就很有效,不需要后面再叠卷积层
  • 参数量需求不大,一百万到一百二十万即可拟合训练数据
  • dropout 非常有用,仔细调
  • batch-normalization 试了才知道有没有效果
  • 激活函数不是特别重要,倒不如说依赖特定激活函数的网络,泛化性可能很差(最近有篇论文据说就有这个问题)

第二点可能需要解释一下。前面说过一点文本的表示方法和嵌入层的初始化,两者都受到一个 MAX_NB_WORDS 参数的控制。如果使用预训练词向量,可以在初始化嵌入层时丢弃序数大于该参数的词向量;如果不使用预训练词向量,像上面代码所写的,Keras 会自动忽略序数大于 20000 的内容。

3.5 调参感言

接下来就是漫无止境的调参了。缩减输入长度和嵌入层尺寸之后,网络终于开始学到东西了。但绝大多数情况都是很快过拟合,分界点大概是验证集准确率到70%的时候,训练 loss 和准确率继续下降、上升,验证 loss 和准确率开始上升、下降。说明网络结构还是有问题。
然后比赛中最搞笑的一幕出现了,突然调到了一个参数,验证集准确率上升到76%,这时候训练集大概是80%左右,看到之后赶紧把模型保存了一下,然后稍微改动了几个参数,又崩了。然后想改回到这个模型再做微调的时候,发现回不去了!

神TM回不去了!

真的,非常诡异的事情。model.summary() 可以看大部分超参,所以排除之后笔者觉得问题应该出在 dropout 上。笔者记得当时给了两个值,第一个 dropout 设的是0.2, 第二个设的是0.4,感觉所有的参数都跟上次的一模一样,但是就出不来那个结果了……
所以笔者那个恨啊。当时就想弃赛了。
再往后几天确实没有做什么,随手调了几次参数发现不灵,于是直接放弃。

4. 反思

4.1 一点调参的经验

调参是个脑力+体力活。调参需要有一些方向性的指导思想,无脑调参的话非常耗时间。好一点的显卡是非常必要的,它能缩短等待的时间,让你尝试更多思路。
关于调参,笔者觉得有一些次序可以作为参考:

  • 最重要的是网络结构,结构不对都白搭。用什么结构参考相关论文。
  • 选好结构,先用比较多的参数——比如样本的十倍——试试能不能过拟合,过拟合是 training 可行与否的标志
  • 过拟合之后开始缩减参数。减少到原先的一半,如果还能过拟合,就使用 dropoutbatch_normdropout0.5 开始调。
  • 或者不削减参数,把初始学习率调得足够小,轮数多没关系,做好 early stopping 和模型保存,一旦训练 loss 和验证 loss 分歧增大就停止训练。

最后还有一点要补充。Andrew Ng. 的深度学习课上也提到了,就是验证集的比例问题。传统的机器学习面对的数据比较少,所以像 7:3、5-fold CV 比较常用,但 Deep Learning 面对的数据一般都很大,像本题复赛有六十万的训练数据,Andrew 说甚至可以 99:1 的。笔者用了 9:1,现在觉得确实没必要,59:1 甚至更高一些也没问题。这样也不用找到合适的模型之后重新组合数据来训练了(实测这样做的效果也很有限)。

4.2 题目

数据比赛跟其他一些学科性比赛有一定相似之处。题目都不是突兀的、浮空的,而是一些暂时没有完美解决方案的具体问题。像文本分类问题是 NLP 领域的基础,也有 YELP 数据库专门做 水军检测,但像本题的长文本“真假”分辨就属于是比较新的问题。
今年 CCS 上有一篇芝加哥大学的文章讲用 RNN 突破 YELP 的评论分辨数据集,为了这个题笔者还专门找来看了一下,结果……什么都没学到。因为 CCS 是网络安全相关而不是机器学习的顶会,而且作者其实没有用什么特别新的方法,只是把 LSTM 当生成模型来用,训练两个生成模型然后用对数比率的方式分类。没有开源,很多东西说得也比较含糊,所以笔者水平实在有限,这文章是仿不出来了。而且文章针对的是短文本,用到这个题上估计也不会有很好的效果。

所以大佬们什么时候开源啊……

4.3 笔者自身的问题

实际做过之后,对自己的水平也有了比较全面的认识。
实事求是地说,这次能拿这个分,全靠了一块泰坦X……

首先是领域知识的欠缺。对 NLP 笔者什么都不会,调参调半天还调出个复现不了的模型,这让人说什么好……

编程实现的过程也有问题。Python 写得少,还是看到官方的 baseline 之后才把整个过程琢磨通。对数据竞赛来说编程依然非常关键,编程能力强至少能顶一块970。Keras 方便是方便,封装太多了,在用的时候会出现很多细节上的问题。看山杯的方案里看到 Pytorch 的比例挺高的,实际照着他们的教程写过一点之后,发现这框架确实非常棒,逻辑非常好,“如丝般顺滑”。API 不是很复杂但正常需要的东西都有。

再就是对 Deep Learning 的理解还是远远不够。说是玩了一两年,论文也看书也看课程也学,但实际做过的东西太少,调参是很玄学没错,不过调多了之后就像炼丹家似的,很多东西心里就有数了。而且看书看课程的很多东西事后想想是完全可以用上的,但当时想不到。这就是知识掌握的程度还不够而且实践太少的缘故。

还有就是队友的问题。交往能力弱鸡如笔者一人参赛,那段时间是非常累的。导师的项目基本没动,想学的东西也都延后了,最后感觉学到的东西也不多(所以说为什么没人开源……),收获最大的居然还是调参的手感……我上哪儿说理去啊。

最后还是要强调一下工具使用、版本管理和模型保存的问题。Jupyter Notebook 其实不很适合做这种重型的数据处理工作——虽然笔者几乎所有的代码都是在 NB 里写的吧——笔者写别的东西的时候发现 Jupyter NB 对一些包的支持可能有问题,而且界面直观呈现本来就有性能代价。一些不需要实时监控的处理写成 .py 会好一些,这样监管进度也方便。
每次修改几行以上的代码最好还是 Git 一下。笔者是抱着反正啥都不会的想法破罐子破摔,这种操作当然是玩火自焚。
模型保存的问题上面说过了。你总不会希望看到突然出现的神一样的模型下一秒就飞了吧。

5. Fun Time

前面说了要从样本里找点乐子。有些是复赛第二的 Yin 挖出来的,向这位大佬表示一下敬意。

我个人觉得真假难分的:

长三角经济协调会梳理协作成就 聚会探”互联网+” 区域合作,关键在价值共识厚植区域共同利益,而不是只看见自己的一亩三分地,看到更将是的广阔的发展空间,更多的发展机遇正是江南草长莺飞时。长三角30个城市的市领导们又一次相聚一起,梳理过去一年协作成就,对话未来合作机遇。自1992年长三角经济协调会成立以来,这样的聚会已经举行了15次。对话主题次次有新意,对话姿态却始终未变–没有大城小市的区别对待,没有地区行政级别之分,有的是,的新经济对前瞻,对协同政策的共同推进,对区域发展共同价值的共识。今年聚会的话题是”互联网+”。在互联网打破了以往经济区位优势,成为市场发展催化剂的今天,区域之间如何协同发展?来自浙江金华的领导说,我们小城市,商务成本低,小微企业发展快,金华的互联网企业数量已经有5万多家,在全国50个大众电子创业最活跃的城市中位列第二,眼下最需要的是科技、人才的跟进;南京的市领导则表示,我们科技力量强,不求所在、但求所用。领导来自江苏的徐州市同样,也点赞共享经济,”我们发展’互联网+装备制造’,设计能推动研发希望、数据管理、工程服务等制造资源开放共享。”资源、数据共享引发更多市长们共鸣。江苏镇江建议,长三角城市在”互联网+生态”方面加强合作,”通过’生态云’实现碳排放、污染物排放和资源利用看得清、道得明、控得住、管得好。”上海则表示,我们有新能源车数据中心,实时监控数万辆电动车运行,长三角新能源汽车基础设施互联互通,这些数据资源都可以共享。论坛上,市长们就区域共同利益讨论得热烈;论坛外,各种城市联盟启动着经济、文化、社会各个领域的互联互通。长三角非物质文化遗产联盟把”江南百工展”办得格外热闹,长三角新能源汽车联盟将政府、企业、民间机构组织在一起,探索充电桩互联互通的路径,长三角青年创新创业联盟则谋求布点长三角青年创客基地。放大共同利益,实现资源信息共享,是互通互联。通则不痛,联则成拳。长三角区域内城市也有过各自为战的时期,随着经济飞速发展,区位优势渐渐淡化,人口红利逐步消失,长三角这片曾经的制造业高地如何持续领跑中国经济?长期在的竞争碰撞中,大家发现,城市群就手指十个像,各有各的优势,何不相互协作,厚植共同利益?有了长三角高质量的腹地经济,上海的”四大中心”才落到实处;因为紧邻上海,江苏苏州”大树底下种好碧螺春”,外向型经济拔得头筹;而活跃的江浙腹地又刺激着上海在城市管理、创业环境、科技进步上的创新……换个角度看看自己所处的经济区域,早已是相互依存的利益共同体。厚植区域共同利益,而不是只看见自己的一亩三分地,看到的将是更广阔的发展空间,更多的发展机遇。今天,长三角跻身世界上最具活力的城市群之一,很大程度上得益于”厚植区域共同利益,平等合作交流”这样的区域价值共识。在长达20多年的时间里,正是本着这样的价值共识,各地政府联合形成区域协调机制推进,培育区域共同市场,拓展区域经济合作范围,促进形成特色鲜明的产业梯度,构建的现代化起区域交通和通讯网络,释放出一体化区域的红利。无论是立足国内经济发展需要,还是参与全球经济的竞争,今天的中国尤其需要释放区域一体化的红利。作为中国最具活力的城市群区域之一,长三角在区域一体化进程中的探索,或可给更多城市群以启发。

这是复赛训练集第一条,笔者乍一看哎逻辑性很强啊,有头有尾的,RNN 做不出这种假来,结果一看标签,Negative……

教育部、国家卫计委:到2020年培养全科医生30万名以上 全科医生是我国当前医疗卫生服务体系的短板。教育部和国家卫计委今天表示,到2020年我国要培养全科医生30万名以上,并提升基层卫生岗位的吸引力。目前,我国累计培训全科医生20.9万人,与建立分级诊疗制度的要求存在较大差距。国家卫生计生委科技教育司司长秦怀金介绍,到2020年我国要培养全科医生30万名以上。要加强薪酬制度改革,传统文化的传承有时候离不开这样的仪式感使在基层工作的全科医生能得到有尊严的收入,也通过加强在基层的综合改革,绩效工资改革,包括推行服务签约家庭医生,激励基层全科医生通过服务得到更好的待遇。提高职业发展前景,吸引力多种措施提升全科医生岗位通过。质量是医学教育的”生命线”,此次改革的核心任务是提高医学人才培养质量。全科医生是当前医疗卫生服务体系的短板,要创新全科医生培养与使用激励机制,多途径加大全科医生培养力度。教育部高等教育司司长吴岩表示,针对全科医生下不去、留不住的问题,进一步完善培养定向医学生订单政策,实行”县管乡用”的用人管理制度,保证定向生回得去、用得上;医学本科及以上学历毕业生经住院医师规范化培训合格到基层医疗卫生机构执业的,可直接参加中级职称考试,考试通过的直接聘任中级职称,提升基层卫生岗位的吸引力。

Negative too。不说肉眼看了,我觉得网络大概看不出来。还有这个:

股指期货持仓信息快报 截至10点05分,股指期货合约:IC1709总量为2879手,持仓24667手,开仓778手,平仓2102手,仓差-1324手。IC1710总量为56手,持仓402手,开仓21手,平仓35手,仓差-14手。IC1712总量为129手,持仓4108手,开仓64手,平仓66手,仓差-2手。IC1803总量为17手,持仓747手,开仓2手,平仓15手,仓差-13手。IF1709总量为3701手,持仓29624手,开仓1083手,平仓2619手,仓差-1536手。IF1710为总量121手,持仓652手,开仓59手,平仓62手,仓差-3手。IF1712总量为305手,持仓5997手,开仓157手,平仓148手,仓差9手。IF1803总量为30手,持仓1017手,开仓11手,平仓20手,仓差-9手。IH1709总量为2393手,持仓16715手,开仓750手,平仓1644手,仓差-894手。IH1710总量为91手,持仓378手,开仓34手,平仓58手,仓差-24手。IH1712101手为总量,持仓3537手,开仓32手,平仓70手,仓差-38手。IH1803总量为28手,持仓774手,开仓18手,平仓10手,仓差8手。

……完全看不懂。标签是负。

女子在游泳馆更衣室玩自拍 多名泳客走光视频流出 夏天来了,很多朋友都喜欢去凉爽的地方待着,游泳玩水成了很多人的选择,也不少朋友发帖询问靖江游泳的地方。如转载稿涉及版权等问题如此清凉一夏岂不快哉?但是接下来小编要说的事儿,你也许就蒙圈了。视频截图原来,在泰州市靖江某游泳馆,有一个女士直接在游泳馆的更衣室举着手机拍起了视频,本来只是对着镜子自拍,但是两秒后画面一转,她的镜头扫向了其他正在衣服换的人,有些正在冲澡的人也被拍了进去!视频截图跟小编爆料的女生很不开心,因为她和游泳馆的朋友经常去自己,忽然在微信里看到这样的视频,这里面的很多人都在不知情的情况下被拍了进去,视频还流传出来了,她说感觉自己的隐私都被侵犯了。的确,如今手机的摄像头像素越来越高,在很多公共场合看到能都有人在用手机玩自拍,令不少人担忧自己的隐私权被侵犯。对于哪些公共场合应当禁止拍摄,国家暂无相关法规,公共浴室、公共更衣室等场所规定、处罚也不明确,在这些场合大家一定要注意自我保护,提防有人借自拍玩偷拍。”假设遇到自己衣冠不整或者裸露身体时被入镜,可要求对方将照片删去,假若对方执意不删,则可求助拨打110选择。”有律师表示,从某种程度上说,拍摄者将他人作为背景拍入镜头内,是侵犯他人的隐私权和肖像权的,假设拍摄者将这些照片用于牟利、敲诈等,可根据情节轻重,对其进行一定的处罚,情节严重的甚至可以进行刑事拘留。能否除了在更衣室其实用手机拍照,最近还有一个话题很火:浙江一位妈妈独自带4岁儿子去游泳,管理员不让男孩进女更衣室。女子说自己被骂哭,她质疑:”妈妈带年幼儿子不能进游泳馆咯?”管理员则称,游泳馆有规定,超过的3周岁孩子,一律不能进入异性更衣室。妈妈独自带着男孩子去游泳,换衣服和洗澡确实是一个难题,如果条件允许,还是尽量让同性带去。会父母一些感觉没什么,但是他人的感受可不一样,而且别人的指指点点甚至是异样的眼光,对孩子都有一定的影响。2~5岁,本身就是孩子的性格形成期,父母的疏忽会影响孩子良好性格形成,也不利于对孩子进行性别教育。本文来源:现代快报责任编辑:胡淑丽_MN7479。

标签负???我觉得这挺真的啊……
所以你们的标准到底是什么……

上面的还算普通,来几条比较神的:

嗯……

……

呃……

结束

本来想官方赶紧开源笔者学习一个而且也能多写点,结果决赛还出了那么不光彩的事情,而且到现在也没开源(摊手。所以就这样吧,先给各位拜个早年呗。(啥)

参考资料:

[1].【360搜索赛题】基于FastText文本分类方法
. https://www.zs-baili.com/s/dLuT49hyPSzJp8tISk0DBw
[2]. 新闻上的文本分类:机器学习大乱斗
. https://www.zs-baili.com/p/26729228
[3]. Implementing a CNN for Text Classification in TensorFlow
. http://www.zs-baili.com/2015/12/implementing-a-cnn-for-text-classification-in-tensorflow/
[4]. A Sensitivity Analysis of (and Practitioners” Guide to) Convolutional Neural Networks for Sentence Classification.