用 conda 安装 GPU 版本 Tensorflow/PyTorch/Mxnet,非源码编译

os安装

目前对 tensorflow 和 cuda 支持最好的是 ubuntu 的 18.04,16.04 这种 lts ,推荐使用 18.04 版本。非 lts 的版本一般不推荐。

Windows 倒是也能用来装深度 GPU 环境,但是 Windows 上的问题实在太多了,而且很多都是跟环境相关的,不具备普遍性,解决了也没有意义。所以真心不推荐 Windows 环境。

其他的发行版本其实也是提供 cuda,nvidia 驱动等的支持的,但是不如 ubuntu 方便。另一方面,国内关于 linux 的资料基本都是 ubuntu,所以除非是真的有特殊理由,一般选择 ubuntu。

这里需要注意的是,ubuntu 有桌面版本和服务器版本的区别,自己用的话,肯定是要桌面版本的,但是如果只是放在角落里做运算机又或者是桌面版本安装失败的时候,可以考虑服务器版本。

ubuntu 官网下载 ubuntu 的 iso 镜像。

用迅雷之类的下载软件速速还是不错的,不过也可以考虑国内的镜像站如 Tuna
https://mirrors.tuna.tsinghua.edu.cn/ubuntu-cdimage/releases/18.04.4/release/

balabalabala...

系统安装就完成了。

我这里安装的是 18.04.4 的 server 版本,安装过程跟 GUI 版本不太一样,特别是最后的时候,有个 openssh 的选项,一定要勾选。

装完之后的设置

升级

装完之后,推荐先升级一下系统。试了一下,默认的源速度不怎么好,换一个国内的源。国内的话,有ali、163、tuna、中科大。参考各个国内源说明文档去修改 /etc/apt/sources.list 即可。

ali 的 ubuntu18.04 sources.list 设置

deb https://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse

deb https://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse

deb https://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse

deb https://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse

server 版本安装的时候,就有软件源配置过程,可以直接输入 aliyun 的地址,后续就不需要配置了。

执行下述命令升级

sudo apt-get update

sudo apt-get upgrade

升级完系统之后,重启一下系统。

sudo reboot

安装 rsync screen aptitude

因为系统里面现在还啥也没有,安装完之后对系统做一个易用性的设置。

sudo apt-get install rsync screen aptitude nmon

这里主要是一些易用性软件,看个人喜欢要不要安装。

安装 nvidia 驱动

想用 nvidia 的显卡,不装 nvidia 的驱动怎么行。ubuntu 官方的仓库里面是自己带 nvidia 的闭源驱动的,比较方便。

sudo apt-get install nvidia-driver-435

注意,可能随着时间的推进,有编号更高的nvidia驱动出现吗,安装编号最高的即可。

很多教程说要去下载sh的驱动文件,进行编译安装,这里不太推荐,因为很麻烦而且容易出问题,ubuntu对非开源驱动的支持还是比较好的,直接用软件仓库里面的驱动就可以。

装完驱动需要重启,重启之后,执行下述命令确认一下是否安装成功:

nvidia-smi

如果安装成功的话,上述命令会输出显卡信息和工作状态。后续的使用中,也是通过上述命令监控显卡工作状态的。

conda 安装设置

conda 是一个开源包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖关系,并在它们之间轻松切换。 它适用于 Linux,OS X 和 Windows,是为 Python 程序创建的,但可以打包和分发任何软件。

Anaconda 是一个开源的 Python 发行版本,包含了 conda、python 等 180 多个科学包及其依赖项。因为包含了大量的科学包,所以 Anaconda 的安装包比较大。如果为了省时间,也可以使用 Miniconda 这个较小的发行版。

Miniconda 是最小的 conda 安装环境,只带了基本的 conda 和 Python。可以根据自己的需要选择下载,个人比较喜欢 Miniconda,因为只有基本的安装环境,需要什么就自己下载什么,自由度比较高。另外,Anaconda 是包含一些 GUI 的东西的,所以 Server 的环境下,个人还是比较推荐 Miniconda 的。

Miniconda 的下载网址为 https://conda.io/miniconda.html

Anaconda 的下载网址为 https://www.anaconda.com/download/

除了官方地址之外,Tuna 也提供了 Miniconda 安装包的下载,可以去这里找。

下载 Miniconda

这里下载的是 Python3.7 版本的 Miniconda Linux 64-bit。

文件名 Miniconda3-latest-Linux-x86_64.sh

文件名就不要纠结了,这几个版本文件名都差不多。

安装 Miniconda

将 Miniconda 的安装包用 rsync,ssh 等上传到刚才装好 lnux 的机器上。

因为我们下载的安装程序其实是一个脚本,而这个脚本本身是没有可执行权限的,所以我们要先给它加个可执行权限。

关于 linux 的权限,有兴趣的话,可以去查一下相关资料。

chmod a+x Miniconda3-latest-Linux-x86_64.sh
./Miniconda3-latest-Linux-x86_64.sh

上述安装脚本基本是全自动的,包括安装路径等,个人建议用默认的就可以。

最后,安装脚本会提示,要不要在 bashrc 里面加一个环境初始化设置。这里推荐是让脚本加上。

注意,这里的环境是全新安装的,只用 Miniconda 作开发环境的管理,不需要做额外设置。但是如果你的电脑上有已经装好的 Python 开发环境,建议谨慎操作,造成的一切损失,本文档作者概不负责。

安装完成之后,记得退出 shell 重新进一下,让 conda 的环境设置脚本起效。

conda配置

因为默认的conda安装源在海外,速度比较慢,所幸国内有一些镜像站,例如Tuna的就很好用。这里根据官方文档做一下配置:

channels:
  - defaults
show_channel_urls: true
channel_alias: https://mirrors.tuna.tsinghua.edu.cn/anaconda
default_channels:
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2
custom_channels:
  conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  msys2: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  bioconda: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  menpo: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  simpleitk: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud

将上述内容添加到 home 目录的 .condarc 文件中即可。

关于 CUDA

早期的时候,作者是用 pip 进行各个框架的安装的,所以 CUDA 是单独安装的,比较繁琐。

而现在,Tensorflow 和 PyTorch 的 GPU 和 CPU 版本都可以用 conda 进行安装设置,方便的多。Mxnet 虽然还没有 conda 安装的方式,但是用 conda 里面自带的 pip 安装是一样的。同时用 conda 安装还可以很好的兼顾各个框架对 CUDA 版本的要求。

所以,现在作者一般都是用 conda 来进行各个框架的安装。

其实现在 dwSun 整个开发环境都是用 conda 装的。 关于 conda,dwSun 个人的经验,如果是初学者,用起来可能会一头雾水,很可能会出现意想不到的错误,属于学习曲线比较高的一个工具。不过相比于 conda 带来便利,学习的那些付出还是值得的。

各框架的安装

注意,下述各个框架的安装各有不同,dwSun 这里因为要经常混用各个框架,所以这里提供的是各个框架能共存的安装方案。如果只是单独安装各个框架的话,不用理会下述步骤中关于各个框架共存的部分。

安装tensorflow

目前,tensorflow 官方很依然很傲娇的没有给出他们 release 的 tensorflow 到底是在哪个版本上编译的,这就给同时安装各个框架造成了很大的麻烦。

要装 tensorflow,有下面几种方式可以选择:

  • 从源代码编译 tensorflow,开启 cuda 的支持,顺便可以做一下优化编译。
  • 这个方案比较但疼的是 tf 的代码在 google 的服务器上,国内需要梯子才能下载,而且编译要几个小时,很麻烦,不太推荐。
  • 使用 nvidia-docker,这是个很不错的主意,而且还能提供环境隔离等,并且不限 host 机的 cuda 环境,因为只跟 docker 内部的 cuda 的环境有关。
  • 这个方案安装 nvidia-docker 已经简单很多了,几乎是傻瓜式操作,但是 docker 的使用还是有些复杂,而且真心不如在本地直接安装方便好用,值得考虑,但是这里不推荐。有兴趣的可以参考我的另一个 blog。
  • 使用自带 cuda 支持的非官方编译 tensorflow,有很多热心网友提供了自己编译好的支持 cuda 的 tensorflow。
  • 这种热心网友不多,而且人家编译好的 tensorflow 未必适合你的机器,例如 compute capability 不兼容等问题。而且,如果碰上一个别有用心的网友,编译好的tf里面带一些恶意软件也不一定。所以慎重考虑这种方式。
  • conda 安装,也就是我们这里采用的方式。

compute capability 是 api 版本,跟算力无关,可以到这里查找你的显卡对应的 compute capability。

tf 官方放在 pypi 里面的 tensorflow-gpu 从 1.7 版本开始就默认开启了 avx2 的指令集优化,如果电脑的 cpu 型号比较老,例如 e3 v2 系列的 cpu,安装完成之后,运行的时候会出现段错误之类的问题,这种情况只能考虑自己编译tf或者换 cpu 了。

至于 conda 中的 tf 版本,默认开启了 mkl 的优化,这是 pypi 里面的版本没有的,dwSun 某个项目里面做过测试,用 conda 安装的 tf 同样的代码,在 intel 的 cpu 上速度要比 pypi 里面的版本快 30%。

上述几种安装方式,各个框架都有,后面就不再赘述了。

执行下述命令进行 tf 安装:

conda install tensorflow-gpu==1.15

如果没有 GPU,可以装 cpu 版本,命令如下:

conda install tensorflow==1.15

这里因为项目的关系,使用的还是 tf 1.15,是tf1.x 系列的最后一个版本。

其实 tf 2.x 也挺好用了,不过历史遗留项目,没办法。

安装的时候会列出 tf 依赖的 cuda 和 cudnn 的版本,记录一下,为后续安装其他框架做准备。

我这里显示如下,可以看出来装的是 cuda10 和 cudnn7.6.5:

cudatoolkit        pkgs/main/linux-64::cudatoolkit-10.0.130-0
  cudnn              pkgs/main/linux-64::cudnn-7.6.5-cuda10.0_0

安装pyTorch

官网已经在首页最醒目的地方给出了安装方式,涉及多个不同的环境。这里使用的安装命令如下:

conda install pytorch torchvision torchtext cudatoolkit=10.0 -c pytorch

如果是 cpu 版本的话,执行下述命令安装:

conda install pytorch torchvision torchtext cpuonly -c pytorch

这里为了跟刚刚装的 tf 共存,cuda 指定的 10.0 版本。另外 torchtext 是我自己加的,用不用得到看个人。

安装mxnet

这里需要说一下,mxnet 早期为了用户的使用方便,提供了各个不同的 cuda、mkl 等等的编译版本供使用,但是一直没有提供 conda 安装的方式,不过好在 conda 里面也带了 pip,所以可以用 pip 进行安装:

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple  # 设置 pypi 国内镜像源,做一次就可以
pip install mxnet-cu100mkl gluoncv gluonts

注意这里安装的是 cu100mkl 版本,代表的是这里安装的 mxnet 是在 cuda10.0 下编译 开启了 mkl 支持。

cpu 版本可以这样安装:

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple  # 设置 pypi 国内镜像源,做一次就可以
pip install mxnet-mkl gluoncv gluonts

这里使用了 Tuna 的 pypi 镜像来进行安装,速度比海外的源快很多。

mkl 是 intel 的数学加速计算库,用于 intel cpu 上加速运算,如果没有 GPU,一定要安装整个库,可以获得 30% 左右的性能提升。

测试安装

Tensorflow

balabalabala

mxnet

balabalabala

pyTorch

balabalabala

总结

个人比较喜欢 mxnet,他的社区和学习资料都是中文为主的,对国人很友好。虽说 api 是参考了 PyTorch,但是用过以后,会觉得 mxnet 的 api 更加人性化一些。而目前发展势头最猛的 PyTorch 也是很值得期待的,有 facebook 这棵大树在后面,整个生态也都在完善起来,特别是最近这些年,各家发论文更多的都在使用 PyTorch。另外,虽然有各种蛋疼的问题,但是目前用户群最广,文档资料最齐全,model-zoo最大的仍然是 tensorflow。而且有 slim 这样的工业级框架加持,tf 绝对是大多数场景的不二之选。

附录:

测试各个框架使用的数据集 mnist,来自 Yann LeCun 的主页,可以自行去下载。

测试各个框架使用的代码来自各个框架的官方示例,这里为了方便做了一些修改,代码列出如下:

这里给出的是 GPU 版本的测试代码,如果要在 CPU 上运行,需要对代码做一些修改。
mnist_tf.py

#!/usr/bin/env python3
# -*- coding=utf-8 -*-

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf


def main(_):
    mnist = input_data.read_data_sets("/home/david", one_hot=True)
    x = tf.placeholder(tf.float32, [None, 784])
    W = tf.Variable(tf.zeros([784, 10]))
    b = tf.Variable(tf.zeros([10]))
    y = tf.matmul(x, W) + b
    y_ = tf.placeholder(tf.float32, [None, 10])
    cross_entropy = tf.reduce_mean(
        tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y)
    )
    train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

    sess = tf.InteractiveSession()
    tf.global_variables_initializer().run()
    for _ in range(1000):
        batch_xs, batch_ys = mnist.train.next_batch(100)
        sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
    correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))


if __name__ == "__main__":
    main()

mnist_torch.py

#!/usr/bin/env python3
# -*- coding=utf-8 -*-

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.optim.lr_scheduler import StepLR


class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(784, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.fc2(x)
        output = F.log_softmax(x, dim=1)
        return output


def train(model, device, train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = F.nll_loss(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % 10 == 0:
            print(
                "Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}".format(
                    epoch,
                    batch_idx * len(data),
                    len(train_loader.dataset),
                    100.0 * batch_idx / len(train_loader),
                    loss.item(),
                )
            )


def test(model, device, test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += F.nll_loss(
                output, target, reduction="sum"
            ).item()  # sum up batch loss
            pred = output.argmax(
                dim=1, keepdim=True
            )  # get the index of the max log-probability
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)

    print(
        "\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n".format(
            test_loss,
            correct,
            len(test_loader.dataset),
            100.0 * correct / len(test_loader.dataset),
        )
    )


def main():
    use_cuda = torch.cuda.is_available()
    torch.manual_seed(42)
    device = torch.device("cuda" if use_cuda else "cpu")
    kwargs = {"num_workers": 1, "pin_memory": True} if use_cuda else {}
    train_loader = torch.utils.data.DataLoader(
        datasets.MNIST(
            "/home/david",
            train=True,
            download=True,
            transform=transforms.Compose(
                [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]
            ),
        ),
        batch_size=128,
        shuffle=True,
        **kwargs
    )
    test_loader = torch.utils.data.DataLoader(
        datasets.MNIST(
            "/home/david",
            train=False,
            transform=transforms.Compose(
                [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]
            ),
        ),
        batch_size=128,
        shuffle=True,
        **kwargs
    )

    model = Net().to(device)
    optimizer = optim.Adadelta(model.parameters(), lr=1.0)

    scheduler = StepLR(optimizer, step_size=1, gamma=0.7)
    for epoch in range(1, 3 + 1):
        train(model, device, train_loader, optimizer, epoch)
        test(model, device, test_loader)
        scheduler.step()


if __name__ == "__main__":
    main()

mnist_mx.py

#!/usr/bin/env python3
# -*- coding=utf-8 -*-

import mxnet as mx
from mxnet import gluon, autograd, ndarray
import numpy as np

ctx = mx.gpu(0)

train_data = mx.gluon.data.DataLoader(
    mx.gluon.data.vision.MNIST(
        root="/home/david",
        train=True,
        transform=lambda data, label: (data.astype(np.float32) / 255, label),
    ),
    batch_size=32,
    shuffle=True,
)
test_data = mx.gluon.data.DataLoader(
    mx.gluon.data.vision.MNIST(
        root="/home/david",
        train=False,
        transform=lambda data, label: (data.astype(np.float32) / 255, label),
    ),
    batch_size=32,
    shuffle=False,
)
net = gluon.nn.Sequential()
with net.name_scope():
    net.add(gluon.nn.Dense(128, activation="relu"))  # 1st layer - 128 nodes
    net.add(gluon.nn.Dense(64, activation="relu"))  # 2nd layer – 64 nodes
    net.add(gluon.nn.Dense(10))  # Output layer

net.collect_params().initialize(mx.init.Normal(sigma=0.05), ctx=ctx)
softmax_cross_entropy = gluon.loss.SoftmaxCrossEntropyLoss()
trainer = gluon.Trainer(net.collect_params(), "sgd", {"learning_rate": 0.1})

epochs = 10
for e in range(epochs):
    for i, (data, label) in enumerate(train_data):
        data = data.as_in_context(ctx).reshape((-1, 784))
        label = label.as_in_context(ctx)
        with autograd.record():  # Start recording the derivatives
            output = net(data)  # the forward iteration
            loss = softmax_cross_entropy(output, label)
            loss.backward()
        trainer.step(data.shape[0])
        curr_loss = ndarray.mean(loss).asscalar()
    print("Epoch {}. Current Loss: {}.".format(e, curr_loss))