SSH免密登录在Keepalived双机热备切换时失效的问题处理过程

背景

近日项目中客户通过SSH免密登录使用我们的主机的STFP服务,主机使用Keepalived热备。在测试的时候发现,主机掉线VIP切到备机上后,备机无法免密登录。后经过一番折腾终于解决问题,过程比较有趣,遂记录下来,以备参考。

问题重现

  • 预配置

生成公私钥对和配置的过程就不在赘述,章节省略,我就给出测试的机器配置
名称 IP 用户
主机11 10.0.10.11 root
主机12 10.0.10.12 root
虚拟主机21 10.0.10.21 root
免密客户机12 10.0.10.12 root
21是keepalived的VIP所在机器,会在11和12之间切换,一开始21指向11。 我们使用12来作为ssh客户端来访问21来开始整个过程
  • 免密正常

正常情况下配置好免密后,12第一次访问21会提示,以后访问则直接进入。
[root@10_12 .ssh]# ssh root@10.0.10.21
The authenticity of host '10.0.10.21 (10.0.10.21)' can't be established.
RSA key fingerprint is 87:87:f0:2c:49:9e:03:a3:9a:80:9f:c9:90:f0:5d:e8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.0.10.21' (RSA) to the list of known hosts.
Last login: Sun Mar 18 04:55:24 2018 from 10.0.10.12
[root@10_11 ~]# exit
logout
Connection to 10.0.10.21 closed.
[root@10_12 .ssh]# ssh root@10.0.10.21
Last login: Sun Mar 18 04:56:51 2018 from 10.0.10.12
[root@10_11 ~]# exit
logout
Connection to 10.0.10.21 closed.
  • 切换主机

这时候,如果把VIP 21从11机器切换到12机器上
[root@10_11 ~]# service keepalived stop
停止 keepalived:                                          [确定]
  • 免密异常

然后再使用12访问21,就会提示以下错误信息
[root@10_12 .ssh]# ssh root@10.0.10.21
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
91:11:3a:62:6d:69:e9:07:05:88:de:ee:a8:d0:e5:8a.
Please contact your system administrator.
Add correct host key in /root/.ssh/known_hosts to get rid of this message.
Offending key in /root/.ssh/known_hosts:1
RSA host key for 10.0.10.21 has changed and you have requested strict checking.
Host key verification failed.

问题分析

问题分析经过了如下过程:
  1. 因为SFTP目录对权限要求很严格,最开始认为可能是SSH配置和SFTP默认目录权限问题。但是几经查找之后并没有发现主机和备机的差异之处,于是第一次尝试陷入了僵局。
  2. 后来查看了主备机的SSH版本发现并不一致,觉得可能是版本太低不支持免密,但做了测试后,遂发现低版本的免密是可用的,遂这条路线也宣告失败。
  3. 以上情况都尝试了,同事们也束手无策,问题陷入了僵局。
  4. 归零,从头开始看。由于这个问题是客户报的,并不是我们自测出来,所以一开始并未仔细观察错误内容,遂联系客户仔细检查了客户发出的错误提示,也是就是本文中免密异常的提示部分,经过相关搜索最终发现了问题所在。
产生这个问题的主要原因是在于ssh的验证过程会引入远程机器指纹的校验。 我们在回头看免密成功时的第一次登录信息
[root@10_12 .ssh]# ssh root@10.0.10.21
The authenticity of host '10.0.10.21 (10.0.10.21)' can't be established.
RSA key fingerprint is 87:87:f0:2c:49:9e:03:a3:9a:80:9f:c9:90:f0:5d:e8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.0.10.21' (RSA) to the list of known hosts.

第一次登录某个主机时,ssh客户端会记录远程机器的RSA指纹,并写入~/.ssh/know_host文件中

[root@10_12 .ssh]# cat ~/.ssh/known_hosts 
10.0.10.21 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAy9M2k50qh6BIgqwP7A8kusrmUkDZKCC7W0EOjDxj+ZElmB+26weRDXoyj4c41w3hucDnM5Wo6q0J+xMxWPTHXoX/5H9WkUQGTDajndy6RFW84uuEVpVyE5UVugRvzL4x4M084ienqR4riIAF9ffL3TU+QJ6ow4fThTv+3phIaxgBJC+n4EoVPxfVFX5mKgahtcnwP/UBYu4i7Yjv5zY5oA54YpvBg8psCDdP1nK8LcrBwkARWxYNEnPFzJaAoy15vTe866eEGfqdqCnG7WzR6Fn2CYBipcm9rTI8Q8v6P2gwRHF/nelQfXBY593NMQdc6DfoIzdjwIKPQW5DLCf33Q==
如果远程的IP未变,但物理机器更换了,正如keepalived切换主备机时候一样,远程的机器的指纹其实发生了变化,我们再看免密异常的错误提示
[root@10_12 .ssh]# ssh root@10.0.10.21
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
91:11:3a:62:6d:69:e9:07:05:88:de:ee:a8:d0:e5:8a.
Please contact your system administrator.
Add correct host key in /root/.ssh/known_hosts to get rid of this message.
Offending key in /root/.ssh/known_hosts:1
RSA host key for 10.0.10.21 has changed and you have requested strict checking.
Host key verification failed.
这个机器的指纹和我们第一次登录时候不一样了,所以根据ssh登录时的默认安全策略,拦截了这次的免密登录。

解决方案

既然问题找到了,那么如何解决这个问题呢。经过一番搜索,找到了一些解决办法
  • 第一种:遗忘法

每次登录时把~/.ssh/known_hosts 删除,不过这个办法不好用来做自动化操作。
  • 第二种:无视法

我们同样可以让ssh客户端不进行指纹检查和不记录指纹,但这样会降低一定的系统安全性 ~/.ssh/config更新如下配置
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
~/.ssh/config权限要求是组和其他用户不能写,否则会失败,例如用户权限是664时,进行登录会有如下错误提示
[root@10_12 .ssh]# ll
总用量 16
-rw-r--r-- 1 root root  408 3月  18 04:33 authorized_keys
-rw-rw-r-- 1 root root   55 3月  18 06:08 config
-rw------- 1 root root 1675 3月  18 04:13 id_rsa
-rw-r--r-- 1 root root  408 3月  18 04:13 id_rsa.pub
[root@10_12 .ssh]# ssh root@10.0.10.21
Bad owner or permissions on /root/.ssh/config
如果出现这样的错误时,我们需要更改下权限即可。
[root@10_12 .ssh]# chmod 644 config

总结思考

这个问题花了很长时间解决,根本上的原因还是由于对ssh验证过程不熟导致。 问题分析过程也表明了,疑难问题要从长计议,不要忽视原始错误信息,可能会给我们带来很大帮助。 有些问题单独处理也许很简单,但结合了一些其他组件可能会变得复杂,要仔细观察问题表现,通过丰富的知识和经验中提取关键因素,再去解开表象从而达到问题本质,这时便迎刃而解 关于本问题其实还可能存在其他处理方式,例如让两机器的指纹一致,这时候需要修改机器指纹,如下文件
[root@10_12 ssh]# ll /etc/ssh
总用量 156
-rw------- 1 root root 125811 3月  22 2017 moduli
-rw-r--r-- 1 root root   2047 3月  22 2017 ssh_config
-rw------- 1 root root   3876 3月  18 04:40 sshd_config
-rw------- 1 root root    668 4月  17 2017 ssh_host_dsa_key
-rw-r--r-- 1 root root    590 4月  17 2017 ssh_host_dsa_key.pub
-rw------- 1 root root    963 4月  17 2017 ssh_host_key
-rw-r--r-- 1 root root    627 4月  17 2017 ssh_host_key.pub
-rw------- 1 root root   1675 4月  17 2017 ssh_host_rsa_key
-rw-r--r-- 1 root root    382 4月  17 2017 ssh_host_rsa_key.pub
但由于这些文件都是ssh验证过程的基础,直接修改会不会导致问题或者组件失效,我也没办法保证也没有去研究,如果有感兴趣的同学们可以去试一试  Continue reading →
Posted in Linux | Tagged , | 1 Comment

kaggle数字识别器训练过程

课题背景

这次课题选择是Digit Recognizer[1],本课题是kaggle上的一个简单的机器学习任务。此任务内容是在MNIST[2](Modified National Institute of Standards and Technology)数据集上训练一个数字识别器。MINIST是一个用于计算机视觉处理入门型的数据集。 数据集为两部分,首先是训练集,本课题的大小为42000个样本,每个样本是0~9的分类和28*28=784个灰度像素值,分别表示数字和对应的手写数字的图像。其次还有个大小为20000个样本的测试集,测试集的样本只包含像素值,不包含分类。 通过训练集训练模型,再使用模型去处理测试集,从而得到测试结果。测试结果包含像素值所表示图像ID(即下标)以及像素值所表示的数字。最后在kaggle上的排名以在测试集合上的分类正确率为依据。 手写数字识别是一种常见的应用场景,典型的例如网站验证码的识别。并由此可推广至文字识别、文档OCR识别这种传统的普遍应用,乃至车牌交通标识识别、新型人机交互系统等等更智能化的应用。故此课题的研究虽然简单,但有广泛的应用价值。

算法背景

根据本任务页面的描述,使用神经网络、SVM以及K-近邻算法可以得到比较好的处理结果。再结合时间公司培训内容,遂选择SVM作为本次课题的主要算法。

SVM简介

SVM(support vector machine支持向量机)[3]是在分类与回归分析中分析数据的监督式学习模型与相关的学习算法。给定一组训练实例,每个训练实例被标记为属于两个类别中的一个或另一个,SVM训练算法创建一个将新的实例分配给两个类别之一的模型,使其成为非概率二元线性分类器。SVM模型是将实例表示为空间中的点,这样映射就使得单独类别的实例被尽可能宽的明显的间隔分开。然后,将新的实例映射到同一空间,并基于它们落在间隔的哪一侧来预测所属类别。如图1: File:Svm separating hyperplanes (SVG).svg 图1:H1不能把类别分开。H2可以,但只有很小的间隔。 H3以最大间隔将它们分开。

线性SVM

我们考虑以下形式的 点测试集: 其中 是1或者−1,表明点 所属的类。 中每个都是一个 向量。我们要求将 的点集 与的 点集分开的“最大间隔超平面”,使得超平面与最近的点 之间的距离最大化。 任何超平面都可以写作满足下面方程的点集 其中 (不必是归一化的)是该法向量。参数 决定从原点沿法向量到超平面 的偏移量。 File:Svm max sep hyperplane with margin.png 图2:设样本属于两个类,用该样本训练SVM得到的最大间隔超平面。在超平面上的样本点也称为支持向量。

硬间隔

如果这些训练数据是线性可分的,可以选择分离两类数据的两个平行超平面,使得它们之间的距离尽可能大。在这两个超平面范围内的区域称为“间隔”,最大间隔超平面是位于它们正中间的超平面。这些超平面可以由方程族: 或是 来表示。 通过几何不难得到这两个超平面之间的距离是 ,因此要使两平面间的距离最大,我们需要最小化 。同时为了使得样本数据点都在超平面的间隔区以外,我们需要保证对于所有的 满足其中的一个条件: 或是 这些约束表明每个数据点都必须位于间隔的正确一侧。 这两个式子可以写作: 可以用这个式子一起来得到优化问题: “在 条件下,最小化 ,对于 ” 这个问题的解 决定了我们的分类器 此几何描述的一个显而易见却重要的结果是,最大间隔超平面完全是由最靠近它的那些 确定的。这些 叫做支持向量。

软间隔

为了将SVM扩展到数据线性不可分的情况,我们引入损失函数, 当约束条件(1)满足时(也就是如果 位于边距的正确一侧)此函数为零。对于间隔的错误一侧的数据,该函数的值与距间隔的距离成正比。然后我们希望最小化 其中参数 用来权衡增加间隔大小与确保位于 间隔的正确一侧之间的关系。因此,对于足够小的 值,如果输入数据是可以线性分类的,则软间隔SVM与硬间隔SVM将表现相同,但即使不可线性分类,仍能学习出可行的分类规则。

核函数

尽管原始问题可能是在有限维空间中陈述的,但用于区分的集合在该空间中往往线性不可分。为此,有人提出将原有限维空间映射到维数高得多的空间中,在该空间中进行分离可能会更容易。为了保持计算负荷合理,人们选择适合该问题的核函数来定义SVM方案使用的映射,以确保用原始空间中的变量可以很容易计算点积。如图2: https://upload.wikimedia.org/wikipedia/commons/1/1b/Kernel_Machine.png 图2:核函数示意图 高维空间中的超平面定义为与该空间中的某向量的点积是常数的点的集合。值得注意的是,更高维的特征空间增加了支持向量机的泛化误差,但给定足够多的样本,算法仍能表现良好。

主要参数

SVM的有效性取决于核函数、核参数和软间隔参数C的选择。核函数通常会选只有一个核参数γ的高斯函数。C和γ的最佳组合通常通过在C和γ为指数增长序列下网格搜索来选取。通常情况下,使用交叉验证来检查参数选择的每一个组合,并选择具有最佳交叉验证精度的参数。或者把随机搜索用于选择C和γ,通常需要评估比网格搜索少得多的参数组合。然后,使用所选择的参数在整个训练集上训练用于测试和分类新数据的最终模型。

整体思路

本课题整体思路分为以下几步骤:
  1. 数据读入
  2. 定制数据处理流程
    1. 选择PCA用于后续数据对齐处理
    2. 选择SVM用于后续模型训练
    3. 使用Pipeline[4]定制PCA和SVM的处理流程
  3. 设置参数搜索
    1. 开始使用大范围参数配合随机搜索
    2. 随后使用确定数目的固定参数集进行网格搜索
    3. 最终使用确定参数
  4. 训练模型
    1. 开始使用随机搜索验证[5]来确定大体范围
    2. 随后使用网格搜索验证[6]来确定详细范围
    3. 最终使用确定参数训练
  5. 预测测试数据
  6. 输出结果
  7. 提交kaggle验证结果有效性
  8. 重复3-7部分以求kaggle得分高

数据处理说明

概要说明

在计算模型训练前,我们需要对原始数据进行处理以适合进行训练。在数据处理部分使用PCA[7](主成分分析)方式对数据进行处理。 PCA顾名思义,即分析数据主要成分来处理数据的方法,其要点为降维处理和白化[8]。以下分别介绍

降维处理

PCA的降维处理方式为:通过计算出n个维度训练数据矩阵的协方差矩阵∑,其大小为n*n。我们先计算出协方差矩阵∑的特征向量,按特征值大小来定位其列排放,而组成特征矩阵 \begin{align} U = \begin{bmatrix} | & | & & | \\ u_1 & u_2 & \cdots & u_n \\ | & | & & | \end{bmatrix} \end{align} 此处, \textstyle u_1 是主特征向量(对应最大的特征值), \textstyle u_2 是次特征向量。以此类推,另记 \textstyle \lambda_1, \lambda_2, \ldots, \lambda_n 从大到小为相应的特征值。 接下来还需要一个旋转的过程。加入我们有一个2维度的数据集,可以把 \textstyle x\textstyle (u_1, u_2) 基表达为: \begin{align} x_{\rm rot} = U^Tx = \begin{bmatrix} u_1^Tx \\ u_2^Tx \end{bmatrix} \end{align} (下标“rot”来源于单词“rotation”,意指这是原数据经过旋转(也可以说成映射)后得到的结果) 对数据集中的每个样本 \textstyle i 分别进行旋转: \textstyle x_{\rm rot}^{(i)} = U^Tx^{(i)} ,这就是把训练数据集旋转到 \textstyle u_1\textstyle u_2 基后的结果。一般而言,运算 \textstyle U^Tx 表示旋转到基 \textstyle u_1 , \textstyle u_2 ,…, \textstyle u_n 之上的训练数据。矩阵 \textstyle U 有正交性,即满足 \textstyle U^TU = UU^T = I ,所以若想将旋转后的向量 \textstyle x_{\rm rot} 还原为原始数据 \textstyle x ,将其左乘矩阵 \textstyle U 即可: \textstyle x=U x_{\rm rot} ,验算一下: \textstyle U x_{\rm rot} = UU^T x = x . 数据的主方向就是旋转数据的第一维 \textstyle x_{{\rm rot},1} 。因此,若想把这数据降到一维,可令: \begin{align} \tilde{x}^{(i)} = x_{{\rm rot},1}^{(i)} = u_1^Tx^{(i)} \in \Re. \end{align} 更一般的,假如想把数据 \textstyle x \in \Re^n 降到 \textstyle k 维表示 \textstyle \tilde{x} \in \Re^k (令 \textstyle k < n ),只需选取 \textstyle x_{\rm rot} 的前 \textstyle k 个成分,分别对应前 \textstyle k 个数据变化的主方向。 PCA的另外一种解释是: \textstyle x_{\rm rot} 是一个 \textstyle n 维向量,其中前几个成分可能比较大(例如,上例中大部分样本第一个成分 \textstyle x_{{\rm rot},1}^{(i)} = u_1^Tx^{(i)} 的取值相对较大),而后面成分可能会比较小(例如,上例中大部分样本的 \textstyle x_{{\rm rot},2}^{(i)} = u_2^Tx^{(i)} 较小)。 PCA算法做的其实就是丢弃 \textstyle x_{\rm rot} 中后面(取值较小)的成分,就是将这些成分的值近似为零。具体的说,设 \textstyle \tilde{x}\textstyle x_{{\rm rot}} 的近似表示,那么将 \textstyle x_{{\rm rot}} 除了前 \textstyle k 个成分外,其余全赋值为零,就得到: \begin{align} \tilde{x} = \begin{bmatrix} x_{{\rm rot},1} \\ \vdots \\ x_{{\rm rot},k} \\ 0 \\ \vdots \\ 0 \\ \end{bmatrix} \approx \begin{bmatrix} x_{{\rm rot},1} \\ \vdots \\ x_{{\rm rot},k} \\ x_{{\rm rot},k+1} \\ \vdots \\ x_{{\rm rot},n} \end{bmatrix} = x_{\rm rot} \end{align} 然而,由于上面 \textstyle \tilde{x} 的后 \textstyle n-k 项均为零,没必要把这些零项保留下来。所以,我们仅用前 \textstyle k 个(非零)成分来定义 \textstyle k 维向量 \textstyle \tilde{x} 。 这也解释了我们为什么会以 \textstyle u_1, u_2, \ldots, u_n 为基来表示数据:要决定保留哪些成分变得很简单,只需取前 \textstyle k 个成分即可。这时也可以说,我们“保留了前 \textstyle k 个PCA(主)成分”。 现在,我们得到了原始数据 \textstyle x \in \Re^n 的低维“压缩”表征量 \textstyle \tilde{x} \in \Re^k ,反过来,如果给定 \textstyle \tilde{x} ,我们应如何还原原始数据 \textstyle x 呢?根据上面的过程可知,要转换回来,只需 \textstyle x = U x_{\rm rot} 即可。进一步,我们把 \textstyle \tilde{x} 看作将 \textstyle x_{\rm rot} 的最后 \textstyle n-k 个元素被置0所得的近似表示,因此如果给定 \textstyle \tilde{x} \in \Re^k ,可以通过在其末尾添加 \textstyle n-k 个0来得到对 \textstyle x_{\rm rot} \in \Re^n 的近似,最后,左乘 \textstyle U 便可近似还原出原数据 \textstyle x 。具体来说,计算如下: \begin{align} \hat{x} = U \begin{bmatrix} \tilde{x}_1 \\ \vdots \\ \tilde{x}_k \\ 0 \\ \vdots \\ 0 \end{bmatrix} = \sum_{i=1}^k u_i \tilde{x}_i. \end{align} 上面的等式基于先前\textstyle U 的定义。在实现时,我们实际上并不先给 \textstyle \tilde{x} 填0然后再左乘 \textstyle U ,因为这意味着大量的乘0运算。我们可用 \textstyle \tilde{x} \in \Re^k 来与 \textstyle U 的前 \textstyle k 列相乘,即上式中最右项,来达到同样的目的。 在训练自动编码器或其它无监督特征学习算法时,算法运行时间将依赖于输入数据的维数。若用 \textstyle \tilde{x} \in \Re^k 取代 \textstyle x 作为输入数据,那么算法就可使用低维数据进行训练,运行速度将显著加快。对于很多数据集来说,低维表征量 \textstyle \tilde{x} 是原数据集的极佳近似,因此在这些场合使用PCA是很合适的,它引入的近似误差的很小,却可显著地提高你算法的运行速度。 通过降维可以使得数据训练过程加快,但降维太少则起不到加速效果,降维太多则损失数据精度。我们需要一个容易掌握的调整的值来表示我们需要降到多少维度,以衡量处理不同维度的数据的维度保留情况。这里可以通过控制保留方差的方式来控制选择剩余维度个数。保留方差的计算公式为 \begin{align} \frac{\sum_{j=1}^k \lambda_j}{\sum_{j=1}^n \lambda_j}. \end{align} 即保留的维度的特征值之和除以全部特征值和。该值的范围是[0,1]。若取值0.9,则可以理解为保留了最主要的90%的特征。在本课题中代码的components参数即代表该值。

白化

我们上面已经了解了如何使用PCA降低数据维度。在执行算法中还需要一个与之相关的预处理步骤,这个预处理过程称为白化。以当前课题举例,训练数据是图像,由于图像中相邻像素之间具有很强的相关性,所以用于训练时输入是冗余的。白化的目的就是降低输入的冗余性;更正式的说,我们希望通过白化过程使得学习算法的输入具有如下性质:
  1. 特征之间相关性较低;
  2. 所有特征具有相同的方差。
白化的公式很简单 \begin{align} x_{{\rm PCAwhite},i} = \frac{x_{{\rm rot},i} }{\sqrt{\lambda_i}}. \end{align} ,具体思想如下: 如何消除输入特征之间的相关性?在前文计算 \textstyle x_{\rm rot}^{(i)} = U^Tx^{(i)} 时实际上已经消除了输入特征 \textstyle x^{(i)} 之间的相关性。 对于维度为2的数据来说,其协方差矩阵如下: \textstyle \lambda_1 0 0 \textstyle \lambda_2 \textstyle x_{\rm rot} 协方差矩阵对角元素的值为 \textstyle \lambda_1\textstyle \lambda_2 绝非偶然。并且非对角元素值为0;因此, \textstyle x_{{\rm rot},1}\textstyle x_{{\rm rot},2} 是不相关的,满足我们对白化结果的第一个要求(特征间相关性降低)。 为了使每个输入特征具有单位方差,我们可以直接使用 \textstyle 1/\sqrt{\lambda_i} 作为缩放因子来缩放每个特征 \textstyle x_{{\rm rot},i} 。具体地,我们定义白化后的数据 \textstyle x_{{\rm PCAwhite}} \in \Re^n 如下: \begin{align} x_{{\rm PCAwhite},i} = \frac{x_{{\rm rot},i} }{\sqrt{\lambda_i}}. \end{align} 这些数据现在的协方差矩阵为单位矩阵 \textstyle I 。我们说, \textstyle x_{{\rm PCAwhite}} 是数据经过PCA白化后的版本: \textstyle x_{{\rm PCAwhite}} 中不同的特征之间不相关并且具有单位方差。 本课题中全部代码默认开启白化来处理数据。

算法及代码

鉴于调整参数时代码基本重复,故只给出首次随机搜索时代码如下:
import pandas as pd
import numpy as np
import time
import random
import matplotlib.pyplot as plt
#pca
from sklearn.decomposition import PCA
from sklearn.pipeline import Pipeline
#svc
from sklearn.svm import SVC,NuSVC
from sklearn.model_selection import  GridSearchCV,RandomizedSearchCV
#显示结果样例
def plot_sample(test_data, result, count=20):
    sample = random.sample(range(0,result.shape[0]),count)
    fig = plt.figure(figsize=[16,9])
    #plt.title("Test Data Sample", fontsize=20, color="b")
    plt.xticks([])
    plt.yticks([])
    plt.axis('off')
    i = 0
    for pos in sample:
        p1 = fig.add_subplot(4,(count/4)+(count%4), i+1)
        title_str="Result: "+str(result[pos])
        plt.title(title_str)
        grid_data = test_data[pos].reshape(28,28) 
        p1.imshow(grid_data, cmap = plt.cm.gray_r)
        i = i+1
        plt.xticks([])
        plt.yticks([])
    plt.show()
def write(clf, test_data):
    start = time.clock()
    result=clf.predict(test_data)
    result = np.c_[range(1,len(result)+1), result.astype(int)]
    df_result = pd.DataFrame(result, columns=['ImageId', 'Label'])
    df_result.to_csv('./data/results.csv', index=False)
    elapsed = (time.clock() - start)
    print("Test Time used:",elapsed)
    plot_sample(test_data, result)
if __name__=='__main__':
    #read data
    start = time.clock()
    X_test = pd.read_csv("./data/test.csv").values
    dataset = pd.read_csv("./data/train.csv")
    X_train = dataset.values[0:, 1:]
    y_train = dataset.values[0:, 0]
    #for fast evaluation
    X_train_small = X_train[:10000, :]
    y_train_small = y_train[:10000]
    #plot_sample(X_train, y_train)
    elapsed = (time.clock() - start)
    print("Read Time used:",elapsed)
   
    #begin progressing
    start = time.clock()
    parameters = dict(
        pca__n_components = np.arange(0.7,0.99,0.05),
        svc__gamma = np.arange(0.01,0.1, 0.02),
        svc__kernel = ['linear', 'rbf'],
        svc__C = np.arange(1,50,5) )
    pipe_clf=Pipeline([('pca', PCA(whiten=True)), ('svc', SVC())])
    
    #fast search
    rs_clf = RandomizedSearchCV(pipe_clf, parameters, n_jobs=-1, verbose=True )
    rs_clf.fit( X_train_small, y_train_small )
    elapsed = (time.clock() - start)
    print("Train Time used:",elapsed)

    print()
    #for params, mean_score, std_scores in rs_clf.grid_scores_:
    #    print("%0.3f (+/-%0.03f) for %r"  % (mean_score, std_scores.std() * 2, params))
    print("Best:", rs_clf.best_params_ , rs_clf.best_score_)
    #test & write
    write(rs_clf, X_test)
    exit(0)

图表和结果展现

运行环境

项目 参数
CPU Intel Core i7-3720QM CPU@2.60GHz 4 Cores 8 Threads
MEMORY 8GB DDR3 x 2@1600Mhz
HD 256GB SSD
OS Windows 10 Pro 64bit
IDE Visual Studio 2017+Anaconda 5.0+Python 3.6 64bit

运行结果

  1. 第一次随机搜索
    1. 控制台输出
Fitting 3 folds for each of 10 candidates,totaling 30 fits [Parallel(n_jobs=-1)]:Done 30 out of 30|elapsed:1.7min finished Train Time used:108.23998763676728 …… Best:{‘svc__kernel’:’rbf’,’svc__gamma’:0.029999999999999999,’svc__C’:36,’pca__n_components’:0.75} 0.9672 Test Time used:6.136322404056656
    1. Kaggle结果
  1. 最终训练
  1. 图形效果
每次程序运行完毕时,会有一个图形样例的输出如下: C:\Users\davel\Desktop\Figure_1.png

参考文档

  1. Digit Recognizer https://www.kaggle.com/c/digit-recognizer
  2. MINST http://yann.lecun.com/exdb/mnist/
  3. SVM https://en.wikipedia.org/wiki/Support_vector_machine
  4. Pipeline http://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html
  5. RandomizedSearchCV http://scikit-learn.org/stable/modules/generated/sklearn.grid_search.RandomizedSearchCV.html
  6. GridSearchCV http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html
  7. PCA http://ufldl.stanford.edu/wiki/index.php/PCA
  8. 白化 http://ufldl.stanford.edu/wiki/index.php/Whitening
Continue reading →
Posted in 机器学习 | Tagged | 8 Comments

近日杂记2

三个月没写日志了,看到大家都在催更,稍稍的有点不好意思。拖得时间也够长了,来篇流水账记录下最近的状态

身体状态

9月份以后身体就好了很多,现在已经没有什么异常感了,只是在吃药,据说要吃到明年。。。

但是鼻炎犯了一直没好,北京的空气,差评!

昨日朋友们拉我一起去爬灵山,大家走了很远的路,看红橙绿叶,赏锦绣河山,话今之喜悦,忆生之多艰。山上很冷,风很大,虽然加了比平时加了线衣和保暖裤,不过耳朵和手这暴露的位置还是冻青了,由于热量不足,快到山顶的时候特别累,好在上去了之后就不在难受了。下次还得注意更加保暖,到时候就从彭冲那里买装备吧。

Continue reading →
Posted in 日记 | 34 Comments

六月三十日记

是月,罹肺疾,嘱勿操劳,遂改易作息。至周末无事,常流连于元大都城垣遗址,读书度事,躬省自行,有感乃作。

病闲无所释 故城独往行 松槐连峦翠 兰蕙映阶青 昔尊金玉台 今唤北土城 不鉴百姓志 帝国亡复兴?

六月三十日于元大都城垣遗址碑前Continue reading →
Posted in 日记 | 69 Comments

DTCC短闻

这是一篇短文。 由于各种机缘巧合,我于4月18日参加了第四届DTCC(中国数据库技术大会)。我参加的会议议题主要有两个:
  • 大数据
  • Nosql
而给我留下深刻印象的几点
  • 大数据,公众服务中从海量数据直接导出决策将大大影响(改善)我们的日常生活,从天气预报到今天要吃什么,都可以给你最佳指导。但这远远不是革命,最多也就是信息革命中的一朵浪花。
  • MapReduce,这种基于并行分治合并的处理思想和架构越来越多的应用到互联网当中,给大数据分析带来基础支撑。
  • SSD,比普通硬盘高1000倍的响应速度将改变数据存储的架构。硬盘的足够快的可以使我们忽略很多很多问题,然而太快而引发的问题会逐步浮现。
  • CAP,当系统出现故障,我们需要恢复P(容错性)的时候,应该牺牲C(一致性)还是A(可用性呢),对于工程来讲,一切都是成本和利润的问题。
个人也记录了第一天内容的笔记,传上来给大家分享,文本问题,请调整TAB尺寸为4以方便阅读。 点击下载DTCC会议记录 大会PPT下载地址官方也已放出,需要提交个人信息后方可下载: http://topic.it168.com/factory/DTCC2013/Continue reading →
Posted in 软件工程 | Tagged | 33 Comments

转载:清华大学开源镜像站即将被关闭

来自清华大学开源社区的消息:

概述

为广大清华师生提供科研支持的清华大学开源镜像站 mirrors.tuna.tsinghua.edu.cn 即将被清华大学网络中心关停。 开源镜像站对于科研提供了巨大的支持,国外优秀的软件项目大多以开放源代码(开源 Open Source)的形式放出。使用开源软件,一方面不需交纳任何费用,另一方面也不存在使用盗版商业软件的道德和法律问题。 而开源镜像站则是将世界上优秀的开源项目收集到一起,方便同学和老师们高速地安装、更新、使用。并且国外许多优秀的理工类院校都有架设开源镜像站的传统。从清华大学开源镜像站安装开源软件,下载速率为10MB/s量级,而从开源项目的官方网站下载,速率约为50KB-500KB/s,不旦浪费网络流量而且加重校园网出口负担。按照统计数据估算,平均每月可为科研节约出校流量约5000GB。简而言之,开源镜像站是科研工作者的“软件管家”,拥有一个校内的开源镜像站更是能让借助计算机完成的科研工作如虎添翼。 清华大学开源镜像站于2011年创立,在短短几年的时间内迅速聚集了一批热心且技术水平过硬的同学维护,目前已经在国际、国内有广泛的影响,多次被IT业界新闻网站报道。目前已经收录44种开源项目镜像共计约5000GB,其中12种开源项目镜像已经被官方认可,官方服务器将来自中国甚至亚洲的访问请求直接定向到清华大学、使用官方的子域名直接解析到清华大学、或者在官方网站显著处写明地址和学校名称,这对于开源镜像站以及清华大学是非常大的荣誉和肯定。 由于一直无法获得网络中心的正式支持,开源镜像站只能“四海为家”,经常被断网、断电、下架。每次搬迁都会导致服务长时间不可用,经常会因此收到国内用户以及国外开源组织的来信询问。 不久前,我们接到最后通知,清华大学开源镜像站将被限期下线并搬迁出校园网机房,届时,使用开源软件的同学和老师将不得不使用其它兄弟院校提供的镜像站或者直接从软件项目的官方网站以较慢的速度下载、更新,不仅低效而且加重校园网出口负担。我们不希望这样的结果发生,期望得到大家的帮助使得这一决定被改变。  

如何帮助我们

  请在人人上分享我们的日志,并且参加联署来支持我们。 ————————————————– 因为内容过长,请大家前往:http://mirrors.tuna.tsinghua.edu.cn/files/petition.html 清华大学开源镜像站:http://mirrors.tuna.tsinghua.edu.cnContinue reading →
Posted in Linux | Tagged , | 13 Comments

高性能程序设计经验总结

最近读了一些书(CSAPP & CLRS),也分析和编写了一些基础算法,有必要归纳从中获取的经验,以备后用。

优化方案

我把优化范围三个领域,如下:

1、问题领域

指的是在设计阶段建立问题模型时,所使用的优化方案
  1. 选择好适当的算法和数据结构,避免使用时间复杂度高的算法或最坏情况(如在大规模数据中使用快速排序代替选择排序,优化快速排序以避免数据已有序的最坏情况)。
  2. 问题的解决方案在宏观上是否可以并行化(矩阵运算,是;斐波那契数列,否)

2、编码领域

抽象于计算机结构之上的普适的编码优化规范
  1. 选择和设计性能较好的和适合业务的基础库(如连接池)
  2. 尽量消除实现时的重复计算(如减少循环内的函数调用和判断等)

3、计算机结构领域

需要结合计算机特性的优化方针
  1. 充分利用处理器多发射超标量等内部并行性(如展开循环)
  2. 提高代码的局部性(多使用局部变量代替存储器引用,内存尽量顺序访问等)
  3. 防止寄存器溢出(不要过多的局部变量,X64比X86有着更多的寄存器)
  4. 防止打断CPU流水线(减少不可预测的分支判断,使用条件转移指令者替代或者以小的代价消除分支)
  5. 使用扩展指令集来提高性能(如MMX,SSE,AVX等)

注意事项

在实际开发中,需要根据具体问题来选择优化方案。因为很多优化选项都会带来更多的限制条件。 如
  • 方案1.1性能更高的红黑树需要比AVL树更复杂的代码,这样就带来开发和维护上的成本增加。
  • 方案1.2必须在多核/多处理机上才能发挥作用。
  • 方案3.1和3.4可能会降低代码的可读性,从而导致维护成本升高。
  • 方案3.5则会降低程序的通用性。
由于优化是有代价的,所以我们在优化前要确定这程序/模块是否需要优化。D.E.Knuth说:过早优化是万恶之源,没有仔细分析的优化无异于耽误开发进度。 在开发程序之前,要根据设计找到性能的热点,在此下功夫。若是优化已有程序则需要用prof工具检测到热点再对其改造。Continue reading →
Posted in 程序设计, 软件工程 | Tagged , | 20 Comments

Steam for Linux 简评

背景

2月14日,看到Steam for linux 正式版发布的新闻,心情略有激动(如果放在5年前那就是非常激动了),linux也终于摆脱了只能玩玩纸牌、扫雷或者quake这种万年不变的游戏,走进新时代了。 对于Steam不熟悉的朋友可以参考来自wiki的描述[参考1]
Steam是美国维尔福公司(Valve Corporation)于2003年9月12日推出的电子软件分发、数字版权管理及社交系统,它用于数字软件及游戏的发行销售与后续更新,支持Windows、Mac OS和Linux等操作系统,目前是全球最大的数字游戏平台。
说到Valve,是不是感觉很面熟。当年风靡网吧的CS就是出于它的手中,简评也用到了CS的,这是后话。 由于后面测试游戏性能,在此先公布下主要配置信息:
型号 Tinkpad W530
CPU Intel Core i7-3720QM
内存 8G DDR3 1600 *2
显卡 Intel HD Graphics 4000(GT2)
硬盘 Plextor M5P 256GB
操作系统1 Fedora Linux 18 x64
操作系统2 Windows 7 Professional x64
  下面就进入正题  

安装

Steam官方只发布了正式版的deb安装包,而且推荐使用的是Ubuntu系统。这就意味着正式版只能安装在Ubuntu等Debian系的发行版上,而很不巧的是,我是Redhat系的拥护者(很多管理工具用顺手就懒得换了),机器上只有Fedora 18。不过Linux下有个好处就是什么都可以折腾,在尝试了使用deb转rpm工具安装失败后(Fedora文件目录格式和Ubuntu不一致),我搜索到Steam官方提供了其他常用Linux发行版的安装指导[参考2]。 在官方指导下顺利安装了Steam,但是这是beta版,不过Steam自身的更新程序会更新到最新支持版本的。下面给出Fedora 18 下的安装步骤:
  1. http://spot.fedorapeople.org/steam/fedora-18/处下载steam及其依赖组件的rpm安装包,其中SDL2-debuginfo 和 steam的src包不是必须的,子目录下的安装包也不用下载。如果是本机32位系统的话,带X86_64字样的64位安装包也无须下载。
  2. 管理员权限下 yum localinstall 所有下载的rpm包,安装之。
以上简单两步即可完成。 安装的过程中发现一些有趣的东西:
  1. Steam使用了SDL作为图形界面库,SDL[参考3]支持绝大部分你能见的用的操作系统。这意味着良好的可移植性和代码的复用度,为Steam for linux的产品质量打下很好的基础。
  2. Steam除使用SDL1.2这个稳定版本外,同时使用了SDL2。SDL2比SDL新增许多特性如:完整的3D硬件加速,OpenGL3.0+的支持,多窗口、多屏幕的支持等。但是SDL2还处于开发阶段,所以虽然会带来性能上的提升,但也会影响稳定性。
  3. Steam依赖于Wayland,Wayland[参考4]是近些年提出的,用于替代传统X window的图形层协议。我在查看Steam进程的时候发现其并未链接Wayland库,考虑到Wayland还未被名大发行版用作默认图形系统。这也许是Steam为以后的平滑升级做的准备。感兴趣的朋友可以参考/参与Steam的论坛上网友对此做的讨论[参考5]
安装的事情叙述完毕,接下来

使用

Steam第一次运行的时候,会到服务器上更新,静等完成即可进入初始的登陆界面。 [caption id="attachment_832" align="alignnone" width="420"]Steam_Linux_Login Steam_Linux_Login[/caption]   登录界面与windows平台下无异。但这时bug出现了,文本框不能输入任何字符,最后用右键菜单的复制粘贴搞定。初步估计是输入法或者键盘布局和Steam冲突造成的,进一步讨论可关注Steam讨论贴”ubuntu12.10 x64 STEAM无法输入账号密码”[参考6]。 登录成功后,进入主界面 [caption id="attachment_840" align="alignnone" width="1181"]Steam_Linux_Main Steam_Linux_Main[/caption] 主界面也与windows下一致,不过库标签里面多了Linux游戏分类,里面是Linux平台下可以运行的游戏列表。另外在主界面任何地方都不能输入字符,好友界面也一样。 关于页面可以看到Steam版本信息 [caption id="attachment_830" align="alignnone" width="351"]Steam_Linux_About Steam_Linux_About[/caption] 刚才在主界面看到Linux已经支持了CS 和 CS:Source等游戏,下面就体验下CS:Source。首先要下载安装游戏,完毕之后就可以进入了。 [caption id="attachment_835" align="alignnone" width="1024"]CS_Linux_Main01 CS_Linux_Main01[/caption] 游戏载入完成之后,傻眼了,这CS主界面上的菜单怎么都没了,而且Steam的快捷键也不能用。我按着记忆中菜单的位置点了下,一个熟悉的窗口蹦了出来 [caption id="attachment_834" align="alignnone" width="1024"]CS_Linux_Main02 CS_Linux_Main02[/caption] 于是恍然大悟:中文无法显示。搜索无果,只好在Steam的游戏属性当中,把这款游戏的语言更改成英文,进入 [caption id="attachment_841" align="alignnone" width="1024"]CS_Linux_Main03 CS_Linux_Main03[/caption] 文字和Steam快捷键都可以正常使用了,这次截图就是使用Steam的F12快捷键,而不是上面两个截图的Alt+PrtScr。Linux版本的CS:Source相比Windows下少了一些菜单,例如测试图形性能的,那我们就进入游戏里面测试吧。 [caption id="attachment_838" align="alignnone" width="1024"]CS_Windows_config CS_Windows_config[/caption] [caption id="attachment_837" align="alignnone" width="1024"]CS_Linux_config CS_Linux_config[/caption] 选择了沙2地图,调整好图形设置,全屏分辨率1920*1080 [caption id="attachment_839" align="alignnone" width="1024"]CS_Windows_dust2 CS_Windows_dust2[/caption] [caption id="attachment_836" align="alignnone" width="1024"]CS_Linux_dust2 CS_Linux_dust2[/caption] 通道里对比 其他地方就不一一列举。和windows相比linux下cs:source有以下值得注意的地方:
  1. 默认图形配置不同:Windows 的推荐图形配置要比Linux下高一些。
  2. 相同配置下图形质量基本相同:从上面截图上看不出质量差异。
  3. 相同配置下windows要比linux游戏性能好很多,从帧数上看到Windows下性能是Linux下的1.6至1.7倍。不过Linux 40-60帧的速度还是比较流畅的,如果调整成推荐配置,平均会提高30帧。这个性能问题和显卡驱动有关,也和整个游戏的生态环境同样亦相关。
  4. Linux下偶尔会出现渲染错误,图像撕裂等现象,影响了娱乐性。
总体来说:虽然Steam for Linux 及其平台下的游戏仍有很多bug,但这是Linux娱乐环境的重大进步,有了Steam,我也可以很方便的在工作之余,虐一把电脑,放松心情了。

参考资料

  1. Steam for linux 发布新闻: http://store.steampowered.com/news/9943/
  2. Steam for linux 各发行版安装指导: https://developer.valvesoftware.com/wiki/Steam_under_Linux#Native_Steam_on_Linux
  3. SDL介绍: http://wiki.libsdl.org/moin.cgi/Introduction
  4. Wayland官网: http://wayland.freedesktop.org/
  5. 讨论贴 “steam with wayland is real?”: http://steamcommunity.com/app/221410/discussions/0/846938351044525469/
  6. 讨论贴 “ubuntu12.10 x64 STEAM无法输入账号密码”: http://steamcommunity.com/app/221410/discussions/0/864958088411422085/
Continue reading →
Posted in Linux, 软件应用 | Tagged , , | 50 Comments