Hi

日常学习、折腾的一些记录

使用AMD显卡搭建深度学习环境

总结 在 linux 系统下基于 ROCm 底层,基本可用,最大的影响是少部分算子性能有问题,其次是现在很多开源项目的量化方案完全基于 CUDA api,比如chatglm-6b,量化层必须在 CUDA 环境下使用。 在 windows 下基于 DirectML 底层,Pytorch 框架下涉及到 **.cuda(),**.to('cuda')等方式写的代码都需要修改 。tensorflow 则没有这个问题,不过现在开源模型用 tensorflow 比较少了。 ROCm 机器学习/深度学习框架的加速实现一般都高度依赖于硬件对应的底层加速方案,英伟达的 CUDA,英特尔的 OpenVINO,各家 NPU 以及加速卡的闭源库,AMD 官方的底层加速框架是 ROCm, 首先尝试了此方案。 打开官网文档,查看 Prerequisite Actions 页面,很遗憾发现我的 6600XT 没在文档里写的确定支持的硬件列表内,不过感觉放弃太快有些不甘心,搜索引擎搜了些关键词,有成功有失败,研究一番后按下面步骤安装(ROCm 只支持linux,我使用了 Ubuntu 20.04,内核版本5.13.0-30-generic): 准备步骤 sudo apt update sudo apt-get install wget gnupg2 sudo usermod -a -G video $LOGNAME sudo usermod -a -G render $LOGNAME echo 'ADD_EXTRA_GROUPS=1' | sudo tee -a /etc/adduser.conf echo 'EXTRA_GROUPS=video' | sudo tee -a /etc/adduser....

2023-04-25 · 390 字 · luokai

PyTorch 源码阅读笔记(7):TorchDynamo

关于 TorchDynamo torchdynamo 为 PyTorch 2.0 的新功能,可以在不修改代码的情况下,对大部分模型提速,基本的使用方式如下 import torch def fn(x, y): a = torch.cos(x).cuda() b = torch.sin(y).cuda() return a + b new_fn = torch.compile(fn, backend="inductor") input_tensor = torch.randn(10000).to(device="cuda:0") a = new_fn(input_tensor, input_tensor) TorchDynamo 原理 官方给出的 TorchDynamo 原理图如下 涉及到关于 Python 编译运行的内容参考 [python 编译运行过程](Python 代码编译运行过程(1):编译过程 | K’s blog (luokai.tech) 字节码优化 torchdynamo 通过捕捉 python 的 frame object 进行字节码优化,运行如下代码 from torch._dynamo import optimize import torch._dynamo.config import logging torch._dynamo.config.log_level = logging.INFO torch._dynamo.config.output_code = True @optimize() def toy_example(a, b): a *= 10 b = b + 1 return b for _ in range(100): toy_example(torch....

2023-03-10 · 387 字 · luokai

Python 代码编译运行过程(2):Python 虚拟机

前言 阅读 Pytorch 源码时涉及到 Python 代码编译执行相关的内容,为了便于理解,简单学习了 Inside The Python Virtual Machine 的部分内容,本文内容主要来自于此,相关细节请参考原文。 Code Objects Python 程序由代码块构成,交互模式下的每个命令、脚本文件都是代码块,当 Python 编译代码块时,都会生成 code object: def fizzbuzz(n): if n % 3 == 0 and n % 5 == 0: return 'FizzBuzz' elif n % 3 == 0: return 'Fizz' elif n % 5 == 0: return 'Buzz' else: return str(n) # 打印代码对象内容 for attr in dir(fizzbuzz.__code__): if attr.startswith('co_'): print(f"{attr}:\t{getattr(fizzbuzz.__code__, attr)}") # 反编译得到的字节码指令 import dis dis.dis(fizzbuzz) 上面代码的打印结果是:...

2023-03-09 · 547 字 · luokai

Python 代码编译运行过程(1):编译过程

前言 阅读 Pytorch 源码时涉及到 Python 代码编译执行相关的内容,为了便于理解,简单学习了 Inside The Python Virtual Machine 的部分内容,本文内容主要来自于此,相关细节请参考原文。 简单总结 Python 程序编译过程的步骤 将源代码转化为 AST(抽象语法树, abstract syntax tree) 生成符号表(symbol table)。 从 AST 生成 code object。 源代码转化 AST 每当从命令行执行 Python 模块时,都会将模块文件的内容分解为一个个合法的 Python tokens 或者发现语法错误时进行报错。 # ==== test.py ==== a = 1 b = 1 c = a + b print(c) # ==== test.py ==== from tokenize import tokenize f = open("./test.py", 'rb') for t in tokenize(f.readline): print(t) 打印结果如下: TokenInfo(type=62 (ENCODING), string='utf-8', start=(0, 0), end=(0, 0), line='') TokenInfo(type=1 (NAME), string='a', start=(1, 0), end=(1, 1), line='a = 1\r\n') TokenInfo(type=54 (OP), string='=', start=(1, 2), end=(1, 3), line='a = 1\r\n') TokenInfo(type=2 (NUMBER), string='1', start=(1, 4), end=(1, 5), line='a = 1\r\n') TokenInfo(type=4 (NEWLINE), string='\r\n', start=(1, 5), end=(1, 7), line='a = 1\r\n') TokenInfo(type=1 (NAME), string='b', start=(2, 0), end=(2, 1), line='b = 1\r\n') TokenInfo(type=54 (OP), string='=', start=(2, 2), end=(2, 3), line='b = 1\r\n') TokenInfo(type=2 (NUMBER), string='1', start=(2, 4), end=(2, 5), line='b = 1\r\n') TokenInfo(type=4 (NEWLINE), string='\r\n', start=(2, 5), end=(2, 7), line='b = 1\r\n') TokenInfo(type=1 (NAME), string='c', start=(3, 0), end=(3, 1), line='c = a + b\r\n') TokenInfo(type=54 (OP), string='=', start=(3, 2), end=(3, 3), line='c = a + b\r\n') TokenInfo(type=1 (NAME), string='a', start=(3, 4), end=(3, 5), line='c = a + b\r\n') TokenInfo(type=54 (OP), string='+', start=(3, 6), end=(3, 7), line='c = a + b\r\n') TokenInfo(type=1 (NAME), string='b', start=(3, 8), end=(3, 9), line='c = a + b\r\n') TokenInfo(type=4 (NEWLINE), string='\r\n', start=(3, 9), end=(3, 11), line='c = a + b\r\n') TokenInfo(type=1 (NAME), string='print', start=(4, 0), end=(4, 5), line='print(c)') TokenInfo(type=54 (OP), string='(', start=(4, 5), end=(4, 6), line='print(c)') TokenInfo(type=1 (NAME), string='c', start=(4, 6), end=(4, 7), line='print(c)') TokenInfo(type=54 (OP), string=')', start=(4, 7), end=(4, 8), line='print(c)') TokenInfo(type=4 (NEWLINE), string='', start=(4, 8), end=(4, 9), line='') TokenInfo(type=0 (ENDMARKER), string='', start=(5, 0), end=(5, 0), line='') CPython 会对 tokenize 结果生成一个 parser tree,然后将其转换成 AST...

2023-03-07 · 1077 字 · luokai

PyTorch 源码阅读笔记(6):PyTorch 2.0 编译与安装

1、关于 PyTorch 2.0 PyTorch 主分支已经是2.0版本,新增了大量特性,参考PyTorch 2.0 2、PyTorch 2.0 编译环境 2.0 不再支持 CUDA 11.6,我之前的编译环境一直是 wsl2 + ubuntu 20.04 + CUDA 11.6 + gcc,这次把环境换到了 wsl2 + debian 11 + CUDA 11.7 + oneapiMKL 2023.0.0 + gcc,同时还试了一下 windows 11 + CUDA 11.7 + visual studio 2022 套件。 3、Python 编译安装 2.0 可以直接用如下命令安装 pip3 install numpy --pre torch[dynamo] --force-reinstall --extra-index-url https://download.pytorch.org/whl/nightly/cu117 自己编译安装的话参考官方命令 python setup.py develop python setup.py install 上面命令安装的 PyTorch 无法运行 TorchDynamo,参照官方说法“To install GPU TorchDynamo dependencies, run make triton in the PyTorch repo root directory....

2023-03-03 · 313 字 · luokai

PyTorch 源码阅读笔记(5):TorchScript

TorchScript 的使用 python api: class MyCell(torch.nn.Module): def __init__(self): super(MyCell, self).__init__() self.linear = torch.nn.Linear(4, 4) def forward(self, x, h): new_h = torch.tanh(self.linear(x) + h) return new_h, new_h scripted_module = torch.jit.script(MyCell().eval()) C++ api: #include <torch/script.h> // One-stop header. #include <iostream> #include <memory> int main(int argc, const char* argv[]) { if (argc != 2) { std::cerr << "usage: example-app <path-to-exported-script-module>\n"; return -1; } torch::jit::script::Module module; try { // Deserialize the ScriptModule from a file using torch::jit::load()....

2023-02-23 · 164 字 · luokai

PyTorch 源码阅读笔记(4):自动微分张量库

张量库 张量接口定义可以在 aten/src/ATen/core/TensorBody.h 看到,Tensor 类含有大量自动生成的代码,可以进行算子调用。 Tensor 类继承自 TensorBase 类,张量相关的大量函数调用自父类 TensorBase ,TensorBase 类有一个关键的成员变量: protected: c10::intrusive_ptr<TensorImpl, UndefinedTensorImpl> impl_; TensorImpl 类为张量的底层表示,包含了实际的数据指针和用以描述张量的元数据,它继承自 c10::intrusive_ptr_target,intrusive_ptr_target 是 c10 模块的侵入式指针模块。 PyTorch 实现了一个侵入式指针来替代 C++ 的 shared_ptr,shared_ptr 使用时需要创建单独的对象进行引用计数,而侵入式指针在使用的类中进行引用计数,所以侵入式指针具有更好的性能。 使用侵入式指针的类都需要实现引用计数的函数,在这里则是都需要继承 c10::intrusive_ptr_target 类,intrusive_ptr_target 有如下两个成员变量,refcount_ 记录引用计数,weakcount_ 记录弱引用计数,弱引用计数可以处理循环引用的问题: mutable std::atomic<size_t> refcount_; mutable std::atomic<size_t> weakcount_; TensorImpl 有一个 Storage 类的成员变量,Storage 有如下成员变量: protected: c10::intrusive_ptr<StorageImpl> storage_impl_; StorageImpl 继承了 c10::intrusive_ptr_target, 是实质上的底层数据类,保存了原始数据指针,对于 Storage 类的设计官方备注是继承自原始的 Torch7 项目,倾向于去掉此模块的设计,但是比较麻烦没人有空做。 Variable 与 Tensor 在较新版本的 PyTorch 中,Variable 与 Tensor 进行了合并,有如下的命名空间定义,不过没有完全去掉 Variable 相关的 api: using torch::autograd::Variable = at::Tensor 自动微分 反向传播 api backward 函数的调用会进行反向传播:...

2023-02-17 · 647 字 · luokai

PyTorch 源码阅读笔记(3):算子调用

算子注册 参考 原生算子注册 算子调用过程 找到 OperatorHandle // cmake-build-debug-wsl-gcc/aten/src/ATen/core/TensorBody.h inline at::Tensor Tensor::add(const at::Tensor & other, const at::Scalar & alpha) const { return at::_ops::add_Tensor::call(const_cast<Tensor&>(*this), other, alpha); } // cmake-build-debug-wsl-gcc/aten/src/ATen/ops/add_ops.h struct TORCH_API add_Tensor { using schema = at::Tensor (const at::Tensor &, const at::Tensor &, const at::Scalar &); using ptr_schema = schema*; // See Note [static constexpr char* members for windows NVCC] STATIC_CONSTEXPR_STR_INL_EXCEPT_WIN_CUDA(name, "aten::add") STATIC_CONSTEXPR_STR_INL_EXCEPT_WIN_CUDA(overload_name, "Tensor") STATIC_CONSTEXPR_STR_INL_EXCEPT_WIN_CUDA(schema_str, "add.Tensor(Tensor self, Tensor other, *, Scalar alpha=1) -> Tensor") static at::Tensor call(const at::Tensor & self, const at::Tensor & other, const at::Scalar & alpha); static at::Tensor redispatch(c10::DispatchKeySet dispatchKeySet, const at::Tensor & self, const at::Tensor & other, const at::Scalar & alpha); } // cmake-build-debug-wsl-gcc/aten/src/ATen/Operators_2....

2023-02-14 · 345 字 · luokai

PyTorch 源码阅读笔记(2):原生算子注册

算子定义 按照官方描述,所有的原生算子(函数)都定义在aten/src/ATen/native/native_functions.yaml文件里面,以一个add算子为例: 如下原生算子: - func: add.Tensor(Tensor self, Tensor other, *, Scalar alpha=1) -> Tensor device_check: NoCheck # TensorIterator structured_delegate: add.out variants: function, method dispatch: SparseCPU, SparseCUDA: add_sparse SparseCsrCPU, SparseCsrCUDA: add_sparse_csr MkldnnCPU: mkldnn_add ZeroTensor: add_zerotensor NestedTensorCPU, NestedTensorCUDA: NestedTensor_add_Tensor tags: [canonical, pointwise] 算子信息注册 算子通过如下宏进行 schema 注册: // 文件自动生成在 cmake-build-debug-wsl-gcc/aten/src/ATen/RegisterSchema.cpp // TORCH_LIBRARY(aten, m) 展开如下 static void TORCH_LIBRARY_init_aten(torch::Library&); static const torch::detail::TorchLibraryInit TORCH_LIBRARY_static_init_aten( torch::Library::DEF, &TORCH_LIBRARY_init_aten, "aten", c10::nullopt, "_file_name_", 6); void TORCH_LIBRARY_init_aten(torch::Library& m) { m.def("add.Tensor(Tensor self, Tensor other, *, Scalar alpha=1) -> Tensor", {at::Tag::core, at::Tag::pointwise}); } 注册发生在 m....

2023-02-13 · 1590 字 · luokai

PyTorch 源码阅读笔记(0):代码结构与编译

PyTorch代码目录结构 参考PyTorch官方描述,大致代码结构如下所述: c10 - Core library —— 核心库,包含最基本的功能,aten/src/ATen/core中的代码在逐渐往此处迁移 aten - PyTorch C++ 张量库,不包括自动梯度支持 aten/src - aten/src/ATen aten/src/ATen/core - 核心函数库,逐步往c10迁移中。 aten/src/ATen/native - 原生算子库,大部分CPU算子在此一级目录下,除了一些有特殊编译需求的算子在cpu目录下 aten/src/ATen/native/cpu - 某些需要类似AVX等特殊指令集编译的cpu算子实现 aten/src/ATen/native/cuda - CUDA 算子 aten/src/ATen/native/sparse - CPU 和 CUDA 的稀疏矩阵算子 aten/src/ATen/native/mkl,aten/src/ATen/native/mkldnn,…… - 如文件夹描述,对应的算子 torch - 实际的PyTorch库,除了torch/csrc之外的都是Python模块 torch/csrc - Python 和 C++ 混编库 torch/csrc/jit - TorchScript JIT frontend torch/csrc/autograd - 自动微分实现 torch/csrc/api - The PyTorch C++ frontend. torch/csrc/distributed - tools - 代码生成模块,PyTorch很多代码是在编译时自动生成的 test - Python前端单元测试模块,C++前端的单元测试在其他文件夹 caffe2 - Caffe2 库合并入PyTorch,具体合并了哪些官方说的太抽象,以后看到了再更新 PyTorch c++ 编译 PyTorch 官方有单独打包的 C++ 库 libtorch...

2023-02-11 · 342 字 · luokai