使用 Docker 构建图像的完整教程
通过一个设置 Ubuntu、默认用户、Miniconda、PyTorch 的例子来了解使用 Docker 编写 Dockerfile 和运行容器所需的一切。
Docker 提供了一种在主机操作系统上运行你的程序的方法。 Dockerfile 提供了关于如何构建映像的说明,然后这些映像作为容器运行。这篇文章讨论了创建 Dockerfile 所需的所有东西,并以设置 Ubuntu 、 Miniconda 和 PyTorch 为例。
建立形象
docker build
命令用于从Dockerfile
和上下文(路径或 URL)构建图像。上下文是指构建命令中指定的所有文件。构建映像的步骤如下
- 由上下文指定的所有文件都被发送到 docker 守护进程。因此,您应该在空目录中创建 Dockerfile,以避免不必要的传输。
- 检查 docker 文件是否有语法错误
- Docker 守护程序通过读取来自
Dockerfile
的指令开始构建映像。
指定上下文
上下文可以是本地文件系统上的PATH
或引用 Git 存储库的远程URL
。下面显示了一个指定构建上下文的示例
> docker build .> docker build /path/to/context
用 URL 指定上下文
Docker 还为您提供了从 Git URL 构建图像的能力。这主要用于持续集成管道。要使用 URL 构建映像,docker 文件应该位于指定的 URL(而不是本地文件系统)。docker build
从 Github URL 构建图像时,将自动执行以下步骤
> git clone {GITHUB_URL}
> git checkout {BRANCH}
> cd repo
> docker build .
构建映像的命令如下
> docker build {GITHUB_URL}#{BRANCH}> docker build https://github.com/KushajveerSingh/Dockerfile.git#master
注意:- 由于指定位置没有 Dockerfile,上述命令将失败。
指定 Dockerfile 文件的位置
您可以使用-f
标志来指定 Dockerfile 的路径。默认情况下,假定 Dockerfile 位于上下文的根位置。
> docker build -f /path/to/Dockerfile .> docker build -f ubuntu_conda_pytorch/Dockerfile https://github.com/KushajveerSingh/Dockerfile.git#master
Docker 会自动将cd
放入 Github 存储库,所以路径不应该包含存储库的名称。
指定存储库和标签
考虑下面的 Dockerfile 文件
FROM ubuntu:18.04LABEL PURPOSE = "test Dockerfile"
当我们构建一个映像时,docker 分配一个提交散列作为IMAGE ID
。
您可以为每个 docker 图像指定一个存储库和标签,然后使用-t
标志轻松访问该图像。REPOSITORY
可以被认为是你的 Github 库的名字,而TAG
是用来指定你的镜像版本的。比如,ubuntu:18.04
,ubuntu:latest
。
您也可以指定多个标签
> docker build -t test:0.1 -t test:latest .
Docker 图像层
Docker 守护进程从上到下逐个运行指令。并且大多数指令的结果(FROM
、ADD
、COPY
)被提交给新的图像。由于这个原因,你需要小心使用这些指令,因为每一次使用它们都会导致创建一个新的图像,从而增加最终图像的大小。
Docker 为什么要这么做?考虑下面的 Dockerfile 文件
FROM ubuntu:18.04RUN COMMAND_1
RUN COMMAND_2
RUN COMMAND_1
现在,当我们建立的形象,我们将创建以下层
- 来自
ubuntu:18.04
Dockerfile 的层 RUN COMMAND_1
将创建一个新层RUN COMMAND_2
将创建一个新层RUN COMMAND_3
将创建一个新层
层基本上是图像或中间图像上的变化。每当我们运行一条指令(如FROM
、RUN
、COPY
)时,我们都在对之前的图像进行修改。这些变化导致新层的创建。拥有中间层有助于构建过程。如果您在 Docker 文件中进行了更改,那么 Docker 将只构建被更改的层和之后的层。这样可以节省很多时间。
注意:- 创建新图层时要小心,因为这会增加图像的尺寸。
构建工具包
Docker 支持 moby/buildkit 后端构建映像。与 Docker 提供的默认实现相比,BuildKit 提供了许多好处
- 检测并跳过未使用的构建阶段
- 并行构建独立的构建阶段
- 在两次构建之间,仅增量传输构建上下文中已更改的文件
- 检测并跳过在您的构建上下文中传输未使用的文件
- 使用具有许多新特性的外部 Dockerfile 实现
- 避免其他 API(中间图像和容器)的副作用
- 为自动修剪设置构建缓存的优先级
要使用 BuildKit 后端,您需要设置DOCKER_BUILDKIT=1
环境变量。
> DOCKER_BUILDKIT=1 docker build .
运筹学
> export DOCKER_BUILDKIT=1
> docker build .
摘要
总之,要构建 Docker 映像,可以使用以下命令
转义字符
这是一个解析器指令的例子。解析器指令是在 Dockerfile 的第一行中指定的,不会向构建中添加层。您可以使用这个来指定在 Dockerfile 中换行的字符。
# escape=\
FROM ubuntu:18.04RUN INSTRUCTION_1 \
INSTRUCTION_2
在 Windows 上\
用于指定路径。因此,将它更改为类似于反勾号的东西会很有用。
# escape=`
FROM ubuntu:18.04RUN INSTRUCTION_1 `
INSTRUCTION_2
从
所有 Dockerfile 文件都以FROM
指令开始。FROM
初始化新的构建阶段,并为后续指令设置基础映像。一般语法是
FROM [--platform=<platform>] <image>[:tag] [AS <name>]
这里的[…]表示可选。您可以从一个零映像开始,并在此基础上构建一切
FROM scratch
或者你可以建立在一个公共形象之上(比如 Ubuntu 、 PyTorch 、 nvidia/cuda )
FROM ubuntu:18.04
在这篇文章中,我建立在 Ubuntu 图像之上。您可以使用以下命令构建映像并尝试运行它
> DOCKER_BUILDKIT=1 docker build -t test:0.1 .
> docker run -it --name temp test:0.1
您会看到这是一个基本安装。它没有任何用户或 sudo。它为我们提供了 Linux 内核,我们必须在此基础上构建一切。
在接下来的几节中,我们将查看 docker 文件中的所有指令,然后我们将使用所有这些指令构建一个 Ubuntu/Miniconda/PyTorch 映像。
参数和环境
环境变量通常用于在脚本中声明变量,或者设置一些在容器运行时会持续存在的变量。Docker 允许我们用两种方式设置变量:ARG
和ENV
。
ARG
指令定义了一个变量,用户将在编译时通过使用--build-arg <name>=<value>
标志的docker build
命令传递该变量。这些将只在 Dockerfile 文件中使用。ENV
指令设置 Dockerfile 文件中的环境变量,当从结果图像运行容器时,环境变量将持续存在。
银
我们可以将 Ubuntu 的版本指定为ARG
(docker file 的代码如下所示)
ARG UBUNTU_VERSION
FROM ubuntu:$UBUNTU_VERSION
然后我们可以在构建映像时指定 ubuntu 的版本
> DOCKER_BUILDKIT=1 docker build -t test --build-arg UBUNTU_VERSION=18.04 .
我们还可以为ARG
指定一个默认值,如下所示
ARG UBUNTU_VERSION=18.04
要访问ARG
的值,可以使用$UBUNTU_VERSION
或${UBUNTU_VERSION}
语法。当您想要访问字符串中的值ARG
时,第二种方法很有用。
使用ARG
- 对于只在 docker 文件中需要而在容器运行时不需要的变量,使用
ARG
。在这种情况下,当容器运行时,不需要 Ubuntu 的版本。 - 在
FROM
之前使用的ARG
只能在FROM
中使用 - 在
FROM
之后使用的ARG
可以在 docker 文件中的任何地方使用(在多阶段构建的情况下有一个例外,即当我们在同一个 docker 文件中使用多个FROM
指令时)
包封/包围(动词 envelop 的简写)
这与ARG
相同,除了当容器从结果图像运行时ENV
将持续。这方面的一个例子包括
ENV PYTORCH_VERSION 1.9.0
ENV LD_LIBRARY_PATH /usr/local/nvidia/lib:/usr/local/nvidia/lib64# Setting PATH variables
ENV PATH /home/default/miniconda3/bin:$PATH
贴上标签并曝光
这两个说明可以被认为是文档说明。Dockerfile 中的这些指令对图像没有影响,它们只是用来提供元数据信息。
标签
LABEL
可用于指定 Dockerfile 的作者等相关信息
LABEL author = "Kushajveer Singh"
LABEL email = "kushajreal@gmail.com"
LABEL website = "kushajveersingh.github.io"
构建图像后,您可以获得标签列表,如下所示
揭露
EXPOSE
指令通知 Docker 容器在运行时监听指定的网络端口。它实际上并不发布端口。它只是作为构建映像的人和运行容器的人之间的一种文档,说明打算发布哪些端口。
EXPOSE 8889
EXPOSE 80/udp
EXPOSE 80/tcp
现在,运行容器的人可以使用-p
标志指定端口,如下所示
> DOCKER_BUILDKIT=1 docker build -t test .
> docker run -p 80:80/tcp -p 80:80/udp test
添加并复制
我们在开始时讨论过,在读取 Docker 文件之前,会将一个上下文传递给 Docker 守护进程。现在,要从上下文向图像添加文件,我们可以使用ADD
或COPY
。两个指令相似,但是ADD
做了一些额外的事情(你需要小心)。两个命令的语法是相同的
ADD [--chown=<user>:<group>] <src>... <dest>COPY [--chown=<user>:<group>] <src>... <dest>
一个使用示例是
COPY --chown=default:sudo /file/on/local/drive /path/on/imageCOPY --chown=default:sudo script.sh /home/default
<dest>
路径要么是绝对路径,要么是相对于WORKDIR
的路径,我们将在后面讨论。
现在让我们看看每条指令之间的区别。
复制
COPY [--chown=<user>:<group>] <src>... <dest>
COPY
指令从<src>
复制文件或目录,并将它们添加到位于<dest>
的容器文件系统中。除非您指定了--chown
,否则这些文件是用默认的 UID 和 GID 0 创建的。就是这样。COPY
会将一个文件从<src>
复制到<dest>
。
注意缺陷障碍 (Attention Deficit Disorder)
ADD [--chown=<user>:<group>] <src>... <dest>
ADD
指令也像COPY
一样将文件或目录从<src>
复制到<dest>
,但它也做一些额外的事情
<src>
可以是远程文件的 URL- 如果
<src>
是一个 tar 文件(identity,gzip,bzip2,xz ),那么它将被解压为一个目录。如果 tar 文件是一个远程 URL,那么它不会被解压缩
注意:- 由于
*ADD*
的这些额外特性,建议您使用*COPY*
,除非您知道*ADD*
到底在给图像添加什么。
工作方向
WORKDIR
指令为 docker 文件中跟随它的任何RUN
、CMD
、ENTRYPOINT
、COPY
、ADD
指令设置工作目录。您可以根据需要多次使用该选项来设置工作目录。
WORKDIR /home/default
RUN ...# You can also provide path relative to previous WORKDIR
WORKDIR ../home
奔跑
该命令的语法是
RUN <command>
它将在一个 shell 中运行该命令(在 Linux 上默认为/bin/sh -c
)。每个RUN
指令将创建一个新层,因此,您应该尝试将多个RUN
指令组合成一个逻辑组。
要对多个RUN
指令进行分组,可以使用分号或&&
。
最好使用&&
而不是;
。原因是当您使用分号将多个命令分组时,无论前一条指令是否出错,下一条指令都会运行。&&
的情况并非如此。如果一个命令失败,那么执行将停止,并且下一个命令将不会被执行。
这些都是我们创建 Docker 映像所需的说明。我留下了一些说明,因为我们不需要它们,但你可以查看完整的文档参考以了解关于这些命令的信息(如ENTRYPOINT
、CMD
、VOLUME
)。
所有命令的摘要
FROM
-每个 docker 文件都以这条指令开始,它提供了我们构建映像的基础映像ARG
——我们可以使用--build-arg
指定命令行参数,只在 docker 文件中使用ENV
-将在 Dockerfile 中使用并在容器运行时保持的环境变量LABEL
-指定元数据,如作者姓名,…EXPOSE
-记录集装箱运行时计划使用的端口COPY
-将文件或目录从上下文添加到图像(仅复制文件)ADD
-将文件或目录从上下文添加到图像(可以从远程 URL 复制,自动解压缩 tar 文件)WORKDIR
-指定使用 path 的其他指令的工作目录RUN
-从 shell 运行任何命令USER
-设置容器运行时的默认用户
PyTorch Dockerfile,Ubuntu
基础图像
我们将使用 Ubuntu 作为基础图片。如前所述,它为我们提供了一个基本的 Linux 发行版,我们必须设置我们需要的一切。
ARG UBUNTU_VERSION=18.04
FROM ubuntu:$UBUNTU_VERSION
更新 ubuntu 并安装实用程序
在讨论正在发生的事情之前,让我们先检查一下文档
第一步是更新 Ubuntu。注意 Ubuntu 上的默认用户是root
。之后,我们设置sudo
和一个default
用户,然后我们将不得不在这些指令后面加上sudo
。这是通过使用
--fix-missing
是可选的。它在依赖关系破裂的情况下使用,在大多数情况下,使用此标志可以帮助我们解决问题。因为我们是从全新安装开始的,所以这个标志没有什么作用。
apt install -y --no-install-recommends
。-y
标志帮助我们绕过是/否提示。Ubuntu 中的每个包都有三个依赖项
- 主要依赖关系
- 推荐的软件包
- 建议的套餐
默认情况下,Ubuntu 将安装主软件包和推荐软件包(要安装推荐软件包,您需要向apt install
提供--install-suggests
标志)。我们的主要目标是保持 docker 映像的大小最小,由于这个原因,我们不想浪费空间安装推荐的包。--no-install-recommends
标志会这样做,因此我们只安装主要的依赖项。
现在你可以安装你可能需要的任何其他包,比如ca-certificates
(需要curl
)、sudo
、curl
、git
。
第二步是清理不再需要的包,并清除所有本地缓存。这是通过使用
RUN apt clean && \
rm -rf /var/lib/apt/lists/*
当我们安装包时,Ubuntu 在/var/cache
中维护了一个包的缓存。这样做的原因是,如果升级时出现问题,我们无法访问网络连接,那么我们可以恢复到缓存中的旧版本来降级软件包。但是我们不需要 Docker 图像的缓存,所以我们可以使用apt clean
删除它。具体来说,apt clean
将删除/var/cache/apt/archives/
和/var/cache/apt/archives/partial
中的文件,留下一个lock
文件和partial
子目录。
/var/lib/apt
存储与 apt 包管理器相关的数据。我们每次运行apt update
时都会自动下载这些数据,因此没有必要存储这些数据,我们可以安全地删除这些数据,以使用rm -rf /var/lib/apt/lists/*
减小图像大小。
注意:- 要在
*rm -rf /var/lib/apt/lists/**
之后安装一个软件包,你必须先运行*apt update*
,然后才可以安装你需要的软件包。
设置 sudo 和默认用户
下一步是设置 root 帐户和默认用户。这样做的 docker 文件的内容如下所示
让我们看看在useradd
每面旗都做了什么
-r
用于创建系统帐户,即操作系统在安装过程中创建的帐户或 root 帐户-m
用于创建一个主目录,如果它不存在的话-d /home/default
用于提供主目录的位置-s /bin/bash
用于指定用户登录 shell 的名称。如果你愿意,可以跳过这个。-g root -G sudo
这个有意思。-g
标志用于指定用户所属的组,而-G
用于提供用户所属的其他组的列表。
默认情况下,root
用户不是组sudo
的成员,我们需要显式设置这一点。root
拥有系统上的所有权限,但我们仍然需要一个sudo
组。当用户属于sudo
组时,这意味着用户可以使用他们的用户密码来执行sudo
命令。
当我们在一个系统上有多个用户时,拥有一个sudo
组是很有用的,每个用户都可以通过使用他们自己的用户密码获得root
特权。
-u 1000
用于提供用户 ID$USERNAME
是将要创建的用户的名称
默认情况下,在 Ubuntu 中,root 帐户没有设置密码。要设置密码,我们可以使用以下命令
echo "${USERNAME}:${PASSWORD}" | chpasswd
在完成上述步骤后,我们完成了以下工作
- 增加了一个用户
default
- 用户
default
可以使用sudo su
并使用default
作为密码获得 root 权限
这里的讨论了sudo
的一个错误,每次你试图用sudo
执行一个命令时,你都会得到下面的警告信息
> sudo hello > /dev/null
sudo: setrlimit(RLIMIT_CORE): Operation not permitted
这个问题已经在最新的补丁中解决了,但是 Ubuntu 没有附带,所以要停止这个烦人的警告,你可以使用下面的命令
echo "Set disable_coredump false" >> /etc/sudo.conf
当你运行这个容器时,你会看到一个关于sudo
的提示,如下所示
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
当你第一次用sudo
运行一个命令时,这个消息将被删除,或者你可以通过添加~/.sudo_as_admin_successful
文件来删除它。
touch /home/$USERNAME/.sudo_as_admin_successful
就这样,您已经设置了sudo
和一个default
用户。当你运行容器时,默认情况下你会是root
。但是您可以使用下面的命令将默认用户设置为default
USER $USERNAME
WORKDIR /home/$USERNAME
到目前为止的 Dockerfile 文件摘要
我们的 docker 文件将包含以下内容
我们可以将所有更新 Ubuntu、设置sudo
和默认用户的命令放在一个RUN
命令中以节省空间。
访问 Nvidia GPU
要访问 Docker 容器中的主机 GPU,可以在运行容器时指定--gpus
标志
> docker run --gpus all -it --name temp test:0.1> docker run --gpus 0,2 -it --name temp test:0.1
这只需要您在主机上安装 Nvidia 驱动程序,然后您可以使用 Docker 容器中的nvidia-smi
来检查是否正在检测 GPU。
安装 Miniconda
Miniconda 可以用于一个简单的 python 设置。只需从文档页面(在本例中是 Python 3.9)获取最新的 Linux 安装程序的链接,并指定要安装 Miniconda 的位置
Miniconda 的设置非常简单
- 设置
MINICONDA_DOWNLOAD_LINK
和MINICONDA_INSTALL_PATH
变量 - 设置环境变量
ENV PATH ${MINICONDA_INSTALL_PATH}/miniconda3/bin:$PATH
,这将允许我们从文件系统中的任何地方运行 conda - 使用
bash Miniconda.sh -b -p ./miniconda3
安装 miniconda - 使用
rm Miniconda.sh
移除.sh
文件以节省空间 conda init
可以跳过,因为我们已经设置了ENV PATH
- 使用
conda update -y --all
将 conda 软件包更新至最新版本
安装 PyTorch
现在我们准备安装 PyTorch。前往 PyTorch 安装页面获取您想要用来安装 PyTorch 的命令。
注意:- 由于某种原因
*numpy*
在我运行*conda install*
命令时没有被安装,所以我必须添加*pip install numpy*
命令。
移除 conda/pip 缓存
Conda 存储索引缓存、锁文件、tarballs 和未使用包的缓存。使用命令conda clean -afy
可以安全移除这些以节省空间。所以把这个作为 Dockerfile 中的最后一个 conda 命令添加进去。
pip
将缓存存储在~/.cache/pip
中,该文件夹可以使用命令rm -rf ~/.cache/pip
安全移除。
建立形象
现在我们已经准备好构建映像了。
> DOCKER_BUILDKIT=1 docker build -t test:0.1 .
然后我们可以运行一个容器来测试图像
> docker run --gpus all -it --name temp test:0.1
default@a7f862b6bf73:~$
我们可以尝试nvidia-smi
来检查是否正在检测 GPU
正如我们所看到的,GPU 和主机的 Nvidia 驱动程序一起被检测到。接下来,我们可以尝试运行一些 PyTorch 命令
我们已经成功地安装了 PyTorch,并且拥有了一个正常工作的 Ubuntu 环境。下一节讨论构建映像、运行容器和将映像推送到dockerhub
所需的各种docker
命令。
有用的 Docker 命令
构建图像
我们已经在第一部分讨论了从 Dockerfile 构建图像的命令。
此处可访问docker build
的参考。
列出所有图像
docker image ls
可用于获取本地文件系统上所有图像的列表。
您可以使用这个命令来检查图像的SIZE
并获取IMAGE ID
,以防您忘记标记图像。Docker 将图像存储在 Linux 上的/var/lib/docker/
中,但是弄乱这个文件夹的内容并不是一个好主意,因为 Docker 的存储很复杂,并且它取决于正在使用的存储驱动。
docker image ls
的参考值可在处访问。
删除图像
通过指定标签或IMAGE ID
,可以使用docker image rm
删除图像。
docker image rm
的参考值可在处访问。
如果您有许多未标记的图像,您可以使用docker image prune
删除所有图像,而不是手动删除每一张图像。
docker image prune
的参考可在处访问。
列出所有容器
docker container ls -a
可用于列出所有容器(运行和停止)。如果您想只列出正在运行的容器,请使用docker container ls
。
我们可以得到所有集装箱的状态。在上述示例中,temp_1
未运行,而temp_2
正在运行。我们还可以看到容器没有使用任何PORTS
。
docker container ls
的参考可在处访问。
启动容器
docker start
可用于启动一个或多个停止的容器(docker container start
也可用于此)。
> docker start temp_1
temp_1
docker start
的参考可在处访问。
附加到容器
要打开一个新的终端会话,可以使用 docker 容器docker attach
。
> docker attach temp_1
此处可访问docker attach
的参考值。
停止集装箱
docker stop
或docker kill
可用于停止多个运行中的集装箱。docker stop
可以认为是优雅的一站。这两个命令的区别在于
docker stop
。停止正在运行的容器。主进程将接收到SIGTERM
,在一段宽限期后,将接收到SIGKILL
。docker kill
。杀死一个正在运行的容器。主进程将接收SIGKILL
或用户使用--signal
指定的任何信号
> docker stop temp_1
> docker kill temp_1
此处可访问docker stop
的参考,此处可访问docker kill
的参考。
删除容器
docker rm
可用于删除停止的集装箱。
> docker rm temp_1
在某些情况下,您可能需要删除一个不存在的容器,同时不抛出错误。这可以按如下方式完成
> docker rm temp_1 || true
docker rm
的参考可在处访问。
运行容器
docker run
命令用于从图像中创建一个容器。使用该命令时,有许多有用的标志
> docker run -it --gpus all --name temp -p 8888:8888 test:0.1
-it
将打开一个与容器连接的终端--gpus all
用于指定哪些 GPU 可以访问容器--name temp
容器的名称-p 8888:8888
发布端口。格式为{HOST_PORT}:{CONTAINER_PORT}
。要在 docker 中使用 jupyter 笔记本,您需要发布一个端口。
此处可访问docker run
的参考。
将新码头连接到集装箱
如果您想要将一个新的终端连接到一个正在运行的容器,那么您可以使用下面的命令
> docker exec -it {container_name} bash
docker attach {container_name}
无法创建新的终端会话。它将连接到旧会话。
删除所有容器/图像
要删除系统上的所有 docker 容器和映像,您可以使用以下两个命令(按照指定的顺序)
# Remove all containers
> docker rm -vf $(docker ps -a -q)# Remove all images
> docker rmi -f $(docker images -a -q)
将图像推送到 Dockerhub
使用以下命令将图像推送到 Dockerhub
下面显示了一个工作示例
第一个参数(test:0.1
)到docker tag
是本地文件系统上映像的名称,第二个参数是 Dockerhub 上您想要将映像推到的位置。
上述命令的参考资料
链接
- kushayveersingh/Dockerfile—您可以从这个存储库中访问 docker file 来构建您自己的映像。在自述文件中提供了所有可用 docker 文件的文档。
- 所有的图片都可以在这个链接中找到。如果你想使用一个图像而不是图像的内容,那么你可以从上面的 Github 链接访问用于创建图像的 Dockerfile 并构建图像。
原载于 2021 年 10 月 3 日https://kushajveersingh . github . io。
关于如何在机器学习项目中使用 Hydra 的完整教程
学习你需要知道的关于如何在你的机器学习项目中使用 Hydra 的一切。Hydra 的所有特性都将通过一个虚拟 ML 示例进行讨论。
为了提高 PyTorch 生态系统的标准化,脸书·艾在最近的博客文章中表示,他们将利用脸书的开源 Hydra 框架来处理配置,并提供与 PyTorch Lightning 的集成。这个帖子是关于九头蛇的。
如果你正在阅读这篇文章,那么我假设你熟悉什么是配置文件,为什么它们有用,以及它们如何增加可再现性。而且你也知道什么是噩梦 argparse 。一般来说,通过配置文件,你可以将所有的超参数传递给你的模型,你可以定义所有的全局常量,定义数据集分割,等等,而不需要接触你的项目的核心代码。
在九头蛇网站上,以下列出了九头蛇的主要特征:
- 可从多个来源组合的分层配置
- 可以从命令行指定或覆盖配置
- 动态命令行制表符结束
- 在本地运行您的应用程序,或者启动它以远程运行
- 用一个命令运行具有不同参数的多个作业
在这篇文章的其余部分,我将通过一个用例的例子来逐一介绍 Hydra 的特性。所以跟着走吧,这将是一次有趣的旅程。
了解 Hydra 设置流程
安装 Hydra(我用的是版本1.0
)
pip install hydra-core --upgrade
对于这篇博文,我将假设下面的目录结构,其中所有的配置都存储在一个config
文件夹中,主配置文件被命名为config.yaml
。为了简单起见,假设main.py
是我们项目的所有源代码。
src
├── config
│ └── config.yaml
└── main.py
让我们从一个简单的例子开始,它将向您展示使用 Hydra 的主要语法,
*### config/config.yaml*
batch_size: 10
lr: 1e-4
以及相应的main.py
文件
*### main.py*
import hydra
from omegaconf import DictConfig
**@**hydra.main(config_path**=**"config", config_name**=**"config")
**def** **func**(cfg: DictConfig):
working_dir **=** os.getcwd()
**print**(f"The current working directory is {working_dir}")
*# To access elements of the config
* **print**(f"The batch size is {cfg.batch_size}")
**print**(f"The learning rate is {cfg['lr']}")
**if** __name__ **==** "__main__":
func()
运行该脚本将得到以下输出
> python main.py
The current working directory is src/outputs/2021-03-13/16-22-21
The batch size is 10
The learning rate is 0.0001
注 :路径被缩短,不包括从根开始的完整路径。同样,您可以将
*config.yaml*
或*config*
传递给*config_name*
。
发生了很多,我们一个一个解析吧。
omegaconf
默认安装有hydra
。它仅用于为func
中的cfg
参数提供类型注释。@hydra.main(config_path="config", config_name="config")
这是当任何函数需要配置文件中的内容时使用的主装饰函数。- 当前工作目录被更改。
main.py
存在于src/main.py
中,但输出显示当前工作目录是src/outputs/2021-03-13/16-22-21
。这是使用九头蛇时最重要的一点。解释如下。
九头蛇如何处理不同的运行
每当使用python main.py
执行一个程序时,Hydra 将在outputs
目录中创建一个新文件夹,其命名方案如下outputs/YYYY-mm-dd/HH-MM-SS
,即文件执行的日期和时间。想一想这个。Hydra 为您提供了一种维护每次运行日志的方法,而您不必为此担心。
执行python main.py
后的目录结构是(现在先不要担心每个文件夹的内容)
src
├── config
│ └── config.yaml
├── main.py
├── outputs
│ └── 2021-03-13
│ └── 17-14-24
│ ├── .hydra
│ │ ├── config.yaml
│ │ ├── hydra.yaml
│ │ └── overrides.yaml
│ ├── main.log
实际上发生了什么?当你运行src/main.py
时,hydra 将这个文件移动到src/outputs/2021-03-13/16-22-21/main.py
然后运行它。您可以通过检查os.getcwd()
的输出来验证这一点,如上例所示。这意味着如果你的main.py
依赖于某个外部文件,比如说test.txt
,那么你将不得不使用../../../test.txt
来代替,因为你不再运行src
目录中的程序。这也意味着您保存到磁盘的所有内容都是相对于src/outputs/2021-03-13/16-22-21/
保存的。
Hydra 提供了两个实用函数来处理这种情况
- hydra . utils . Get _ original _ CWD():获取原当前工作目录,即
src
。
orig_cwd **=** hydra.utils.get_original_cwd()
path **=** f"{orig_cwd}/test.txt"*# path = src/test.txt*
- hydra . utils . to _ absolute _ path(文件名):
path **=** hydra.utils.to_absolute_path('test.txt')
*# path = src/test.txt*
让我们用一个简短的例子来概括一下。假设我们想读取src/test.txt
并将输出写入output.txt
。完成此操作的相应函数如下所示
**@**hydra.main(config_path**=**"config", config_name**=**"config")
**def** **func**(cfg: DictConfig):
orig_cwd **=** hydra.utils.get_original_cwd()
*# Read file
* path **=** f"{orig_cwd}/test.txt"
**with** open(path, "r") **as** f:
**print**(f.read())
*# Write file
* path **=** f"output.txt"
**with** open(path, "w") **as** f:
f.write("This is a dog")
在运行python main.py
之后,我们可以再次检查目录结构。
src
├── config
│ └── config.yaml
├── main.py
├── outputs
│ └── 2021-03-13
│ └── 17-14-24
│ ├── .hydra
│ │ ├── config.yaml
│ │ ├── hydra.yaml
│ │ └── overrides.yaml
│ ├── main.log
│ └── output.txt
└── test.txt
文件被写入 hydra 创建的文件夹。当你在开发一些东西的时候,这是一个保存中间结果的好方法。您可以使用此功能保存具有不同超参数的模型的精度结果。现在,您不必花费时间手动保存用于运行脚本的配置文件或命令行参数,也不必为每次运行创建一个新文件夹来存储输出。
注意 :每个
*python main.py*
都运行在一个新的文件夹中。为了保持上面的输出简短,我删除了之前运行的所有子文件夹。
主要的一点是使用orig_cwd = hydra.utils.get_original_cwd()
来获得原始的工作目录路径,这样你就不必担心 hydra 在不同的文件夹中运行你的代码。
每个子文件夹的内容
每个子文件夹都有以下子结构
src/outputs/2021-03-13/17-14-24/
├── .hydra
│ ├── config.yaml
│ ├── hydra.yaml
│ └── overrides.yaml
└── main.log
config.yaml
-传递给函数的配置文件的副本(如果您传递了foo.yaml
也没关系,该文件仍将被命名为config.yaml
)hydra.yaml
-hydra 配置文件的副本。我们稍后将看到如何更改 hydra 使用的一些默认设置。(您可以在此指定python main.py --help
的消息)overrides.yaml
-您通过命令行提供的任何参数的副本,它改变了一个缺省值,将被存储在这里main.log
-记录器的输出将存储在此处。(对于foo.py
,该文件将被命名为foo.log
)
如何使用日志记录
有了 Hydra,你可以在代码中轻松使用 Python 提供的日志包,无需任何设置。日志的输出存储在main.log
中。下面显示了一个使用示例
import logging
log **=** logging.getLogger(__name__)
**@**hydra.main(config_path**=**"config", config_name**=**"config")
**def** **main_func**(cfg: DictConfig):
log.debug("Debug level message")
log.info("Info level message")
log.warning("Warning level message")
在这种情况下,python main.py
的日志将会是(在main.log
中)
[2021-03-13 17:36:06,493][__main__][INFO] - Info level message
[2021-03-13 17:36:06,493][__main__][WARNING] - Warning level message
如果您想将DEBUG
也包括在内,那么覆盖hydra.verbose=true
或hydra.verbose=__main__
(即python main.py hydra.verbose=true
)。在这种情况下,main.log
的输出将是
[2021-03-13 17:36:38,425][__main__][DEBUG] - Debug level message
[2021-03-13 17:36:38,425][__main__][INFO] - Info level message
[2021-03-13 17:36:38,425][__main__][WARNING] - Warning level message
OmegaConf 快速概述
OmegaCong 是一个基于 YAML 的分层配置系统,支持合并来自多个来源(文件、CLI 参数、环境变量)的配置。你只需要知道 YAML 就能使用九头蛇。OmegaConf 是九头蛇在后台用的,帮你处理一切。
您需要知道的主要内容显示在下面的配置文件中
server:
ip: "127.0.0.1"
port: ??? *# Missing value. Must be provided at command line*
address: "${server.ip}:${server.port}" *# String interpolation*
现在在main.py
中,您可以如下访问服务器地址
**@**hydra.main(config_path**=**"config", config_name**=**"config")
**def** **main_func**(cfg: DictConfig):
server_address **=** cfg.server.address
**print**(f"The server address = {server_address}")
*# python main.py server.port=10
# The server address = 127.0.0.1:10*
从上面的例子中你可以猜到,如果你想让某个变量和另一个变量取相同的值,你应该使用下面的语法address:${server.ip}
。我们稍后将看到一些有趣的用例。
在 ML 项目中使用 Hydra
现在你知道了 hydra 的基本工作原理,我们可以专注于使用 Hydra 开发一个机器学习项目。查看这篇文章之后的 hydra 文档,了解这里没有讨论的一些内容。我也不会在这篇文章中讨论 结构化配置(YAML 文件的替代文件),因为没有它们你也可以完成所有的事情。
回想一下,我们项目的src
目录有如下结构
src
├── config
│ └── config.yaml
└── main.py
我们有一个单独的文件夹来存储我们所有的配置文件(config
),我们项目的源代码是main.py
。现在让我们开始吧。
资料组
每个 ML 项目都是从收集数据和创建数据集开始的。在进行影像分类项目时,我们会使用许多不同的数据集,如 ImageNet、CIFAR10 等。每个数据集都有不同的相关超参数,如批次大小、输入影像的大小、类的数量、用于特定数据集的模型的层数等等。
我没有使用一个特定的数据集,而是使用一个随机的数据集,因为它可以使事情变得通用,你可以将这里讨论的东西应用到你自己的数据集上。此外,我们不要担心创建数据加载器,因为它们是一回事。
在讨论细节之前,让我向您展示代码,您可以很容易地猜到发生了什么。本例中涉及的 4 个文件是
src/main.py
src/config/config.yaml
src/config/dataset/dataset1.yaml
src/config/dataset/dataset2.yaml
*### src/main.py ###*
import torch
import hydra
from omegaconf import DictConfig
**@**hydra.main(config_path**=**"config", config_name**=**"config.yaml")
**def** **get_dataset**(cfg: DictConfig):
name_of_dataset **=** cfg.dataset.name
num_samples **=** cfg.num_samples
**if** name_of_dataset **==** "dataset1":
feature_size **=** cfg.dataset.feature_size
x **=** torch.randn(num_samples, feature_size)
**print**(x.shape)
**return** x
**elif** name_of_dataset **==** "dataset2":
dim1 **=** cfg.dataset.dim1
dim2 **=** cfg.dataset.dim2
x **=** torch.randn(num_samples, dim1, dim2)
**print**(x.shape)
**return** x
**else**:
**raise** ValueError("You outplayed the developer")
**if** __name__ **==** "__main__":
get_dataset()
相应的配置文件是,
*### src/config/config.yaml*
defaults:
- dataset: dataset1
num_samples: 2 *### src/config/dataset/dataset1.yaml*
*# @package _group_*
name: dataset1
feature_size: 5 *### src/config/dataset/dataset1.yaml*
*# @package _group_*
name: dataset2
dim1: 10
dim2: 20
老实说,这几乎是你在项目中使用 hydra 所需要的一切。让我们看看上面的代码中实际发生了什么
- 在
src/main.py
中,您将看到一些通用变量,即cfg.dataset
和cfg.num_samples
,它们在所有数据集之间共享。这些是在主配置文件中定义的,我们使用命令@hydra.main(...)
传递给 hydra。 - 接下来,我们需要定义一些特定于每个数据集的变量(比如 ImageNet 和 CIFAR10 中的类的数量)。为了在 hydra 中实现这一点,我们使用以下语法
defaults:
- dataset: dataset1
- 这里的
dataset
是文件夹的名称,该文件夹将包含每个数据集的所有对应的 yaml 文件(即本例中的dataset1
和dataset2
)。所以目录结构看起来像这样
config
├── config.yaml
└── dataset
├── dataset1.yaml
└── dataset2.yaml
- 就是这样。现在,您可以在上述每个文件中定义特定于每个数据集的变量,这些变量彼此独立。
- 这些被称为配置组。每个配置文件都独立于文件夹中的其他配置文件,我们只能选择其中一个配置文件。为了定义这些配置组,您需要在每个文件
# @package _group_
的开头包含一个特殊的注释。
我们只能从
*dataset1.yaml*
**dataset2.yaml*
中选择一个配置文件作为*dataset*
的值。为了告诉 hydra 这些是配置组,我们需要在这些文件的开头加上特殊的注释*# @package _group_*
。*注 :在九头蛇 1.1 中,
*_group_*
将成为默认的*package*
,无需添加特殊注释。
- 什么是默认值?在我们的主配置文件中,我们需要某种方法来区分普通的字符串值和配置组值。就像在这种情况下,我们希望将
dataset: dataset1
解释为一个配置组值,而不是一个字符串值。为此,我们在defaults
中定义了所有的配置组。正如您所猜测的,您为它提供了一个默认值。
注意 :
*defaults*
需要一个列表作为输入,所以需要每个名字都以一个*-*
开头。
*defaults:
- dataset: dataset1 *# By default use `dataset/dataset1.yaml*
*## OR*
defaults:
- dataset: ??? *# Must be specified at command line**
我们可以检查上面代码的输出。
*> python dataset.py
torch.Size([2, 5])*
和
*> python dataset.py dataset=dataset2
torch.Size([2, 10, 20])*
现在停下来想一想。您可以使用同样的技术为所有优化器定义超参数值。只需创建一个名为optimizer
的新文件夹,并写入sgd.yaml
、adam.yaml
文件。而在主config.yaml
中,你只需要多加一行
*defaults:
- dataset: dataset1
- optimizer: adam*
您还可以使用它来创建学习率调度器、模型、评估指标和几乎所有其他东西的配置文件,而不必在主代码库中实际硬编码任何这些值。您不再需要记住用于运行该模型的学习率,因为用于运行 python 脚本的配置文件的备份始终存储在 hydra 创建的文件夹中。
模型
有一种特殊情况你也需要知道。当使用 ImageNet vs CIFAR10 时,如果您希望您的 ResNet 模型具有不同的层数,该怎么办?天真的解决方案是在每个数据集的模型定义中添加if-else
条件,但这是一个糟糕的选择。如果明天你添加一个新的数据集。现在您必须修改您的模型if-else
条件来处理这个新的数据集。因此,我们在配置文件中定义一个值num_layers
,然后我们可以使用这个值来创建我们想要的层数。
假设我们使用两个模型,resnet 和 vgg。根据上一主题中的讨论,我们将为每个模型创建一个单独的配置文件。config
文件夹的目录结构应该是
*config
├── config.yaml
├── dataset
│ ├── cifar10.yaml
│ └── imagenet.yaml
└── model
├── resnet.yaml
└── vgg.yaml*
现在,假设我们希望 resnet 模型在使用 CIFAR10 时有 34 个层,而其他数据集有 50 个层。在这种情况下,config/model/resnet.yaml
文件应该是
**# @package _group_*
name: resnet
num_layers: 50 *# As 50 is the default value**
现在,我们希望在用户指定 CIFAR10 数据集时设置值num_layers=34
。为此,我们可以定义一个新的配置组,在其中我们可以定义所有特殊情况的组合。在主要的config/config.yaml
中,我们将进行以下更改
*defaults:
- dataset: imagenet
- model: resnet
- dataset_model: ${defaults.0.dataset}_${defaults.1.model}
optional: true*
这里我们创建了一个名为dataset_model
的新配置组,它采用由dataset
和model
指定的值(就像imagenet_resnet
、cifar10_resnet
)。这是一个奇怪的语法,因为defaults
是一个列表,所以你需要在名字前指定索引,例如defaults.0.dataset
。现在我们可以在dataset_model/cifar10_resnet.py
中定义配置文件
**# @package _global_*
model:
num_layers: 5*
注 :这里我们用
*# @package _global_*
代替*# @package _group_*
。
我们可以如下测试代码,这里我们简单地打印出配置文件返回的特性数量
***@**hydra.main(config_path**=**"config", config_name**=**"config")
**def** **main_func**(cfg: DictConfig):
**print**(f"Num features = {cfg.model.num_layers}")> python main.py dataset=imagenet
Num features = 50> python main.py dataset=cifar10
Num features = 34*
我们必须指定optional: true
,因为如果没有它,我们将需要指定dataset
和model
的所有组合(如果用户输入值dataset
和model
,这样我们就没有该选项的配置文件,那么 Hydra 将抛出一个缺少配置文件的错误)。
本主题的文档。
流程的其余部分是相同的,为优化器、学习率调度程序、回调、评估指标、损失、培训脚本创建单独的配置组。就创建配置文件并在项目中使用它们而言,这是您需要知道的全部内容。
随机的事情
显示配置文件
在不运行函数的情况下,打印传递给函数的配置文件。用法--cfg [OPTION]
有效OPTION
有
job
:你的配置文件hydra
:九头蛇的配置all
:job
+hydra
当您想要检查传递给函数的内容时,这对于快速调试非常有用。例子,
*> python main.py --cfg job
# @package _global_
num_samples: 2
dataset:
name: dataset1
feature_size: 5*
多次运行
这是九头蛇的一个非常有用的特性。查看文档了解更多详情。主要思想是你可以使用一个命令运行不同学习率值和不同权重衰减值的模型。下面显示了一个示例
*❯ python main.py lr**=**1e-3,1e-2 wd**=**1e-4,1e-2 **-**m
[2021**-**03**-**15 04:18:57,882][HYDRA] Launching 4 jobs locally
[2021**-**03**-**15 04:18:57,882][HYDRA] *#0 : lr=0.001 wd=0.0001* [2021**-**03**-**15 04:18:58,016][HYDRA] *#1 : lr=0.001 wd=0.01* [2021**-**03**-**15 04:18:58,149][HYDRA] *#2 : lr=0.01 wd=0.0001* [2021**-**03**-**15 04:18:58,275][HYDRA] *#3 : lr=0.01 wd=0.01**
Hydra 将使用lr
和wd
的所有组合运行您的脚本。输出将存储在一个名为multirun
(而不是outputs
)的新文件夹中。该文件夹也遵循将内容存储在日期和时间子文件夹中的相同语法。运行上述命令后的目录结构如下所示
*multirun
└── 2021-03-15
└── 04-21-32
├── 0
│ ├── .hydra
│ └── main.log
├── 1
│ ├── .hydra
│ └── main.log
├── 2
│ ├── .hydra
│ └── main.log
├── 3
│ ├── .hydra
│ └── main.log
└── multirun.yaml*
它和outputs
一样,除了这里为运行创建了四个文件夹,而不是一个。您可以查看文档,了解指定运行脚本的变量值的不同方式(这些被称为扫描*)。*
此外,这将在本地按顺序运行您的脚本。如果您想在多个节点上并行运行您的脚本或者在 AWS 上运行它,您可以查看以下插件的文档
给终端添加颜色
你可以通过安装这个插件来增加 Hydra 终端输出的颜色
*pip install hydra_colorlog **--**upgrade*
然后在配置文件中更改这些默认值
*defaults:
- hydra/job_logging: colorlog
- hydra/hydra_logging: colorlog*
指定帮助消息
您可以查看您的一次运行的日志(在.hydra/hydra.yaml
下,然后转到help.template
)来查看 hydra 打印的默认帮助信息。但是您可以在主配置文件中修改该消息,如下所示
**### config.yaml*hydra:
help:
template:
'This is the help message'> python main.py --help
This is the help message*
输出目录名
如果您想要更具体的东西,而不是 hydra 用来存储所有运行输出的日期/时间命名方案,您可以在命令行指定文件夹名称
*python main.py hydra.run.dir=outputs/my_runORpython main.py lr=1e-2,1e-3 hydra.sweep.dir=multirun/my_run -m*
今天就到这里。希望这有助于您在项目中使用 Hydra。
原载于 2021 年 3 月 16 日https://kushajveersingh . github . io*。*
2021 年学习数据科学的完全免费路线图
简明的资源选择
德尔菲·德拉鲁阿在 Unsplash 上拍摄的照片
近年来,数据科学经历了辉煌的发展。越来越多的企业试图找到在运营中实施数据科学的方法。因此,对数据科学家的需求不断增加,这导致许多人转行从事这一领域的工作。
数据科学是一个不断发展的领域,它在传统教育体系中尚未得到很好的确立。因此,有抱负的数据科学家使用其他资源进行学习,如 MOOC 课程、博客、在线教程、youtube 频道等。
然而,这些资源实在太多了。人们甚至会在丰富的选择中感到迷茫。在本文中,我将提供一个路线图,其中包括学习数据科学的资源的精选列表。
我不会给出一堆清单,比如要读的书或要关注的 youtube 频道。相反,我将尽可能具体地说明,防止您四处寻找从哪里开始。
什么是数据科学?
第一步是获得数据科学的高级概述。我们需要全面了解数据科学意味着什么,它旨在解决什么样的问题,以及它是如何做到的。
数据科学是一个跨学科领域,包括统计学、编程、数学。商业智能和领域知识也是重要的软技能。了解数据科学中每个构件的作用是非常重要的。
以下教程提供了数据科学的全面概述。这是相当长的,但你不必一次完成。其实还是分成几段,一段一段的完成比较好。
程序设计语言
数据科学生态系统中最受欢迎的两种语言是 Python 和 r。这两种语言都有大量的库和框架,可以加速和简化分配给数据科学家的典型任务。
Python 的范围更广,而 R 更侧重于统计计算。如果想学一门可以用在 web 开发等其他领域的编程语言,我建议学 Python。否则,对于数据科学相关的任务来说,两者都很好。
为了保持路线图简洁明了,我将选择其中一个,那就是 Python。我们的路线图包括 4 个学习 Python 的资源。
这是一门麻省理工学院的课程,录制并发布在 youtube 上。它包含 12 个讲座。
本课程为计算思维和编程提供了极好的介绍。一些基本的算法解释得很清楚,比如搜索和排序。面向对象编程的概念在本课程中也有很好的解释。
这是第一道菜的延续。它包含 15 个讲座。
前两节课主要讲最优化问题。优化是任何机器学习的核心,因为模型试图基于一些约束优化损失函数。有了像样的理论知识,对实践方面肯定也有帮助。
本课程更侧重于机器学习和数据科学相关主题。例如,分类和聚类解释得非常清楚。
该网站提供分类的短视频教程。目前有 59 个类别的 428 个视频。
视频中的例子结构良好,解释清晰。根据主要类别对视频进行分组可以逐步提高技能。
这是杰克·范德普拉斯写的免费电子书。这是一本非常实用的书,包含大量代码示例。
顾名思义,用 Python 专注于数据科学。这本书全面介绍了 Python 最常用的数据科学库。
[可选]:如果你也想了解 R 如何处理数据科学相关的任务,下面这本书是一个好的开始。
统计数字
数据科学的组成部分之一是统计学。如果没有足够的统计知识,理解或解释数据将会非常困难。
统计学帮助我们解释数据。我们使用统计学根据从总体中抽取的样本来推断总体的结果。此外,机器学习和统计学有很多重叠之处。
我们的路线图包括两个统计资源。第一篇是一篇冗长的博客文章,全面介绍了数据科学中的统计学。
第二个是非常受欢迎的 youtube 频道,名为 StatQuest,由 Josh Stamer 创建。我真正喜欢 StatQuest 的是它如何清晰简单地解释概念。你不必浏览所有的视频。这更像是当你很难理解一个主题时使用的参考指南。然而,看所有的视频确实很有帮助。
线性代数
数据以多种不同的格式收集,从数字到图像,从文本到声波。然而,我们需要将数据转换成数字,以便对其进行分析和建模。
仅仅将数据转换成标量(单个数字)是不够的。随着数据量的增加,用标量完成的操作开始变得低效。我们需要矢量化或矩阵运算来有效地进行计算。这就是线性代数发挥作用的地方。
我们很幸运能够免费上麻省理工学院 Gilbert Strang 教授的一堂优秀的线性代数课。
机器学习和深度学习
机器学习是数据科学的一个子领域。它允许计算机在没有明确编程的情况下从数据中学习。为了学习和实现机器学习,人们需要了解机器学习算法是如何工作的,以及用于实现它们的实用工具。
我分享两个机器学习的资源。第一个是谷歌准备的速成班。它提供了一个结构良好的机器学习概述。第二篇是我写的一篇博文,简要解释了 15 种流行的机器学习算法。
深度学习可以被认为是机器学习的一个子领域,其中算法是基于神经网络设计的。我建议,一旦你对机器学习算法和概念感到舒适,就开始致力于深度学习。
我分享两个深度学习的资源。第一本是由深度学习领域的先驱伊恩·古德菲勒、约舒阿·本吉奥和亚伦·库维尔写的书。伊恩·古德菲勒是生成性广告系列网络的创始人。
第二个是克里斯托弗·奥拉创建的博客。他非常清楚地解释了神经网络的内部工作机制。我强烈建议阅读他的文章,以便对神经网络有一个透彻的了解。
熟能生巧
练习提高自己的技能是极其重要的。除此之外,拥有一门理论知识还不足以创造数据科学产品。你需要能够将你的想法付诸实施。
练习的另一个好处是你可以学到比阅读或观看资源更多的东西。当你试图解决一个问题时,你可能会遇到你没有预料到的问题。这些问题将帮助你了解非常有价值的信息。
我把练习分成三个部分:
作业环境
这是练习的首要要求。我们需要一个交互式的环境来实现代码。你可以在你的电脑上安装一个 IDE,比如 VSCode,或者使用一个基于网络的 IDE,比如 Google Colab。我建议先用 Google Colab,因为不用任何安装就可以瞬间开始编码。
数据集
数据科学的燃料是数据。没有适当的数据,即使是最先进的模型也无法创造任何价值。因此,我们需要数据集来练习。我会列出两个可以免费下载数据集的资源。你可以很容易地在网上找到更多的数据集。
挑战自我
练习的最后但并非最不重要的部分是挑战自己。
Kaggle 举办了很多比赛。有些是初级水平,你可以在其中练习基本技能。也有付费比赛,极具挑战性。
HackerRank 和 LeetCode 是在线平台,你可以在这里解决问题以提高技能。这些问题结构合理,分成几个部分,这样你就可以练习一个特定的主题。这个平台也可以作为你求职时的参考。
最后的话
本文的目标是为您提供一个简明的资源列表,让您的数据科学知识达到一定的水平。我不认为这些资源会让你成为数据科学家。事实上,成为一名数据科学家需要更多的时间、努力和奉献。
数据科学是一个跨学科的领域,所以我试图以一种涵盖数据科学每个组成部分的方式来挑选资源。完成这些练习后,您将了解一名优秀的数据科学家应该具备的素质,以及成为一名高效的数据科学家需要具备哪些技能。
最后,我想列出数据科学生态系统中常用的 Python 库和框架。
- 数据分析和处理:NumPy,熊猫
- 数据可视化:Matplotlib,Seaborn,Altair
- 机器学习和深度学习:Scikit-learn、TensorFlow、Keras、PyTorch
感谢您的阅读。如果您有任何反馈,请告诉我。
构建量子计算控件
如何创建受控-受控-非门
本帖是本书的一部分: 动手用 Python 学习量子机器 。
作者弗兰克·齐克特的图片
量子变换门允许我们使用量子位。RY-gate 允许我们指定量子位状态向量角度θ,该角度控制测量量子位的概率为0
或1
。我们用它让一个量子位代表边际概率。
作者弗兰克·齐克特的图片
X 门(非门)切换量子位的概率振幅。我们用它来设置一个量子位值给特定状态下的1
。例如,在我们计算了边际概率之后,处理剩余的部分。
作者弗兰克·齐克特的图片
这是很有用的,因为当另一个量子位(控制量子位)处于|1⟩.状态时,一些门只对一个量子位(控制量子位)进行转换例如,受控的 RY-gate (CRY-gate)让我们指定边际剩余的联合概率和另一个概率。
作者弗兰克·齐克特的图片
呼叫器是一种复合门。在这篇文章中,我们学习了如何从更多的基本门创建这个门。首先,我们使用 CNOT 门。
乍一看,如果一个量子位是|1⟩,对另一个量子位应用 X-gate 的能力似乎并不重要。但是 X-gate 在创造更高级别的量子比特时起着核心作用,因为它纠缠着两个量子比特。概念上,纠缠量子比特共享一个叠加态。实际上,CNOT 门是大多数复合量子变换门的组成部分。
下面的代码描述了 CRY-gate 的分解。
作者弗兰克·齐克特的图片
如果你想应用某个门,当且仅当另外两个量子比特处于|1⟩态时,会怎么样?你可能会反对AND
不是一个有效的量子位门。简单看一下真值表就知道AND
算子是不可逆的。如果它的输出是 false,就无法判断输入是什么。它可能是三种不同状态中的一种。
作者弗兰克·齐克特的图片
但是 CNOT 门提供了一条出路。请记住,当我们构造 CRY-gate 时,我们使用 CNOT 门在整个旋转的前半部分和后半部分的旋转过程中转换受控量子位的振幅。类似的模式允许我们创建一个受控门。这种门包含一种AND
关系,因为它有两个控制量子位,并且只有当两个控制量子位都处于|1⟩.状态时,它才改变受控量子位
下图描述了 CCNOT 门(一种受控-受控非门)的电路。
The CCNOT-gate is also known as the Toffoli-gate. The Toffoli-gate has a different algorithm than this one. The Toffoli-gate uses qubit phases. Phases are concept we cover later in this book. The implementation we presented here is not optimal but it provides a vivid explanation of the underlying concept.
作者弗兰克·齐克特的图片
下面的列表描述了这个 CCNOT 门序列的代码。我们定义了一个可重用的函数ccnot
(第 4 行)。它以量子位 q0 作为控制量子位的受控旋转开始(第 6 行)。它将受控量子位旋转约θ=π/2,这是我们之前定义的值(第 2 行)。
然后,我们有另一个受控旋转,其量子位与控制量子位(第 11 行)相同,被封装到 CNOT 门(第 10 行和第 12 行)中。需要注意的是,这种封装的 CRY-gate 以θ为参数。它表示反方向旋转。
最后,我们有另一个关于θ的受控旋转,量子位 q1 是控制量子位。
让我们一个接一个地检查电路。首先,我们定义我们的θ=π/2(第 2 行)。值π/2 表示旋转了圆周的四分之一。这是我们想要应用的整体旋转的一半。旋转半圈(π)会将振幅从|0⟩转换到|1⟩,反之亦然。
在第一步中,如果量子位 q1 通过一个 CRY(π/2)门(第 8 行)处于|1⟩状态,我们旋转受控量子位大约四分之一圈。
如果两个控制量子位都处于|1⟩状态,这个门的结果如下图所示。
作者弗兰克·齐克特的图片
按照我们的初始化,两个控制量子位(从右到左读取)都处于|1⟩状态。然后,有一半的时间(按照大约π/2 的旋转),受控量子位处于|1⟩.状态
接下来,我们应用一系列 CNOT 门,q0 是控制量子位,q1 是受控量子位。因为 q0 处于|1⟩,所以它将 q1 的状态从|1⟩更改为|0⟩.随后用 q1 作为控制量子位的受控旋转没有影响,因为 q1 现在处于|0⟩状态,并且如果控制量子位处于|1⟩.状态,则 CRY-gate 仅改变受控量子位下一个 CNOT 门恢复了第一个 CNOT 门的效果。因为控制量子位 q0 仍然处于|1⟩状态,它将 q1 的状态从|0⟩状态切换回|1⟩.状态
如果两个控制量子位都处于|1⟩状态,这三个门根本没有作用。
最后,我们用 q0 作为控制量子位,施加一个约π/2 的受控旋转。这使得受控量子位 q2 的状态从一半时间处于|1⟩状态变为一直处于|1⟩状态。它将量子位状态向量旋转了圆周的四分之一,总共旋转了半圈。如下图所示,绕圆旋转半周,|0⟩就变成了|1⟩。
作者弗兰克·齐克特的图片
让我们看看代码和结果,如果两个控制量子位都处于|1⟩.状态
作者弗兰克·齐克特的图片
我们看到量子位 q2 一直处于|1⟩状态。它完全从初始状态|0⟩.
如果其中一个控制量子位不在|1⟩态呢?假设量子位 q0 处于|0⟩.态
同样,第一个 CRY-gate 将受控量子位的量子位状态向量旋转π/2——四分之一圈——因为控制量子位 q1 处于|1⟩.状态
但是这一次,下面的 CNOT 门不起作用。因为量子位 q0 处于|0⟩状态,它不会将量子位 q1 的状态从|1⟩切换到|0⟩.因此,θ=,π/2 的下述 CRY-gate 生效。它恢复了第一个哭泣之门的效果。因为量子位 q0 处于|0⟩.状态,所以随后的非门和最后的安全门不起作用因此,我们只应用了前两个 CRY-gate,第二个恢复了第一个。让我们看看代码和结果。
作者弗兰克·齐克特的图片
我们看到整体状态没有改变。受控量子位仍处于|0⟩.状态
最后,让我们看看,如果只有控制量子位 q0 处于|1⟩态,而量子位 q1 不处于,会发生什么。然后,第一次呼喊没有效果地通过了。封装在 CNOT-gates 中的第二个 CRY-gate 的以下序列首先将量子位 q1 从|0⟩切换到|1⟩,然后应用受控量子位的大约θ=-π/2 的旋转,并将量子位 q1 从|1⟩切换回|0⟩.现在受控量子位已经反方向旋转了半圈。下图描述了迄今为止的结果。
作者弗兰克·齐克特的图片
一半时间,受控量子位处于|1⟩.状态由于概率是我们通过旋转改变的振幅的平方,我们在这里看不到负值。
最后,由于控制量子位 q0 处于|1⟩.状态,最后一个 CRY-gate 将控制量子位往回旋转θ结果又是原始状态,如下面的代码和结果所示。
作者弗兰克·齐克特的图片
结论
我们通过 CNOT 门和哭泣门的组合创造了一个受控非门。我们甚至可以通过 CNOT 门和里门进一步创作哭喊门。这有效地显示了 CNOT 门的重要性。这不仅是解释量子纠缠的最好例子,也是制造更多受控门的基础。或受控门。甚至是受控门。你可以继续这个序列,直到你用完量子位。
本帖是本书的一部分: 用 Python 动手做量子机器学习 。
在这里免费获得前三章。
组合人工智能:企业人工智能的未来
行业笔记
AI/ML 网格,集成数据操作、多操作、API 操作
摘要。在过去的几年里,企业对人工智能/人工智能服务的采用显著加快。然而,大多数 ML 模型仍然是以解决单一任务为目标而开发的,例如预测、分类。在这项工作中,我们将提出合成人工智能的新兴范式,也称为合成学习。组合人工智能设想无缝组合现有的人工智能/人工智能服务,以提供一个新的(复合)人工智能/人工智能服务,能够解决复杂的多领域用例。在企业环境中,这在开发和维护工作中实现了重用、敏捷性和效率。
这是我最近在 2021 年 3 月 31 日俄罗斯斯科尔科沃举行的国际 数据融合会议 上发表的一篇主题演讲的扩展文章。( youtube 录制)( ppt )
企业人工智能
如今,企业 AI/ML 用例无处不在。企业用例可以根据支持它们的三个核心 AI/ML 功能进行大致分类:自然语言处理(NLP)、计算机视觉/图像识别和预测分析(如下图所示)。
企业人工智能用例(图片由作者提供)
在过去的 2-3 年里,我们一直在努力在企业中实现各种 AI/ML 用例。然而,我们一直专注于构建最具性能的模型
大多数 AI/ML 模型仍然是以解决单一任务为目标开发的,例如预测、分类。
考虑到这一点,是时候超越模型精度,转向更全面的组合 AI 框架,实现企业中已部署 AI/ML 解决方案的最大重用。
合成人工智能场景
人工智能合成示例—维修服务(图片由作者提供)
考虑一家奢侈品供应商的在线维修服务。这项服务包括一个计算机视觉( CV )模型,给定客户上传的产品图片,该模型能够评估所需的维修。如果用户对报价满意,评估之后将进行订购聊天机器人对话,获取处理用户维修请求所需的其他详细信息,例如产品损坏详细信息、用户名、联系方式等。
人工智能合成示例—产品推荐服务(图片由作者提供)
未来,当企业在找模特开发一个产品推荐服务;考虑维修服务。维修服务收集的数据:用户拥有的产品状态(由 CV 评估模型收集)及其人口统计数据(由订购聊天机器人收集)-为推荐服务提供额外的训练数据。然而,在这种情况下,CV 评估应用程序、订购聊天机器人[1]的隐私政策,或管理分层生态系统的任何全球隐私政策(例如,FTC FIPs [2],要求用户数据仅用于特定目的,对此用户已提供明确的选择加入)可能会阻止他们的数据被合并,这样,他们就不能用于描述客户。
人工智能合成示例—制造缺陷检测(图片由作者提供)
现在让我们考虑另一个分层组合场景,其中企业进一步希望开发一个 CV 应用程序/模型来检测制造过程中的缺陷产品。维修服务可以提供帮助,因为它已经标记了受损产品的图像(提供给聊天机器人的产品损坏描述充当“标签”)。因此,它将标记的图像作为反馈环提供给 CV 模型,作为训练数据来改进其底层模型。
背景
组合性是指通过组合现有(组件)服务的功能来形成新(组合)服务的能力。现有的服务本身可能是组合的,从而导致分层组合。
这个概念不是新的,以前在不同的背景下研究过;最值得注意的是,Web 服务组合和安全协议的安全组合。
Web 服务组合
Web 服务遵循面向服务的计算(SOC)方法,将业务功能包装在自包含的服务中。组合服务主要有两种方法:动态和静态。在动态方法中,给定一个复杂的用户请求,系统根据运行时可用 Web 服务的能力提出一个满足请求的计划。在静态方法中,给定一组 Web 服务,组合服务是在设计时结合它们的功能手工定义的。
BPEL 是这种规范的一个很好的例子,它将现有的 Web 服务编排(或编排)到复杂的业务工作流中。在解决 Web 服务组合的发现[3]、监控[4]和可靠性[5]方面已经有了相当多的工作。
安全合成
安全合成协议的一个很好的例子是 Ran Canetti 的通用可合成(UC)框架[6]。
给定一个复杂的任务,基本思想是将该任务划分为多个更简单的子任务。然后,设计安全实现子任务的协议。UC 框架确保由(UC-secure)子协议组成的协议将继续保证在新颖、不可预测和敌对的执行环境中的安全性,即使其他协议同时运行。
ML 现有技术
集成学习
集成学习试图优化来自多个模型的预测,然而迎合了相同的问题。
集成学习(图片来自作者)
常用的集成学习技术包括:Bagging、Boosting 和 Stacking。基本思想是分割训练数据,在分割的数据集上建立模型(预测器、分类器),然后组合模型-在预测的情况下,这可能意味着平均预测值;在分类的情况下,这可能意味着选择具有最高精度的输出。
在这种情况下,“整体”指的是融合模型,但不一定是融合解决不同问题的模型——组合人工智能的目标。
联合学习
联合学习(FL) [7] **,也称为协作学习,或隐私保护机器学习,**使多个(不信任的)实体能够协作在它们的组合数据集上训练 ML 模型。
集成和联合学习的主要区别在于,这种情况下的训练数据属于不同的组织,这些组织可能不一定信任彼此来共享数据;但是仍然对开发具有与在组合数据集上训练的模型相当的精确度的全局模型感兴趣。
最近的工作考虑了基于 FL 的深度神经网络(DNNs)训练[8]。
深度神经网络的联邦训练(图片由作者提供)
- 所有节点同意相同的神经网络架构和任务来训练全局模型。协调器节点充当参数服务器,维护全局模型参数值的最新版本。
- 在每个时期,叶节点从它们的父节点下载全局模型参数,并且使用它们的本地数据集上的梯度下降的一些变体来本地更新它们;与协调器节点共享更新后的值。
- 协调器节点对从所有节点收集的参数值进行平均。
- 这种联合训练一直持续到全局模型收敛。
人工智能服务
在这种背景下,让我们回到绘图板,并尝试定义一个“人工智能服务”。
AI 服务:数据+模型+ API(图片作者提供)
在监督学习的非常原始的世界中,一个人工智能服务由(标记的)数据组成,用于训练一个模型,然后它被公开为一个 API 。
当然还有一个替代的部署管道,可以将一个经过训练的模型部署在一个边缘设备上,以离线方式执行[9]。然而,我们改天再讨论这个问题。
有趣的是,今天有重叠的方法,DataOps、MLOps 和 APIOps(或 API 管理)试图分别解决数据、模型和 API 的操作方面。
数据操作
DataOps 是一种自动化、面向流程的方法,由分析和数据团队使用,用于提高质量和缩短数据分析的周期时间—维基百科
在当今的大多数组织中,“数据湖”是执行不同数据转换步骤的分层平台:(免责声明:下面显示的工具绝不是对工具/供应商的认可;还有提供类似功能的替代工具,对其功能的详细比较超出了本文的范围。)
数据湖分层架构(图片由作者提供)
- 数据摄取:侧重于从不同的数据源(传统、专有、云和内部)获取和保存数据。
- 数据(预)处理:与清理、质量检查和提取/转换相关数据以进行集成相关。
- 数据访问:这一层使用户和应用程序可以分别以报告和 API 的形式使用经过处理的数据。
- 数据集成:是本次讨论中最有趣的一层。它显示了数据生态系统的成熟度,通过多种工具/框架从聚合数据中获得洞察力。数据集成方法的例子包括
(i)联合:数据保留在原来的源系统中。联邦查询引擎负责分割给定的查询,并将子查询委托给它们各自的源系统;然后合并不同源系统返回的结果。
(ii)数据集市:在这种情况下,数据被物理地移动,相关数据被复制、合并并存储为数据集市以备访问。
(iii)知识图:捕获不同数据源之间的语义关系,使得能够利用确定的图结构进行复杂的查询。
不幸的是,今天缺乏类似的用于人工智能服务的集成/融合工具——这是组合人工智能的一个关键要求。
数据治理
到目前为止,我们已经考虑了操作部分:数据操作、MLOps、APIOps。
答案是否在于建立一个治理框架?
如下图所示,数据治理是一种横向能力,包含许多与数据相关的方面,例如,数据目录、数据字典、主数据管理(MDM)、数据起源和沿袭跟踪等。
治理框架(图片由作者提供)
在这种情况下,一个有希望的框架是 FAIR [9],它已经开始在医疗保健研究中被广泛采用。
- F indable:数据对于人类和机器来说都应该容易找到,这意味着丰富的元数据和唯一/持久的标识符。
- 答可访问性:通过认证和授权条款,可以以可信的方式访问数据。
- 互操作性:用于知识表示的共享本体,确保数据可以与多个应用程序/工作流进行互操作,以进行分析、存储和处理。
- eusable:清晰的出处/沿袭规范和使用许可,这样数据可以在不同的环境中重用。
虽然从数据治理的角度来看,FAIR 非常有趣,但它如何在医疗保健研究之外被采用还有待观察。公平原则在指定数据血统和出处、最大化重用以及使用户能够决定哪些数据适合他们的目的方面提供了指导。软件/ ML 代码部分——不考虑数据如何转换。这导致了潜在的开放数据与开源软件框架的冲突。开放数据许可证(如 Creative Commons)与更成熟的开源软件(如 Apache、MIT [10])大相径庭。
很有可能我们会陷入另一场标准化/许可混乱,而不是一个以统一方式存储、访问和分析数据和模型(代码)的综合框架。
伦理人工智能——合成人工智能中的隐私问题
另一个值得注意的治理趋势是企业中的道德 AI 治理框架/委员会,其任务是确保 AI/ML 应用程序得到负责任的培训和部署,符合企业战略和政策。不同的政府组织和监管机构发布了各自的指导方针和政策,对术语的定义几乎没有达成一致,这使得这项工作变得复杂,例如,“公平”的定义有 20 多种。
伦理人工智能包含许多人工智能/人工智能原则,例如,可解释性、偏见/公平、问责制和隐私。在这篇文章中。我们关注隐私方面。关于其他方面的讨论,请参考[11]。
组合设置中的主要隐私问题是(训练的)模型可能仍然包含专有信息或泄露与基础训练数据集相关的见解[12]。
这是因为(在反向传播过程中)DNN 给定图层的梯度是使用该图层的特征值和下一图层的误差来计算的。例如,在顺序完全连接的层的情况下,
误差 E 相对于 toW_l 的梯度定义为:
也就是说,W_l 的梯度是来自下一层的误差和特征 h_l 的内积;以及梯度和特征之间的相关性。这就是 esp。如果权重矩阵中的某些权重对参与者数据集中的特定特征或值敏感(例如,语言预测模型中的特定单词[13]),则为 true。
在这种情况下,联邦贸易委员会最近的裁决[14]指出,当用户选择退出时,仅仅删除数据是不够的;组织还需要删除根据该数据训练的模型/算法。
在组合设置中实施这一点需要捕获直接或间接访问底层(受影响的)培训数据的(较高级别的)组合服务。
类似地,如前所述,隐私政策,例如 FTC FIPs [2],建议数据仅用于特定目的(用户已明确选择加入),并且不与其他数据集结合以揭示可用于描述用户的额外见解。
在组合设置中,这种数据聚合可能很难检测,因为(更高)级别的组合服务可以(通过中间服务)聚合属于不同服务的数据,而无需它们的明确批准。
结论
总之,我们在本文中介绍了组合人工智能的新兴范例。一旦我们有了复合 AI/ML 服务,由多个(组件)AI/ML 服务组成;我们考虑过
你如何获取潜在不同服务的部分信息,底层数据、模型、APIs 并以无缝的方式组合新的服务,照顾治理、隐私、血统和其他伦理/非功能方面——是组合 AI/ML 的全部挑战。
合成 AI/ML 摘要(图片由作者提供)
参考
- D.比斯瓦斯。隐私保护聊天机器人对话。在保护隐私的机器学习研讨会(PPML),2020 年,https://ppml-workshop.github.io/pdfs/Biswas.pdf
- R.盖尔曼。公平信息实践:基础历史—版本 2.20 。2021.doi: 10.2139/ssrn.2415020。
- D.比斯瓦斯。Web 服务发现和约束组合。2007 年经常资源:73-87。
- D.维德亚桑卡·比斯瓦斯。监控分层 Web 服务组合。TES 2005 年:98–112。
- D.维德亚桑卡·比斯瓦斯。受限可见性下分层 Web 服务组合的最优补偿。IEEE APSCC 2009:293–300。
- 兰·卡内蒂。2020.普遍可组合证券。J. ACM 67,5,第 28 条(2020 年 10 月)。
- D.比斯瓦斯。联邦学习——隐私保护机器学习。(中型)
- B.麦克马汉等人。艾尔。从分散数据进行深度网络的通信高效学习。AISTATS 2017:1273–1282。
- 公平行事。科学数据管理和监督的公平指导原则 ( 链接)
- D.比斯瓦斯。*在企业中管理开源软件。*中等,2020( 链接)
- D.比斯瓦斯。伦理人工智能:其对企业人工智能用例及治理的影响。在 2020 年 12 月举行的 Linux 基金会开放合规峰会上发表(中)
- Nasr,m .,Shokri,r .,& Houmansadr,A. (2019)。深度学习的综合隐私分析:针对集中式和联邦式学习的被动和主动白盒推理攻击。2019 年 IEEE 安全与隐私研讨会(SP),739–753。
- H.b .麦克马汉、e .摩尔、d .拉梅奇、s .汉普森和 B. A .阿卡斯。从分散数据中进行深度网络的通信高效学习, 2017,https://arxiv.org/abs/1602.05629
- 联邦贸易委员会。2021 年,加州公司解决了 FTC 指控其在照片存储应用中使用面部识别欺骗消费者的问题
利用人工智能的组合成像来改进乳腺癌检测
癌症研究与技术
我们使用双能 X 射线成像技术和神经网络来研究新的健康成分成像生物标志物
癌性和非癌性乳腺病变类型的成分热图。作者图片
摘要
- 本文是对标题为“双能量三室乳腺成像用于成分生物标记物以改善恶性病变检测”的文章的直接回应。
- 成分乳腺成像允许发现新的癌症生物标志物
- 成分信息与诊断相关,有助于改善癌症检测,从而减少不必要的活检数量。
当前乳腺癌成像技术
截至今天,(2021)乳腺癌是全球女性癌症死亡的主要原因。有几种治疗方案,如手术切除、化疗和放疗,但这只是解决方案的一部分。早期发现和识别癌症与治疗本身同样重要,如果不是更重要的话。
成像对降低癌症死亡率产生了巨大的影响。乳房 X 线照相术是一种基于 X 射线的成像技术,是乳腺癌的主要成像技术,它允许临床医生看到下面的组织和结构。在过去的十年中,数字乳房断层合成(DBT)已经成为护理的标准,这种成像技术产生三维(3D) X 射线图像,而标准乳房 X 线照相术提供 2D 图像。
作者图片
图 1: 计算机辅助检测(CAD)发现肿块(绿色)、钙化点(红色)和钙化簇(蓝色)。放射科医生发现的实际癌症病变是黄色的。
放射科医生检查从标准乳房 x 线照相术或 DBT 获得的图像,寻找可疑的病变和钙化。乳房组织中的钙化并不少见,有时预示着癌症或发展为癌症的风险。计算机辅助检测或 CAD 软件已经开发出来,并获得食品和药物管理局(FDA)的批准,以帮助临床医生筛查癌症图像。人工智能(AI)的最新进展导致了 CAD 的巨大进步,甚至基于 AI 的 CAD 软件的开发。
活检的问题
因此,随着乳腺成像技术的巨大进步,对抗癌症应该变得更容易了,对吗?简单的答案是肯定的,但有一个小问题。虽然成像确实有助于识别更多的癌症并降低死亡率,但仍然存在假阳性活检的问题。当一个病变在影像学上被认为可疑时,通常会进行活检。活组织检查是一种侵入性程序,从妇女的乳房中取出可疑病变的组织样本,在显微镜下观察。假阳性活检意味着病理学家在组织中没有发现任何癌症迹象,并且在成像上看到的病变是良性或非癌性的。从本质上讲,假阳性会导致不必要的侵入性活检,并可能对一个人的身心健康有害。这个问题的存在部分是因为成像变得更加灵敏,可以看到更多的病变。然而,特异性还没有提高到足够的程度,这意味着仍然很难区分癌性病变和非癌性病变。
什么是合成成像
三室乳房(3CB)成像是一种双能 X 射线技术,可生成成像乳房内不同组织类型的图像或地图。3CB 技术受双能 X 射线吸收法(DXA)的启发,双能 X 射线吸收法通常用于评估骨密度和身体成分。使用 3CB,可以快速连续拍摄两幅图像。一幅图像是低能量的,相当于标准的乳房 x 线照片,而另一幅图像是以较高的能量拍摄的。不同能量的 X 射线在穿过不同的组织时将具有不同的行为,并且高能量和低能量 X 射线图像之间的对比被用于导出特定的组织类型。3CB 成像技术有助于可视化和量化乳房中的脂质、水和蛋白质的量。
作者图片
图 2: 标准乳房 x 线照片,旁边是从双能三室乳房(3CB)成分成像获得的脂质、水分和蛋白质图。
应该注意的是,在 3CB 中使用较高能量的 X 射线是安全的,辐射水平没有什么可担心的。事实上,3CB 成像只需要 2 张图像,而标准 DBT 成像需要大约 7 到 9 张图像,具体取决于系统。因此,3CB 技术产生的额外辐射水平与 DBT 图像序列相当,如果不是更低的话。为了进一步强调 3CB 的安全性,对比增强乳房 x 线照相术(CEM)使用几乎相同的成像协议来采集高能和低能图像。CEM 也获得了 FDA 的批准,总体上可能比 3CB 风险更大,因为在 CEM 使用的造影剂可能会引起过敏反应的不良反应。
作文能有什么帮助
众所周知,组成对于乳腺癌是重要的。乳房密度是一个强大的风险因素,密度往往是由于高纤维腺组织或蛋白质。其他人在细胞/分子水平上研究了乳腺癌病变,并发现浸润性病变类型倾向于消耗或代谢脂质。据推测,这种行为的机制是由于侵入性乳腺病变的侵略性生长性质。脂质或脂肪是一种能量来源,侵袭性肿瘤会消耗一切可以为其快速生长提供燃料的物质。结果,我们发现我们可以获得表征恶性病变类型的信号。该信号由与周围区域相比具有较低脂肪或脂质含量的癌性病变组成。
作者图片
图 3: 在距离病变 2、4 和 6 毫米处捕获脂质、水和蛋白质信号。橙色和蓝色虚线之间的空间显示了癌性和非癌性病变类型在组成上的显著差异。
人工智能的作用
为了证明我们的 3CB 成分成像的效用,我们使用人工智能来观察是否可以学习癌性和非癌性病变之间的成分差异。我们还想知道从 3CB 技术中获得的成分信息如何为现有的临床范例增加有用的诊断信息。我们使用 CAD 作为我们的基线,我们想看看具有 3CB 的 AI 是否能胜过 CAD。
作者图片
**图 4:**95%置信区间(CI)下 CAD 和 CAD+3CB 性能的受试者操作特征曲线(AUC)下面积。橙色线表示与单独的 CAD 相比,具有组合的 CAD 具有更好的检测性能,以蓝色显示。
*代码用于生成 AUC 与 CI 图详细 此处 或 此处
正如您从我们的区域的受试者操作特征曲线(AUC) 中看到的,AI+3CB+CAD 模型的表现优于单独的 CAD。这表明构图很重要,并提供了 CAD 无法单独捕捉的附加信息。换句话说,CAD 和放射科医生使用标准乳房 x 线照相术使用形状、纹理和形态测量信息来识别癌症。3CB 提供了相同的信息以及成分,这已被证明是有益的。综合辨别改进(IDI)图表明,由于假阳性的减少,性能的改进是显著的。如前所述,存在活检问题,使用 3CB 组合物减少假阳性可以减少不必要的活检并改善该问题。
作者图片
图 5: 综合判别改善(IDI)和净重新分类指数(NRI)显示 3CB 改善特异性。乳房成像报告和数据系统(BI-RADS)临界值旁边标有指标,供临床参考。
*代码用于生成 IDI 与 NRI 的剧情详细 此处 或 此处
3CB 的下一步是什么
我们正在继续我们对 3CB 的工作和研究,通过修改协议使其适用于 CEM 机器1r 01 ca 257652–01a 1。使用 FDA 批准的现有对比增强乳房 x 光机进行 3CB 成像将有助于加快该技术的采用。
参考
【1】Leong,l .、Malkov,s .、Drukker,k .、Niell,b .、Sadowski,p .、Wolfgruber,t .…&Shepherd,j .、双能量三隔室乳腺成像(3CB),用于改进恶性病变检测的新型成分生物标志物,(2021)。
【https://www.lambertleong.com】原载于 2021 年 11 月 14 日https://www.lambertleong.com/projects/compositional-breast-cancer-ai。
主成分分析综合指南
用 python 实现主成分分析的理论和实践部分
***Table of Contents* 1\. Introduction
2\. Principal Component Analysis (PCA)
3\. Theory
3.1\. Calculating PCA
3.1.1\. Rescaling (Standardization)
3.1.2\. Covariance Matrix
3.1.3\. Eigenvalues and Eigenvectors
3.1.4\. Sorting in Descent Order
3.2\. Is PCA one of the feature extraction&feature selection methods?
4\. Implementation
4.1\. Traditional Machine Learning Approaches
4.2\. Deep Learning Approaches
5\. PCA Types
5.1\. Kernel PCA
5.2\. Sparse PCA
5.3\. Randomized PCA
5.4\. Incremental PCA**
1。简介
本文涵盖了 PCA 的定义,没有 Sklearn 库的 PCA 理论部分的 Python 实现,PCA 与特征选择&特征提取的区别,机器学习&深度学习的实现,并举例说明了 PCA 的类型。
2.主成分分析
主成分分析是一种基于数学和统计学的非常有用的方法,它通过从不同角度对数据集进行评价来进行降维。它在机器学习中的任务是减少数据集中输入的维数,并通过算法或根据无监督方法中的特征对数据集进行分组来促进学习。这个降维过程是各种数学运算的结果。以坐标平面中具有两个特征的 2D 数据集(x,y)为例。当我们用主成分分析将数据集转换成 1D 时,数据集的分类变得容易得多。现在让我们用 PCA 实现并可视化降维:
癌症数据集(编码中定义为 cancer_data)由 596 个样本和 30 个特征组成。首先使用 StandardScaler 对这些数字特征进行缩放,然后使用 Sklearn 库导入的 PCA 方法对数据集进行二维处理,并对“恶性”和“良性”目标进行着色,如图 1 所示。X 轴代表 8 个分量中的第一个,y 轴代表 8 个分量中的第二个分量。
图一。第一主成分图和第二主成分图,作者图像
从图 1 中可以看出,在 PCA 过程之后,不使用任何算法就可以进行分类,这几乎是人眼可以预测的。然而,考虑到 30 个特征的数值数据集,这对于人类来说是根本不可能的。
看各个分量的方差值,看到有**【0.44272026,0.18971182,0.09393163,0.06602135,0.05495768,0.04024522,0.02250734,0.01588724】**。第一和第二分量对应于整个数据集的 63%。8 个组成部分的累积方差图如图 2 所示。
图二。组件数量的累积方差,按作者排序的图像
将数据集转换为不同维度时,在新维度中定位数据的过程称为投影。在图 3 中,根据新创建的维度和与 mglearn 库中 PCA 图的差异,可以看出区别。
图 3。使用 mglearn 库的 PCA 可视化,图片由作者提供
那么这个将 30 维转化为 2 维的神奇过程背后到底隐藏着什么呢?
3.理论
PCA 改变组件的方向以实现最大方差,并以此方式降低数据集的维数。
方差:给出关于数据集分布的信息。例如,让我们举一个将 5cl 液体装入瓶子的例子。假设第一种情况下的瓶子是 4cl、5cl、5cl、5cl、6cl,第二种情况下的瓶子是 2cl、3cl、5cl、7cl、8cl。虽然两者的平均值都是 5cl,但是第一种情况下的填充物将比第二种情况下的填充物更均匀,因为第一种情况下的样本分布方差低于第二种情况下的样本分布方差。这表明分配更加成功。
3.1.计算 PCA
图 4 显示了如何使用 PCA 进行降维的流程图。
图 4。降维流程图,作者图片
PCA 是在不使用 sklearn 库的情况下通过数学运算创建的,并且与 sklearn 库的组件进行比较。
每一步的输出都逐步显示在表格中。
3.1.1。重新缩放(标准化)
在第一阶段,对数值数据集应用缩放。为此,计算每个特征的平均值和标准差。使用这些计算,按照公式 x _ new =(x-mean(x 的列))/STD(x 的列)创建新的数据集。对于此操作,每个特征的平均值= 0,标准值= 1(用标准定标器标准化)
图 5。数据集(左)和缩放数据集(右),按作者分类的图像
3.1.2。协方差矩阵
协方差矩阵根据以下公式创建,缩放后的数据集根据彼此之间的关系完全重建:
图 6。协方差矩阵公式,来源
根据该方程计算所有协方差值后,得到(n_features,n_features)的矩阵。主要目标是重新排列数据集,以最大化数据集中的方差。为了检测这一点,需要协方差矩阵。
协方差是相关性的度量。通过协方差,我们知道两个变量一起变化的方向(正的话方向相同,负的话方向相反)。然后我们可以用相关性来找出这种变化的程度。协方差以单位来度量。在数据科学中,协方差涵盖两个变量或数据集的关系。
图 7。协方差矩阵,图片由作者提供
3.1.3。特征值和特征向量
从具有协方差矩阵的数据集中计算特征值,并且获得相应的特征向量作为特征的总数。
图 8。特征值(左)和相应的特征向量(右),作者图片
3.1.4。按降序排序
特征值从最高到最低排序。希望为 PCA 选择多少分量,选择对应于该数量的特征值的特征向量,并降低数据集维数。
关于特征值的提示:
矩阵 x 的迹等于其特征值之和。
矩阵 x 的行列式等于其特征值的乘积。
矩阵 x 的秩等于矩阵 x 的非零特征值的个数。
图 9。无 Sklearn 的 PCA 结果无 Sklearn(左),有 Sklearn 的 PCA 结果(右),图片作者
用数学方程计算数据集的第一和第二主成分,可以看出,结果与导入 Sklearn 库的结果相同。
3.2。PCA 是特征选择&特征提取方法之一吗?
既可以是也可以不是。由于主成分分析降低了特征的维数,因此它可以被理解为提取特征或选择影响结果的最有效的特征。但是理解了上面提到的理论部分,这个就清楚了。在 PCA 机器学习应用之外,它是关于在另一个坐标系中解释数据集。我们可以认为这是用傅里叶变换将信号从时间轴转换到频率轴。
通过考虑数值和连续变量的方差值,对它们进行重新评估,并使用 PCA 从不同的窗口查看数据集。虽然在技术上可以实现,但对分类变量使用 PCA 不会产生可靠的结果。同样,当理解上述理论部分时,**用 PCA 执行特征选择的条件是合理的,因为影响结果的最重要的特征具有最大的方差。**当然,技术上还是可以实现的,只是选择权在开发者。
4.履行
4.1.传统的机器学习方法
有人提到,主成分分析是一种非常有用的方法,尽管会丢失信息,但会降低维数减少和特征值。在影像数据集中,每个像素都被视为一个要素。换句话说,对于 128x128 RGB (3 通道)图像,有 1281283 = 49152 个特征。这个数字对于监督学习模型来说是相当高的。在这一部分中,在由 81 个杯子、74 个盘子和 78 个盘子组成的厨房用具图像数据集上,在利用 PCA 对数据集进行图像增强和维度缩减之后,XGBoost 被应用如下:
数据集从本地文件夹导入后,使用定义的 Imagedatagenerator 复制 15 次,获得 3495 个样本。在编码中, *x:代表数据集,y:代表标签。*然后,为了测量来自不同源的模型的泛化性能,下载 5 个杯子、5 个盘子和 5 个盘子,并且这些也从本地文件夹中导入。在必要的数据预处理之后,将图像添加到 x 的末端,将标签添加到 y 的末端。**将获取的图像添加到训练和测试数据集中以评估模型泛化性能的原因是相同的 PCA 过程应用于所有图像。**数据集合并后,使用 sklearn 库导入 PCA,49152 像素(特征)减少到 300。此时,使用 NumPy 再次提取具有 15 个样本的模型泛化性能数据集,并且 3495 个数据集被分离为训练数据集和测试数据集。这里的问题不是是否使用五氯苯甲醚,而只是作为一种应用。也可以使用 SelectPercentile 进行特征选择。然后,在标签适应 XGBoost 模型之后,训练数据集被训练,并且用测试数据集评估该模型。最后,用分离的 15 个样本的外部数据集检验了模型的预测。
结果如图 10 所示。
图 10。外部数据集的混淆矩阵(左)和测试数据集的混淆矩阵(右),图片由作者提供
4.2.深度学习方法
编码器和解码器是深度学习处理的首选。然而,应用五氯苯甲醚在技术上是可行的。让我们对用上述数据导入和预处理操作以及用 PCA 降维准备的数据集进行分类,用密集层。
结果如图 11 所示:
图 11。外部数据集的混淆矩阵(左)和测试数据集的混淆矩阵(右),图片由作者提供
5.PCA 类型
5.1.核主成分分析
虽然 PCA 是一个线性模型,但在非线性情况下可能不会给出成功的结果。内核 PCA 是一种方法,也称为内核技巧,可以非线性地分离数据。
图 12。数据集(左)、含 PCA 的数据集(中)、含内核 PCA 的数据集(右)、按作者分类的图像
5.2.稀疏主成分分析
其目的是在稀疏主成分分析中更容易解释模型。虽然整个数据集的线性组合是 PCA 中的每个主成分,但是每个主成分是稀疏 PCA 中数据集子集的线性组合。
5.3.随机化主成分分析
随机化 PCA 与随机梯度下降一起工作,被称为随机化 PCA。通过查找前 x 个主成分来加快 PCA 过程。
5.4.增量 PCA
它通过将大规模数据集以小批量保存在内存中来执行 PCA 方法。
方法在上面的 Sklearn 库中给出,并且可以根据数据集容易地实现。
回到指引点击此处。
https://ibrahimkovan.medium.com/machine-learning-guideline-959da5c6f73d
多类分类标准综合指南
成为生活的书签:你需要的所有多类分类标准都有清晰的解释
介绍
我最近发表了我最具挑战性的文章,主题是多类分类(MC)。我在这个过程中所面临的困难很大程度上是由于我必须学习和解释过多的分类标准。当我完成的时候,我已经意识到这些指标应该有一篇自己的文章。
因此,这篇文章将是关于 7 个最常用的 MC 指标:精确度、召回率、F1 值、ROC AUC 值、Cohen Kappa 值、Matthew 相关系数和对数损失。您将了解它们是如何计算的,它们在 Sklearn 中的细微差别,以及如何在您自己的工作流程中使用它们。
https://ibexorigin.medium.com/membership
获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:
https://alphasignal.ai/?referrer=Bex
解释 N 乘 N 混淆矩阵
您今天将要介绍的所有指标都以这样或那样的方式与混淆矩阵相关联。虽然 2 乘 2 混淆矩阵直观且易于理解,但更大的混淆矩阵可能会真正混淆。出于这个原因,在深入研究从矩阵中导出的指标之前,最好先了解一些较大的 N 乘 N 矩阵。
在本文中,我们将使用钻石分类的例子。具体来说,目标包含 4 种类型的钻石:理想、优质、优质和一般。对该钻石数据的任何分类器进行评估将产生一个 4x 4 矩阵:
尽管随着类别数量的增加,解释矩阵变得越来越困难,但是有一些万无一失的方法可以找到任何形状的矩阵。
第一步是始终确定你的积极和消极类。这取决于你要解决的问题。如果分类是平衡的,也就是说,你平等地关心每一个类(这种情况很少发生),可能就不会有任何积极或消极的类。如果是这种情况,正类和负类是按类定义的。
但是,作为珠宝店老板,您可能希望您的分类器能够更好地分类理想钻石和优质钻石,因为它们更贵。在这种情况下,理想和优质标签将是一个积极的类别,其他标签被统称为消极的。
识别阳性和阴性类别后,定义真阳性、真阴性、假阳性和假阴性。就我们自己的问题而言:
- 真阳性,类型 1 :实际理想值,预测理想值
- 真阳性,类型 2 :实际保费,预测保费
- 真否定:正确预测的任何否定类别标签(好、一般)
- 误报:实际值属于“良好”或“一般”类别,但预测为“理想”或“溢价”
- 假阴性:实际值属于阳性类别,但预测为良好或一般
一旦你定义了 4 项,从矩阵中找出每一项应该很容易,因为这只是简单的加法和减法。
从二元度量到多元类
默认情况下,大多数分类指标都是针对二元情况定义的。在将这些二进制度量扩展到多类时,使用了几种平均技术。
首先,使用一对一(OVO)或一对其余(OVR,也称为一对所有)方法,将多类问题分解为一系列二元问题。OVO 存在计算上的缺陷,所以专业人士更喜欢 OVR 方法。正如我在我的上一篇文章中详细讨论了这两种方法的区别,我们今天将只关注 OVR。
本质上,一对一策略将一个多类问题转化为目标中每个类的一系列二元任务。例如,分类 4 种类型的钻石类型可以用 OVR 二进制化为 4 个任务:
- 任务 1:理想与[优质、良好、一般] —即理想与不理想
- 任务 2:优质与[理想、良好、一般]——即优质与非优质
- 任务 3:好与[理想、优质、一般] —即好与不好
- 任务 4:公平与[理想、优质、良好] —即公平与不公平
对于每个任务,将构建一个二进制分类器(在所有任务中应该是相同的分类器),并使用二进制分类度量(如 precision)(或我们今天将讨论的任何度量)来测量它们的性能。结果将是 4 个精确分数。为了比较一个分类器和另一个分类器,我们需要一个单一的精度分数,而不是 4,所以我们需要一种方法来表示所有类的精度。这就是平均技术的用武之地。
具体来说,有 3 种适用于多类分类的平均技术:
- macro :这是跨类的所有指标的简单算术平均值。这种技术对所有类别赋予相同的权重,使其成为平衡分类任务的好选择。
- 加权:通过计算目标中每个类别的样本数加权的二进制度量的平均值,说明类别不平衡。如果 3 个类别的 3 ( 精度分数)分别为:类别 1 (0.85)、类别 2 (0.80)和类别 3 (0.89),则通过将每个分数乘以每个类别的出现次数并除以样本总数来计算加权平均值。
- 微:这个和精度一样。微平均是用矩阵对角线单元之和除以所有单元之和,即精度。由于精确度是一个误导性的指标,这种平均技术很少使用。
现在,让我们最终转向实际的指标!
多类分类的精度和召回率
Precision 回答了“预测阳性与实际阳性的比例是多少?”当然,你只能用二元分类来回答这个问题。这就是为什么你问这个问题的次数和目标中类的数量一样多。每一次,你都将针对一个班级向其他班级提出问题。对于我们的钻石分类,一个例子是“预测的理想钻石有多少比例实际上是理想的?”
通过将真阳性除以真阳性和假阳性之和来计算精度(三 p 规则):
让我们计算理想类的精度。以下是混淆矩阵,供参考:
理想钻石的真阳性是左上角的单元格(22)。假阳性是其他类型的钻石被预测为理想的所有细胞。这些是左上角单元格下面的单元格(5 + 2 + 9 = 19)。因此,精度将是:
精度(理想):22 / (22 + 19) = 0.536 —很恐怖的分数。
当你想减少假阳性的数量时,你应该优化你的模型的精确度。在我们的案例中,针对理想钻石的精度进行优化是有意义的。原因是理想钻石是最贵的,得到一个误报意味着将一个更便宜的钻石归类为理想。如果你不小心漏掉了这样的事件,你可能会被指控欺诈。现在,让我们继续回忆*。*
Recall 回答了“有多少比例的实际阳性被正确分类?”它的计算方法是将真阳性的数量除以真阳性和假阴性的总和。
让我们为特级钻石计算一下。
有 27 个真阳性(第 2 行,第 2 列)。假阴性是指优质钻石被归类为理想、良好或一般的任何情况。这些将是真阳性细胞左边和右边的细胞(5 + 7 + 6 = 18)。因此,召回将是:
召回(溢价):27/(27+18)= 0.6——也不是个好成绩。
如果你想减少假阴性的数量,你应该优化你的召回模型。如果您试图在黄色和红色香蕉中检测蓝色香蕉,您会希望减少假阴性,因为蓝色香蕉非常罕见(如此罕见,以至于您第一次听说它们)。你不会想把它们和普通的香蕉混在一起。
如果你想查看所有类的精度和召回率以及它们的宏观和加权平均值,你可以使用 Sklearn 的classification_report
函数。假设我们的标签在y_test
并且预测在y_pred
,钻石分类的报告将是:
最后两行显示的是精度和召回率的宏观和加权平均值,看起来不太好!
多类分类的 F1 分数
由于它们的性质,精确度和召回率是一种权衡关系。你可能不得不以牺牲另一个为代价来优化一个。然而,如果您想要一个分类器,它在最小化误报和漏报方面同样出色,该怎么办呢?例如,有一个模型是有意义的,该模型同样擅长于捕捉意外出售廉价钻石作为理想钻石的情况,以便您不会被起诉,并检测意外以更低的价格出售理想钻石的情况。
这就是 F1 分数的来源。它是通过取精度和召回率的调和平均值来计算的,范围从 0 到 1。
为什么要取调和平均值而不是简单的算术平均值?调和平均值有一个很好的算术特性,代表一个真正平衡的平均值。如果准确率或召回率较低,就会受到严重影响。例如,假设我们正在比较两个分类器。第一个分类器的精度和召回率分别为 0.9、0.9,第二个分类器的精度和召回率分别为 1.0 和 0.7。计算两者的 F1 得到 0.9 和 0.82。如你所见,第二个分类器的低回忆分数降低了分数。
使用分类报告输出,您可以看到 F1 的两个平均分数:
F1 分数通常介于精确度和召回率之间,但是取加权平均值可能会给出超出其范围的值。
在接下来的几节中,我们将讨论 ROC AUC 得分并将其与 F1 进行比较。你会发现这两个指标的主要缺点。
多类分类的 ROC AUC 得分
二元分类中另一个常用的度量是受试者操作特征曲线下的面积(ROC AUC 或 AUROC)。它量化了模型区分各个类别的能力。该度量仅用于可以生成类成员概率的分类器。就 Sklearn 估计器而言,这些是具有predict_proba()
方法的模型。
例如,如果目标包含猫和狗类,那么使用predict_proba
方法的分类器可以为每个样本生成隶属概率,例如猫为 0.35,狗为 0.65。然后,基于像 0.5 这样的决策阈值对每个预测进行分类。在进一步解释 AUROC 之前,我们先来详细看看它是如何计算 MC 的。
在使用predict_proba
方法的二元分类器被选择之后,它被用于为 OVR 的第一个二元任务生成成员概率。然后,选择接近 0 的初始决策阈值。使用阈值,进行预测,并创建混淆矩阵。从这个混淆矩阵中,计算出两个度量,真阳性率(与回忆相同)和假阳性率:
然后,选择新的、更高的阈值,并创建新的混淆矩阵。使用这个混淆矩阵,计算新的 TPR 和 FPR。对于 0 和 1 之间的许多不同的判决阈值重复该过程,并且对于每个阈值,找到新的 TPR 和 FPR。最后,所有 TPR 和 FPR 相互对应绘制:
该图是在我们的 diamonds 数据集中计算理想类相对于其他类的 ROC 曲线的实现。对于所有其他二进制任务,重复整个过程。换句话说,又发现了 3 条 ROC 曲线:
最终的图还显示了这些曲线下的面积。AUROC 越大,等级之间的差别就越大。最终的 AUROC 也使用宏观或加权方法进行平均。以下是这一切在 Sklearn 中的实现:
我们的平均得分是 0.82。
多类分类中 ROC AUC 分数与 F1 分数的对比
简而言之,ROC AUC 和 F1 之间的主要差异与类别不平衡有关。以下是阅读了许多 StackOverflow 线程后对如何选择一个线程的总结:
如果你有很高的类别不平衡,总是选择 F1 分数,因为高的 F1 分数同时考虑了精确度和召回率。为了获得高 F1,假阳性和假阴性都必须低。另一方面,ROC AUC 可以以足够高的假阳性数给出宝贵的高分。此外,您还可以将 ROC AUC 分数视为在各种阈值下评估的 F1 分数(好的和坏的)的平均值。当你有阶级不平衡的时候,总是使用 F1。更高的 ROC AUC 不一定意味着更好的分类器。
如果你想更多地了解这种差异,下面的讨论对我有所帮助:
- F1 得分与 ROC AUC 的对比
- 如何解释几乎完美的准确性和 AUC-ROC 但 f1-得分、精确度和召回为零
- 如何在 ROC AUC 和 F1 成绩之间做出选择?
- AUC 和 F1-score 有什么区别?
Cohen 的多类分类 Kappa 评分
你可以把 kappa 评分看作是准确性的增强版,这个版本也整合了对机会和等级不平衡的测量。
正如你可能知道的,准确性可能非常误导人,因为它没有考虑到阶级的不平衡。在正负比率为 10:100 的目标中,如果分类器简单地正确预测所有负样本,您仍然可以获得超过 90%的准确性。此外,由于机器学习算法依赖于数据的概率假设,我们需要一个分数来衡量生成预测时固有的不确定性。以雅各布·科恩(Jacob Cohen)命名的 Kappa 评分是少数几个可以在一个指标中代表所有这些的评分之一。
在官方文献中,它的定义是“量化两个评分者之间的一致程度的度量标准。”这是维基百科的定义:
Cohen 的 kappa 系数(κ)是一种统计数据,用于测量定性(分类)项目的评分者之间的可靠性(以及评分者内部的可靠性)。一般认为这是一个比简单的百分比一致计算更稳健的方法,因为κ考虑了偶然发生一致的可能性。
以下是官方公式:
在分类中,这个公式解释如下:
P0是实际值和预测值之间的观测比例一致。这将是任何混淆矩阵的对角线单元的总和除以非对角线单元的总和。换句话说,简单准确性的另一个名称。
P_e 是真值和假值偶然重合的概率。我们将看到如何使用我们在本指南中使用的矩阵来计算这些值:
让我们先找出精确度:对角线单元格的总和除以非对角线单元格的总和— 0.6。为了找到 P_e 的值,我们需要找到每个类的真实值与预测值偶然相同的概率。
- **理想类—真实值和预测值都是理想值的概率。共有 250 个样品,其中 57 个是理想钻石。因此,随机钻石成为理想钻石的概率是
P(实际 _ 理想)= 57 / 250 = 0.228
现在,在所有 250 个预测中,有 38 个是理想的。所以,随机预测成为理想预测的概率是
P(预测 _ 理想)= 16 / 250 = 0.064
两个条件都为真的概率是它们的乘积,所以:
P_e(实际 _ 理想,预测 _ 理想)= 0.228 * 0.064 = 0.014592
现在,我们将为其他类做同样的事情:
- **溢价类——真实值和预测值的概率都是溢价偶然:
P(actual _ premium)= 45/250 = 0.18
P(预测保费)= 28 / 250 = 0.112
P_e(实际 _ 溢价,预测 _ 溢价)= 0.02016
- **好类—真实值和预测值都是好偶然的概率:
P(实际 _ 良好)= 74 / 250 = 0.296
P(predicted _ good)= 26/250 = 0.104
P_e(实际 _ 良好,预测 _ 良好)= 0.030784
- **一般类—真实值和预测值的概率都一般偶然:
P(实际 _ 公平)= 74 / 250 = 0.296
P(predicted _ fair)= 30/250 = 0.12
P_e(实际 _ 公平,预测 _ 公平)= 0.03552
最终 P_e 是上述计算的总和:
P_e(最终)= 0.014592+0.02016+0.030784+0.03552 = 0.101056
精度,P_0 = 0.6
插入数字:
好消息是,您可以使用 Sklearn 在一行代码中完成所有这些工作:
一般来说,0.8 以上的分数被认为是优秀的。我们得到的分数是一个谦逊的温和派。
想了解更多信息,我建议阅读这两篇优秀的文章:
多类分类的马修相关系数
来看看精确度的另一个单一数字替代品——马修相关系数。我认为这是统计学家能够想出的唯一一个包含所有 4 个矩阵术语并且实际上有意义的指标:
即使我知道为什么它是这样计算的,我也不会费心去解释它。您只需要知道这个度量表示真实值和预测值之间的相关性。类似于皮尔逊的相关系数,范围从-1 到 1。1.0 的分数意味着完美的分类器,而接近 0 的值意味着我们的分类器不比随机机会好。
MCC 的酷之处在于它完全对称。与精确度和召回率不同,交换正类和负类会得到相同的分数。况且它只关心每个类预测的好不好,不考虑类的不平衡性。根据维基百科,一些科学家甚至说 MCC 是在混淆矩阵上下文中建立分类器性能的最佳得分。
幸运的是,Sklearn 也包含了这个指标:
我们得到了 0.46 的分数,这是一个中等强度的相关性。一般来说,超过 0.7 的值被认为是好成绩。
顺便说一下,上面的公式是针对二元分类器的。对于多类,Sklearn 给出了一个更可怕的公式:
图片由 Sklearn 提供。(他们甚至不辞辛苦,试图简化公式,他们真好!)
多类分类的对数损失
最稳健的单一数字度量之一是对数损失*,称为交叉熵损失和逻辑错误损失。它不是一个点度量(越大越好),而是一个误差函数(越低越好)。因此,尽可能最小化对数函数的分类器被认为是最好的分类器。*
log loss 的另一个优点是,它只对概率分数起作用,或者换句话说,是可以生成概率成员分数的算法。这意味着这个误差函数考虑了模型的不确定性。例如,分数为 0.9 的类预测比分数为 0.6 的预测更确定。我们今天讨论的许多指标使用预测标签(即 1 类、2 类),这些标签隐藏了模型在生成这些预测时的不确定性,而测井曲线损失则没有。
它严重地惩罚了模型预测低分数的类成员的情况。对于二进制情况,其公式为:
以上是二进制情况的公式。多类的情况更加复杂。我将不再解释这个函数是如何计算的,因为这已经超出了本文的范围。因此,我将用 Sklearn 展示一个例子,并留下一些链接,可能有助于您进一步理解这一指标:
这里有几个链接可以巩固你的理解:
摘要
今天,我们学习了如何以及何时使用 7 个最常见的多类分类指标。我们还了解了它们是如何在 Sklearn 中实现的,以及它们是如何从二进制模式扩展到多类的。使用这些指标,您可以评估任何分类器的性能,并将它们相互比较。
这是最后一张备忘单,根据你在多类问题中的需要来决定使用哪种度量标准:
- 以单一指标比较一个分类器与另一个分类器的整体性能—使用马修相关系数、科恩 kappa 和对数损失。
- 衡量分类器在平衡分类中区分各类别的能力: ROC AUC 得分
- 在不平衡分类中最小化假阳性和假阴性的度量: F1 分数
- 专注于减少单个类的误报:该类的精度
- 专注于减少单个类别的假阴性:召回该类别的。
您可能也会对…感兴趣
助推模型综合指南
图片来源:https://Twitter . com/wrathofgnon/status/1250287741247426565/photo/1
在表格数据建模领域,有一个统治王朝:增强模型。
虽然情况在变化,但 ML 和 AI 中的大多数问题仍然是监督建模问题。尽管深度学习应用程序(图像、视频、文本、语音)占据了左右头条,但现实生活中的大多数问题都使用表格数据,尤其是在商业领域。在这个世界上只有一个统治者:推动模型。
参加过 Kaggle 比赛的人都非常熟悉助推模型。XGBoost 开始统治大多数比赛。王冠很快就传给了 LightGBM。但这些并不是唯一的助推算法。最近,有人在 AdaBoost 上问我一个问题,这让我意识到我忽略了这一点。所以我决定写一个全面的指南,主要是作为自我参考,但也作为其他人的总结。这篇博文旨在讨论这些模型之间的结构差异及其性能。
关于集合和推进的初级读本
在我们深入研究不同的模型之前,让我们快速了解一下什么是 boosting 及其同类。Boosting 是一种用于组合输出的方法,如果你没有接触过这个概念,这听起来相当神秘。集成是一种组合不同模型/决策/功能的输出的方法。助推是集合的一种方式。有三种主要的组装方法:装袋、助推和堆叠。
基本上,在集成中,我们有一堆更简单的模型(所谓的“弱学习者”),它们理想地捕捉我们数据的不同方面,我们使用他们的所有判断来达到最佳解决方案。有点像算法代议制民主。就像在民主国家一样,如果观点非常相似,你最终会进入一个回音室,这个回音室过度适应你的数据的一个方面,你的模型就会在实践中失败。所以我们的“弱学习者”在理论上具有低相关性。关于这一点的更正式和更少固执己见的解释,请参见偏差-方差权衡。
在助推/打包树算法中,当我们说弱学习者时,我们实际上指的是决策树。但是弱学习者是一个通用术语,用于任何比随机机会表现稍好的 ML 模型。
制袋材料
Bagging 是一种方法,在这种方法中,我们使用来自所有弱学习者的决定。基本上,你有来自每个弱学习者的投票,你决定使用最普遍的意见(在分类的情况下)或平均值(在回归的情况下)。假设我们有 5 个学习能力差的人,其中 3 个回答“是”,2 个回答“这个人会在泰坦尼克号上幸存吗?”最后的决定是肯定的。随机森林是在这种背景下最著名的 bagging 算法。
图片来源:https://en . Wikipedia . org/wiki/File:Random _ forest _ diagram _ complete . png
助推
另一方面,助推以不同的方式解决问题。它通过调整每一步的误差反复建立自己的观点。每个弱学习者都建立在前一组学习者的基础上。例如,假设我们已经建立了 5 个弱学习者,假设这 5 个学习者的组合输出决策是固定的,第 6 个学习者被期望与训练输出相比纠正他们的错误。在 boosting 中,每个弱学习者都以这样或那样的方式被加权,以优化损失函数。这就是为什么叫‘助推’。它“提升”了前一套模型的性能。这种加权机制是增强算法彼此不同的一个方面。
堆垛
概括一下,装袋平均产量。增强将输出与一些权重相结合。从这个意义上说(除了联合和迭代建模方法),bagging 和 boosting 在加权方法上有所不同。Bagging 对所有弱学习者具有相同的权重,boosting 基于弱学习者在损失函数上的表现对其进行加权。相比之下,堆叠使用又一个学习器来学习如何组合弱学习器。堆叠层可以有任何模型作为所有弱学习者的组合子。所以基本上代替权重,你可以使用,比如说,神经网络来组合来自你的弱学习者的输出。
推进模型:又名我们如何从“大卫·鲍依的”创造出“约翰·沃森”?
如果你读过我以前的任何作品,你就会知道我永远无法抗拒一句晦涩难懂的引用,但这一句是如此贴切,而不是真的那么晦涩难懂,我不得不给出一个参考。现在我已经通过引用以最清晰的方式解释了 ensembling 的作用,下面我可以继续列出不同的升压模型。
- adaboost 算法
- 梯度推进机器
- XGBoost
- CatBoost
- LightGBM
adaboost 算法
AdaBoost 是最初的升压模型,代表自适应升压。在每次提升迭代中,它根据模型在每个样本上的当前性能调整权重。使用这些权重,数据集被重新采样(替换)到新的训练集中。基本上,通过增加每个错误分类样本的权重并减少正确分类样本的权重,我们增加了这些错误分类样本在下一次迭代中出现在我们的训练子集中的机会(可能不止一次)。因此,该模型不断地重新关注我们无法分类的项目,以找到一些表示它们的方式。
AdaBoost 在行动——来源:http://srl.informatik.uni-freiburg.de/videos
梯度推进机器
如前所述,并不是所有的 boosting 模型都像 AdaBoost 一样进行更新。在 GBM 的情况下,与改变数据的采样权重相反,模型更新是通过给定损失函数的梯度下降来完成的。更新规则如下:
图片来源:维基百科
基本上,这告诉你的是,在迭代 m 时,选择弱学习者 h ,其最小化实际值 y_i 和我们先前估计的 F_{m-1}(x_i) + 预测的 h (即弱学习者)。找到使其最小化的 h 的方法是通过梯度下降。
在优化回归的均方误差的情况下,梯度就是残差。并且每个连续的弱学习器适合于残差,因为我们试图补偿误差。想象一下这个场景,当我们开始时,我们对我们的输出没有任何线索。因此,我们的输出向量 y 中的每个值实际上都是一个残差。因此,我们首先让弱学习者适应我们的初始残差。然后,我们的弱学习者做出一些预测,其中一些比另一些更好,所以我们试图学习错误并继续这样下去。这个过程相当于使用梯度下降来训练 boosting 模型。数学很简单,但是太长了,不能放在这里。对于没有优化 MSE 的分类和其他回归问题,我们以这样的方式训练我们的弱学习者,他们将执行梯度下降。因此,GBM 是广义 boosting 方法,可以适用于任何可微损失函数。这里的是关于这个主题的一套很好的课堂讲稿,它也展示了拟合残差和梯度下降的等价性。
极端梯度推进(又名 XGBoost)
XGBoost 是 GBMs 的一个规范化变体。XGBoost 没有像上述 GBM 中那样最小化损失,而是在损失函数中增加了一个正则化项。这是一个简单但非常有效的防止过度拟合的技巧。该正则化术语强制要求应用更新的最低梯度水平。如果正则项比梯度大得多,那么梯度将完全没有效果,因此更新被停止。如果正则项非常小,那么这个更新等价于 GBM。因此,不是最小化上面 GBM 等式的 argmin 部分,我们现在找到最小化以下内容的学习者 f_t :
XGBoost 学习。图片来源: XGBoost docs 。
此外,XGBoost 增加了叶节点的比例收缩,以进一步防止过度拟合和其他与处理器性能相关的变化。XGBoost 中的 x 与数据科学或 ML 相关概念无关,它更多地指向开发人员必须将分布式快速算法引入 ML 空间的工程焦点。
CatBoost
虽然 XGBoost 是处理表格数据的一个很好的算法,但它本身并不处理分类值。它们必须通过某种形式的数字编码来手动处理。另一方面,CatBoost 能够处理分类变量,只要您指定列名。其工作方式是,它生成虚拟(单热编码)变量,最多可达由 one_hot_max_size 参数提供的最大列数。如果分类变量具有比该参数更多的唯一值,则使用巧妙的排列和随后的类平均来创建每个类和类别的平均目标值。下面给出了公式,它看起来有点复杂,但它的意思是:对于一个给定的排列,找出计数到这一点为止的样本数(j),其中 *y(输出变量)*等于 1(假设二进制分类),在它之前加上一些,然后除以到这一点为止的对象总数。
这意味着几件事情,给定一个特征 x ,我们将它 n 次,以获得该特征的不同排序,然后逐个样本,我们计算带有一些偏移的正类与迄今条目数的比率。这意味着,在第一个实例中,如果我们有类值 a ,并且 y=1 ,这意味着我们的替换值是(1+ a.P)/(1+a),在第二个实例中,假设我们有类值 b 和 y = 0 ,那么我们的替换值将是(0 + a.P)/(2 + a)。就这样,我们继续前进。请注意,我们使用扩展窗口扫描所有样本,因此如果样本数量较多,这可能会非常慢。本质上,该公式是使用贝叶斯在线估计观察到该特性中该类别值的正类的概率。它的“贝叶斯”成分来自先验§和系数 a 是我们对初始先验概率的信任程度。公平地说,这不是一个真正的原创想法,它已经被许多数据科学家用于分类表示。CatBoost 是第一个将它正式化的 boosting ML 模型。它适用于具有大量类别的分类特征,但是 DS 从业者可以在不直接依赖 CatBoost 的情况下实现它。
LightGBM
LightGBM 是对 XGBoost 的另一个改进,它也可以直接处理分类值。这个模型实现了一些结构上的变化。
- 计算分裂时的不同采样策略:基于梯度的单侧采样(GOSS)。XGBoost 使用基于直方图的分割策略。基本上,它计算数据的直方图,并基于直方图上的信息增益执行树分裂。另一方面,LightGBM 首先执行基于梯度的采样,然后根据样本子集计算分割。其工作方式是,保留所有具有高梯度的行,并对 x%的具有低梯度的行进行采样,然后在该子集上建立新的模型。这个算法的伪代码可以在论文中找到:https://papers . nips . cc/paper/2017/file/6449 f 44 a 102 FDE 848669 BDD 9 EB 6b 76 fa-paper . pdf
- 高效的功能捆绑,通过删减专有功能集来减少功能数量。想法是,您可以将非零值之间没有重叠的要素分组为一个要素。
- 逐叶分割与逐级分割:LightGBM 生长叶,最大程度地减少损失函数。理论上,这棵树只能长在一边。其他算法默认逐层增长树,这意味着树一次只增长一层。XGBoost 还可以执行逐叶生长。这提高了算法的速度和效率,因为它是一种贪婪的搜索机制,然而会导致过拟合,尤其是在数据稀疏和/或小的情况下。
结论和实用资源
这篇文章只是收集了算法中的设计差异,然而基于这些设计决策,算法之间存在性能差异。
- GBM 是 OG(原始梯度),在不同的数据集上表现良好,但是它不能处理多种实际情况,如空值或分类值。它在多个标准库中都有实现,比如 scikit-learn,所以作为基线模型之一使用总是一个好主意。我尝试使用多种方法:GLMs、决策树、Random Forest 和 GBM,看看哪种复杂程度能给出可接受的基线性能。它指导下面的模型选择决策,但我现在离题了。
- XGBoost 相对于 GBM 是一个显著的性能改进,但是在 sklearn 中没有实现。也不能处理分类变量。可以处理空值。
- CatBoost 是对 XGBoost 分类值增加的改进,但训练效率较低。适用于大量独特的特征值。sklearn 中没有原生实现。
- LightGBM 是对 XGBoost 改进,它可以处理分类值,也非常有效,但是如果不仔细调整,它会被修剪以适应过度。sklearn 中没有原生实现。然而,Python 包有一个 sklearn 子模块,它符合 sklearn fit-transform 语法,并且可以很好地与 sklearn 集成。
关于如何调优和使用这些算法的实用技巧,下面是一组我个人引用过并继续引用的博客帖子。这些软件包的官方文档也非常好,所以请务必参考这些文档。最后,检查原始论文也是一个好主意,因为它们提供了一种以更结构化的方式阅读创作者想法的方式。
- XGB 与 LGBM 参数调整
- CatBoost vs. XGB vs. LGBM 参数比较
- XGBoost 文档
- CatBoost 文档
- CatBoost 论文
- CatBoost 其他论文
- LightGBM 文档
- LightGBM 纸
- LightGBM 回购
数据仓库综合指南
您需要知道的一切,包括与数据湖的比较
数据仓库是公司或组织中的中央数据存储,它从各种来源收集关系数据。信息从不同的交易系统或其他关系数据库转移到数据仓库,在那里它可供分析师和决策者使用。
数据仓库可以用来做什么?
数据仓库用于商业环境的许多领域。整个公司都使用该数据库来制定数据驱动的决策或检查流程。由于中央数据仓库从许多不同的系统中提取信息,因此它被视为一个单一的事实。这是为了确保公司中的每个人都在谈论相同的数据,并且决策是基于这些信息的。
跨部门,数据仓库可用于以下任务:
- 成本和资源分析
- 内部流程分析(如生产、雇佣等)。)
- 商业智能
- 计算和提供公司范围内的关键绩效指标
- 用于分析或数据挖掘的数据源
- 将公司范围的数据标准化为固定的模式
数据仓库的属性
在创建中央数据仓库时,可以遵循某些特征,这些特征应该有助于更好地缩小数据仓库的结构和必要数据。
主题定位
数据仓库包含特定主题的信息,而不是单个业务交易的信息。例如,这些主题可以是销售、采购或营销。
该仓库旨在借助商业智能和目标 KPI 来支持决策。与决策无关或用于分析的信息最初不会出现在这个中央数据库中,这一事实也支持这种解释。
综合
该仓库集成了来自各种系统和来源的数据。因此,必须为信息创建一个通用的模式,以便它是统一的和可比较的。否则,集中分析和 KPI 创建是不可能的。
时间段参考
数据仓库存储特定时期的数据,因此与过去相关。此外,数据通常以聚集的形式传输,例如在每日级别,因此数据量仍然有限。因此,粒度可能不够细,正如人们习惯于从操作系统中得到的那样。
另一方面,操作系统是基于时间的,因为它们输出当前正在积累的信息。同时,可以非常详细地查看信息。
非挥发性
中央仓库的另一个重要特征是数据的非易失性。在操作系统中,信息通常只临时存储很短一段时间,一旦添加新数据,旧数据就会被覆盖。另一方面,在数据仓库中,数据是永久存储的,即使添加了新数据,旧数据也会持续存在。
数据湖和数据仓库的区别
这个中央数据库还可以由一个数据湖来补充,在这个数据湖中,非结构化的原始数据以较低的成本被临时存储,以便以后使用。这两个概念的主要区别在于它们存储的数据和存储信息的方式。
数据仓库和数据湖的比较|图片:作者
这是你应该带走的东西
- 数据仓库集中存储公司范围的信息。
- 这是为了支持数据驱动的决策,并使商业智能成为可能。
- 数据湖中的非结构化原始数据为数据仓库中的关系数据和已处理数据提供了很好的补充。
如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你希望有无限制的 访问我的文章和数以千计的精彩文章,不要犹豫,点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5*获得会员资格*
*https://medium.com/@niklas_lang/what-does-google-know-about-me-find-it-out-c6115dca17e4 https://medium.com/@niklas_lang/what-are-recurrent-neural-networks-5c48f4908e34 https://medium.com/@niklas_lang/kubernetes-k8s-simply-explained-3dc2e1558b7c *
Sklearn 多类分类综合指南
模型选择、制定策略和选择评估标准
学习如何用 Sklearn 解决任何多类分类问题。本教程涵盖了如何选择模型选择策略、几个多类评估指标,以及如何使用它们完成超参数调整,以优化用户定义的指标。
介绍
尽管多类分类并不常见,但它确实比二元分类问题提出了更大的挑战。你可以相信我的话,因为这篇文章是我写过的最有挑战性的文章(已经写了将近 70 篇)。
我发现多类分类这个话题很有深度,充满了细微差别。我读了那么多文章,读了多个 StackOverflow 线程,自己创建了几个,花了几个小时浏览 Sklearn 用户指南,做实验。多类分类的核心主题,例如
- 选择将问题二值化的策略
- 选择基本模式
- 理解非常多的指标
- 筛选出解决您业务问题的单一指标,并对其进行定制
- 为此定制度量调整超参数
- 最后用 Sklearn 把所有的理论付诸实践
都分散在互联网黑暗肮脏的角落里。这足以得出结论,没有一个单一的资源显示了在互联网上处理多类分类问题的端到端工作流(也许,我错过了)。
出于这个原因,本文将是一个关于如何使用 Sklearn 解决任何多类监督分类问题的综合教程。您将学习上述核心概念的理论和实现。这将是一个漫长而技术性的阅读,所以去喝杯咖啡吧!
https://ibexorigin.medium.com/membership
获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:
https://alphasignal.ai/?referrer=Bex
本地多类分类器
根据你选择的模型,Sklearn 以 3 种不同的方式处理多类分类问题。换句话说,Sklearn 估计器根据其处理多类数据的策略分为 3 类。
第一组也是最大的一组估计器本身支持多类分类:
[naive_bayes.BernoulliNB](https://scikit-learn.org/stable/modules/generated/sklearn.naive_bayes.BernoulliNB.html#sklearn.naive_bayes.BernoulliNB)
[tree.DecisionTreeClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html#sklearn.tree.DecisionTreeClassifier)
[tree.ExtraTreeClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.tree.ExtraTreeClassifier.html#sklearn.tree.ExtraTreeClassifier)
[ensemble.ExtraTreesClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.ExtraTreesClassifier.html#sklearn.ensemble.ExtraTreesClassifier)
[naive_bayes.GaussianNB](https://scikit-learn.org/stable/modules/generated/sklearn.naive_bayes.GaussianNB.html#sklearn.naive_bayes.GaussianNB)
[neighbors.KNeighborsClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html#sklearn.neighbors.KNeighborsClassifier)
[svm.LinearSVC](https://scikit-learn.org/stable/modules/generated/sklearn.svm.LinearSVC.html#sklearn.svm.LinearSVC)
(设置 multi_class="crammer_singer ")`[linear_model.LogisticRegression](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html#sklearn.linear_model.LogisticRegression)
(设置 multi_class= “多项式”)[linear_model.LogisticRegressionCV](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegressionCV.html#sklearn.linear_model.LogisticRegressionCV)
(设置 multi_class= “多项式”)
对于一个 N 类问题,他们会产生 N 乘 N 的混淆矩阵,大部分评估指标都是从中推导出来的:
我们将在教程的后面关注多类混淆矩阵。
一对一(OVO)策略的二元分类器
其他监督分类算法主要是为二进制情况设计的。然而,Sklearn 实现了两种称为一对一(OVO)和一对其余(OVR,也称为一对所有)的策略,将一个多类问题转换为一系列二元任务。
OVO 将一个多类问题分解成每对类的一个二元分类任务。换句话说,对于每一对,将建立单个二元分类器。例如,一个具有 4 个类别(脑癌、肺癌、乳腺癌和肾癌)的目标使用 6 个单独的分类器将问题二值化:
- 分类器 1:肺与乳房
- 分类器 2:肺对肾
- 分类器 3:肺对脑
- 分类器 4:乳房对肾脏
- 分类器 5:乳房对大脑
- 分类器 6:肾对脑
Sklearn 建议这些分类器最好与 OVO 方法一起使用:
- svm。NuSVC
- svm。SVC
- 高斯 _ 过程。GaussianProcessClassifier (设置 multi_class = "one_vs_one ")
Sklearn 还在sklearn.multiclass.OneVsOneClassifier
下提供了上述模型的包装器估算器:
这种策略的一个主要缺点是它的计算工作量。由于每对类都需要一个单独的二进制分类器,所以基数高的目标可能需要很长时间来训练。为了计算将为 N 类问题构建的分类器的数量,使用以下公式:
在实践中,由于这个缺点,一对其余的策略是更可取的。
一对一(OVR)策略的二元分类器
或者,OVR 策略为目标中的每个类创建一个单独的分类器。本质上,每个二进制分类器选择一个类,并将其标记为正,编码为 1。其余的类被认为是负标签,因此用 0 编码。对 4 种癌症进行分类:
- 分类器 1:肺 vs .[乳腺、肾、脑]——(肺癌,不是肺癌)
- 分类器 2:乳房对[肺、肾、脑]——(乳腺癌,不是乳腺癌)
- 分类器 3:肾对[肺、乳房、脑]——(肾癌,不是肾癌)
- 分类器 4:大脑 vs .[肺、乳腺、肾]——(脑癌,不是脑癌)
Sklearn 建议这些分类器最好与 OVR 方法一起使用:
[ensemble.GradientBoostingClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingClassifier.html#sklearn.ensemble.GradientBoostingClassifier)
[gaussian_process.GaussianProcessClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.gaussian_process.GaussianProcessClassifier.html#sklearn.gaussian_process.GaussianProcessClassifier)
(设置 multi_class = "one_vs_rest ")[svm.LinearSVC](https://scikit-learn.org/stable/modules/generated/sklearn.svm.LinearSVC.html#sklearn.svm.LinearSVC)
(设置 multi_class="ovr ")[linear_model.LogisticRegression](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html#sklearn.linear_model.LogisticRegression)
(设置 multi_class="ovr ")[linear_model.LogisticRegressionCV](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegressionCV.html#sklearn.linear_model.LogisticRegressionCV)
(设置 multi_class="ovr ")[linear_model.SGDClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.SGDClassifier.html#sklearn.linear_model.SGDClassifier)
[linear_model.Perceptron](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Perceptron.html#sklearn.linear_model.Perceptron)
或者,您可以使用默认OneVsRestClassifier
的上述模型:
尽管这种策略显著降低了计算成本,但事实上只有一个类被认为是正面的,而其余的被认为是负面的,这使得每个二元问题成为一个不平衡的分类。对于目标中比例较低的班级,这个问题更加突出。
在这两种方法中,根据传递的估计量,所有二元分类器的结果可以用两种方式总结:
- 多数投票:每个二元分类器预测一个类别,从所有分类器中获得最多投票的类别被选择
- 根据类成员概率分数的 arg max:LogisticRegression 等分类器计算每个类的概率分数(
.predict_proba()
)。然后,选择分数总和的 argmax。
我们将在本教程的后面部分详细讨论如何对这些策略进行评分。
样本分类问题和预处理流水线
作为一个示例问题,我们将使用 Kaggle 的钻石数据集来预测钻石的质量:
上面的输出显示了不同比例的特征,建议我们使用某种类型的归一化。这一步对于许多基于线性的模型的良好运行是必不可少的。
数据集混合了数值和分类特征。我在我的上一篇文章中详细介绍了二进制分类的预处理步骤。您可以很容易地将这些想法应用到多类的情况中,所以我在这里将保持解释简洁明了。
目标是“削减”,它有 5 个等级:理想、优质、非常好、良好和一般(质量递减)。我们将用 OneHotEncoder 对文本特征进行编码。
让我们快速浏览一下每个数字要素的分布,以决定使用哪种类型的归一化:
>>> diamonds.hist(figsize=(16, 12));
价格和克拉显示偏斜分布。我们将使用对数转换器使它们尽可能呈正态分布。对于其他的,简单的标准化就足够了。如果您不熟悉数字转换,请查看我的关于该主题的文章。同样,下面的代码包含了一个 Sklearn 管道的例子,你可以从这里了解所有关于它们的内容。
让我们开始工作:
我们管道的第一个版本使用了RandomForestClassifier
。让我们通过生成预测来查看它的混淆矩阵:
在第 8 行和第 9 行,我们创建了矩阵并使用特殊的 Sklearn 函数来绘制它。ConfusionMatrixDisplay
也有display_labels
参数,我们将通过pipeline.classes_
属性访问的类名传递给它。
解读 N 乘 N 混淆矩阵
如果你读过我的另一篇关于二进制分类的文章,你就会知道混淆矩阵是监督分类问题的圣杯。在 2 乘 2 矩阵中,矩阵项易于解释和定位。
尽管随着类别数量的增加,解释矩阵变得越来越困难,但是有一些万无一失的方法可以找到任何形状的矩阵。
第一步是始终确定你的积极和消极类。这取决于你要解决的问题。作为珠宝店老板,我可能希望我的分类器能够比其他类型更好地区分理想钻石和优质钻石,使这些类型的钻石成为我的正面类别。其他类将被视为负面。
在早期建立正类和负类对于评估模型性能和超参数调整非常重要。这样做之后,你应该定义你的真阳性、真阴性、假阳性和假阴性。在我们的案例中:
- 阳性等级:理想和优质钻石
- 负等级:非常好,好,和公平的钻石
- 真阳性,类型 1 :实际理想值,预测理想值
- 真阳性,类型 2 :实际保费,预测保费
- 真阴性:其余钻石类型预测正确
- 假阳性:实际值属于 3 个阴性类别中的任何一个,但预测为理想值或溢价值
- 假阴性:实际值为理想值或溢价值,但由 3 个阴性类别中的任何一个预测。
总是以这种方式列出矩阵的术语,你的工作流程的其余部分将会容易得多,正如你将在下一节看到的。
Sklearn 如何计算多类分类指标— ROC AUC 得分
这一节仅仅是关于 Sklearn 如何为多类分类计算公共度量的基本细节。具体来说,我们将探究 4 个最常见的指标:ROC_AUC、precision、recall 和 f1 得分。尽管我将对每个指标做一个简要的概述,但我将主要关注在实践中使用它们。如果您想更深入地了解每个指标衡量的内容,请参考这篇文章。
我们将讨论的第一个指标是 ROC AUC 得分或受试者工作特征曲线下的面积。当我们想要测量一个分类器的性能来区分每一个类时,它是最常用的。这意味着 ROC AUC 更适合平衡分类任务。
实质上,ROC AUC 分数用于二进制分类,并与可以基于某个阈值生成类别成员概率的模型一起使用。以下是计算二元分类 ROC AUC 步骤的简要概述:
- 一个二元分类器,可以用它的
predict_proba
方法生成类成员概率,比如 LogisticRegression。 - 选择接近 0 的初始决策阈值。例如,如果概率高于 0.1,则预测该类为负,否则为正。
- 使用该阈值,创建混淆矩阵。
- 发现真阳性率(TPR)和假阳性率(FPR)。
- 选择新的阈值,并重复步骤 3-4。
- 对 0 到 1 之间的各种阈值重复步骤 2-5,以创建一组 TPR 和 FPR。
- 绘制所有 TPR 与 FPR 的关系图,以生成接收器工作特性曲线。
- 计算这条曲线下的面积。
对于多类分类,您可以使用 OVO 或 OVR 策略计算所有类的 ROC AUC。由于我们一致认为 OVR 是一个更好的选择,以下是 OVR 分类的 ROC AUC 计算方法:
- 使用 OVR 创建的每个二元分类器使用上述步骤找到其自身类别的 ROC AUC 分数。
- 然后使用以下两种方法之一对所有分类器的 ROC AUC 评分进行平均:
- “宏观”:这只是分数的算术平均值
- “加权”:通过找到一个加权平均值,将阶级不平衡考虑在内。每个 ROC AUC 乘以其类别权重并求和,然后除以样本总数。
例如,假设目标中有 100 个样本—类别 1 (45),类别 2 (30),类别 3 (25)。OVR 创建了 3 个二元分类器,每个类别一个,它们的 ROC AUC 分数分别为 0.75、0.68、0.84。所有类别的加权 ROC AUC 分数将为:
ROC AUC(加权)😦(45 * 0.75)+(30 * 0.68)+(25 * 0.84))/100 = 0.7515
以下是这一切在 Sklearn 中的实现:
上面,我们为我们的钻石分类问题计算了 ROC AUC,得到了一个优秀的分数。使用roc_auc_score
时,不要忘记正确设置multi_class
和average
参数。如果您想要生成某个特定课程的分数,以下是您的操作方法:
ROC AUC 分数只是一个很好的衡量标准,可以看出分类器如何区分不同的类别。更高的 ROC AUC 分数不一定意味着更好的模型。最重要的是,我们更关心我们的模型对理想钻石和优质钻石进行分类的能力,因此 ROC AUC 这样的指标对我们的情况来说不是一个好的选择。
多类分类的精确度、召回率和 F1 分数
衡量渠道绩效的更好指标是使用精确度、召回率和 F1 分数。对于二进制情况,它们易于理解且直观:
作者提供的图片
在多类的情况下,这 3 个度量是基于每类计算*。例如,让我们再来看看混淆矩阵:*
精度告诉我们有多少比例的预测阳性是真正的阳性。如果我们想要计算理想钻石的精度,真正的阳性将是正确预测的理想钻石的数量(矩阵的中心,6626)。假阳性将是任何细胞计数的次数,我们的分类预测其他类型的钻石是理想的。这些单元格位于矩阵中心的上方和下方(1013 + 521 + 31 + 8 = 1573)。使用精度公式,我们将其计算为:
精度(理想)= TP/(TP+FP)= 6626/(6626+1573)= 0.808
召回的计算方法类似。我们知道真正阳性的数量——6626。假阴性将是对分类器预测属于任何其他阴性类别的钻石的理想类型的次数进行计数的任何单元。这些单元格位于矩阵中心的左右两侧(3 + 9 + 363 + 111 = 486)。使用回忆公式,我们计算出它是:
回忆(理想)= TP/(TP+FN)= 6626/(6626+486)= 0.93
那么,对于理想的类,我们如何在召回率和精确度之间进行选择呢?这取决于你试图解决的问题的类型。如果你想尽量减少其他更便宜的钻石被预测为理想钻石的情况,你应该优化精度。作为一个珠宝店老板,你可能会因为将便宜的钻石冒充昂贵的理想钻石出售而被起诉欺诈。
另一方面,如果您想最大限度地减少意外低价出售理想钻石的情况,您应该优化理想类的召回。的确,你不会被起诉,但你可能会赔钱。
第三个选择是拥有一个同样擅长上述两种场景的模型。换句话说,一个具有高精度和高召回率的模型。幸运的是,有一个指标可以衡量这一点:F1 分数。F1 得分取精确度和召回率的调和平均值,并产生一个介于 0 和 1 之间的值:
因此,理想班级的 F1 分数应该是:
*F1(理想)= 2 (0.808 * 0.93)/(0.808+0.93)= 0.87
至此,我们只计算了理想类的 3 个指标。但是在多类分类中,Sklearn 为所有类计算它们。你可以用classification_report
来看这个:
你可以检查我们对理想类的计算是否正确。表格的最后一列— support
显示了每个类别有多少个样本。此外,最后两行显示了 3 个指标的平均分数。我们已经介绍了 ROC AUC 示例中的宏观平均值和加权平均值。
对于像这样的不平衡分类任务,你很少选择平均精度,F1 分数的回忆。同样,选择一个指标来优化一个特定的类取决于您的业务问题。对于我们的情况,我们将选择优化理想和高级类的 F1 分数(是的,您可以同时选择多个类)。首先,让我们看看如何计算所有类别的加权 F1:
以上与classification_report
的输出一致。要选择理想和高级课程的 F1 分数,请指定labels
参数:
最后,让我们看看如何通过超参数调优来优化这些指标。
调整超参数以优化自定义指标的模型性能
为一个指标优化模型性能几乎与我们为二进制情况所做的一样。唯一的区别是我们如何将评分函数传递给 GridSearch 这样的超参数调谐器。
到目前为止,我们一直使用 RandomForestClassifier 管道,因此我们将为此估计器创建一个超参数网格:
不要忘记在每个超参数名称前面加上您在评估器管道中选择的步骤名称。当我们创建管道时,我们将 RandomForests 指定为“base”。更多信息见本讨论。
我们将使用 HalvingGridSeachCV (HGS ),它比常规的网格搜索要快得多。你可以阅读这篇文章来看看我的实验:
</11-times-faster-hyperparameter-tuning-with-halvinggridsearch-232ed0160155>
在我们将上面的网格提供给 HGS 之前,让我们创建一个定制的评分函数。在二进制的情况下,我们可以传递字符串值作为我们想要使用的度量的名称,比如“precision”或“recall”但是在多类的情况下,这些函数接受额外的参数,如果我们将函数名作为字符串传递,我们就不能这样做。为了解决这个问题,Sklearn 提供了make_scorer
功能:
正如我们在上一节中所做的,我们粘贴了average
和labels
参数的自定义值。
最后,让我们初始化 HGS,并通过三重交叉验证使其适合全部数据:
搜索完成后,您可以分别使用.best_score_
和.best_estimator_
属性获得最佳得分和估计值。
你的模型的好坏取决于你选择用来评估它的标准。超参数调整将是耗时的,但假设您在此之前做了所有正确的事情,并且给出了足够好的参数网格,那么一切都会如预期的那样。如果没有,这是一个迭代过程,所以调整预处理步骤,重新审视您选择的指标,也许可以扩大您的搜索范围。感谢您的阅读!