机器学习 - 环境

Conda

安装

linux

1
2
3
4
mkdir -p ~/.miniconda3
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/.miniconda3/miniconda.sh
bash ~/.miniconda3/miniconda.sh -b -u -p ~/.miniconda3
rm -rf ~/.miniconda3/miniconda.sh

配置 Shell

1
2
3
4
# for bash
~/.miniconda3/bin/conda init bash
# for zsh
~/.miniconda3/bin/conda init zsh

环境

列出环境

1
$ conda info --envs

创建环境

1
$ conda create -n ml

克隆环境

1
$ conda create --name new_name --clone old_name

启用环境

1
$ conda activate {env-name}

环境重命名

1
conda rename -n old_name new_name 

使用 yml 文件更新环境

1
$ conda env update --file env.yml --prune

删除环境

1
$ conda remove --name {env-name} --all

默认不启用 conda base 环境

1
$ conda config --set auto_activate_base false  # 关闭默认使用 base

为环境添加 channel

1
$ conda config --append channels conda-forge

包管理

conda 的包管理有 channel 的概念,如果不指定则为默认的 defaults。如果我们想要安装其他 channel 的包,示例如下。

1
$ conda install anaconda::gcc_linux-64

查询可用包

1
$ conda search {package}

或在 这里 搜索,页面有安装命令,比如。

1
2
3
$ conda install anaconda::gcc_linux-64
# 另外一个包
$ conda install conda-forge::gcc_linux-64

已安装包

1
$ conda list

移除包

1
$ conda uninstall {package}

安装包

1
2
3
4
5
6
7
8
# 默认包
$ conda install {package}

# 指定channel
$ conda install {channel}::{package}

# 指定版本
$ conda install {package}={version}

Kaggle

安装

详见文档Github 仓库

1
$ pip install kaggle

User Profile 页面 Create New Token,并将下载的文件放到 ~/.kaggle/kaggle.json

你行走的时候会一直看路上有钉子吗?

先从线上发现的一个小问题说起。

先说下背景,我们有个仓库维护公共的 proto 文件,且配置了 CI/CD,合并时触发编译,并打新的 tag。我们有个服务 A,依赖这个库。

小问题发生的过程如下。

  • proto 仓库增加了一个文件,该文件没有指定 go_package ,CI/CD 触发编译,生成 go 代码时报错(proto 未指定 go_package),tag 未更新
  • 服务 A 需求,需要向 proto 文件新增字段,合并后等待 CI/CD 触发编译
  • 等一段时间后,拷贝最新 tag(路径依赖,感觉留了足够时间跑编译流程,实际编译失败,最新的 tag 未包含改动)
  • 服务 A 上线
  • 上线后发现不符合预期
  • 修复 proto 编译问题后,发布 hotfix 版本

上面是小问题短周期的描述,如果把时间线拉长。

  • A 服务创建了一个 DEPENDS 文件,记录依赖的库及其版本,编译时通过脚本读取
  • 对 A 服务进行重构,重构服务就记为 B
  • 从 proto 库中拷贝需要的文件到服务 B 的代码库
  • 对 B 代码库的 proto 进行新增、修改
  • 觉得这种方式很容易出问题
    • 把服务 B 内 proto 的改动迁移到 proto 仓库
    • 通过 git submodule 的方式,在服务 B 内引入 proto 文件
  • 服务 B 是 C++ 服务,无需生成 go 代码,估新加的 proto 文件无 go_package
  • 此处接小问题发生过程

至此,这个事情陈述完毕。

我想再讲一个 case。我们在做用户数据迭代时,需要离线写入一份事件列表数据,我期望的是存储团队交付一个按时间戳由大及小排序好的时间序列,这样线上读取时无需再做一次排序。最后评审时,交付的是一个 map 结构,提出异议时,存储团队坚持认为 cassandra 集群的 map 是有序的,故符合需求,最终以 map 结构交付。由于原有封装好的读取接口,对 cassandra map 结构使用 unordered_map 保存。迫于排期,只能先读为 unordered_map,再转换为 vector,最后按时间戳排序。然而事情并没有结束,在后续做特征一致性校验时,出现不一致,原因是在以 map 存储时,key 是以秒为单位的时间戳,故如果多个事件具有相同的时间戳,只能保留一个。

想讲这些问题,是因为这样的事情,在过去的日子里,一次又一次的发生。更痛苦的是,在将来的日子里,也会一次又一次的发生。

更恐怖的是,当出现问题,我们往往舍本逐末、不追究 root cause,却做了很多其他的事情。就像,我们走路的时候,脚被钉子扎了,于是穿上了铁板鞋、两手提着两个大磁铁、身上背着医药箱、头低到膝盖走路,然而一切的原因,只是为了抄一点近路而选择了满是建筑废料的小路。

如果说两点之间直线最短,那么以正确的方式做正确的事情就是那条直线。

正确的方式,一定是简单且优雅的。

最后,因为偷懒或者其他原因留的每个坑,只要时间足够长,都会被踩到,而且可能不止一个地方,不止一次不挖坑,就不会踩坑。

vcpkg - 自定义仓库

使用步骤

  • 安装 VCPKG,或更新至最新 VCPKG
  • 创建项目,并使用 vcpkg 管理依赖
  • 使用 vcpkg 打包项目(overlay port)
  • 将项目添加到 Registry

安装 VCPKG

1
2
3
4
# 下载
$ git clone https://github.com/microsoft/vcpkg.git
# 执行依赖检查、下载 vcpkg 可执行文件
$ cd vcpkg && bootstrap-vcpkg.bat

设置环境变量。

1
2
export VCPKG_ROOT="/path/to/vcpkg"
export PATH=$VCPKG_ROOT:$PATH

更新 VCPKG

1
2
3
4
$ cd $VCPKG_ROOT
$ git pull
$ chmod a+x bootstrap-vcpkg.sh
$ ./bootstrap-vcpkg.sh

创建项目

编写工程代码

编写工程源码,以及 CMakeLists.txt 文件。

创建 manifest 文件 vcpkg.jsonvcpkg-configuration.json

1
$ vcpkg new --application

添加 Git Registry 到 vcpkg-configuration.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
...
"registries": [
{
"kind": "git",
"repository": "https://github.com/Microsoft/vcpkg",
"baseline": "42bb0d9e8d4cf33485afb9ee2229150f79f61a1f",
"packages": "*"
}
]
}
# or
{
...
"registries": [
{
"kind": "git",
"repository": "https://github.com/Microsoft/vcpkg",
"baseline": "42bb0d9e8d4cf33485afb9ee2229150f79f61a1f",
"reference": "master",
"packages": [
"fmt",
"spdlog"
]
}
]
}
  • reference 是可选项,如果你的 commit id 不在默认分支,则必须使用 reference 指定对应的分支,否则会报 failed to unpack tree object

添加依赖包

1
$ vcpkg add port fmt

构建和运行

创建 CMakePresets.json

在项目中创建 CMakePresets.json 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"version": 2,
"configurePresets": [
{
"name": "default",
"generator": "Ninja",
"binaryDir": "${sourceDir}/vcpkg-build",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
}
}
]
}

文件包含一个为 CMake 配置的 default 预定配置(Preset),设置 CMAKE_TOOLCHAIN_FILE 变量,允许 CMake 识别 vcpkg 提供的 C++ 库。

运行 CMake configuration

1
$ cmake --preset=default # 指定预设

构建工程

1
$ cmake --build vcpkg-build

使用 vcpkg 打包库

设置自定义 overlay

在项目目录下,创建 {project}-vcpkg-custome-overlay

1
$ mkdir {project}-vcpkg-custome-overlay

设置 Port 相关文件

vcpkg.json

1
2
$ cd {project}-vcpkg-custome-overlay
$ mkdir {project}-vcpkg-custome-overlay/{project}-library

创建 port 的 vcpkg.json 配置。

1
$ touch {project}-vcpkg-custome-overlay/{project}-library/vcpkg.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"name": "{project}-library",
"version": "1.0.0",
"homepage": "https://project.com/home",
"description": "project description",
"license": "MIT",
"dependencies": [
{
"name" : "vcpkg-cmake",
"host" : true
},
{
"name" : "vcpkg-cmake-config",
"host" : true
},
"{other-packages}"
]
}

关于 vcpkg.json 更多的说明,参考这里

usage

创建 usage 文件。

1
$ touch {project}-vcpkg-custome-overlay/{project}-library/usage
1
2
3
4
{project}-library provides CMake targets:

find_package({project}_lib CONFIG REQUIRED)
target_link_libraries(main PRIVATE {project}::{project})

关于 usage 文件的更多说明,参考 这里

portfile.cmake

1
$ touch {project}-vcpkg-custome-overlay/{project}-library/portfile.cmake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
vcpkg_check_linkage(ONLY_STATIC_LIBRARY)

vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO {user}/{project}
REF "${VERSION}"
SHA512 0 # This is a temporary value. We will modify this value in the next section.
HEAD_REF {project}-lib
)


vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
)

vcpkg_cmake_install()

vcpkg_cmake_config_fixup(PACKAGE_NAME "{project}_lib")

file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")

file(INSTALL "${SOURCE_PATH}/LICENSE" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright)
configure_file("${CMAKE_CURRENT_LIST_DIR}/usage" "${CURRENT_PACKAGES_DIR}/share/${PORT}/usage" COPYONLY)

关于 portfile.cmake 文件的详细说明,参考这里

附,从 gitlab 下载。

1
2
3
4
5
6
vcpkg_from_git(
OUT_SOURCE_PATH SOURCE_PATH
URL git@gitlab.compony.com:group/repo.git
REF {commit-id} # 必须是 commit id
HEAD_REF main
)

更新 portfile.cmake 的 SHA512

第一次运行 vcpkg install ... 获取 SHA512。

1
$ vcpkg install {project}-library --overlay-ports=/path/to/{project}-vcpkg-custom-overlay

在输出中找到如下内容。

1
2
Expected hash: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Actual hash: 4202125968a01219deeee14b81e1d476dab18d968425ba36d640816b0b3db6168f8ccf4120ba20526e9930c8c7294e64d43900ad2aef9d5f28175210d0c3a417

拷贝 “Actual hash” 的值,覆盖 portfile.cmakeSHA512 的值。

添加编译选项(definitions)

1
2
3
4
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
OPTIONS -D...=... -D...=... ...
)

打包安装库

再次运行 vcpkg install ...

1
$ vcpkg install {project}-library --overlay-ports=/path/to/{project}-vcpkg-custom-overlay

注意 依赖要在 {project}-vcpkg-custom-overlay/vcpkg.json 中添加,比如。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
...
"dependencies": [
{
"name": "vcpkg-cmake",
"host": true
},
{
"name": "vcpkg-cmake-config",
"host": true
},
"protobuf"
]
}

再次运行

1
$ vcpkg remove {project}-library # 先移除再重新安装

验证 Port Build

修改代码

复用或新建工程,修改如下内容。

1
2
3
4
5
6
7
# file: {project}/vcpkg.json
{
"dependencies": [
"...",
"{project}-library"
]
}
1
2
3
4
5
6
7
8
9
10
11
12
# file: {project}/vcpkg-configuration.json
{
"default-registry": {
...
},
"registries": [
...
],
"overlay-ports": [
"/path/to/{project}-vcpkg-custom-overlay"
]
}

修改 CMakeLists.txt,添加库依赖。

1
2
3
4
...
find_package({project}_lib CONFIG REQUIRED) # Add this line
add_executable({project} main.cpp)
target_link_libraries({project} PRIVATE {project}_lib::{project}_lib) # Add this line

配置(Configuration)

1
$ cmake --preset=default # 需预先配置 CMakePresets.json, 见上文

构建(Build)

1
$ cmake --build vcpkg-build

运行

1
$ ./vcpkg-build/{project}

创建 Git Registry

1
2
3
4
5
6
$ mkdir ports
$ mkdir versions
$ vim versions/baseline.json
{
"default": {}
}

向 Repository 添加 Port

完整示例,新增依赖库需要修改 / 新增如下文件。

1
2
3
4
5
6
7
8
ports
- {port-name}
- portfile.cmake
- vcpkg.json
versions
- {port-name-first-char}-
- {port-name}.json
- baseline.json

没个仓库都会有 versions/baseline.json 文件,包含在一个特定 commit 的一系列 Ports 的最新版本。

拷贝 Overlay Port 到 Repository(Registry)

拷贝 Overlay Port 到 Repository 的 ports 目录。

1
xcopy <path/to/{project}-library> <ports/{project}-library> /E

Commit

注意,Ports 和 Version 不能同时添加。

Commit Ports

1
2
$ git add ports/<{project}-library>
$ git commit -m '...'

Commit Version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 更新版本
$ vcpkg --x-builtin-ports-root=./ports --x-builtin-registry-versions-dir=./versions x-add-version --all --verbose --overwrite-version
$ git commit -m '...version'

# 查看帮助
$ vcpkg x-add-version --help

# 跳过格式检查
--skip-formatting-check
--skip-version-format-check

# 格式化
vcpkg format-manifest /path/to/vcpkg.json

# 注: 下面代码仅适用于更新微软 open-source ports,且将 ports 复制到 $VCPKG_ROOT/ports 下
# $ vcpkg x-add-version vcpkg-sample-library

更新 Registry 中已经存在的库版本

  • 从 Registry 拷贝 ports/{library} 到 Custome Overlay 目录
  • 更新版本、portfile.cmake,再走一遍添加 Port 到 Registry 的流程

附录

Trubleshooting

debug/share 目录不存在

原因

没有文件写入到 share 目录(license 文件拷贝貌似不在 debug 拷贝)。

通常,依赖库在 install 时指定 export target,并 install(export target …),会生成对应库的 {lib}Config.cmake ,并拷贝到 share 目录下,示例如下。

1
2
3
4
5
install(TARGETS {lib}
EXPORT {lib}
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib)
install(EXPORT {lib} DESTINATION share/{lib} FILE {lib}Config.cmake)

修复

方案一,手动创建目录,在 portfile.cmake 中添加。

1
file(MAKE_DIRECTORY ${CURRENT_PACKAGES_DIR}/debug/share/{lib})

方案二,依赖库添加 install export

方案三,依赖库添加 install pkgconfig file

failed to unpack tree object

1
2
3
4
5
-- Running vcpkg install
Fetching registry information from https://github.com/sunzhenkai/vcpkg-example-registry (master)...
error: failed to execute: /usr/bin/git --git-dir=/home/wii/.cache/vcpkg/registries/git/.git --work-tree=/home/wii/.cache/vcpkg/registries/git-trees/68d1cfd34c43686c9f60c0d0f780525f29bb046f_3332708.tmp -c core.autocrlf=false read-tree -m -u 68d1cfd34c43686c9f60c0d0f780525f29bb046f
error: git failed with exit code: (128).
fatal: failed to unpack tree object 68d1cfd34c43686c9f60c0d0f780525f29bb046f

原因

git registry 中依赖库的 versions/{x}-/{port}.json 里面的 git-tree 值,是在非默认分支打的,默认克隆 HEAD,或者指定的是默认分支,导致 check out 不出来对应的数据。

解决办法

需要更新 Git Registry 中 port 版本的 git-tree 数据,命令如下。

1
vcpkg --x-builtin-ports-root=./ports --x-builtin-registry-versions-dir=./versions x-add-version --all --verbose --overwrite-version

多编译器

如果系统安装多个版本的 gcc/g++,想使用特定的编译器,可以通过下面命令指定。也可用来解决,多个编译器版本时,不同依赖库使用不同编译器的问题(会有查找编译器程序的差异)。

1
2
export CC=/path/to/gcc
export CXX=/path/to/g++

CMake 工程测试

1
2
3
4
5
$ mkdir build
$ cd build
$ cmake ..
$ make -j
$ cmake --install . --prefix="$PWD" # 安装到当前目录

聊聊小聪明

有句古话,聪明反被聪明误。小聪明有点像贪心算法,选局部或者短期最优解。应用在工作上,就是要么得钱、要么得闲。

今天突然有一个想法。只有骗子才会在没有创造价值之前,就得到回报。正常情况,都是先创造价值,再得到回报。对于普通的上班族来说,尤其是工资对能力匹配敏感度比较高的行业或者岗位,当前的工资是对目前能力的匹配。如果想在这个行业有更好的发展,只能不断地通过提升能力来创造更多价值。如果带着开多少工资干多少活的心态,其实是耍小聪明的做法。

避免消极情绪

摆脱未成年、学生这些 buff 后,步入社会,遇到各种各样的打击很正常。有些是因为自己的错误,有些是无厘头的。如何应对这些打击是人生的必修课,想逃避即不可能,也没必要。

收到打击之后,通常会消极情绪。消极情绪不可怕,可怕的是被消极情绪支配,甚至走向极端。

处理消极情绪

  • 要保持理性(无论如何)
  • 早睡早起
  • 坚持
    • 洗脸
    • 刷牙
    • 洗澡
    • 刮胡子(男生,女生可以化化妆,打扮打扮)
    • 穿干净的衣服
  • 分散注意力
    • 刷剧
    • 听音乐
    • 读书
  • 运动(最好的,如果不排斥的话)

总之,要保持整洁,转移注意力,清空大脑。就我个人而言,基本所有的消极情绪,很快就会结束(通常一两天吧)。

最后

有人穷尽一生追求真理,有人真理张口就来。有些事情很无厘头,但我们知道,我们改变不了别人。

我们能做的,就是要想海绵一样,给自己留一个缓冲区,不要伤到自己,不要过度否定自己。想不明白的事情,就先放一放,说不准过几天就明白了,也说不准过几天就不重要了。

挨打要站稳

致自己:

  • 如果你觉得,你的能力配不上你的期望,那就低下头好好干活、好好学习。如果你觉得,你的回报配不上你的能力,那就好好产出价值。

  • 挨打要站稳,抱怨没有用,打铁还需自身硬,不要怨天尤人。

  • 是金子总会发光没错,但你是金子吗?你真的是金子吗?你真的是金子,还是你自己以为自己是金子?