CUDA Toolkit 与 Pytorch 安装过程的一些坑

首先区别显卡驱动和CUDA Toolkit, 首先显卡做为一个硬件,需要驱动软件,才能保障显卡的基本正常工作。

在显卡驱动安装完成的基础上,再安装CUDA Toolkit 才能支持并行计算。但是显卡驱动版本和CUDA Toolkit版本之间是有要求的。

https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html

我之前装了半天pytorch,但是一直无法检查到cuda设备就是因为这个原因。

如何查看cuda版本以及驱动版本。

查看CUDA(将路径已经写入环境变量)

nvcc -V

查看显卡驱动版本

dpkg –list | grep nvidia-* 或者 cat /proc/driver/nvidia/version

我所使用的服务器cuda 版本是9.1, 显卡驱动版本时390,作为一个非管理员用户勉勉强强可以再自动的用户目录下成功装上支持GPU加速有的pytorch版本(0.4.1)。但是pytorch版本1.3都已经出来了,就想着可不可以再自己的用户目录下装一个高版本的CUDA ,然后用新版本的pytorch,那岂不是爽歪歪?

以下为尝试过程,但是失败了,原因时服务器的显卡驱动版本过低(390),与CUDA10.1版本不匹配,因此即使装上了pytorch 1.3,也检测不到cuda设备。

失败过程如下,如果你所使用的服务器显卡驱动版本较高,而你又是非管理员用户,又想用新版本pytorch的可以尝试一下。

https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&target_distro=Ubuntu&target_version=1604&target_type=runfilelocal

下载CUDA Toolkit ,并安装,注意不要安装驱动的选项,因为你没有劝降,并且将其他安装路径都设置再用户目录以及子目录。

再自己目录下安装好CUDA10.1之后,要将CUDA相关的一些环境变量写入,以方便后续conda安装pytorch。可以参考这篇博客

接着利用conda创建虚拟环境进行pytorch安装,详见我的另一篇博客

https://pytorch.org/get-started/locally/

安装之后就是失败啦,torch.cuda.is_avialible() 返回的结果时False。

原因时服务器显卡驱动版本为390,而CUDA Toolkit 10.1要求显卡驱动版本>=418,只能让管理员去升级显卡版本啦。

Ubuntu 16.04 Cuda 9.1 安装支持gpu加速的pytorch

环境:Ubuntu 16.04 ,cuda 9.1,并且预装好了对应的Cudnn,如果服务器是多用户使用,这些是管理员完成的操作。

对于非root非管理员的用户,首先在自己的根目录下安装好Anaconda 3,网上有很多教程,安装anaconda3 也比较简单,不展开说了。

将系统根cuda的路径,配置到用户的环境变量中

然后用 source ~/.bashrc 来使用配置好的环境变量

接着使用conda包管理工具来安装gpu版本的pytorch

在之前将conda源改为国内的清华镜像源,这样下载过程中比较快。

conda config –add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/

conda config –add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/

conda config –set show_channel_urls yes

利用conda包管理工具,创建一个名称为pytorch-gpu的虚拟环境,虚拟环境的python版本设置为3.6.

conda create -n pytorch-gpu python=3.6

进入虚拟环境中

source activate  

切换虚拟环境

conda activate pytorch-gpu

安装pytorch,这里安装版本匹配需要和pytorch匹配,因为cuda9.1不支持新版本的pytroch,安装版本不匹配则无法用cuda成功加速。

pip install torch==0.4.1

pip install torchvision==0.2.2

安装完成后则可以验证一下是否可用。

参考资料:

代码整洁之道(Clean Code)阅读笔记

本文介绍的内容来自于书籍,《Clean Code》Robert C. Martin 著 韩磊 译(中文名《代码整洁之道》),个人总结出一些比较重要的内容。

第一章

读写代码所花费时间的比例超过10:1。也就是说在写代码时,我们一直在读旧代码。既然比例如此之高,想让读变得轻松,编写代码就变得更难。不可能光写不读,易读即易写。

第二章

如果名称需要注释来补充,那就不算是名副其实。

魔术数最好不要出现,一些常量最好定义一些好的名称

名称满足非误导性(O和0,l和1等)、作有意义的区别(NameString和Name没有区别)、可读性(尽量用可读的单词)、可搜索性(提高查找效率)等

类名不应该为动词,方法名通常为动词或者动宾结构短语,命名也别弄一些语言梗。命名风格最好一以贯之。

取好名字最难的地方在于需要良好的描述技巧和共有文化背景。

第三章

函数最重要的就是要保持短小,避免嵌套层数过多,代码冗长,逻辑混乱

一个函数应该只做一件事,功能单一。

函数名称要具有描述性,参数尽量少,标识参数尽量不要有(分成两个函数),参数过多的换,是不是考虑封装成一个对象传入。

函数内部尽量避免时序耦合。

分隔指令与询问,防止代码语义的混淆,例子如下

if (set("username", "Amy"))....

if (attributeExists("username") {
setAttribute("username", "Amy")...

使用异常替代返回错误码,返回错误码会导致更深层次的嵌套结构,使用异常则能够将错误处理代码从主路径代码中分离出来。

同时通常错误码时一个枚举类型的数据,几乎所有文件都会引用它,如果使用java语言,当错误枚举类修改的时候,所有的类都需要重新的编译和部署。

使用异常替代错误码,新的异常可以从异常类派生出来,不需要重新编译或者部署。

Edsger Dijkstra 的结构化编程规则:每个代码只有一个入口一个出口,只有一个return,最好不要有break continue goto之类的语言,上述规则一般长、功能复杂函数中作用体现比较明显,原来函数就比较精简则帮助不大。

类中的函数位置应该根据调用关系,最好被调用函数直接放在调用函数的下方,这样可以提高阅读性,但不绝对。

怎么做:一开始的函数一般都冗长而复杂,逐步重构代码,分解函数,修改名称,消除重复。不要求一开始就按照各种条条框框的规则写函数,因为没有人会这么做吧。

第四章

别给糟糕的代码加注释——重新写吧

每次写注释,你都该对自己做个鬼脸,感受自己再表达能力上的失败。

用代码来阐述你的故事:

//Check to see if the employee is eligible for full benefits
if ((employee.flags & HOUURLY_FLAG) && (employee.age > 65)) ....

if (employee.isEligibleForFullBenefits()) ...

当然有些注释是必须的,也是有利的,唯一真正好的注释是你想办法不去写的注释。

一些很长的函数往往会有很多注释,然而这又回到了写一个好的函数的问题,把函数写短就可以避免无用的注释。短的函数根本不需要注释,一个好的函数名比注释好一万倍。

注释代码可以再短时间测试的时候使用,但是需要及时清理,否则久了,这些代码会腐烂。

第五章

代码格式中,再导包的声明,每个函数之间都应该有空行分隔开

你是否曾经再某个类中摸索过,从一个函数跳到另一个函数,上下求索,想要弄清楚这些函数的操作,如何互相相关,最后却被搞糊涂了。

关系密切的概念应该相互靠近。

相关函数。若某个函数调用了另一个,就应该把他们放到一起,而且调用者应该尽可能放在被调用者的上面。

编码格式中的风格。下面的代码时本人编码格式的一些样式:

public class Student {
public String name;
void setName(Strint name) {
int sum = 0;
for (int i = 0; i < 10; i++)
sum += -i * 5;
this.name = name;
}

通常再非末尾的符号之后会有一个空格,这也是英文文本的基本格式要求(不排除一些特殊情况,如”(“后面一般不加空格)。二元操作符,前后都加空格。一元操作符,通常不加空格;if、while、for 之类的关键词后面也加空格,函数名称与”(“之间不加空格。

团队工作,统一风格,个人服从于集体。

第六章

暴露给用户的通常时比较抽象的功能就可以,而不需要将底层的逻辑暴露,用户也不会去关心的。

如果用户关心当前汽车还剩多少油,直接返回当前剩余油量的百分比就可以,可不需要将获取汽车油量上限,获取汽车当前油的余量,两个接口不需要暴露出来。

我们不愿意暴露数据细节,更愿意以抽象形态表述数据。

面向对象和面向过程:面向过程便于在不改动既有数据结构的前提 下添加新函数,面向对象便于在不改动既有函数的前提下添加新类(多态)。

第七章

尽量使用异常机制,不要使用错误标识码。虽然异常机制可能比较丑。异常抛出尽量要将信息一并传递出去。别传递null值,这样就不用花费精气去检查null值了。

第十章

保持类的内聚性,将较大的函数切割为小函数会导致更多的类出现。在拆分函数时,需要将变量提升为类的实体变量,这样就不需要写函数之间的参数传递了。

第十一章

一开始就做对系统纯属神话,我们应该只去实现今天的用户故事,然后重构,明天再拓展系统,实现新的用户故事,这是迭代和增量敏捷的精髓所在。

第十二章

简单设计规则:运行所有测试,全面测试并持续通过所有测试的系统,就是可测试的系统。不可测试的系统同样不可验证,不可验证的系统就不应该部署。

简单设计规则:重构。测试消除了对清理代码就会破坏代码的恐惧。重构过程中应该提升内聚降低耦合,消除重复,保证表达力,尽可能减少类和方法的数量。

做到有表达力重要方式就是尝试,因为下一位读代码的人最有可能就是你自己。所以,要多尊重自己的手艺,花一点点时间再每个函数和类上。选用较好的名称,将大函数切分成小函数,时不时看看自己所创造的东西。用心是最珍贵的资源。

第十三章

不要将系统错误归咎于偶发事件,系统(编译里)出错的概率远比你自身编程出现缺陷的概率低。并发代码难写,加入多线程和共享数据后,简单的代码也会变成噩梦。要编写并发代码,就得严格的编写整洁的代码,否则将面临细微和不频繁发生的失败。因为偶发事件往往是再高负载下出现的。

第十四章

保持代码持续整洁和简单,永不让腐坏有机会开始。

第十五章

重构常常会导致另一次推翻此次重构的重构。重构是一种不停试错的迭代过程。

Ubuntu18.02下Docker+WordPross+MySQL 环境搭建

本人近来在阿里云上搞了一台云主机,然后想用来搭建个人博客,用于记录所学所想。

下面简单介绍一下搭建的过程。

建议环境搭建过程使用root账号或者管理员账号。

首先是利用阿里云的源安装docker-ce

apt update # apt full-upgrade -y

# apt install apt-transport-https ca-certificates curl software-properties-common -y

# curl -fsSL http://mirrors.cloud.aliyuncs.com/docker-ce/linux/ubuntu/gpg | apt-key add –

# add-apt-repository \ “deb [arch=amd64] http://mirrors.cloud.aliyuncs.com/docker-ce/linux/ubuntu \ $(lsb_release -cs) \ stable”

# apt update

# apt install docker-ce -y

运行命令docker -v

查看docker的版本号,显示出了版本号说明安装成功。

然后从docker库,下载wordpress和mysql的镜像

docker pull wordpress:latest

docker pull mysql:5.6

这里选取了5.6版本的mysql,本人尝试过安装最新8.1版本的mysql,但是后续建站会报错,因此选择了一个比较稳定的版本。

接下来在docker启动容器

docker run -d –name wp_mysql -v /root/mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=你的密码 mysql:5.6

参数解释, docker run 表示运行容器;末尾的mysql:5.6表示将之前下载好的镜像在该容器中运行,并且将这个容器命名为wp_myusql; -d表示后台运行,而不是交互式运行的状态。 -e MYSQL_ROOT_PASSWORD=你的密码:指定容器的环境参数,此处初始化mysql的root密码; -v /root/mysql-data:/var/lib/mysql 表示将宿主机的目录 /root/mysql-data 挂载到容器中的 /var/lib/mysql 目录下。

docker run –name wp_wordpress -d -p 80:80 –link wp_mysql:mysql -v /root/wordpress-html:/var/www/html wordpress

大功告成

一些其他命令

docker ps -l

docker stop

docker rm

参考文献

https://my.oschina.net/taadis/blog/1569239

极大似然估计与极大后验估计

极大似然估计(MLE,Max Likelihood Estimation)

极大后验估计(MAP, Maximum A Posteriori probability estimation)

似然函数与概率函数的区别 \(p(x|\theta)\)

频率学派和贝叶斯学派

先验与后验

贝叶斯估计

先验与后验共轭是贝叶斯估计问题可以进行简化

贝叶斯预测中的解构\(P(\hat{x}|X)\)

贝叶斯估计与贝叶斯预测

先占个坑,详细的内容后续有时间再填完

贝叶斯公式的理解

首先上图展示了贝叶斯公式的一个定义和简单的变形。

B:表示证据,或者说结果;A表示理由,原因\(\overline{A}\)则表示除了A之外的原因。

P(A|B)表示一个结果发生了,有多大的概率是A这个原因导致的,即How much you can trust the evidence,你有多大的把握相信B这个结果是由A导致的。

带入实际的场景很很好理解:B表示老板骂你,A表示工作失误。工作失误可能是老板骂你的原因,那么有一天老板骂你了,由多大的概率是因为你工作出现了失误呢?这就是通过P(A|B)进行刻画。

假设\(\overline{A}\)表示老板被家暴了,老板收到家庭中的暴力,迁怒至你的身上也是一种你被骂的原因。

全概率公式展开:P(老板骂你)=P(老板骂你| 老板被家暴)P(老板被家暴)+ P(老板骂你| 工作失误 )P(工作失误) ,可以老板骂你的概率由两个起因来决定。

P( 工作失误|老板骂你)= P(老板骂你| 工作失误 )P(工作失误) /P(老板骂你)
这样就容易理解贝叶斯公式的含义了。

P(老板骂你| 工作失误 ) 这一项如何理解,表示出现工作失误时,老板骂你的概率。这就可能取决于老板的性格。

  • 假设1:如果老板是一个对工作十分严格的人,并且家庭十分和睦。某一天老板骂你了,那么大概率时你工作出现了失误。
  • 假设2:如果老板时对工作很宽容的人,即使你工作出现失误,鲜有骂人的现象,但是你不知到家庭是否和睦。某一天他骂你了,你更应该相信是老板被家暴了。
  • 假设3:如果老板家庭很不和睦,老板是一个由原则的人,不会迁怒至不相干的人。那么有一天老板骂你了,更有可能是你工作出现了失误。

上述假设中,要么假设某种原因的先验概率P(A)很小,要么假设原因和evidence之间的关联P(B|A)很小(似然),这些都会导致最后的后验情况不同。

P(A|B)通常被成为后验概率,即evidence在后项。

e.g. P(θ|x)通常在深度学习中被称为后验概率,表示数据是已有的结果,或者说证据,二参数就是导致数据出现的原因。

再考虑一个现实场景,假设你是一个很优秀的程序员,犯错的概率很小,有一天你的代码编译失败,你更愿意相信是编译器的问题还是你自己犯错倒置编译失败了呢?当然是你自己的问题了,因为P(编译器出现问题)这个先验概率实在是太小啦,你再优秀能和编译器比吗?除非你是写编译器的那个人,嘻嘻。

KL散度与Seq2Seq模型倾向生成通用回复现象之间的联系

KL散度定义

KL散度是用来衡量两个分布之间差异的非对称性度量,即K(p||q)不等于K(q||p)

KL散度越接近1,说明两个分布约相似,越接近0则分布差异性越大

性质:

证明:

证明过程中使用了Jensen’s inequality(Jensen不等式)性质。

对话生成中Seq2Seq模型的优化目标

采用极大似然估计的优化策略,加负号变为损失函数,可以进行下面的变形:

根据KL散度的定义,结合图像

在点A时,被积函数为正,且绝对值较大
在点B时,被积函数为负,且绝对值较小

对于到Seq2Seq的优化目标中,真实分布概率高而生成分布概率低的情况导致损失函数的值较高。

真实分布是既定事实,无法调整,而生成分布可以通过调整参数,使那部分的生成概率升高,来降低损失函数的值。真实分布中出现概率较高的回复就是通用回复,因此模型倾向于提高这些通用回复的生成概率,导致训练好的Seq2Seq模型倾向于生成通用回复。