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值了。

第十章

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

第十一章

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

第十二章

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

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

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

第十三章

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

第十四章

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

第十五章

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