PyTorch - Startup

nn

Module

所有神经网络模块的基类,自定义的模型也应把该类作为基类。

1
forward(*input) : 定义每次调用时的计算逻辑,所有子类都应该重写

Sequential

提供了一种简单的方式来按顺序堆叠神经网络的层,用于快速构建一个顺序的神经网络模型。在模型进行前向传播时,nn.Sequential会按照层的顺序依次调用每个层的forward方法,将前一层的输出作为下一层的输入,直到最后一层输出结果。

Linear

ReLU

DDP

  • rank,全局进程序号
  • local_rank,本机进程序号,可以使用 local_rank = torch.distributed.get_rank() % torch.cuda.device_count() 计算(在每个机器进程数一致时)
  • world size,全局并行数

附录

MetaSpore - Startup

打包

  1. 先编译 C++ 库,生成 metaspore.so
  2. 再使用 setuptools 和 wheel 工具,打包 python 库

编译

镜像

  • 环境变量

    1
    2
    export REPOSITORY={hub-repo}
    export VERSION={version}
  • 构建 Dev 镜像(基础环境)

1
2
3
DOCKER_BUILDKIT=1 docker build --network host --build-arg http_proxy=${http_proxy} --build-arg https_proxy=${https_proxy} --build-arg RUNTIME=gpu -f docker/ubuntu20.04/Dockerfile_dev -t $REPOSITORY/metaspore-dev-gpu:${VERSION} .

DOCKER_BUILDKIT=1 docker build --network host --build-arg http_proxy=${http_proxy} --build-arg https_proxy=${https_proxy} --build-arg RUNTIME=cpu -f docker/ubuntu20.04/Dockerfile_dev -t $REPOSITORY/metaspore-dev-cpu:${VERSION} .
  • Serving

    • Build 镜像(基于 Dev 镜像进行编译)
    • Service 镜像
  • Training

    • Build 镜像(基于 Dev 镜像进行编译)

      1
      DOCKER_BUILDKIT=1 docker build --network host --build-arg http_proxy=${http_proxy} --build-arg https_proxy=${https_proxy} -f docker/ubuntu20.04/Dockerfile_training_build --build-arg DEV_IMAGE=$REPOSITORY/metaspore-dev-cpu:${VERSION} -t $REPOSITORY/metaspore-training-build:${VERSION} .
    • Spark Training 镜像

      1
      DOCKER_BUILDKIT=1 docker build --network host --build-arg http_proxy=${http_proxy} --build-arg https_proxy=${https_proxy} -f docker/ubuntu20.04/Dockerfile_training_release --build-arg METASPORE_RELEASE=build --build-arg METASPORE_BUILD_IMAGE=$REPOSITORY/metaspore-training-build:${VERSION} -t $REPOSITORY/metaspore-training-release:${VERSION} --target release .
  • Jupyter

    1
    DOCKER_BUILDKIT=1 docker build --network host --build-arg http_proxy=${http_proxy} --build-arg https_proxy=${https_proxy} -f docker/ubuntu20.04/Dockerfile_jupyter --build-arg RELEASE_IMAGE=$REPOSITORY/metaspore-training-release:${VERSION} -t $REPOSITORY/metaspore-training-jupyter:${VERSION} docker/ubuntu20.04

MetaSpore C++

MetaSpore C++ 包含几个模块。

  • common
  • serving
  • metaspore (shared)

common

  • globals

    • 定义 gflags 变量
  • hashmap

  • arrow

  • features

metaspore

  1. 提供离线训练、在线 serving 的共用代码
  2. 离线使用
    1. 使用 pybind11 库定义并绑定 C++ 代码接口
    2. python 代码加载共享库,像调用 python 代码一样调用使用 pybind11 定义的 C++ 接口

Getting Started

文档链接

步骤

  • 定义模型(PyTorch Module)
  • 定义 Estimator
    • PyTorchEstimator,封装 PyTorch 模型,并在分布式环境下训练(调用 fit 方法并传入 DataFrame 进行训练)
    • 调用 launcher.launch() 在个节点启动 PS 进程(server、worker、coordinator)

定义模型

1
2
3
4
5
6
7
8
embedding_size      : 每个特征组的 embedding size
# MetaSpore 相关
sparse : ms.EmbeddingSumConcat
sparse.updater : ms.FTRLTensorUpdater
sparse.initializer : ms.NormalTensorInitializer
dense.normalization : ms.nn.Normalization
# Torch 相关
dense : torch.nn.Sequential

初始化内容。

  • EmbeddingSumConcat
    • SparseFeatureExtractor
      • 解析原始特征列配置文件
      • 向计算图中添加计算 Hash 特征的 Node
    • EmbeddingBagModule
  • TensorUpdater,Sparse & Dense 数据更新类
    • FTRLTensorUpdater
  • TensorInitializer,张量初始化器
    • NormalTensorInitializer,归一化张量初始化器
  • Normalization,归一化

训练模型

1
PyTorchEstimator
  • 定义 PyTorchEstimator
    • module
    • worker / server 数量
    • 模型输出路径
    • Label 列索引
1
2
3
4
5
PyTorchAgent
PyTorchLauncher
PyTorchHelperMixin
PyTorchModel
PyTorchEstimator

核心概念

  • JobRunner
  • PyTorchEstimator
    • pyspark.ml.base.Estimator
  • Launcher
    • PSLauncher
  • Agent
  • Module
    • EmbeddingOperator
    • TensorUpdater
    • TensorInitializer
    • Normalization
  • PyTorchModel
    • pyspark.ml.base.Model
  • Metric

MetaSpore - Startup

C++

common

特征计算相关

FeatureComputeContext 和 FeatureComputeExecContext 都是在 cpp 文件中进行定义。

FeatureComputeContext

FeatureComputeExecContext

FeatureComputeExec

metaspore

Python

embedding

EmbeddingBagModule

1
EmbeddingBagModule -> torch.nn.Module

EmbeddingSumConcat

1
EmbeddingSumConcat -> EmbeddingOperator -> torch.nn.Module

nn

Normalization

others

FTRLTensorUpdater

NormalTensorInitializer

附录

boost

机器学习平台

开源方案

训练框架

Inference / Serving

MLOps

云商产品

文章

训推一体方案

img

稀疏数据集

文章

Python 机器学习 基于 Pytorch 和 Scikit-Learn - 第二章

神经网络与感知机学习规则

基于神经元模型,提出了感知机学习规则。感知机规则提出了一个可以自动学习的权重优化算法。

感知机算法步骤如下。

  1. 初始化权重和偏置项为 0 或很小的随机数
  2. 遍历每个训练样本
    1. 计算感知机输出值
    2. 更新权重和偏置项

需要注意的是。

  1. 只有当训练数据线性可分时,才能保证感知机具有收敛性
    1. 此时需要设置训练数据集的最大循环次数,或容错次数的阈值,来结束训练
  2. 权重、偏置项使用很小的初始化值替代 0 ,如果全是 0 则学习率会失去对决策边界的影响
    1. 学习率只影响权重向量的大小,不影响其方向

CUDA - Coding

错误处理

运行时 API 错误码

调用 CUDA 运行时 API 时,接口返回错误码。

1
__host__ __device__ cudaError_t cudaGetDeviceCount ( int* count ); // 获取设备数量, 返回错误码

错误检查

1
2
__host__ __device__ const char* cudaGetErrorName ( cudaError_t error );     // 获取错误码的枚举名称
__host__ __device__ const char* cudaGetErrorString ( cudaError_t error ); // 获取错误码的解释描述

定义错误检查函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
__host__ void error_check_entry() {
int device_id_in_use;
error_check(cudaGetDevice(&device_id_in_use), __FILE__, __LINE__);
error_check(cudaSetDevice(999), __FILE__, __LINE__);
// char *p_c;
// error_check(cudaMalloc(&p_c, 100), __FILE__, __LINE__);

cudaDeviceSynchronize();
} /** output
error_check, ok
CUDA error:
code=101, name=cudaErrorInvalidDevice, description=invalid device ordinal,
file=/data/code/cook-cuda/src/sample/hello_world.cu, line=51
*/

核函数中的异常

核函数的返回值必须是 void。

1
__host__ __device__ cudaError_t cudaGetLastError ( void ); // 返回最后一次错误码
1
2
3
4
5
6
7
8
9
__global__ void kernel_error_entry() {
dim3 block(2048);
print_build_in_vars<<<2, block>>>(); // block size 最大 1024
error_check(cudaGetLastError(), __FILE__, __LINE__);
} /** output
CUDA error:
code=9, name=cudaErrorInvalidConfiguration, description=invalid configuration argument,
file=/data/code/cook-cuda/src/sample/hello_world.cu, line=67
*/

性能评估

事件计时

1
2
3
4
5
__host__ cudaError_t cudaEventCreate ( cudaEvent_t* event );
__host__ __device__ cudaError_t cudaEventRecord ( cudaEvent_t event, cudaStream_t stream = 0 );
__host__ cudaError_t cudaEventSynchronize ( cudaEvent_t event );
__host__ cudaError_t cudaEventElapsedTime ( float* ms, cudaEvent_t start, cudaEvent_t end );
__host__ __device__ cudaError_t cudaEventDestroy ( cudaEvent_t event );

示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cudaEvent_t start, end;
error_check(cudaEventCreate(&start), __FILE__, __LINE__);
error_check(cudaEventCreate(&end), __FILE__, __LINE__);
error_check(cudaEventRecord(start), __FILE__, __LINE__);
cudaEventQuery(start);

// run GPU Task

error_check(cudaEventRecord(end), __FILE__, __LINE__);
error_check(cudaEventSynchronize(end), __FILE__, __LINE__);
float elapsed_time_ms;
ERROR_CHECK(cudaEventElapsedTime(&elapsed_time_ms, start, end));

printf("elapsed time: %f ms\n", elapsed_time_ms);
ERROR_CHECK(cudaEventDestroy(start));
ERROR_CHECK(cudaEventDestroy(end));

error_check。

1
2
3
4
5
6
7
8
__host__ __device__ cudaError_t error_check(cudaError_t err, const char *fn, int line) {
if (err != cudaSuccess) {
printf("CUDA error:\n\tcode=%d, name=%s, description=%s, \n\tfile=%s, line=%d\n", err, cudaGetErrorName(err),
cudaGetErrorString(err), fn, line);
}
return err;
}
#define ERROR_CHECK(exp) error_check(exp, __FILE__, __LINE__)

nvprof

nvprof 是评估 cuda 程序性能的工具。不过目前已经是过时的工具,不适用 compute capability >= 8.0 的设备。新设备适用 nsys 替代。

1
$ nvprof {cuda-program}

nsys

1
2
$ nsys profile {cuda-program} # 运行并记录程序的 profile 到 nsys-rep 文件
$ nsys analyze {nsys-rep} # 分析 profile 文件

获取 GPU 信息

运行时 API

1
__host__ cudaError_t cudaGetDeviceProperties ( cudaDeviceProp* prop, int  device )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
__host__ void PrintDeviceInfo() {
int deviceCount;
cudaGetDeviceCount(&deviceCount);
std::cout << "GPU device count: " << deviceCount << std::endl;

for (int i = 0; i < deviceCount; ++i) {
// sm: 流式多处理器, Streaming Multiprocessor
cudaDeviceProp dp{};
cudaGetDeviceProperties(&dp, i);
std::cout << "device.0 " << std::endl;
std::cout << " sm count: \t\t\t\t" << dp.multiProcessorCount << std::endl;
std::cout << " shared memory per block: \t\t" << dp.sharedMemPerBlock / 1024 << "KB" << std::endl;
std::cout << " max threads per block:\t\t" << dp.maxThreadsPerBlock << std::endl;
std::cout << " max threads per multi processor:\t" << dp.maxThreadsPerMultiProcessor << std::endl;
std::cout << " max threads per sm:\t\t\t" << dp.maxThreadsPerMultiProcessor / 32 << std::endl;
std::cout << " max blocks per multi processor:\t" << dp.maxBlocksPerMultiProcessor << std::endl;
}
}

helm - usage

local git repo

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ tree
.
├── ace
│   ├── nginx
│   │   ├── Chart.yaml
│   │   ├── configmap
│   │   │   └── sources.list
│   │   ├── templates
│   │   │   ├── configmap.yaml
│   │   │   ├── deployment.yaml
│   │   │   ├── _helpers.tpl
│   │   │   └── service.yaml
│   │   └── values.yaml
│   └── ...
├── Makefile
└── README.md

安装 Chart

1
2
3
4
5
6
7
$ helm install ace ace/nginx
NAME: ace
LAST DEPLOYED: Mon Aug 5 13:42:17 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

更新 Chart

1
2
3
4
5
6
7
8
$ helm upgrade ace ace/nginx
Release "ace" has been upgraded. Happy Helming!
NAME: ace
LAST DEPLOYED: Mon Aug 5 13:47:34 2024
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None

卸载 Chart

1
2
$ helm uninstall ace
release "ace" uninstalled

示例

1
2
3
4
5
6
$ helm upgrade --install {release-name} {chart-path} \
--create-namespace -n ${kubeNamespace}
-f {path-to-helm-values-file} \
--set app.tag={tag-value} \
--kube-context {kube-context} \
--kubeconfig {path-to-kube-config-file}

解释

  • --install :更新 release,如果不存在则安装
  • --create-namespace:如果 namespace 不存在,则创建,和 --install 配合使用
  • --set app.tag :指定 Value app.tag 的值

问题排查 - CPU

原因分析

计算任务

  • 计算量过大的任务占用过多 CPU
  • 死循环

上下文切换

  • 死锁
  • 频繁加锁
  • 过多的并发
  • 内存不足
  • 频繁 GC(Java、GO 等语言)

问题排查

借助 TOP 命令

1
top -Hp {pid}  # 查看指定进程内各线程占用 CPU 的情况

查看线程数量

1
ps -p {pid} -L | wc -l

排查进程的上下文切换情况

pidstat

1
pidstat -w -p {pid}

其中,<PID> 是目标进程的进程 ID。上述命令将显示指定进程的 CPU 上下文切换统计信息,包括自愿切换(voluntary switches)和非自愿切换(non-voluntary switches)。

1
2
3
4
Linux 4.14.301-224.520.amzn2.x86_64 (...) 	2024年07月04日 	_x86_64_	(32 CPU)

10时23分19秒 UID PID cswch/s nvcswch/s Command
10时23分19秒 0 3637168 0.17 0.00 ...
1
2
# 安装
yum install sysstat -y

perf

1
2
3
4
5
6
7
8
9
10
11
12
perf stat -e cs,<event> -p <PID>
# event: cs (所有模式切换) , cs:u (用户模式切换), cs:k (内核模式切换)
$ perf stat -e cs,cs:u,cs:k -p 3637168 # Ctrl-C 结束收集

^C
Performance counter stats for process id '3637168':

44,981 cs
0 cs:u
44,981 cs:k

27.447834538 seconds time elapsed

perf stat 和 perf record 区别

  • perf stat

  • 快速查看程序基本性能指标

  • 采集 CPU 指令、缓存命中率、上下文切换等

  • perf record

  • 可采集系统或特定进程的性能事件

  • 采集指令、缓存、分支等事件

  • 可导出文件,用于后续的分析

1
yum install perf -y

其他问题

  • 如何区分是计算任务占用 CPU 还是过多上下文切换占用任务
  • 区分 IO 线程和 Work 线程的必要性