小米笔记本Pro Manjaro Deepin 配置备忘

虽然Windows10 有了WSL,用作开发的易用性提高了许多,但是偶尔还是有需要Linux系统的时候,所以需要装双系统,小米笔记本Pro支持扩展M2 SATA接口固态硬盘,在这块硬盘划出128G用于Linux系统即可。选择Manjaro系统搭配 Deepin 桌面这样的组合,是综合考虑了Manjaro 的驱动友好,软件及其丰富,以及Deepin 桌面的美观与对中文的开箱即用的良好支持。

当前版本:Manjaro Deepin 18.02

0x00 准备工作

  • 系统镜像下载地址
  • 使用rufus软件将系统镜像刻录到U盘,建议选择DD写入模式
  • 关闭小米笔记本Pro BIOS的安全启动 可参考

0x01 安装

使用U盘启动系统后,使用Live镜像的安装指引程序安装系统。

启动时选择第二项boot(non-free),Manjaro自带的驱动精灵会帮你安装好所需驱动,笔记本双显卡则会帮你安装bumblebee。

注意为了和Windows10和平共处,需要使用自定义分区方案:

  • win10系统的硬盘内有个efi的分区(格式为FAT32,一般大小为100M),需要指定为/boot/efi分区,注意选择保留不要格式化
  • 新划出的128G系统挂载 / 分区,如果需要休眠到硬盘,最好建立swap分区,一般大小和内存大小一致即可。

注意当前版本可能会遇到一个问题,第一次连接Wifi网络总是报告密码错误,需要用终端使用命令行连接Wifi,安装时操作一次后,使用中再没遇到过这个问题。

sudo nmcli device wifi connect SSID-NAME password SSID-PASSWORD

0x02 安装后基本配置

1 笔记本双显卡设置

查看显卡NVIDIA状态

lspci| grep -i vga

测试 Bumblebee 是否工作:

optirun glxgears -info

如果需要不依赖Bumblebee来使用CUDA, 为开启NVIDIA显卡,运行:

sudo tee /proc/acpi/bbswitch <<< ON

2.时间和日期

如果安装的是双系统,注意Manjaro Setting Manager > Time and Date勾选以下选项 –set time and date automatically –hardware clock in local time zone

3 源镜像与系统更新

  • 排列源
sudo  pacman-mirrors -i -c China -m rank  

#增加archlinuxcn库和antergos库

echo -e "\n[archlinuxcn]\nSigLevel = TrustAll\nServer = https://mirrors.tuna.tsinghua.edu.cn/archlinuxcn/\$arch\n\n[antergos]\nSigLevel = TrustAll\nServer = https://mirrors.tuna.tsinghua.edu.cn/antergos/\$repo/\$arch\n"|sudo tee -a /etc/pacman.conf
  • 升级系统:
sudo pacman -Syyu
  • 安装archlinuxcn签名钥匙&antergos签名钥匙
sudo pacman -S --noconfirm archlinuxcn-keyring antergos-keyring
  • 软件管理界面开启AUR支持。
  • 安装增强的命令行软件管理工具yay
sudo pacman -S yay

4 安装中文字体和输入法

  • 安装中文字体
    sudo pacman -S --noconfirm wqy-microhei wqy-microhei-lite wqy-bitmapfont wqy-zenhei adobe-source-han-sans-cn-fonts adobe-source-han-serif-cn-fonts noto-fonts-cjk && fc-cache -fv
    
  • 安装谷歌输入法
sudo pacman -S --noconfirm fcitx-im fcitx-configtool fcitx-googlepinyin 
  • 配置fcitx, 配置完需要重启
sudo echo -e "export GTK_IM_MODULE=fcitx\nexport QT_IM_MODULE=fcitx\nexport XMODIFIERS=@im=fcitx">>~/.xprofile

5 Windows硬盘挂载

/etc/fatab 新增

/dev/nvme0n1p3 /mnt/c ntfs defaults 0 2
/dev/sda1 /mnt/d ntfs defaults 0 2

6 VPN 和 SSH

  • 导入OpenVPN配置
sudo nmcli connection import type openvpn file xxx.ovpn
  • 恢复备份的ssh配置
cp -r path/to/bak/.ssh ~/.ssh
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa 

0x03 软件备忘

使用pacman或yay安装

  • chrome
  • intellij-idea-ultimate-edition
  • deepin-wine, deepin-wxwork(需要导入如下字体映射配置)
vim zh-font.reg 
REGEDIT4
 
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink]
"Lucida Sans Unicode"="wqy-microhei.ttc"
"Microsoft Sans Serif"="wqy-microhei.ttc"
"MS Sans Serif"="wqy-microhei.ttc"
"Tahoma"="wqy-microhei.ttc"
"Tahoma Bold"="wqy-microhei.ttc"
"SimSun"="wqy-microhei.ttc"
"Arial"="wqy-microhei.ttc"
"Arial Black"="wqy-microhei.ttc"
deepin-wine regedit zh-font.reg 
  • easystroke
  • variety
  • nextcloud
  • git-cola
  • postman-bin
  • xmind (removed “–add-modules=java.se.ee” in /usr/share/xmind/XMind/XMind.ini )
  • python-tensorflow-opt-cuda python-pytorch-cuda
  • nvidia-docker
  • ipython jupyter
  • youdao-dict deepin-wine-thunderspeed
  • netease-cloud-music
  • tusk
  • typora
  • visual-studio-code-bin
  • wps-office
  • dbeaver-ce
  • foxitreader

青蛙与缓存:简化实用版动态规划

1. 从一只青蛙说起

青蛙

话说有一只青蛙,想要跳下n级台阶下水塘,它每次可以跳1个台阶或者2个台阶,那么请问它一共有多少种跳法下水塘(比如,n=30时)?

用数学的语言来看,我们需要求一个青蛙跳的函数f(n),对这种自变量取值为非负整数的函数,我们可以从比较小的情况开始考虑,不难得到f(1)=1, f(2)=2,问题是以后的穷举越来越麻烦。

想象你就是那只青蛙,面对n级台阶,第一次你可以先跳1级,那么剩下n-1级,有f(n-1)种跳法,第一次也可以跳两级,那么剩下n-2级,有f(n-2)种跳法,所以这个问题的答案并不陌生,是神奇的斐波拉契数列:

解决这类求函数值问题的第一步,是找到一个递推式。我们把递推式翻译成python代码:

def fib(n):
    if n==0:
        return 1
    if n==1:
        return 1
    return fib(n-1)+fib(n-2)
%%time
fib(30)

Wall time: 269 ms
832040

运行时间284ms,有够慢的,为什么慢?因为重复计算实在太多,以计算f(5)为例,调用关系如下:

f(5)==>f(4), f(3)
f(4)==>f(3), f(2), f(3)==>f(2), f(1)
f(3)==>f(2), f(1), f(2)==>f(1), f(0), f(2)==>f(1), f(0), f(1)
f(2)==>f(1), f(0), f(1), f(1), f(0), f(1), f(0), f(1)
f(1), f(0), f(1), f(1), f(0), f(1), f(0), f(1)

那么一个很自然的想法是我们把中间计算结果都缓存下来,幸运的是,python中自带了这个“电池”。

from functools import lru_cache
@lru_cache()
def fib(n):
    if n==0:
        return 1
    if n==1:
        return 1
    return fib(n-1)+fib(n-2)
%%time
fib(30)
Wall time: 0 ns
832040

快到没计量出时间来。python中lru_cache的基本原理是构建一个字典,字典的key为调用参数,value就是该参数的计算结果。大致等价于如下代码:

def fib(n):
    if n in fib.cache:
        return fib.cache[n]
    if n==0:
        ans = 1
    elif n==1:
        ans = 1
    elif:
        ans = fib(n-1)+fib(n-2)
    fib.cache[n] = ans
    return ans
fib.cache = {}

当然,针对这个问题,我们可以使用更加细致的缓存方法, 乃至去掉递归改用循环(相当于只保留两个缓存,大大减少了空间占用,但是如果我们要反复计算各个n值,那么或许前一个方法才更合适):

def fib(n):
    a, b = 0, 1
    for i in range(n):
        a, b = b, a+b
    return a

本题等同于 leetcode 70, 在leetcode上的python3解答如下:

from functools import lru_cache
class Solution:
    @lru_cache()
    def climbStairs(self, n: int) -> int:
        if n==0:
            return 1
        if n==1:
            return 1
        return self.climbStairs(n-1)+self.climbStairs(n-2)

执行用时52 ms,内存消耗13.2MB。

2. 简化实用版动态规划

我们从这只青蛙中取得比较通用的启示,解决类似的可构造递推函数的问题:

  1. 寻找一个递推关系,建立递归函数,问题变成多个子问题的求解;
  2. 为了防止反复计算同样的子问题,使用缓存,用空间换时间。

在一般的算法教材或面试题解中,会花不少时间来设计这个缓存结构,在实际的工程问题中,我们可能对多使用一些缓存空间没有那么敏感,因此只需要开发递归函数,再加上通用的缓存方案就基本解决问题了。只有在缓存空间成为问题时,我们才需要进一步去考虑适应问题的更小的缓存。

为了检验这套方案,我们再看几道题,直接在leetcode上再找几个来刷。

2.1 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

我们考虑数组中每一个位置结尾能得到的最大和的递推关系。 基于此不难得到最终结果为 在leetcode中翻译成python3代码如下:

from functools import lru_cache
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        self.nums = nums
        return max(self.f(i) for i in range(len(nums)))
    
    @lru_cache()
    def f(self, k):
        if k == 0:
            return self.nums[0]
        else:
            return max(self.f(k-1), 0) + self.nums[k]

执行耗时76 ms,内存消耗13.7 MB。

2.2 最小路径和

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

示例:

输入: [ [1,3,1], [1,5,1], [4,2,1] ] 输出: 7 解释: 因为路径 1→3→1→1→1 的总和最小。

将矩阵中每个位置作为右下角,求最小路径和,不难得到如下递推公式:

在leetcode中翻译成python3代码如下:

from functools import lru_cache
class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        self.grid = grid
        return self.f(len(grid)-1, len(grid[0])-1)
    @lru_cache()
    def f(self, x, y):
        if x == 0 and y == 0:
            return self.grid[0][0]
        elif y == 0:
            return self.f(x-1, 0) + self.grid[x][0]
        elif x == 0:
            return self.f(0, y-1) + self.grid[0][y]
        else:
            return min(self.f(x-1,y), self.f(x,y-1)) + self.grid[x][y]

执行耗时1052ms,内存消耗13.9M。

投资,一种生活方式

社会要发展,除了消费拉动,还需要投资再生产,否则企业无法发展,公共事业也会停滞不前。在一定的时期内,各类资源是有限的、恒定的,用于消费的多了,用于投资的就少了,为了发展,必须有人拿钱出来投资。

如何驱动起来?一种是政府驱动,通过税收与公共事业建设运作;另一种是利益驱动,私人的债权投资和股权投资。对个人而言,买理财产品、买股票的本质应该是什么?是我付出了劳动,挣到了相应的钱作为衡量,但是我不完全消费掉,而是让渡出部分回报,用于投资再生产,将来在适当的时候,我可以兑现我的回报。

以上是从社会生产的方面说。个人和家庭,一样有消费和投资的矛盾,拿到工资,钱是是用于享受,还是用于自我提升,还是为将来的人生目标攒钱,需要我们仔细衡量。

很多人和我讨论股票,我都建议他们不要炒股,闲钱买点余额宝一类的货币基金就好,基本上没有风险和波动。因为投资是一件很严肃的事情,需要纳入个人的生活方式,家庭的运作方式去考虑。可能你不懂比较深入的理财知识,如果能努力挣钱,合理消费,适当储蓄,至少也能有个安稳的日子;即使你是个月光族,只要不遇上大的灾祸,也能活得不错。最可怕的反而是,焦虑着想要发大财,去搞所谓的“投资”,最后血本无归倾家荡产。

太多的人,投资买基金买股票往往是对金钱焦虑的一种应激反应,老想着靠股市挣到一笔钱买辆车甚至买套房,这其实和去澳门赌一把没有本质区别。

我这里没有讨论任何知识层面的东西,而是想说,如果炒股的出发点是错的,那么它必然只会导向一个错误的结果。老话说:吃不穷,穿不穷,不会盘算一世穷。对绝大部分人而言,投资不能成为一种职业,不是一种挣钱的方式,而只能是构建家庭理财计划的一部分,用于资产保值。没有站在十年二十年的尺度去规划,而是想着赚快钱,还是不要买基金买股票为好。

投资,是一种生活方式;赌博是另一种。

2018年小结

18年彻底完成了职业转换,在新的行业当一个无名小卒,在广州这个城市也逐渐找到了生活的滋味,虽然赚到的钱没有多少,但是都在预期之内。

在鸡汤与焦虑当道的自媒体时代,我有点喜欢的是丧文化,但更喜欢得知生活的种种无望与残酷后,依然像一个小朋友一样,不带太多压力,傻傻地认真努力。有三和大神的人生,也有过劳死的人生,我越来越倾向于认为人生本没有任何意义,或者只是作为基因的载体,意义只关乎选择。所以19年,继续不后悔不抱怨,仔细的选择,为所有的选择负责。

2018年的头衔变成了算法工程师,2019年大概率会继续这个头衔,人工智能的浪潮或许会变冷,整个IT行业喊叫冬天来了。冬天来了,继续当一个能解决问题的人,物质生活的要求简单一点,还是能心安的努力下去的吧,喧嚣与焦虑都与我无关。

2018年读了不到30本书,2019年希望读书能做到旧书新书三七开,多一点时间看看值得反复读的书。

2018年跑步记录有据可查的只有20次不到,更多的被散步取代了,身体的活力其实有点在危险边缘。

2018年投资收益-23%,满仓跨年,但是内心很平静。

所有的年度总结与计划过后拿出来比对,容易变成大型的打脸现场,因为我们总是高估自己的热血,低估人性的怠惰,瞧不起每天一点一滴的努力,想要一口气吃成胖子。只有认清人性,先接纳自己的普通,才能跳出目标定得太高与一点点努力都不做的恶性循环。

人总是低估长期的变化,而高估短期的变化,无论是投资、锻炼身体、孩子的教育,还是自我的提高。最好的努力是不悲不喜的习惯性努力。所以我秉承微习惯的理念,不再制定那些注定难以完成的大计划,只为自己定几个注定难以失败的计划:

  1. 每天做一个俯卧撑或者仰卧起坐;
  2. 每天看1页纸的上市公司年报或研报;
  3. 做一个业余项目,不设完成时限,早期用户只有自己,具体到每天必作的工作是打开项目文件夹,随便做点什么,1行代码或者1行文案都行。

新年快乐。

不会花钱的人,活该一辈子受穷

spend

好吧,这个题目只是标题党。其实,我想说的是,身为穷人,我们更应该去努力学习如何花钱,才能活得相对幸福一点,而真正的富人,自然是不需要看我这篇文章的。

在我老家湖南常德的农村,最近几年开始流行“送货下乡”,比如去年,某某品牌就“惠及农民兄弟”,在乡里推销净水器,疑似三无产品的净水器卖到一千多,至于效果如何,只能在推销时表演的浑水变清水节目上可以感受一下,很多老实巴交的老人平时一分钱舍不得多花,竟然就鬼使神差的装了净水器,这些产品既无品牌背书,更无售后服务,收到钱后谁还管你滤芯要不要换这种事,很多人用了没多久也就闲置了。

你要说这是彻底的诈骗,倒也谈不上,只是明明有无数的正规品牌可以买,为什么非要买一些三无产品呢?首先这自然是大品牌们发掘市场不利,其次,据我妈反映,这些装净水器的人,在村里也是相对比较穷的,身为穷人的可怕就在这里了,本来就没几个钱了,还要被各类骗子和疑似骗子的商家反复收割一番。

挣钱和花钱哪个更需要学习,鸡汤一点的话,我当然会说挣钱更值得学习,毕竟,只要我们钱够多,花钱的时候就不必畏畏缩缩,也不怕吃亏上当,所谓“财务自由”嘛。可是现实一点说,其实花钱更值得学习,因为我不得不很遗憾很颓废的指出,写本文和看本文的人,可能终其一生,也不过是挣扎过了温饱线,一辈子能挣到的钱不用计算器就能估算出来,要达到想怎么花就怎么花的状态,不过是痴心妄想罢了。

所以问题就变成了我们要把有限的钱花到什么地方,才能尽可能的活得相对幸福一些。最颓废的做法,自然是破罐子破摔,反正都是穷鬼,随随便便花,凑活过一辈子也就是了。不过我想的是,既然挣钱这回事太难,不如花钱的时候认真一点,就当我们在解一道限定条件的方程题,或者限定了韵脚去作一首五言绝句,用有限的金钱获得最大的幸福感,少被富人们收割几刀,也不失为失败者的另类成功。

不要误会,这不是“什么值得买“一类的公众号,我们不推荐具体的产品,只探讨一些理念性的问题。

关于花钱,我觉得需要弄清楚的只有一件事,就是我们付出了什么,又得到了什么,像一个炼金术士一样,我希望这两者基本是平衡的。

在消费中,我们付出的首先是显性成本,也就是钱,这似乎很好算,但是你看看”首付三千元,新车开回家“这种广告就容易犯迷糊,发现这笔帐也不是那么好算的。除了钱,我们还要付出时间成本,这是最容易忽略的,因为穷,会更加不珍视自己的时间,干一些花半天时间省五毛钱的事情。

在消费中,我们得到的是商品,每件商品的价格大致由三部分构成,产品的功能、品牌附加值、智商税。产品的功能自然是我们消费的首要目的,比如一件衣服的遮羞御寒、舒适美观的功用,为了减轻产品甄别的负担,品牌附加值往往是我们自愿付出的代价,比如有人认准了苹果手机,他在买手机时就不需要再费心的去比对参数和价格。而智商税,则是我们被有意无意的欺骗而付出的代价,比如小到买了一张彩票、一件防辐射的孕妇装,大到相信自己的孩子能统治世界,给大师打个几千万的培训费这种。

所以,我们穷人的目标很明确:权衡金钱和时间的花费,购买自己真正需要的产品功能,尽量少为品牌溢价付费,坚决不交智商税。

那么要如何才能做到呢?可惜这毕竟不是解方程,没有什么万能公式、标准答案,没有必要也没有可能做到完美。但是,这是一个值得不断学习,不但反思而去接近的目标。在移动支付无比发达的当下,不妨每个月打开自己的支付宝和微信支付账单,去反思一番,自己究竟买到的是什么?

另外很重要的就是,要想不去为防辐射孕妇装这种东西支付智商税,那么对于科学知识,就像一个长者说过的,我们还是要学习一个。

一点荧光——财富基因发刊词

glowworm

欢迎你成为公众号《财富基因》的读者。

一个现代人拥有的财富,越来越呈现出虚拟的形态,它往往只是一个数字,这个数字可能印刷在纸币上,可能存储在银行系统、股权登记系统中,甚至可能是区块链中的一串比特。这些数字本身不具备价值,而只是价值的代表。相信这些数字代表着财富,无论我们是相信银行系统背后的国家信用,还是比特币背后的密码学,都是人类想象力的伟大胜利。

人类的生物基因,并不会教会人如何看待、获取、利用财富,毕竟财富相关的概念产生在人类社会逐渐成熟时,最多不过几千年,相比漫长的进化史,它来得太迟,又变化得太快。不仅如此,人类基因中那些适应远古生活、来不及变化的部分,有时候反而是我们正确对待财富的障碍,即所谓人性的弱点。

只有借助文化基因的传承,我们才有可能学会正确的财富观,《财富基因》公众号,就是想在正确的财富观的传播上,做一点微小的工作。主要内容是我对当代社会消费、理财、投资的一系列思考。

开这个公众号的想法萌芽了很久,但是一度放弃,本想十年之后再考虑。毕竟,我只是一个刚解决了温饱问题的小市民,有什么资格大言不惭的谈论高大上的财富问题呢?可是一个突如其来的背悲剧让我改变了想法。

前不久,我的一个堂兄因重度抑郁症不幸去世了,在震惊悲痛之余,我也在反思,在这个悲剧中,不太健康的消费观带来的经济压力可能是一个诱因。如何消费、如何理财,和每个人的生活息息相关,或许并不是那么高大上。我自责在这个悲剧酝酿的过程中什么都没做。

有无数堪比日月的思想家,而我能做的,最多不过一个萤火虫,当一个观念的二道贩子,如果能给任何人带来思考上的一丝光明,那么这个写这个公众号也就存留了一丝意义。

二分类问题的评分值与损失函数

二分类问题是监督学习中最基本的问题之一,本文旨在从评分值、损失函数的角度,对解决二分类问题的诸多机器学习算法做一个梳理。相关算法的详细介绍可以参考相关教材12

loss

  1. 统计学习方法 李航 

  2. 机器学习 周志华 

本来无一物

img

题图是中国当代艺术家宋冬推出的“物尽其用”装置艺术展的一角,宋冬的母亲一辈子舍不得扔东西,将所有物品囤积起来,包括这些牙膏皮。

基于RNN实现加法

知乎上有人问题了个有趣的问题:

深度学习能够学会加法吗?

撇开所谓的哲学意义不谈,已经证明了通过RNN可以模拟实现图灵机,这篇神奇的论文看这里。我的大致理解是,图灵机也是一种模式匹配,而只要有足够多的学习样本,RNN总是可以学会这种模式匹配的。

假设我们要让神经网络学会3位数的加法,比如111+222=333,123+987=110,注意为了简单期间,我们不考虑最高位的进位。

加法可视为两个加数构造的序列来预测作为和的序列,因此可以用RNN来实现。示意图如下:输入序列、输出序列长度均为3,使用一层RNN存储进位信息。输入输出均使用one-hot编码后,输入特征数为20,输出特征数为10,中间层使用RNN,其隐藏节点数设为32。

addtion-by-rnn-1

使用keras搭了一下模型,图示如下:

addtion-by-rnn-2

代码如下:

注意为了画上述示意图,需要安装Graphviz

# -*- coding: utf-8 -*-

from keras.models import Sequential
from keras.utils import plot_model
from keras import layers
import numpy as np

import os
os.environ["PATH"] += os.pathsep + 'C:/Program Files (x86)/Graphviz2.38/bin/'

class CharacterTable(object):
    """
    one-hot encode/decode
    """
    def __init__(self, chars):
        self.chars = sorted(set(chars))
        self.char_indices = dict((c, i) for i, c in enumerate(self.chars))
        self.indices_char = dict((i, c) for i, c in enumerate(self.chars))

    def encode(self, C, num_rows):
        x = np.zeros((num_rows, len(self.chars)))
        for i, c in enumerate(C):
            x[i, self.char_indices[c]] = 1
        return x

    def decode(self, x, calc_argmax=True):
        if calc_argmax:
            x = x.argmax(axis=-1)
        return ''.join(self.indices_char[x] for x in x)

TRAINING_SIZE = 50000
DIGITS = 3

chars = '0123456789'
ctable = CharacterTable(chars)

questions = []
expected = []
seen = set()
print('Generating data...')
while len(questions) < TRAINING_SIZE:
    a = ''.join(np.random.choice(list('0123456789')) for i in range(DIGITS))
    b = ''.join(np.random.choice(list('0123456789')) for i in range(DIGITS))
    key = tuple(sorted((a, b)))
    if key in seen:
        continue
    seen.add(key)

    ans = str((int(a[::-1]) + int(b[::-1]))%1000).zfill(DIGITS)[::-1]
    
    questions.append((a,b))
    expected.append(ans)
print('Total addition questions:', len(questions))

print('Vectorization...')
x = np.zeros((len(questions), DIGITS, len(chars)*2), dtype=np.bool)
y = np.zeros((len(questions), DIGITS, len(chars)), dtype=np.bool)
for i, (a, b) in enumerate(questions):
    x[i] = np.column_stack((ctable.encode(a, DIGITS), ctable.encode(b, DIGITS)))
for i, ans in enumerate(expected):
    y[i] = ctable.encode(ans, DIGITS)
    
print('Split data...')
indices = np.arange(len(y))
np.random.shuffle(indices)
x = x[indices]
y = y[indices]

split_at = len(x) - len(x) // 10
(x_train, x_val) = x[:split_at], x[split_at:]
(y_train, y_val) = y[:split_at], y[split_at:]

print('Training Data:')
print(x_train.shape)
print(y_train.shape)

print('Validation Data:')
print(x_val.shape)
print(y_val.shape)

HIDDEN_SIZE = 32
BATCH_SIZE = 128

print('Build model...')
model = Sequential()

model.add(layers.SimpleRNN(HIDDEN_SIZE, input_shape=(DIGITS, len(chars)*2), return_sequences=True))
model.add(layers.TimeDistributed(layers.Dense(len(chars))))
model.add(layers.Activation('softmax'))
model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

print('Plot model...')
model.summary()
plot_model(model, to_file='model.png')

print('Training...')
ITER = 20
for iteration in range(ITER):
    print()
    print('-' * 50)
    print('Iteration', iteration)
    model.fit(x_train, y_train,
              batch_size=BATCH_SIZE,
              epochs=1,
              validation_data=(x_val, y_val))
    print("10 validate examples:")
    for i in range(10):
        ind = np.random.randint(0, len(x_val))
        rowx, rowy = x_val[np.array([ind])], y_val[np.array([ind])]
        preds = model.predict_classes(rowx, verbose=0)
        question = ctable.decode(rowx[0][:,:10]) + '+' + ctable.decode(rowx[0][:,10:])
        correct = ctable.decode(rowy[0])
        guess = ctable.decode(preds[0], calc_argmax=False)
        print('Q', question[::-1], end=' ')
        print('A', correct[::-1], end=' ')
        print('√' if correct == guess else '×', end=' ')
        print(guess[::-1])

能在较短时间内训练到100%的准确率。毕竟,这个模式识别起来不太复杂。

至于如何让机器理解什么是加法,这就是另外一个问题了,毕竟我们也难以理解人类是如何学会理解加法的,甚至人类是否理解加法都不知道。

停止日更

从今年元旦开始,每天更新一篇博客,已经约280天,如果我愿意的话,可以像行为艺术一般坚持下去,不过我觉得坚持日更的目的已经达到了,它拉动了我思考学习的进程,但是非要每日一更已经成为一个负担,真正有创造性的东西要日更几乎不可能,而片言只语式的更新只能算是微博,如果只是读书笔记又其实不适合用博客的形式写出来。接下来博客的写作主要做以下几件事:

  1. 由日更变为周更,每周1~2篇质量较高的,有一定创造性的文章;
  2. 将过去的博客中原创性较低的读书笔记以gitbook的形式重新整理发布到learn子域名;
  3. 整理过去的博客,修正错别字,有一些内容不完善的补充完善;
  4. 对有价值的内容在主页上做好导航。