扔掉代码表!用RNN“破解”摩斯电码

做者 | Sandeep Bhupatiraju
译者 | Liu Zhiyong
编辑 | Debra Chen
AI 前线导读:100 多年前,在美国,人们利用 Morse 电码发出了人类历史上的第一份电报,今后人类又揭开一页崭新的篇章。Morse 电码的问世,对人类社会发生全面而深远的影响,极而言之甚至改变了人类历史的发展方向。只不过随着当代信息的爆炸式发展,多种通讯方式渐渐将方式单一的电报挤到了角落。电报从最初先驱者的试验品,于战争年代发展到顶峰,最后在现代被基层群众所遗忘。虽然目前仍然有坚持使用电报的机构和民间爱好者,可是那点可悲的使用量彻底没法和电话等通讯方式相抗衡。要解读 Morse 电码编码的电文,就须要一本代码表才能知道电文的意思。若是没有代码表怎么破?能够用 RNN 来破解 Morse 电码吗?固然能够!这是很好的点子。一位研究数学和数据科学的大牛 Sandeep Bhupatiraju 就撰写了一篇文章:“Cracking” Morse code with RNNs,让咱们看看如何使用 RNN 来“破解”Morse 电码?

更多干货内容请关注微信公众号“AI 前线”,(ID:ai-front)

剧透警告:其实 Morse 电码并不须要破解。它颇有用,由于能够用最少的设备来发送消息。我之因此说它不须要破解,是由于这种代码是众所周知的,点和破折号的组合并非什么秘密。可是从理论上讲,它是一种替代密码(substitution cipher):每一个字母和每一个数字都是使用点和破折号来表示,以下图所示:python

AI 前线:Morse 电码是一种时通时断的信号代码,经过不一样的排列顺序来表达不一样的英文字母、数字和标点符号。它发明于 1837 年。Morse 电码是一种早期的数字化通讯形式,可是它不一样于现代只使用零和一两种状态的二进制代码,它的代码包括五种: 点、划、点和划之间的停顿、每一个词之间中等的停顿以及句子之间长的停顿。git

让咱们先不要怀疑,假设咱们收到了 Morse 电码的消息,但咱们不知道如何解读。假设咱们还有一些代码示例和它们对应的单词。如今,咱们能够推测它是一种替代密码,而后咱们能够最终计算出每一个字母的代码,从而解码消息。github

或者,咱们能够构建一个 encoder-decoder 模型 来猜想(几乎)全部的单词!做为受虐狂,咱们固然会选择后者。照这样说,让咱们猛拍一下堂·吉诃德的战马 Rocinante,开始大战风车的征程。算法

AI 前线:Rocinante,西班牙语,驽骍可贵 [音译],《堂·吉诃德》中堂·吉诃德所骑的马的名字。数组

这里有个手头问题: 咱们有几个编码序列及对应的可理解的例子。使用这些例子,咱们必须学习一些模式,并使用这些信息来预测新的编码标记(单词)可能会是什么。与咱们预测数值结果的常见回归问题不一样,咱们手头有一些序列到序列(sequence-to-sequence)的学习问题,在数据中有时间结构。这是递归神经网络(RNN)可能有用的一个即时提示(它用于语音和语音数据的 RNN,以及用于图像数据的 CNN 和用于图像字母的 RNN 组合)。粗略地说,这属于一类问题:也包含了机器翻译的问题;这个模型的结构在这里起到了启发的做用。有关此主题的更多信息请参阅 [1]。限于篇幅咱们不会赘述 RNN 的理论,但对于这个主题的简要介绍请参考文献 [2] 的一系列文章。安全

若是你想知道这个问题是否能够用不一样的方法来解决,答案是:YES。马尔可夫链蒙特卡罗方法(Markov Chain Monte Carlo)会获得相似的结果。在这种状况下,咱们将会遵循优秀论文 [3] 中第一个例子所提到的过程。微信

AI 前线:马尔可夫链蒙特卡罗算法(简写为 MCMC)的核心思想是找到某个状态空间的马尔可夫链,使得该马尔可夫链的稳定分布就是咱们的目标分布 p(x)。这样咱们在该状态空间进行随机游走的时候,每一个状态 x 的停留时间正比于目标几率 p(x)。在用 MCMC 进行抽样的时候,咱们首先引进一个容易抽样的参考分布 q(x),在每步抽样的过程当中从 q(x) 里面获得一个候选样本 y,而后按照必定的原则决定是否接受该样本,该原则的肯定就是要保证咱们获得的本来刚好服从 p(x) 分布。网络

马尔可夫链,因安德烈·马尔可夫(A.A.Markov,1856-1922)得名,是指数学中具备马尔可夫性质的离散事件随机过程。该过程当中,在给定当前知识或信息的状况下,过去(即当前之前的历史状态)对于预测未来(即当前之后的将来状态)是无关的。函数

在马尔可夫链的每一步,系统根据几率分布,能够从一个状态变到另外一个状态,也能够保持当前状态。状态的改变叫作转移,与不一样的状态改变相关的几率叫作转移几率。随机漫步就是马尔可夫链的例子。随机漫步中每一步的状态是在图形中的点,每一步能够移动到任何一个相邻的点,在这里移动到每个点的几率都是相同的(不管以前漫步路径是如何的)。post

梗概

粗略地说,咱们想从 (x_1,...,x_n) 输入序列预测输出序列 (y_1,...,y_m),这就涉及了条件几率(conditional probability)的学习。

AI 前线:条件几率是指事件 A 在另一个事件 B 已经发生条件下的发生几率。条件几率表示为:P(A|B),读做“在 B 条件下 A 的几率”。条件几率能够用决策树进行计算。条件几率的谬论是假设 P(A|B) 大体等于 P(B|A)。

这里的主要障碍是,从可变尺寸的输入来预测可变尺寸的输出。在元级这一层次上,是经过将两个 RNN 组合在一块儿来解决的,其中,第一个 RNN 将可变尺寸的输入映射到固定尺寸的输出,另外一个 RNN 接受固定尺寸输入并返回可变尺寸的输出。固定尺寸的中间向量,成为上下文矢量(context vector),它将信息从输入序列中封装起来,每次输入一个字符。产生上下文矢量的机制是使 RNN 对于捕获时间结构有用:上下文矢量是最终的 timestep 或它的某些函数以后的隐藏状态。上述条件几率使用链式法则(chain rule) 计算的。

AI 前线:链式法则是微积分中的求导法则,用于求一个复合函数的导数,是在微积分的求导运算中一种经常使用的方法。复合函数的导数将是构成复合这有限个函数在相应点的 导数的乘积,就像锁链同样一环套一环,故称链式法则。可见搞人工智能,学好数学很是重要。

其中,h 是上下文矢量。最后,可使用 softmax 函数来计算上述方程右边的条件几率,该函数将字符 y_{i-1},...,y_1 的独热编码(one-hot encoded)矢量做为输入,在第二 RNN 中的递归层(recurrent layer)的输出和上下文矢量。这里使用的特定类型的 RNN 是 LSTM,有效克服了 Simple-RNN 的局限性,Simple-RNN 受到渐变问题的影响,并可以更好地捕获远程依赖关系。

AI 前线:Simple-RNN 是最简单的循环神经网络,它是 LSTM 的基础。Simple-RNN 与 BP 同样都有前馈层与反馈层。可是 Simple-RNN 引入了基于时间 (状态) 的循环机制。

数据准备

咱们将引入一个特殊的字符(11)来表示每一个字母的代码之间的空格。例如,SOS 的代码将表示为:...11---*...(替换...---...)。咱们这样作是为了确保与给定代码相对应的单词是惟一的。接下来,咱们将使用已编译数据集(words_alpha)中的英文单词,而不是用随机生成的字母集做为咱们的数据。为了得到数据的感受,参看下图所示的单词长度的直方图。从直方图上能够看出,长度超过 5 个字母的单词要比短的单词多。

对包含编码单词的数据进行训练的网络,通常会预测长的单词。请记住,网络并不会计算出生成数据的“公式”,也就是说,它并不会学习图 1 中的图表。

咱们开始数据准备工做,构建一个函数,将所输入的英文单词编码为它的 Morse 电码并输出。


为了说明的目的,咱们将从给定的固定长度的单词生成训练和验证数据。在这里,咱们将这个长度固定为 9,由于长度为 9 的字数足够大(参见上述直方图)。注意,此举将意味着来自网络的输出单词的长度将是固定的,可是输入的 Morse 电码并不必定都是相同的长度。假定咱们知道每一个字母的编码长度最长是 4(其实咱们不用这个特定的假设,能够选择的长度最长的 Morse 电码 max_length_x 来训练数据。)所以,若是单词的长度为 n ,那么与其对应的 Morse 电码的长度最多为 4n+(n-1),其中 n-1 对应的是 11s 的数量。咱们用左边的空格填充代码,使它们的长度相同,这意味着咱们输入字符的词汇表是 {‘.’, ‘—’, ‘*’, ‘ ’}。通常而言,咱们让输出的字符词汇表是全部字母和空格的特殊字符。回到关于网络猜想长单词的评论,咱们的意思是,因为长单词的数量会形成不平衡,所以网络将会倾向于猜想更少的空格。在下面的代码片断中,output_list 将包含英文单词,input_list 将包含填充的 Morse 电码。


如今,咱们构建输入中字符的一个独热编码矢量,使输入数据适合神经网络。为此,咱们构建了一个类对象(相似于 Keras 文档中的例子),它将有助于编码和解码,并将 Morse 电码和英语单词解码。咱们将类分配给具备适当字符集的对象。

AI 前线:独热编码即 One-Hot 编码,又称一位有效编码,其方法是使用 N 位状态寄存器来对 N 个状态进行编码,每一个状态都有它独立的寄存器位,而且在任意时候,其中只有一位有效。


将数据拆分,生成一个训练集 x_train,从整个数据集 x,y 的四分之一中提取出 y_train,咱们将保留剩下的四分之三做为验证集 x_val、y_val。注意,咱们最好将训练集的一部分做为验证集,其他的做为测试集,但考虑咱们是随便弄的设置,咱们对模型构建更感兴趣,而非参数调优。如今,咱们已经准备好了训练和测试(验证)数据,而且能够继续对网络进行修改。


构建神经网络的最简单方法是使用 Keras 模型和顺序 API。因为咱们不须要 TensorFlow 的所有功能和灵活性,因此咱们选择了 Keras。

模型结构 (Encoder-Decoder 模型)

咱们所选的模型拓扑结合了简单的 RNN 的强大变体,称为长短时间记忆(Long Short Term Memory,LSTM)网络。

AI 前线:长短时间记忆网络是一种时间递归神经网络,适合于处理和预测时间序列中间隔和延迟相对较长的重要事件。基于长短时间记忆网络的系统能够实现机器翻译、视频分析、文档摘要、语音识别、图像识别、手写识别、控制聊天机器人、合成音乐等任务。

第一个 LSTM 用做编码器,接受一个可变长度的输入序列,每次一个字符,并将其转换为固定长度的内部潜在表示。另外一个 LSTM 则做为解码器,将潜在的表示做为输入,并将其输出传递到一个密集层,该层使用 softmax 函数,每次预测一个字符。

该模型的编码器和解码器组件可能具备多层 LSTM,一般并不清楚哪一种拓扑最适合使用。通常来讲,对机器翻译而言,深层网络工做得更好。根据经验法则,咱们指望多层可以学习更高层次的时间表示,所以当数据具有了一些层次结构时,咱们就会使用它。对咱们来讲,每层有一层就足矣。

该模型使用 Sequential() 构建,而且每次只添加一个层。第一个 LSTM 层将 3D 张量做为输入,并要求用户指定输入维度。这能够用代码中指定的 input_shape 简单地完成,其中第一个组件表示时间步骤的数量,第二个组件表示特征的数量。对咱们来讲,特征的数量就是输入序列的词汇表中元素的数量,也就是 4,由于咱们有“.”、“-”、“*”和空白字符“”。因为咱们每次只提供一个独热编码矢量,所以时间步骤的数量就是 max_len_x。咱们还将指定层中的内存单元(或块)的数量(此处用 latent_dim 参数表示,咱们使用 256),这是潜在表示的维度。请注意,咱们但愿将 LSTM 的最终隐藏状态做为潜在表示返回,这包含了来自全部时间步骤的信息,即所有输入序列。若是咱们使用 return_sequences = true 选项,那么将获得每一个时间步骤的隐藏状态的输出,但这只包含到该步骤的序列信息。


这就获得了简单的编码器模型。咱们将构建一个相似的层做为解码器。但上面的代码片断的输出是个 2D 数组。咱们经过使用方便的 RepeatVector 层重复输出时间数量 max_len_y,将其转换为 3D 张量,并做为下一个 LSTM 层(解码器)。如今,咱们在这个 LSTM 中使用 return_sequences = True 选项来输出隐藏状态序列,咱们须要使用这些信息进行预测。为此,咱们使用一个 TimeDistibuted 密集层,它输出一个长度为 max_len_y 的矢量,咱们使用 softmax 激活函数来选择最有可能的字母。为了快速了解 TimeDistributed 层的目的,请参阅 Jason Brownlee 撰写的博文:How to Use the TimeDistributed Layer for Long Short-Term Memory Networks in Python。

https://machinelearningmastery.com/timedistributed-layer-for-long-short-term-memory-networks-in-python/


如下是对网络和各类输入、输出维度的快速摘要。

咱们将模型拟合到数据中,在集合 x_train、y_train 上进行训练,并使用 x_val 和 y_val 来查看咱们的工做状况。须要设置的最后一组参数是轮数(epochs)的数量和批大小。批大小是在梯度降低算法中经过网络传递的训练集的大小,而后对网络中的权重进行更新。一般批大小就是计算机内存可处理的最大值。一个轮数是经过这些批次的训练数据的全面训练一次。此处,咱们设置了 1024 的批大小,使用了 120 个轮数,在下图便可看到,在大约 100 个轮数以后,精度并无明显的增长。通常来讲,看哪一个参数起做用须要反复试验才知道。如今咱们用 fit() 方法来拟合模型。



最后,从上面的图表能够看出,在验证集上咱们能够获得大约 93% 的准确率,结果彷佛并不坏。固然,若是咱们增大训练数据的规模,还能够作得更好。下面是对一组随机选择的单词的预测。

在左边输入代码,中间输入相应的单词,右边输入预测。若是预测正确,那么该单词为绿色,不然为红色。

正如你所看到的,错误的预测也不算太坏。咱们必须提醒本身,破译密码并无破解代码,也就是说,弄清楚每一个字母表明什么。事实上,咱们能够输入字母的代码,并查看网络预测的单个字母的代码,以下所示,咱们离目标还很远!

做为 encoder-decoder 模型的另外一个例子,你能够尝试使用凯撒加密(Caesar cipher)或者其余代码来查看这种方法的有效性如何。

AI 前线:凯撒加密,或称恺撒加密、恺撒变换、变换加密,是一种最简单且最广为人知的加密技术。它是一种替换加密的技术,明文中的全部字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。例,当偏移量是 3 的时候,全部的字母 A 将被替换成 D,B 变成 E,以此类推。这个加密方法是以凯撒的名字命名的,当年凯撒曾用此方法与其将军们进行联系。凯撒密码一般被做为其余更复杂的加密方法中的一个步骤。凯撒密码还在现代的 ROT13 系统中被应用。可是和全部的利用字母表进行替换的加密技术同样,凯撒密码很是容易被破解,并且在实际应用中也没法保证通讯安全。

References:

[1] Sequence to Sequence Learningwith Neural Networks

http://papers.nips.cc/paper/5346-sequence-to-sequence-learning-with-neural-networks.pdf

[2] Understanding LSTM Networks

http://colah.github.io/posts/2015-08-Understanding-LSTMs/

[3] THE MARKOV CHAIN MONTE CARLO REVOLUTION

http://www.ams.org/journals/bull/2009-46-02/S0273-0979-08-01238-X/S0273-0979-08-01238-X.pdf


更多干货内容,可关注AI前线,ID:ai-front,后台回复「AI」、「TF」、「大数据」可得到《AI前线》系列PDF迷你书和技能图谱。

相关文章
相关标签/搜索