Docker 图像与容器
理解 Docker 中图像和容器的区别
Docker 一直是软件开发领域的游戏规则改变者,因为它使开发人员能够共享一个隔离的环境,让整个团队(或用户组)以一种非常一致和直观的方式构建、测试和部署应用程序。
由于这项技术已经存在了相当长的时间,我敢肯定你已经遇到了像图像、容器、卷甚至 Dockerfile 这样的术语。Docker 图像和容器是这种特殊技术的两个最基本的概念,许多 Docker 的新手都很难清楚地区分这两者。
在接下来的部分中,我们将讨论 Docker 图像和容器是什么,以及它们的主要区别。
Docker 图像(容器图像)
docker 映像是包含多个层的不可变文件,每个层对应于可以包含依赖关系、脚本或其他配置的文件系统。
这种分层提高了可重用性并加快了映像构建,因为每一步都可以缓存。现在,下一次构建将从缓存中加载一个步骤,除非自上次构建以来该步骤已被修改。
现在,docker build
命令被用来从Dockerfile
构建一个图像——一个由用户可以在命令行上调用的命令组成的文本文件来创建一个想要的图像。
Docker 图像是容器的基础。映像是根文件系统更改和相应的执行参数的有序集合,供容器运行时使用。一个映像通常包含一个相互堆叠的分层文件系统的联合体。
图像存储在 Docker 注册表中——默认的是 Docker Hub ,但是您甚至可以托管您自己的 Docker 注册表,只有您的组织才能访问。
您可以通过运行以下命令来查看主机上的所有图像
$ docker images
码头集装箱
现在 docker 容器是 Docker 映像的实例,运行在完全隔离的环境**(即与机器上运行的任何其他进程隔离)中,并且可以在任何操作系统上执行(可移植性!).**
容器是轻量级和可移植的运行时环境,用户可以在其中独立于底层主机运行应用程序。
可以停止正在运行的容器,但同时它可以保留设置和任何文件系统更改,以便下次重新启动时可以重用它们。
容器是 docker 映像的运行时实例。
码头集装箱包括
-码头工人图像
-执行环境
-一套标准的指令
这个概念是从海运集装箱借用的,它定义了一个在全球运输货物的标准。Docker 定义了一个运送软件的标准。
— 码头术语表
您可以通过运行以下命令来查看所有正在运行的容器
$ docker ps
如果您还想列出没有启动和运行的容器,您必须传递-a
选项:
$ docker ps -a
最后的想法
Docker 为开发人员提供了一个开发、运行和发布应用程序的平台。理解如何有效地使用 Docker 肯定会对你的软件开发之旅和职业生涯有所帮助。
因此,首先理解该技术的基本原理和组件很重要,这将有助于您更舒适地使用 Docker。这个旅程从您能够区分 Docker 图像和 Docker 容器开始。
简而言之,Docker 映像是一个由多层组成的结构,正如在Dockerfile
中所指定的,而 Docker 容器是 Docker 映像的一个(运行中的)实例(这也许是您可能想要使用 Docker 的原因!).
成为会员 阅读媒介上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。
https://gmyrianthous.medium.com/membership
相关文章你可能也喜欢
对接烧瓶 ML 应用
使用 Flask 部署 ML 模型和将您的工作容器化的指南
部署 ML 模型是 ML 生命周期中必不可少的一步,这一步经常被数据科学家所忽视。没有模型部署/托管,在现实世界的应用中就没有机器学习模型的使用。有很多种方法来保存你的 ML 模型,其中最简单也是最有效的是烧瓶。
Flask 是一个用 Python 实现的微型网络框架。使用 Flask,我们将通过一个例子演示如何在本地为 ML 模型提供推理服务。如果你对 Flask 完全陌生,我建议你看一下这个指南,它会让你快速上手。
烧瓶只是我们要解决的首要问题的第一步。是的,我们可以找到一种方法来使用 Flask 托管我们的模型,但是我们如何在生产环境中维护这个环境呢?我们如何在实时应用程序中跟踪我们可能必须对软件进行的任何更改?
通过 Docker 容器,我们提供了一个轻量级的包装器,可以非常容易地复制您的环境和依赖关系。例如,如果我们有一个软件更新需要安装在我们的应用程序中,Docker 使实现和跟踪这个更新变得很简单。通过利用诸如 Docker Registry 之类的服务,您还可以跟踪不同的映像版本以进行部署。使用 Docker 容器来保存我们的环境和我们所做的任何更改,软件生命周期变得更加简单和高效。关于 Docker 的完整分类,请查看下面的资源。
在本文中,我们将通过一个我们训练过的 ML 模型来看一下这两个步骤。
注意:本文将假设您对 Python、Docker、Flask 和使用 CLI 有基本的了解。我们也将使用一些基本的 HTML/CSS,但是你可以原样复制模板。这篇文章也将使用一个 ML 模型来服务,但我们不会涵盖模型构建背后的任何理论,关于模型的具体信息,请参考这篇文章。
资料组
对于我们的例子,我们将使用来自 Kaggle 的汽油消耗回归数据集。原始数据源在这里被许可。
模型结构
对于本文,我们不会在模型构建或从数据科学性能角度选择最准确的模型上花费太多时间。对于我们的回归问题,我将通过 Sklearn 框架利用一个 RandomForest 回归模型。一般来说,当我需要快速访问工作的 ML 模型示例时,我会尝试维护一个模型工具箱,它会给我样板代码来加速这个过程。
准备数据
训练随机森林模型
我使用 Joblib 模块对模型进行序列化,为了验证它是否有效,我将快速预测一个样本数据点。
样本推断
确保保留生成的“model.pkl”文件(也可以创建 model.joblib)。这是我们将在 Flask 服务器上加载用于推理的模型工件。
在 Flask 上托管模型
我们的 Flask 应用程序有两个部分。首先,我们想使用一些基本的 HTML/CSS 为我们的前端构建一个模板。在这个模板中,我们所做的只是创建一个简单的表单,它将接受四个输入:汽油税、平均收入、铺设的公路和人口驾驶执照百分比。这是我们的模型在后端期望的四个参数,我们将通过这个表单捕获它们。
形式
其次,我们可以配置 Flask 应用程序来处理这些表单输入。让我们看看必要的导入并加载模型,这就是我们的 model.pkl 文件派上用场的地方。
负载模型
在配置处理表单之前,让我们用 Flask web 服务器测试一个 GET 请求。我们渲染自己创建的 index.html 页面(请原谅我糟糕的 HTML/CSS 技能)。
烧瓶应用程序设置
如果在 Flask 应用程序文件所在的目录中运行以下命令,则可以启动 Flask 服务器。
启动服务器
表单模板(作者截图)
现在我们可以处理表单的输入,并将它们提供给我们的模型。
流程表单输入
在 HTML 方面,我们希望反映模型返回的输出变量“res ”,所以我们在 index.html 代码中添加了这一部分。
反映结果
如果我们现在用一些虚拟值填写表单,我们应该能够在页面上看到推理输出。
提交表单(作者截图)
推理显示(作者截图)
太好了!我们有一个工作的 Flask 应用程序,现在让我们看看如何正确地将它容器化。
将烧瓶应用程序归档
在开始之前,确保 Docker 已安装并正常运行。
您需要知道两个主要的 Docker 实体: Docker 图像和 Docker 容器。Docker 映像包含我们的源代码、依赖项和环境。Docker 容器是 Docker 图像的一个实例。使用 Docker 图像作为模板,我们可以运行启动应用程序的容器。要进一步了解不同之处,请参考这篇文章。
为了建立我们的 Docker 映像,我们需要提供一个 Docker 文件,其中包含基本的设置说明。有了 Dockerfiles,我们可以使用基础映像,这些映像已经预装了依赖项和代码。我们将从基础 Python 映像开始。
基础图像
接下来,我们使用 WORKDIR 命令来设置容器内的工作目录。利用容器中的根目录不一定是最佳实践,因为它可能导致类似的文件名和类似的小问题。
工作目录
现在,为了设置我们的环境,我们需要提供安装所需的依赖项,我们可以通过 requirements.txt 文件来完成。
申请要求
然后,我们可以首先复制这个需求文件,然后将它安装在我们的容器环境中。
安装要求
接下来,我们可以将剩余的文件复制到工作目录中。
将剩余文件复制到工作目录
最后,利用 CMD 我们可以在启动容器时提供一个默认命令。
在容器中启动 Flask Web 服务器
现在我们有了 Docker 文件,我们可以通过在 Docker 文件所在的路径中运行以下命令来构建我们的 Docker 映像。
构建 Docker 映像
这将创建一个标记为“ramveg/ml-app”的 Docker 图像,您可以随意重命名它。默认情况下,这也是“最新”的版本,如果你想改变,你可以做“ramveg/ml-app:dev”或类似的事情。
构建图像
我们可以通过运行下面的命令来看到这个图像。
码头图片(作者截图)
或者,您也可以使用以下命令检查容器的内容。
检查码头集装箱
这应该会在您的容器中启动一个 shell,在这里您可以运行普通的 linux 命令来了解您的目录结构。这里我们看到了所有我们复制过来的文件。
容器结构(作者截图)
现在我们可以启动容器,这里的关键部分是我们需要指定端口 5000,因为这是 Flask 运行的默认端口。
在容器上启动 Flask 服务器
如果我们转到“localhost:5000”,我们应该会看到与本地相同的 Flask 应用程序。只是为了验证我们也可以再次进行推理。
Flask 服务器(作者截图)
推论(作者截图)
其他资源和结论
https://github.com/RamVegiraju/ML-FlaskApp-Docker
您可以通过上面的链接访问示例的完整代码。我希望通过这篇文章,您对在生产类型环境中部署模型有了更深的理解。关于了解 ML 模型托管的更多内容,请查看我的 MLFlow 和 SageMaker 系列。如果你有兴趣学习更多关于容器的知识,Stephen Grider 在 Udemy 上有一个很棒的课程,可以帮助你从头开始。
如果你喜欢这篇文章,请在 LinkedIn 上与我联系,并订阅我的媒体 简讯 。如果你是新手,使用我的 会员推荐 报名。
对接 Jupyter 项目
将您的项目和 conda 环境容器化的简单方法
Docker 是一个虚拟化工具,比虚拟机更精简。在本教程中,我将讨论在conda
环境中容器化 Jupyter 笔记本。
这一切都源于我的研究和分析更加开放和透明的动机。我一直在尝试将我的脚本容器化,以便其他人更容易地重新创作我的作品。
当然,集装箱化并不是唯一的解决方案。人们可以用需求文件(conda env -f requirements.yaml
)重建您的环境,然后下载您的脚本/数据来重建您的分析。集装箱化包装了一切。
在本教程中,我将解释适合我的方法,希望它能帮助你更深入地理解这种方法。
我将带您一步一步地从提取基础映像到将最终映像推送到存储库。我正在找出在这个工作流程中最有效的方法,欢迎并鼓励提出意见。
先决条件
如果您对 Docker 完全陌生,请按照这里的说明安装它
https://docs.docker.com/get-docker/
基本图像
最佳实践是从官方 Docker 基础映像开始。由于我使用的是 miniconda ,我将从 Dockerhub 上的 Continuumio 基本图像开始。
docker pull continuumio/miniconda3:4.10.3p1
另一种方法是从 Linux OS 镜像开始,自己安装 miniconda。
提取后,您可以在如下容器中运行映像:
docker run -it continuumio/miniconda3:4.10.3p1 /bin/bash
这是在一个带有 bash shell 的交互式终端中运行 Docker 映像。-it
是--interactive --tty
的简写,它告诉 Docker 在容器内分配一个交互式虚拟终端会话。我只是认为-it
的意思是“我与 T21 的终端互动”
从这里,您可以访问基本 Unix 命令(ls
、pwd
、mkdir
等)。)以及conda
命令。
要验证是否安装了conda
,您可以运行:
which conda
有了 conda,你可以安装所有你喜欢的软件包
conda install numpy pandas matplotlib jupyterlab
既然我们安装了 JupyterLab,我们可以启动一个服务器:
jupyter-lab --ip=0.0.0.0 --no-browser --allow-root
您可以通过将提示链接复制到浏览器中来访问 Jupyterlab。
朱庇特实验室输出。将黄色框中的链接复制到您的浏览器中,以访问 JupyterLab(图片由作者提供,使用 carbon.now.sh 创建)
完成后,按 Ctrl-c 停止服务器和exit
容器。
这样做有一个陷阱,你做的任何事情都不会持久。一旦关闭容器,您创建的每个笔记本都将丢失。Docker volumes 将解决这个问题,我将很快讨论这个问题。
尽管容器中没有任何数据,但我们可以使用wget
或scp
来获取一些数据。例如,wget [https://www.ncei.noaa.gov/pub/data/cmb/ersst/v5/netcdf/ersst.v5.185401.nc](https://www.ncei.noaa.gov/pub/data/cmb/ersst/v5/netcdf/ersst.v5.185401.nc)
将下载 1854 年 1 月的海面温度。同样,一旦关闭容器,这些数据将会丢失。
然而,预装一些数据或笔记本不是很好吗?我们也能做到!坚持住。
让我们从 miniconda 基础映像开始,并在它的基础上构建我们的映像。
创造你自己的形象
我们上面所做的一切都可以是一个映像,所以我们不必每次都安装软件包并输入 jupyterLab 的 run 命令。
为此,我们创建了一个Dockerfile
,它是一个图像的蓝图,定义了如何创建我们的图像。
将以下内容复制到名为Dockerfile
( 不要添加扩展名)的文件中
FROM continuumio/miniconda3:4.10.3p1RUN conda install \
xarray \
netCDF4 \
bottleneck \
numpy \
pandas \
matplotlib \
jupyterlabCMD ["jupyter-lab","--ip=0.0.0.0","--no-browser","--allow-root"]
FROM
表示我们正在构建的基础图像RUN
表示我们希望在基础映像中运行的命令CMD
表示容器启动时我们想要运行的命令
建立形象
然后我们可以建立我们自己的形象,并给它一个名字。(我把我的命名为jupyterlab
,标记为latest
)
docker build -t jupyterlab:latest .
在这种情况下,-t
是--tag
的简写。这使用了name:tag
格式,其中 name 是您想要的图像标题,tag 提供任何附加信息(例如版本号)。.
和 end 指定dockerfile
在当前目录下。
在我的机器上,构建过程花了大约 2 分钟。构建完成后,您可以列出所有可用的图像以确保其可用。
docker image ls
如果我们想要删除该图像,这提供了诸如IMAGE_ID
和图像的SIZE
的信息。我们看到这张图片的大小略大于 1GB。
docker 图片 ls(图片由作者提供,使用 carbon.now.sh 创建)
运行映像
一旦构建了映像,我们可以像这样运行它:
docker run --rm -it -p 8888:8888 jupyterlab:latest
因为我们正在启动一个 Jupyerlab 服务器,所以我们必须告诉docker
服务器正在哪个端口上运行,这就是-p 8888:8888
所做的。
当您运行容器时,会提示一个 URL,您可以从 web 浏览器访问 JupyterLab。
在下一节中,我们将学习如何向图像添加数据。
用数据预装容器
如果您使用图像来共享您的分析,那么您将需要包括脚本和数据。让我们假设您有以下目录结构,包括一个笔记本、一个数据集和一个自述文件。
.
├── Dockerfile
├── README.md
├── data
│ └── dataset.txt
└── notebooks
└── my_analysis.ipynb
让我们通过添加下面一行到我们的Dockerfile
来添加一个/project
目录到图像中
WORKDIR /project
如果这个目录还不存在的话,Docker 会很聪明地创建它。
COPY
命令让我们(你猜对了)将数据复制到我们的映像中。
COPY ./README.md /project
COPY ./data /project/data
COPY ./notebooks /project/notebooks
这里的语法是:
COPY /path/on/host /path/in/container
我们改装的Dockerfile
看起来是这样的:
FROM continuumio/miniconda3:4.10.3p1WORKDIR /projectRUN conda install \
xarray \
netCDF4 \
bottleneck \
numpy \
pandas \
matplotlib \
jupyterlabCOPY ./README.md /project
COPY ./data /project/data
COPY ./notebooks /project/notebooksCMD ["jupyter-lab","--ip=0.0.0.0","--no-browser","--allow-root"]
现在,当我们构建并运行我们的容器时,我们启动/project
目录。
jupyterlab 在容器中运行的截图(图片由作者提供)
厉害!现在,我们有了一种将数据从主机文件系统放入容器的方法。在这个阶段,我们可以将我们的图像推送到 Dockerhub 这样的存储库中,这样其他人就可以获取并运行它。这个我会在最后讨论。
现在,让我们想象有人拉我们的形象,他们做了一些改变。他们可能希望这些变化持续下去。码头工人卷来救援!
Docker 卷(基础)
在我们开始将卷连接到我们的容器之前,了解什么是卷以及我们可以连接到卷的不同方式是很重要的。
卷有助于在容器重新启动时保存数据。因此,它们允许数据在容器关闭后继续存在。
这是将主机操作系统上的文件系统连接到容器中的虚拟文件系统。主机上的文件系统可以是已经存在的路径(例如~/Documents
)或者是 Docker 创建的新路径。
将主机文件系统连接到虚拟文件系统的示意图(图片由作者提供)
有三种类型的卷:
- 主机卷
- 匿名卷
- 命名(匿名)卷
我们连接卷的方式是在指定我们想要运行的映像之前使用docker run
中的-v
标志
主机卷
对于主机卷,我们将主机文件系统上的路径显式连接到容器中的路径。例如,这可能是您的~/Documents
文件夹中的路径。
docker run -v /dir/on/host/data:/dir/in/container/data imageName
主机卷提供了主机和容器之间的简单连接。您在主机文件系统中所做的任何更改都会在容器中看到,反之亦然。这种类型的卷在主机和容器之间提供了一个通道,非常适合在两者之间移动数据。
但是,主机卷并不是数据持久性的最佳解决方案。为此,我建议匿名或命名卷
匿名卷
对于匿名卷,我们只需指定希望卷装载在容器上的位置,而它在主机上的位置由 Docker 负责。
docker run -v /dir/in/container/data imageName
在此示例中,在/dir/in/container/data
时在容器中创建虚拟文件系统,而在/var/lib/docker/volumes/
时在主机上自动创建文件系统。
这对于在容器关闭后保存您在容器中所做的更改非常有用,从而解决了数据持久性问题。
但是,对于从主机向容器中获取数据来说并不太好,并且不能像主机卷那样很好地发挥管道的作用。
命名(匿名)卷
匿名卷和命名卷之间的唯一区别是,对于命名卷,您可以指定一个唯一的名称供以后参考。
docker run -v name:/dir/in/container/data
唯一的名称使我们能够轻松地将该卷连接到其他容器。
列出您的卷
要列出您的卷,您可以使用ls
docker volume ls
创建命名卷
您可以在使用之前创建一个空的命名卷,如下所示:
docker volume create volume-name
主机上的匿名/命名卷数据在哪里?
您所有的卷宗都可以在/var/bin/docker/volumes
查看
在 macOS 上,它存储在一个虚拟机中。将文件移动到主机操作系统的一种方法是通过 Docker 仪表板。从 Docker 控制面板中,您可以导航到卷→单击您的命名卷→数据→然后在最右侧单击“另存为”将文件复制到您的主机操作系统。我知道这很复杂。
查看这个堆栈溢出线程了解更多细节,如果有更简单的方法,请告诉我。
想要查看 macOS 虚拟机上存储的/var/bin/docker/volumes
吗?下面是步骤(谢谢堆栈溢出)
- 运行以下容器
docker run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh
2.导航到卷目录
cd /var/lib/docker/volumes
3.ls
此目录和列表导航到您的命名卷
将卷连接到容器
现在我们已经了解了卷以及主机卷和命名卷之间的权衡,让我们将卷连接到容器。
在本例中,我将使用一个命名卷,因为我想保留我在容器中所做的更改。
这将名为my_volume
到/project
的卷连接到运行jupyterlab:latest
映像的容器。因为我们在/project
目录中预装了数据,所以我们所做的任何更改都将保存在my_volume
上,即使在容器关闭后,我们也可以从停止的地方继续。
我们甚至可以将该卷连接到运行不同映像的不同容器。还记得我们刚开始连接到我们的基础映像时,映像中什么也没有吗?现在尝试将my_volume
连接到它:
现在我们可以从运行基本映像的容器中访问my_volume
中的数据了!
使用 Docker compose 进一步简化
使用端口和卷键入运行容器的命令有点麻烦。我们可以做的一件事是在一个 docker-compose.yaml
文件中定义所有的东西,这样我们所要做的就是用一个连接的卷启动预加载的 Jupyterlab 服务器
docker-compose up
为此,创建一个文件docker-compose.yaml
并将以下内容复制到其中
version: "3"services:
jupyter:
image: jupyterlab:latest
ports:
- 8888:8888
volumes:
- my_volume:/projectvolumes:
my_volume:
external: true
docker-compose 文件剖析
- **版本:**定义 docker-compose 的版本
- **服务:**这定义了容器。
jupyter
可以改名为任何东西。然后,我们提供映像名称、我们正在连接的端口和我们正在连接的卷 - **卷:**我们所连接的卷的名称,并声明它是外部的
要启动我们的 jupyterLab 服务器,我们只需运行
docker-compose up
确保您与 docker-compose 文件在同一个目录中。
如果您收到一条错误消息,指出您的外部卷不存在,那么您需要首先创建一个命名卷。使用 docker-compose,您必须在连接之前确保该卷存在。
docker volume create conda-work
自定义 JupyterLab 令牌
要制作您自己的令牌,您只需将以下内容添加到services:
environment:
JUPYTER_TOKEN: "docker"
现在从浏览器访问 JupyterLab 将永远是
localhost:8888/lab?token=docker
想了解更多?
这个视频帮助我理解 docker-compose
共享您的图像
在这里,我将展示如何将您的映像推送到 Docker Hub 存储库。
首先,在 Docker Hub 上创建一个帐户
当您构建映像时,它必须符合以下命名约定,以便推送至 docker hub:
docker_ID/image_name:tag
docker_ID
是您注册 Docker Hub 时选择的名称image_name
是您想要的图像名称吗tag
is 提供附加信息(新版本、不同架构等。)
现在从命令行登录 Docker Hub
docker login -u docker_ID docker.io
你将被提示输入密码(我强烈推荐启用 2FA ,因为安全很重要)
登录后,只需将您的图像推送到回购
docker push docker_ID/image_name:tag
一旦完成,任何人都可以使用docker pull
来拉你的图像并运行它!
最后的想法
我很喜欢 Docker,认为这是与同事分享数据分析和透明分析的好方法。因为运行脚本所需的一切都打包在一起,所以减少了提取脚本和运行脚本之间的摩擦。
当一个项目完成时,分析代码应该存储在一个代码库中,如 GitHub 或 GitLab,并且应该创建一个 Docker 映像来轻松地重新创建结果。通过这种方式,代码是开放的,可以免费获得,并且分析可以通过 Docker 映像重现。
我自己仍在学习这项技术,但我希望这篇文章能给你一些如何将自己的项目容器化的想法。一如既往,欢迎并鼓励提出意见和建议。
关于如何缩小你的 docker 图片尺寸的建议,请看这个博客。一些 Docker 最佳实践可以在这里找到。我在这篇文章中描述的类似解决方案可以在这里找到
感谢您阅读和支持媒体作家
https://lukegloege.medium.com/membership
使用 Python 和 OCR 进行文档解析
作者图片
使用计算机视觉从任何类型的文档中检测和提取文本、图形、表格
摘要
在本文中,我将使用 Python 和计算机视觉展示如何解析文档,比如*pdf、*并提取信息。
文档解析 涉及检查文档中的数据,提取有用的信息。这对公司来说是必不可少的,因为它减少了大量的手工工作。想象一下,必须手动搜索 100 页的表格,然后将它复制并粘贴到其他地方……如果有一个程序可以在 1 秒钟内完成,那该有多酷?
一种流行的解析策略是将文档转换成图像并使用计算机视觉。 文档图像分析 是指应用于文档图像以从像素数据中获取信息的技术。这可能很棘手,因为在一些情况下,对于预期的结果应该是什么样子(文本、图像、图表、数字、表格、公式……)没有明确的答案。最常用的技术是 OCR。
【OCR(光学字符识别) 是通过计算机视觉检测并提取图像中文字的过程。它是在第一次世界大战期间发明的,当时以色列科学家伊曼纽尔·戈德堡创造了一种可以阅读字符并将其转换成电报代码的机器。今天,该领域已经达到了非常高的复杂程度,混合了图像处理、文本定位、字符分割和字符识别。基本上是一种文本对象检测。
在本教程中,我将展示如何使用 OCR 进行文档解析。我将展示一些有用的 Python 代码,这些代码可以很容易地用于其他类似的情况(只需复制、粘贴、运行),并通过注释遍历每一行代码,这样您就可以很容易地复制这个示例(下面是完整代码的链接)。
我将用一家上市公司的 PDF 格式的财务报表作为例子(下面的链接)。
特别是,我将经历:
- 环境设置:导入包、读取数据、预处理
- 检测(文本、图形、表格)
- 摘录(文本、图表、表格)
设置
文档解析令人烦恼的地方在于,有如此多的工具可以处理不同类型的数据(文本、图形、表格),但没有一个能够完美地工作。以下是基于人们想要遵循的策略的最流行的包的当前框架:
- 将文档处理为文本:用 PyPDF2 提取文本,用 Camelot 或tabulopy提取表格,用 PyMuPDF 提取数字。
- 将文档转换为图像(OCR) :用 pdf2image 进行转换,用pytesserac加上其他很多支持库,或者只是 LayoutParser 提取数据。
也许你会问自己:“为什么要麻烦地将页面转换成图像,而不是直接处理 PDF 文件?”。你可以这么做。这种策略的主要缺点是编码方案:文档可以有多种编码(如 UTF 8、ASCII、Unicode),因此转换成文本可能会导致数据丢失。因此,为了避免任何问题,我将使用 OCR 并用 pdf2image 将页面转换成图像。请注意, PDF 渲染库Poppler需要运行。
****# with pip**
pip install python-poppler
**# with conda** conda install -c conda-forge poppler**
您可以轻松阅读该文档:
****# READ AS IMAGE**
import **pdf2image**doc = pdf2image.convert_from_path("doc_apple.pdf")
len(doc) **#<-- check num pages** doc[0] **#<-- visualize a page****
作者图片
如果要在计算机上保存页面图像,可以使用下面的代码:
****# Save imgs** import **os**folder = "doc"
if folder not in os.listdir():
os.makedirs(folder)p = 1
for page in doc:
image_name = "page_"+str(p)+".jpg"
page.save(os.path.join(folder, image_name), "JPEG")
p = p+1**
最后,我们需要设置我们将使用的 CV 引擎。 LayoutParser 似乎是第一个针对 OCR 的通用包,基于深度学习。它使用两个著名的任务模型:
- 检测:Detectron,脸书最先进的物体检测库(我们将使用第二版 Detectron2 )。
**pip install layoutparser torchvision && pip install "git+https://github.com/facebookresearch/detectron2.git@v0.5#egg=detectron2"**
**pip install "layoutparser[ocr]"**
现在,我们已经准备好开始信息检测和提取的 OCR 过程。
**import **layoutparser** as lp
import **cv2** import **numpy** as np
import **io**
import **pandas** as pd
import **matplotlib**.pyplot as plt**
侦查
(物体)检测是在一张图片中寻找多条信息,然后用一个长方形的包围盒将其包围起来的过程。对于文档解析,这些信息是标题、文本、图表、表格…
因此,让我们来看一个包含所有内容的复杂页面:
第 22 页(作者图片)
这个页面以一个标题开始,有一个文本块,后面是一个图和一个表,所以我们需要一个经过训练的模型来识别这些对象。幸运的是 Detectron 能够胜任这项任务,你只需要从这里选择一个模型,并在代码中指定它的路径。
作者图片
我将要使用的模型只能检测这 4 个对象(文本、标题、列表、表格、图形)。因此,如果你需要识别其他东西(比如方程式),你必须改变模型。
****## load pre-trained model**
model = lp.Detectron2LayoutModel(
**"lp://PubLayNet/mask_rcnn_X_101_32x8d_FPN_3x/config",**
extra_config=["MODEL.ROI_HEADS.SCORE_THRESH_TEST", 0.8],
label_map={0:"Text", 1:"Title", 2:"List", 3:"Table", 4:"Figure"})**## turn img into array** i = 21
img = np.asarray(doc[i])**## predict** detected = model.detect(img)**## plot** lp.draw_box(img, detected, box_width=5, box_alpha=0.2,
show_element_type=True)**
作者图片
**预测对象“检测到”包含每个检测到的布局的细节,如边界框的坐标。根据输出在页面上出现的顺序对其进行排序非常有用:
****## sort**
new_detected = detected.sort(key=lambda x: x.coordinates[1])**## assign ids**
detected = lp.Layout([block.set(id=idx) for idx,block in
enumerate(new_detected)])**## check** for block in detected:
print("---", str(block.id)+":", block.type, "---")
print(block, end='\n\n')**
作者图片
完成 OCR 的下一步是正确提取边界框内的信息。
提取,血统
为了帮助 OCR 模型,通常使用边界框分割图像,然后使用模型处理分割的图像。最后,我将把提取的输出保存到一个字典中。
由于我们看到不同类型的输出(文本、标题、图形、表格),我将准备一个函数来显示结果。
****'''
{'0-Title': '...',
'1-Text': '...',
'2-Figure': array([[ [0,0,0], ...]]),
'3-Table': pd.DataFrame,
}
'''**
def **parse_doc**(dic):
for k,v in dic.items():
if "Title" in k:
print('\x1b[1;31m'+ v +'\x1b[0m')
elif "Figure" in k:
plt.figure(figsize=(10,5))
plt.imshow(v)
plt.show()
else:
print(v)
print(" ")**
让我们用文本来试试:
****# load model**
model = lp.TesseractAgent(languages='eng')dic_predicted = {}for block in [block for block in detected if block.type in [**"Title","Text"**]]:
***## segmentation***
segmented = block.pad(left=15, right=15, top=5,
bottom=5).crop_image(img)
***## extraction***
extracted = model.detect(segmented)
***## save***
dic_predicted[str(block.id)+"-"+block.type] =
extracted.replace('\n',' ').strip()**# check**
parse_doc(dic_predicted)**
作者图片
继续关注人物**😗*
**for block in [block for block in detected if block.type == "**Figure**"]:
***## segmentation***
segmented = block.pad(left=15, right=15, top=5,
bottom=5).crop_image(img)
***## save***
dic_predicted[str(block.id)+"-"+block.type] = segmented**# check**
parse_doc(dic_predicted)**
作者图片
很好,但是那相对容易,桌子就难多了。尤其是示例中的这个,因为它没有垂直线来分隔列,而列名在单独的行上。
**for block in [block for block in detected if block.type == "**Table**"]:
***## segmentation***
segmented = block.pad(left=15, right=15, top=5,
bottom=5).crop_image(img)
***## extraction***
extracted = model.detect(segmented)
***## save***
dic_predicted[str(block.id)+"-"+block.type] = pd.read_csv(
io.StringIO(extracted) )**# check**
parse_doc(dic_predicted)**
作者图片
如您所见,提取出的表格并不好。没有 OCR 会不会不一样?让我们尝试在不将文档转换成图像的情况下处理它。我将使用 TabulaPy 包:
**import **tabula**
tables = tabula.read_pdf("doc_apple.pdf", pages=i+1)
tables[0]**
作者图片
结果稍微好一点,因为现在表有列了,即使名称仍然是错误的。
结论
这篇文章是演示如何用 OCR 执行文档解析的教程。我用 LayoutParser 包走了一遍检测和提取的全过程。我展示了如何处理 PDF 文档中的文本、图表和表格。
我希望你喜欢它!如有问题和反馈,或者只是分享您感兴趣的项目,请随时联系我。
👉我们来连线👈
本文是 Python 系列 CV 的一部分,参见:
用聪明的方式记录你的机器学习项目
如何使用 Sphinx 和顶点 AI 管道的分步教程。
作者:@西格蒙德,unsplash.com
本文的目的是分享使用 Sphinx 自动生成机器学习项目文档的过程。
我将使用 Sphinx 的高级功能,例如添加了徽标、注释、图像和降价文档。此外,我将展示您需要的 python 包,以便 Sphinx 可以提取顶点管道中的文档字符串。
一些背景
我们开始吧!如你所知,拥有最新的机器学习项目文档对于生产和概念验证阶段都是至关重要的。 **为什么它至关重要?**因为它有助于澄清和简化你的模块,与你的团队合作,快速整合新的团队成员,更快地发展,并与业务所有者分享。
就我个人而言,我经历过很多这样的情况,由于上市时间的限制,文档被忽略了,但是一旦项目在生产中发布,这就变成了致命的。因此,我建议你避开任何手动程序来生成你的文档,因为这样的程序总是以失去同步和耗时而告终。
因此,在发布您的项目之前,花一些时间检查您的项目的可读性。在我的例子中,我倾向于使用以下文件:
- 自述文件 —一个易于阅读的文件,提供项目的介绍和一般信息,如目的、技术信息、软件组件
- 许可 —一个文件,提到了贡献者需要遵循的许可分步程序
- 用法 —解释如何使用项目的文件
- CHANGELOG —一个跟踪项目变更和发布版本的文件
请注意,最重要的文件是自述文件。贡献和使用信息可以直接添加到自述文件中。可以在生产中发布项目之前添加 changelog 文件。要编辑文件,您可以使用https://www.markdownguide.org/**、简单文本、或重组文本 。
参见下面我们将要描述的过程的概述。
过程概述(图片由作者提供)
斯芬克斯是什么?
Sphinx 是一个强大且易于使用的开源自动生成器工具,被 Python 社区广泛使用。它能够生成优秀的结构化文档。存在一些替代品,如 MkDocs、Doxygen、pdoc 等,但是 Sphinx 仍然是一个完整且易于使用的强有力的竞争对手。
主要特点:
- 支持多种输出格式:HTML、PDF、纯文本、EPUB、TeX 等。
- 文档的自动生成
- 自动链接生成
- 多语言支持
- 各种扩展可用
步骤:
一、设置环境
二世。安装虚拟环境
三。安装 Sphinx
四。设置 Sphinx
动词 (verb 的缩写)建立文档
一、搭建环境
- Python 3
- 本地虚拟机或 Vertex AI 工作台(用 Python 3 运行在虚拟环境中的 Jupyter 笔记本)
- 包含顶点 AI 代码的 Python 项目
- 虚拟人
- Kfx—ekube flow 管道 sdk 扩展
- MyST 解析器 Markdown 风格
- 包含 sdk 管道的顶点项目
让我们用一个 Apache-2.0 许可下的顶点 AI 管道 的端到端开源例子。该项目是一个很好的例子,因为该项目使用顶点管道,而不使用文档生成器。
首先克隆源代码,转到顶点-管道-端到端-样本 目录 :
*git clone [https://github.com/GoogleCloudPlatform/vertex-pipelines-end-to-end-samples.git](https://github.com/GoogleCloudPlatform/vertex-pipelines-end-to-end-samples.git)
cd vertex-pipelines-end-to-end-samples*
二。创建一个虚拟环境&激活它
III。安装斯芬克斯
创建一个文件 requirements-sphinx.txt 并添加:
*myst-parser==0.15
requests==2.28.1
sphinx==4.5.0
sphinx-click==4.3.0
sphinx-me==0.3
sphinx-rtd-theme==1.0.0
rst2pdf==0.99
kfx*
立即安装需求-sphinx.txt 中列出的 Sphinx 及其扩展:
*pip install -r requirements-sphinx.txt*
创建一个 docs 目录(如果不存在)来存储 Sphinx 布局:
*mkdir docs
cd docs*
使用 sphinx-quickstart 命令生成初始目录结构:
***sphinx-quickstart***
选择单独的源代码和构建目录、项目名称、作者姓名、项目版本和项目语言。您可以在下面找到我的配置:
您应该获得以下树结构:
如你所见,我们选择分离构建和源目录。关于它的内容我们来做几个说明。
build/ 目录是用来保存生成的文档。因为我们还没有生成任何文档,所以它现在是空的。**
****make . bat(Windows)**和 Makefile(Unix) 文件是简化文档生成的脚本。
source/conf.py 是 Sphinx 项目的配置文件。它包含默认的配置密钥和您为 sphinx-quickstart 指定的配置。
source/index . rst是包含目录树(toctree)指令的项目的根文档,您应该在其中列出您想要包含在文档中的所有模块。**
_static 目录包含自定义样式表和其他静态文件。
_templates 目录存储了 Sphinx 模板。
四。设置狮身人面像
识别 python 模块:/pipelines
目录/ 管道包含我们想要包含在 Sphinx 文档中的 python 代码。注意只有当您添加 init 时,Sphinx 才会看到 pipelines 包中的子模块。/ 管道目录下的 py 文件。
生成狮身人面像来源
使用 sphinx-apidoc 构建您的 API 文档(确保您位于项目的根目录)。创建的 Sphinx 源代码存储在 docs/source/pipelines 中。
**sphinx-apidoc -f -o docs/source/pipelines pipelines/**
您可以检查以下文件是在 docs/source/pipelines 中创建的:
将降价文件复制到 docs/source
在 Sphinx 源目录(docs/source/)中自动复制 README.md、CONTRIBUTING.md 和 USAGE.md 文件。在 docs/Makefile 中添加以下行以自动同步降价文件:
**COPY_README = ../README.md
COPY_CONTRIBUTING = ../CONTRIBUTING.md
COPY_USAGE = ../USAGE.md#sincronyze MD files
$(shell cp -f $(COPY_README) $(SOURCEDIR))
$(shell cp -f $(COPY_CONTRIBUTING) $(SOURCEDIR))
$(shell cp -f $(COPY_USAGE) $(SOURCEDIR))**
编辑 index.rst 编辑
使用****注释指令获取您想要突出显示的信息。****
****.. note:: Sphinx with Vertex AI.****
使用图像指令添加图像。推荐的图像尺寸宽度在 400-800 像素之间。
****.. image:: ../images/xgboost_architecture.png
:align: center
:width: 800px
:alt: alternate text****
在to tree指令下,列出你希望包含在最终文档中的所有模块(自述文件、模块)。
****.. toctree::
:maxdepth: 2
:caption: Contents: README
pipelines/modules
CONTRIBUTING
USAGE****
请在下面找到我的 index.rst:
编辑 conf . py—Sphinx的主配置文件
定义路径:
****# Define path
sys.path.insert(0, os.path.abspath("../.."))****
添加您的扩展:
****extensions = [
"sphinx.ext.duration",
"sphinx.ext.doctest",
"sphinx.ext.viewcode",
"sphinx.ext.autosummary",
"sphinx.ext.intersphinx",
"sphinx_rtd_theme",
"sphinx_click",
"myst_parser",
"sphinx.ext.todo",
"sphinx.ext.coverage",
"myst_parser",
]****
列出要解析的文件列表:
****source_suffix = {
".rst": "restructuredtext",
".md": "markdown",
}****
指定 HTML 主题:
****html_theme = "sphinx_rtd_theme"****
要添加一个徽标,请确保图像出现在/_ static**源中。我用过 v ertex AI logo 。然后,您可以定义徽标路径:**
****html_logo = "_static/vertex.png"****
列出降价文件中存在的所有外部链接:
****intersphinx_mapping = { "python": ("[https://python.readthedocs.org/en/latest/](https://python.readthedocs.org/en/latest/)", None)}****
请参阅我的配置文件 conf.py:
V .构建文档
要使用 Sphinx 生成 HTML 文档,请转到/docs 并使用命令:
****make html****
使用 Firefox 打开 HTML 页面:
****firefox docs/build/html/index.html****
如果你设法完成了所有的步骤,你应该能够看到一个更吸引人的 HTML 页面。
KFX 扩展将使 Sphinx 能够读取 Kubeflow 组件、函数名、参数和文档字符串。
使用 Makefile(位于项目的根目录)自动构建 文档。编辑 Makefile 并添加以下几行:
****create-sphinx-sources:
cd docs; make clean; cd ..; rm -r docs/source/pipelines; sphinx-apidoc -f -o docs/source/pipelines pipelines/generate-doc:
@ $(MAKE) create-sphinx-sources && \
cd docs; make html****
然后调用 make generate-doc:
****make generate-doc****
我们带着斯芬克斯到达了旅程的终点。我希望这些内容对你有用!
摘要
我们已经看到了如何使用 Sphinx,这是一个为您的机器学习项目生成文档的强大工具。我们定制了带有徽标、图像和降价内容的文档。当然,Sphinx 附带了许多其他扩展,您可以使用它们来使您的文档更加吸引人。
感谢您的阅读!
如果你想在收件箱里收到我未来的故事,别忘了订阅。
如果您喜欢阅读我的故事,并希望支持我成为一名作家,请考虑注册成为 Medium 会员,并获得数千篇数据工程和数据科学文章。
****https://medium.com/@anna.bildea/membership
用 Sphinx 记录 Python 代码
照片由 Patrik Fore 发自 Un spash
当从事一个需要在特定时间框架内完成的项目时,除了代码审查、自动化测试、单元测试和许多其他事情,我们很少有时间留给文档。不管你在开发什么,迟早你和你的同事会再次访问那段代码。当这一天到来时,我们大多数人都将迷失在这些代码块中!
由于耗费时间,文档被省略了,但是如果所有这些都可以自动化,并且只需一瞥,您就可以生成一个漂亮的网站来记录您的全部代码,那会怎么样呢?这就是斯芬克斯的用武之地!
什么是斯芬克斯?
简而言之,狮身人面像吸收了你的。rst 文件并将其转换为 HTML,所有这些都是通过一堆命令完成的!像 Django 、 NumPy 、 SciPy 、 Scikit-Learn 、 Matplotlib 等主要 Python 库都是用 Sphinx 编写的。现在,轮到我们使用它了,让我们开始吧。
安装斯芬克斯:
pip install sphinx sphinx_rtd_theme
这将安装 sphinx 包和主题*(可选)*,这将使我们的网站大放异彩。
文件夹结构:
首先,你可以克隆我的 Github 库,它的结构如下。在其中,数学存放了我们的 python 代码,这些代码需要进行文档化,我们的文档将放在 docs 文件夹中。
📦sphinx_basics
┣ 📂docs
┗ 📂maths
┃ ┣ 📜add.py
┃ ┣ 📜divide.py
┃ ┣ 📜multiply.py
┃ ┣ 📜subtract.py
┃ ┗ 📜__init__.py
一般来说,这是使用的惯例,你的代码将在一个源目录中(在我们的例子中是数学目录), docs 将存放你的文档。既然你已经装好了。遵循以下给出的步骤:
第一步:斯芬克斯-快速入门
在 docs 文件夹中运行下面的命令
sphinx-quickstart
运行该命令后,接受默认值。它看起来会像这样:
sphinx 快速入门示例
sphinx-quickstart 是一个交互式工具,它询问一些关于您的项目的问题,然后生成一个完整的文档目录和一个make.bat
文件,稍后将使用该文件生成 HTML。
第二步:编辑 conf.py 文件
转到您的conf.py
文件,取消第 13、14 和 15 行的注释。将os.path.abspath('.')
改为os.path.abspath('..')
。这里,我们告诉 sphinx 代码位于当前 docs 文件夹之外。
现在转到扩展部分,添加下面给出的扩展
更新的扩展列表
最后,进入主题,将“雪花石膏”替换为“斯芬克斯 rtd 主题”,完整的更新后的conf.py
文件如下所示:
最终 conf.py 文件
第三步:生成。rst 文件
到目前为止,您的 docs 文件夹中有index.rst
,这将是您的文档的登录页。但是我们还没有生成maths.rst
,里面有我们的 python 代码。
转到父文件夹 sphinx_basics,运行命令:
sphinx-apidoc -o docs maths/
在这个命令中,我们告诉 sphinx 从 maths 文件夹中获取我们的代码,并输出生成的。docs 文件夹中的 rst 文件。在这个命令之后,您将看到在您的文档中生成的maths.rst
和modules.rst
。
第四步:包含 module.rst 并生成 html
现在,将生成的modules.rst
文件包含在您的index.rst
中
index.rst 文件
然后我们就可以生成漂亮的文档了,进入 docs 文件夹并运行命令
make html
就是这样!!!。您可以在 docs 中的 _build 文件夹中看到生成的 HTML 文件,该 HTML 文件如下所示
文档网站的登录页面
数学包
此外,除此之外,您还可以在搜索页面上搜索任何方法、子包等
搜索页面
就是这样!Sphinx 完成了创建 HTML 文档的所有繁重工作。
现在,假设您对代码做了一些更改,现在您想重新构建 HTML,请转到 docs 文件夹并编写
make clean html
然后
make html
这将重新构建您的 HTMLs,同时考虑您的更改。
还有,你可以改变主题,尝试不同的;通过格式化文档字符串和更多的东西来改变文档格式!
耶!我们成功地自动化了催眠文档部分。希望这篇文章对你有所帮助,你可以随时记录下你的下一个项目🚀
自动更正让生活更美好吗?
系统性机器学习失败的警示故事
劳拉·里维拉在 Unsplash 上的照片
将数据科学应用于许多产品和业务的潜在好处之一是有望减少我们日常生活中的摩擦和不便。这个想法是精心制作的机器学习模型嵌入到我们使用的所有设备和服务中。当我们越来越自由地专注于生活中重要的事情时,他们会不知疲倦地努力消除我们生活中的各种烦恼和负担。
这只是过于乐观的白日梦吗?
如果我们要实现这些技术的潜力,我们需要评估机器学习在日常生活中让我们失败的许多小方面。我们可以继续列举一些事情,比如种族主义的图像分类器,性别歧视的招聘工具,或者聊天机器人中可能出现的多种形式的精神变态。相反,让我们关注一种更平凡、更普遍的机器学习失败形式:自动纠错,这种失败对少数群体和多数群体都有影响。
自动校正是一种简单的数字辅助形式。你输入一些东西,机器识别出这不是一个单词,所以它把它改成它认为你想输入的。这些系统被嵌入到我们的手机中,既在我们的操作系统中,有时也在手机的特定应用程序中。一些版本只是单词相似性和频率的基本统计模型,其他版本采用机器学习并考虑句子中的其他单词。从表面上看,他们的目的很清楚;我们想把我们写的文字中的错别字去掉。
我写“【wutocorecorect】,设备将其更改为“ 【自动更正】 ”
我写了"“,设备猛扑过来,把它改成” 【失败】
当我们查看一个句子中某个关键词的更正时,问题就出现了。
我打" 你需要什么? “而自动更正将其改为” 你为什么需要?【①
突然之间,我试图问一个需要澄清或说明的问题,变成了对正当理由的推诿。整个句子的意思发生了变化,伴随着潜在的负面情绪解释。雪上加霜的是,原文尽管有拼写错误,但完全可以理解。 这最后一个事实对于许多不同的错别字来说都是常见的,并且由短信中去掉单词的常见做法完美地证明了这一点。
值得停下来思考最后一点。我相对现代的智能手机上的自动纠错功能很高兴地推出来了,它以一种可以改变句子意思的方式纠正单词。即使在我们有证据表明,在大多数情况下,拼写错误最糟糕的结果是阅读速度变慢的情况下,它也会这样做。
这是技术上的失败。
这个复杂的软件功能并没有为我提供实用性,而是主动妨碍了我的交流。怎么会这样呢?如果我们要在世界上推进数据科学的部署,我们应该彻底了解这样一个平凡的任务如何导致产生负面结果的产品。
根本原因是,在构建这些模型时,它们是使用与对最终用户的影响无关的指标进行评估的。在一个理想的世界里,我们会考虑对我们写作的任何改变会如何影响我们写作的可读性和理解性。但是,获得一个允许机器学习开发者评估最终目标的数据集是很难的。更简单的方法是收集一些关于特定单词被错误键入的常见方式的数据,并使用描述单词被正确修改与被错误修改的比例和比率的标准度量来评估它们(例如参见[3])。公平地说,这些模型可以用在对沟通失误不太敏感的情况下,比如纠正搜索查询的内容。最近关于评估自动纠错方法的学术工作强调了单词上下文的重要性[4]和文本的可理解性[5]。然而,他们都没有把对理解的预期影响作为评估的中心。
这就是机器学习项目如何增加我们的负担。它们是由这样的人创建的,他们要么与最终用户脱节,被最终用户所需的复杂性所淹没,要么没有时间或资源使用反映真实世界使用情况的数据来评估模型。所以他们简化了。他们构建一些能够完成合格且可测量的任务的东西,并假设这是朝着正确方向迈出的一小步。有时行得通,有时行不通。如果不是这样,我们就会被归入一种会让我们的生活变得更糟糕的技术,即使它起初看起来是一种进步。
理想情况下,任何文本修改模型的评估都将根据单词对句子理解的重要性来加权单词,或者使用试探法,当只有一个元音缺失时,严厉惩罚返回错误单词的模型。尚不清楚完美的评价会是什么,但这值得研究,因为人类的交流远远不只是一个大型的分布式拼写比赛。
如果技术的进程随着每个单独的模型而停止,那么情况就不会那么糟糕。随着时间的推移,设计不良的系统将被更好的系统所取代。不幸的是,技术发展还有其他更复杂的历史过程。次优决策可能会在以后的开发中固定下来。
让我们考虑一下 Swypo 的情况。
我的一个朋友最近向我介绍了术语 swypo ,指的是使用触摸屏滑动界面绘制字母时创建的消息中的错误单词。部分问题是界面必须解释想要的字母。他试图给我发信息“我想亲自告诉你”,但我收到的是“我会亲自带你去地狱。”
似乎自动纠错模式对完美拼写的痴迷正在影响第二层技术。我朋友使用的滑动界面试图生成拼写正确的单词序列。在这样做的同时,它创造了句法上尴尬的句子,与原意相差甚远,以至于它们促成了一种新的喜剧形式。
这就是机器学习失败成为系统性问题的原因。最初的捷径似乎是合理的,并导致模型提供了表面上的效用,但却产生了一层挫折和低效。这些方法及其固有的问题被构建在其上的后续技术层所固定。逐渐地,糟糕、仓促或次优的决策成为我们设备的基石。这个过程并不新鲜,科技史上有很多例子,qwerty键盘是最常被引用的。但有了机器学习,这种技术路径依赖有望加速。机器学习模型不太明显,通常也不太了解技术层。开发中的捷径和次优的设计选择将聚集起来,创造出一个微妙的系统性失败的世界。
如何才能避免这种情况?
这里有一个测试。如果你是一名数据科学家或创建机器学习模型的开发人员,你应该非常清楚如何选择要部署的模型。如果您的选择标准是基于某种标准的 ML 指标(如 RMSE),那么您应该问问自己,该指标减少一个单位将如何影响该模型的业务流程或用户。如果你不能对这个问题给出一个明确的答案,那么你可能根本没有解决这个问题。您应该返回到涉众那里,尝试并准确理解模型将如何被使用,然后设计一个评估标准来估计真实世界的影响。
你可能仍然会优化像 RMSE 这样的东西,但你会根据它对人们的影响来选择一个模型,你甚至会发现你的模型没有增加任何价值。在这种情况下,你能为社会做的最好的服务就是说服涉众不要部署,直到开发出一个改进的模型。
[1]在 Google Pixel 4 智能手机的 SMS 应用程序中生成的示例。
[2]基思·雷纳,萨拉·j·怀特,丽贝卡·l·约翰逊,西蒙·p·利弗塞奇,雷丁著《有一笔费用》,2006 年,
《心理科学》,17(3),192–193 页
[3]彼得·诺维格, 如何写拼写校正器 (2007)
[4]丹尼尔·茹拉夫斯基和詹姆斯·马丁。拼写纠正和
嘈杂频道(2021)https://web.stanford.edu/~jurafsky/slp3/B.pdf
*[5]HLA dek D,Staš,Pleva M. 自动拼写纠正调查。(2020);电子。9(10):1670.【https://doi.org/10.3390/electronics9101670 *
[6]这里收集了许多例子https://www.damnyouautocorrect.com/
更好的代码等于更好的数据科学吗?
在最近的一篇文章中, Galen 写了关于重代码和低代码数据工程角色之间日益扩大的差距,以及新工具如何塑造该领域的职业道路。它呼应了一场更广泛的、正在进行的关于编程技能以及它们对数据科学家有多重要(或有多重要)的辩论。
因为结论还没有出来,而且很可能还会有一段时间,所以本周,我们将重点介绍一批新的文章,这些文章专注于编码工作流以及数据科学家如何在该领域开发(原谅双关语)一些新的力量。让我们开始吧。
- 你需要新的软件工程学习策略吗? 不管你从事的是哪一个以数据为中心的特定学科,找到一种可持续的方法来学习新技能和有效地完成编程任务都是至关重要的。 Semi Koen 分享了五个策略,可以帮助你以一种简化、实用的方式处理复杂的编程概念。
- 几个自定义触摸可以让你在终端中有宾至如归的感觉 。在终端中工作和编码有时会感到冷漠和疏远——这是对不那么友好和用户友好时代的一种倒退。朱利安·维斯特决定不一定要这样,所以他写了一个全面的指南,教你如何用 oh-my-zsh 插件定制你的终端体验,让它成为你自己的。
- 熟悉动态编程 。Peggy Chang 解释了动态编程的基础知识,这是一种优化技术,“可以在降低时间或空间复杂性的情况下产生高效的代码”,然后通过几个有用的例子进行一些实际操作。
- 关于嵌套循环以及如何充分利用它们 。 Andrea Gustafsen 深入到日常编程任务的具体细节,探索嵌套 for 循环,并解释它们如何工作以及何时使用它们最有意义。
- 一个老派的工具拯救了一天 。你已经把 GNU Make 整合到你的工作流程中了吗?对于 Sean Easter 来说,“经典且历史悠久的构建工具”已经成为了一个意想不到的重要元素,它可以创建更健壮、更易于迭代、更易于协作和修改的数据项目。
但是等等,还有呢!(总会有……)
最近几周,我们发表了一些非常有趣的项目的帖子,并深入探讨了许多有趣的话题。以下是必读书目的一小部分:
- 汇集了机器学习、诗歌和区块链的世界, Jeremy Neiman 向我们展示了开发俳句生成器的过程。
- 如果你不喜欢俳句,但喜欢迷因,你肯定会想看看乔希·比克特在他创造的人工智能迷因创造者上的首次 TDS 帖子。
- 为了更广泛地了解最近人工智能方面的工作——尤其是围绕计算机视觉——Ygor Serpa汇编了一份 10 篇优秀论文的清单,添加到你的阅读队列中。
- 你是一个热爱地图的数据科学家吗?你会喜欢华景石关于用几行 Python 代码创建交互式地理空间可视化的循序渐进的教程。
- 在短暂的中断后,TDS 播客上周回归:主持人 Jeremie Harris 与人工智能研究员 Sam Bowman 聊起了“低估”——从业者中低估人工智能当前能力的趋势。
感谢您的时间和支持,感谢您让 TDS 成为您学习旅程的一部分。
直到下一个变量,
TDS 编辑
GPT-3 有个性吗?
AI + MBTI =?
一个机器人坐在空房间里,看着镜子大厅里自己无尽的映像和潜在的个性。由作者使用 DALLE-2 生成
迈尔斯-布里格斯类型指标(T1)或 MBTI 是一种常见的性格测试,旨在根据四个类别将人们分为 16 种性格类型(T2 ):内向或外向、感知或直觉、思考或感觉、判断或感知。虽然 MBTI 被批评为伪科学,也许和星座没什么区别,但测试并和他人比较你的性格类型仍然是一项有趣的练习。
因此,在这篇文章中,我展示了我如何使用 GPT-3,一个由 OpenAI 创建的生成式人工智能语言模型,来完成两件事。首先,我让 GPT 3 号模仿一个 20 个问题的游戏来猜测我的性格。第二,一旦 GPT-3 能够(正确!)猜猜我的性格类型,为了确定它的性格类型,我用了 GPT-3 问我的同样的问题。
第 1 部分:促使 GPT-3 猜测我的个性
我首先向 GPT-3 提示了以下内容:
Let's play 20 questions!You are a psychologist and personality type expert. Your job is to guess my MBTI personality type by asking 20 questions.The questions can be freeform answers but should NOT be a yes/no question.Do NOT repeat questions that have already been asked.Ask ONE question at a time.
我让 GPT-3 一次产生一个问题(用绿色突出显示)。我在下面的行中回答。以下是 GPT-3 提出的一些问题和我提供的答案。
GPT-3 提出的示例问题和我给出的自由形式的答案(使用 OpenAI Playground )。作者截图
不像大多数性格测试,通常需要你回答从同意到不同意的问题,我能够用纯文本来回答问题。与简单的同意/不同意量表相比,这些自由形式的回答可能会让你对自己的个性有更多的了解,并透露更多的细微差别。
来自 16 种性格的 MBTI 性格测试示例。这些测试通常会将你的答案分为同意和不同意。
在所有 20 个问题被问完和回答完之后,我用下面的话提示 GPT-3:
Given my answers, what MBTI personality type would you categorize me as and why? Answer in a step-by-step fashion and back up your reason with analysis.
GPT-3 的输出预测了作者的 MBTI 人格类型。作者截图。
GPT-3 能够准确预测我的性格类型(是的,我是 ENFP)。相当令人印象深刻!GPT-3 也能够解释哪些特征对我成为 ENFP 人贡献最大。虽然这感觉有点像看我的星座运势,但知道 GPT-3 从 20 个问题中了解了我的个性,我感到一丝肯定。
第二部分:促使 GPT-3 猜测 GPT-3 的个性
在第一部分中,GPT-3 提出了 20 个问题:
1\. Do you prefer to live in the moment or plan for the future?
2\. What are some things you like to do in your free time?
3\. Do you like to be alone or do you feel antsy and lonely when by yourself?
4\. What kind of emotions do you feel the most?
5\. Do you like to move quickly and be constantly active or do you like slower-paced activities and moments?
6\. Are you more of a 'big picture' person or do you like details and specifics?
7\. Do you prefer to work independently or with a team?
8\. Do you like to have a lot of change in your life or do you like more stability?
9\. What kind of environments do you feel the most comfortable in?
10\. Do you like to have a lot of people around you or do you feel drained after being around people
11\. Do you like to plan and organize your time or do you prefer to go with the flow?
12\. Do you like to be in control of every situation or do you like to let others take the lead?
13\. What is your ideal career?
14\. Do you like to take risks or play it safe?
15\. Do you like to be spontaneous or do you like to plan everything in advance
16\. What is your ideal romantic partner like?
17\. Do you believe there is such thing as true love?
18\. Do you like to be challenged mentally and emotionally or do you like things to be easy
19\. Do you like intellectual conversations or do you prefer small talk
20\. Do you feel like you need to save the world or make a difference in some way?
我很好奇 GPT-3 是什么性格类型。我可以提示 GPT-3,直接问它(“你的性格类型是什么?”),不过那也挺无聊的。相反,让 GPT-3 回答它在第一部分中问我的 20 个问题会更有趣。然后,我可以让 GPT 3 号根据它自己对问题的回答推断出它的个性。
我给 GPT 三号做了如下提示,鼓励 GPT 三号按照一定的格式回答:首先按顺序回答 20 个问题中的每一个,然后输出它对什么样的 MBTI 人格最符合答案的分析。
Let's play 20 questions!Answer each of the questions in the following format.
${Question number}. ${Question}
${free-response answer}After you have answered all of the questions,
Given the answers to all of the questions, what MBTI personality type would you categorize the answers as and why? Answer in a step-by-step fashion and back up your reason with analysis.1\. Do you prefer to live in the moment or plan for the future?
2\. <the rest of the 20 questions>
下面是我从 GPT-3 得到的四个不同的结果。很快,你可能会注意到有些不对劲。在每一个例子中,GPT-3 的答案都有轻微的变化,导致它预测四种不同的性格类型!GPT-3 是 INTJ、ENFP、ESTP 还是 INFP?
GPT-3 提供的四个样本答案的截图,用于回答 20 个人格问题并确定其人格类型。作者截图
这种结果的多样性源于随机性:GPT-3 是一个概率性的,而不是确定性的模型,同样的提示你会得到不同的结果。随机性部分来自于温度和 top-p 等参数,这些参数控制着模型预测的随机性。
我很好奇,如果我让 GPT-3 进行 4 次以上的人格测试,会发生什么。因此,我使用 OpenAI API 提示 GPT-3 进行 100 次人格测试。然后,我评估了输出,并计算了每种 MBTI 类型出现的次数。
100 次 API 调用之后,结果如下:GPT-3 在 36%的时间里被识别为 ENFP。然而,第二种最常见的人格类型,INTJ,与 ENFP 截然不同。在预测的 MBTI 类型中似乎没有太多的模式,除了“N”很常见这一事实(这意味着 GPT-3 可能有更多的直觉而不是感觉特征)。然而,如图所示,GPT-3 并没有单一的主导人格类型。相反,它是许多不同性格类型的融合。
GPT-3 作为某个 MBTI 组回答的次数超过 100 次。由作者创建的图形。
作为一个非常仓促的“真实世界”比较,我发现一个数据源列出了性格类型的分布。我在引用中使用“真实世界”(请有所保留),因为它来自 MBTI 性格测试平台(16 种性格),并不代表世界上所有人的性格类型。MBTI 也不是代表或分类人格特质的最佳测试。然而,这个数字是作为一个比较点,与我们在上面看到的 GPT-3 的反应。
人格类型的“现实世界”分布。由作者创建的图形。数据来源于16 位名人。
那么这意味着什么呢?GPT 3 号有 36%的时间是 ENFP?互联网上有更多具有 ENFP 特征的人(特别是 Reddit),这构成了 GPT-3 训练数据的很大一部分?你的猜测和我的一样好。
关于参数的一个注记
在所有这些实验中(在 OpenAI Playground 和 API 中),我保留了相同的(默认)参数。未来的实验可以尝试消融研究,观察 GPT-3 对 20 个人格问题的回答如何随着一个参数(如 top-p 或温度)的不同值而变化,同时保持其他参数不变。
{
temperature: 0.7,
top_p: 1,
max_tokens: 700
}
结论
在第一个实验中,GPT 3 号试图猜测我的个性,我展示了 GPT 3 号在让用户判断他们的个性时是有洞察力的。在第二个实验中,GPT 3 号试图猜测自己的个性,我发现 GPT 3 号的反应并不一致。相反,它的反应是相当多样的,这表明也许 GPT-3 不是一个单一的、有强烈认同感的有凝聚力的意识。
一个机器人走过镜子大厅的最后临别图像。由作者使用 DALLE2 生成
希望你喜欢阅读这篇文章!希望听到任何评论或反馈。
最初的咖啡萃取能预测最终的萃取吗?
咖啡数据科学
收集数据,一次一汤匙
最近,我买了一个像样的咖啡机。这对我来说是一个很大的变化,因为通常,我在手柄上有一个手柄,可以从我的 Kim Express 杠杆机器上拍摄。有了这只新的空出来的手,我很好地利用了它来收集最初的样本:最初的几滴。
我根据一些指标对我的档案进行了修改,经过几轮修改后,我决定一次做几件事。有些是时间,但最终的衡量标准是提取率。我从理论上讲,如果我品尝了最初几滴浓缩咖啡,我可以修改预冲泡,以获得最佳的预冲泡效果。我想这些滴剂可能有助于预测我是否在改善预输注方面做得更好。
草案
这是我每次拍摄时使用的协议:
- 拿着勺子收集最初的几滴
- 立即倒入杯中。这可能会在勺子上留下一些可溶物。
- 称一下杯子
- 使用等量的水进行滴定
- 测量 TDS 并乘以 2 以说明滴定。
设备/技术
浓缩咖啡机:体面浓缩咖啡机(DE1Pro)
咖啡研磨机:小生零
咖啡:家庭烘焙咖啡,中杯(第一口+ 1 分钟)
预输注:瞄准一个较长的大约 25 秒的时间
输液:压力脉动
过滤篮 : 20g VST
其他设备: Atago TDS 计、 Acaia Pyxis 秤、 Kruve 筛
绩效指标
我使用三个指标来评估数据:
用折射仪测量总溶解固体量(TDS),这个数字结合弹丸的输出重量和咖啡的输入重量用来确定提取到杯中的咖啡的百分比,称为提取率(EY)** 。**
强度半径(IR) 定义为 TDS 对 EY 的控制图上原点的半径,所以 IR = sqrt( TDS + EY)。这一指标有助于标准化产量或酿造比的击球性能。
数据分析
我收集了 45 个样本,同时浏览了 22 个配置文件。每个配置文件都有多次更改。根据这个数据,前几滴的 TDS 似乎没有什么趋势。
部分原因可能是最初的几滴并不总是来自同一个地方。如果它们来自篮子的侧面,它可能没有中间的强度大。
我不认为这是决定性的证据,证明最初的几滴不能预测一杯浓缩咖啡的最终表现。我的许多镜头都有一些环形提取(在冰球中间较慢的流动),这在不同的镜头之间有所不同。如果圆盘上的提取变得更加均匀,那么再次测试这个指标可能会很有意思。
如果你愿意,请在 Twitter 、 YouTube 和 Instagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。也可以在 LinkedIn 上找到我。也可以在中和订阅关注我。
我的进一步阅读:
您的笔记本电脑对数据科学重要吗?旧 ThinkPad 与新 MacBook Pro 对比
英特尔 i3–6100 u 上的 Pandas 和 TensorFlow 性能指标评测与 M1 Pro MacBook Pro 相比,速度慢了 50 倍?
文章缩略图— ThinkPad L470(左)和 M1 Pro MacBook(右)(图片由作者提供)
自发布以来,16 英寸的 M1 Pro MacBook 一直是我的首选。它在超高级的表面下提供了令人难以置信的性能,同时可以持续一整天,并拥有可能是业界最好的屏幕。
*但是一台旧的双核联想 ThinkPad 呢?第六代英特尔处理器能接近现代的发电站吗?*继续阅读寻找答案。
在今天的文章中,我们将在一系列基准测试中比较旧的 ThinkPad L470 和现代的 M1 Pro MacBook Pro,从合成产品到熊猫和 TensorFlow。基准测试的范围是有限的,所以一切都可以在十分钟内完成。
在继续之前,让我们比较一下硬件规格:
图 1 —硬件规格对比(图片由作者提供)
这不是真正的苹果之间的比较。Mac 起价 2499 美元,我买的 ThinkPad 不到 200 美元。这要便宜 12 倍以上,所以 Mac 有理由这么做。
不想看书?请观看我的视频:
英特尔酷睿 i3–6100 u 性能指标评测——现在还不太好
6100U 早在 2015 年就发布了,所以我们真的不能指望它能与更现代、更强大的芯片相媲美。此外,综合基准只能带我们走这么远,但它们是一个很好的起点。
以下是 i3–6100 u 与单核部门的 M1 Pro 芯片的比较:
图 2 — Geekbench 单核性能(图片由作者提供)
M1 专业版大约快 3.5 倍,这是意料之中的。即使在今天,大多数 Windows 笔记本电脑也无法与之媲美,所以这真的不足为奇。
多核是有趣的地方。i3 只有两个内核,而 M1 Pro 有 8 个性能内核和 2 个效率内核。结果如下:
图 3 — Geekbench 多核性能(图片由作者提供)
如你所见,Mac 在这个测试中快了 12 倍。这是一个巨大的差异,你肯定会在日常使用中注意到这一点。此外,macOS 需要更少的资源来顺利运行,所以这是你必须考虑的另一个问题。
现在让我们进入实际的数据科学基准,从熊猫开始。
Pandas 基准测试—典型数据工作流程时间对比
在一篇文章中,我只能比较这么多,所以让我们继续讨论基础知识。我们将比较创建、保存、读取和转换熊猫数据帧所需的时间。
创建一个熊猫数据框架
让我们创建两个数据集——第一个有 100 万行,第二个大约有 5 行。这些应该足以将 ThinkPad 推向极限。
下面的代码片段导入了本节需要的所有库,还声明了一个函数create_dataset()
,它创建了数据集。
import random
import string
import numpy as np
import pandas as pd
from datetime import datetime
np.random.seed = 42
def create_dataset(start: datetime, end: datetime, freq: str) -> pd.DataFrame:
def gen_random_string(length: int = 32) -> str:
return ''.join(random.choices(
string.ascii_uppercase + string.digits, k=length)
)
dt = pd.date_range(
start=start,
end=end,
freq=freq, # Increase if you run out of RAM
closed='left'
)
df_size = len(dt)
df = pd.DataFrame({
'date': dt,
'a': np.random.rand(df_size),
'b': np.random.rand(df_size),
'c': np.random.rand(df_size),
'd': np.random.rand(df_size),
'e': np.random.rand(df_size),
'str1': [gen_random_string() for x in range(df_size)],
'str2': [gen_random_string() for x in range(df_size)]
})
return df
现在,让我们使用它来创建 1M 和 5M 数据集:
########## 1M Dataset ##########
df_1m = create_dataset(
start=datetime(2010, 1, 1),
end=datetime(2020, 1, 1),
freq='300s'
)
########## 5M Dataset ##########
df_5m = create_dataset(
start=datetime(2010, 1, 1),
end=datetime(2020, 1, 1),
freq='60s'
)
以下是笔记本电脑和数据集的时间:
图片 4-创建数据集所需的时间(图片由作者提供)
从各方面考虑,这并不是一个很大的差别。对于 1M 数据集,Mac 大约快 7 倍,对于 5M 数据集,大约快 5 倍。ThinkPad 上没有发生任何崩溃或冻结,只是需要更多的时间。
将数据集保存到 CSV 文件
在整个项目过程中,您可能会将数据保存在不同的状态中,因此将花费在这里的时间减到最少会很好。下面的代码片段将两只熊猫的数据帧转储到一个 CSV 文件:
########## 1M Dataset ##########
df_1m.to_csv("/Users/dradecic/Desktop/1Mdf.csv", index=False)
########## 5M Dataset ##########
df_5m.to_csv("/Users/dradecic/Desktop/5Mdf.csv", index=False)
结果如下:
图 5-将数据集保存为 CSV 文件所需的时间(图片由作者提供)
和我们之前的故事相似。将熊猫数据保存到 CSV 文件时,Mac 的速度大约快了 5 倍。
从磁盘读取 CSV 文件
但是反过来呢?在 Mac 上读取 CSV 文件也会快 5 倍吗?代码如下:
########## 1M Dataset ##########
df_1m = pd.read_csv("/Users/dradecic/Desktop/1MDF.csv")
########## 5M Dataset ##########
df_5m = pd.read_csv("/Users/dradecic/Desktop/5MDF.csv")
结果是:
图 6 —读取 CSV 文件所需的时间(图片由作者提供)
到目前为止,5X 时差似乎是普遍存在的。
将函数应用于列
通过调用 DataFrame 列上的apply()
方法,可以在 data frame 列上应用自定义函数。下面的代码片段颠倒了最初包含 32 个字符的随机字符串的str1
列:
def reverse_str(x) -> str:
return x[::-1]
########## 1M Dataset ##########
df_1m['str1_rev'] = df_1m['str1'].apply(reverse_str)
########## 5M Dataset ##########
df_5m['str1_rev'] = df_5m['str1'].apply(reverse_str)
让我们来看看结果:
图 7 —反转一列字符串所需的时间(图片由作者提供)
又一次,5X 时差对 Mac 电脑有利。
总结一下——M1 Pro MacBook 一直比 ThinkPad L470 快 5 倍左右。这并不令人惊讶,但让我们看看 TensorFlow 是否能让这种差异变得更大。
TensorFlow 基准测试—自定义模型和迁移学习
只是为了解决房间里的大象——可以在 i3–6100 u 或任何其他低端芯片上安装甚至运行 TensorFlow。它可以工作,但是很慢,因为处理器不是很强大,也没有 GPU 的支持。
对于 TensorFlow 基准测试,我使用了来自 Kaggle 的狗与猫数据集,该数据集是在知识共享许可下授权的。长话短说,你可以免费使用。至于数据集准备,如果您想复制结果,请参考本文中的。
让我们开始第一项测试。
自定义张量流模型
第一个 TensorFlow 基准测试使用数据扩充,并将两块卷积模型应用于图像数据集。它没有什么特别的,只是一个你在学习 TensorFlow 时可能会偶然发现的模型架构:
import os
import warnings
from datetime import datetime
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
warnings.filterwarnings('ignore')
import numpy as np
import tensorflow as tf
tf.random.set_seed(42)
####################
# 1\. Data loading
####################
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
train_data = train_datagen.flow_from_directory(
directory='/Users/dradecic/Desktop/data/train/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
valid_data = valid_datagen.flow_from_directory(
directory='/Users/dradecic/Desktop/data/validation/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
####################
# 2\. Model
####################
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), input_shape=(224, 224, 3), activation='relu'),
tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), activation='relu'),
tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(2, activation='softmax')
])
model.compile(
loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(),
metrics=[tf.keras.metrics.BinaryAccuracy(name='accuracy')]
)
####################
# 3\. Training
####################
model.fit(
train_data,
validation_data=valid_data,
epochs=5
)
该模型在每台机器上训练了 5 个历元,下图比较了平均历元时间:
图 8-自定义模型上每个历元的张量流平均时间(图片由作者提供)
正如你所看到的,TensorFlow 可以在一台旧笔记本电脑上运行,但与 Mac 相比慢了大约 8 倍。如果你刚刚开始,这不是一个交易破坏者。
张量流迁移学习模型
下面的代码片段或多或少与上一个相同,但有一个重要的区别——它现在使用一个预训练的 VGG-16 网络来分类图像:
####################
# 1\. Data loading
####################
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
train_data = train_datagen.flow_from_directory(
directory='/Users/dradecic/Desktop/data/train/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
valid_data = valid_datagen.flow_from_directory(
directory='/Users/dradecic/Desktop/data/validation/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
####################
# 2\. Base model
####################
vgg_base_model = tf.keras.applications.vgg16.VGG16(
include_top=False,
input_shape=(224, 224, 3),
weights='imagenet'
)
for layer in vgg_base_model.layers:
layer.trainable = False
####################
# 3\. Custom layers
####################
x = tf.keras.layers.Flatten()(vgg_base_model.layers[-1].output)
x = tf.keras.layers.Dense(128, activation='relu')(x)
out = tf.keras.layers.Dense(2, activation='softmax')(x)
vgg_model = tf.keras.models.Model(
inputs=vgg_base_model.inputs,
outputs=out
)
vgg_model.compile(
loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(),
metrics=[tf.keras.metrics.BinaryAccuracy(name='accuracy')]
)
####################
# 4\. Training
####################
vgg_model.fit(
train_data,
validation_data=valid_data,
epochs=5
)
这次的结果大不相同:
图 9 —迁移学习模型中每个时期的张量流平均时间(图片由作者提供)
正如你所看到的,在 Mac 上,每个时期的平均训练时间几乎没有增加,但在 ThinkPad 上却飞速增长。在 i3–6100 u 上等待迁移学习模型完成培训花费了一天中的大部分时间,因为与 Mac 相比,它几乎慢了 54 倍。
那么,**我们从这些基准测试中学到了什么?**接下来让我们回顾一些见解。
旧笔记本电脑上的数据科学—需要考虑的事项
在旧笔记本电脑上进行数据争论或数据科学工作并不是不可能的,尤其是如果你刚刚起步的话。有几件事你应该记住,所以让我们复习一下。
旧笔记本电脑+深度学习=谷歌 Colab
没有理由仅仅因为你刚开始深度学习就让一台旧笔记本电脑经历磨难。 Google Colab 是一个令人惊叹的预配置笔记本电脑环境,不需要任何成本,并且包含 GPU 支持。如果你需要更多的功能和更长的运行时间,可以考虑 Colab Pro。
港口的缺乏
说 Mac 有更好的端口选择很奇怪,但在这种情况下确实如此。ThinkPad L470 已经 5+岁了,所以它没有 USB-C,甚至没有 HDMI 端口。连接外接显示器就没那么容易了。
有一个 VGA 端口可用,但这需要一个适配器,如果你有一个现代的外部显示器,图像质量会有所下降。不是交易破坏者,只是需要考虑的事情。
有了 Mac,我可以用一根 USB-C 电缆连接 4K 外部显示器、监视器灯、手机充电器、麦克风和耳机。
可怕的屏幕
我从来没有对 ThinkPad 的显示器印象深刻,但 L470 将这种糟糕带到了一个完全不同的水平。显示屏为 14 英寸,对于笔记本电脑来说非常出色,但分辨率不是 1366x768。所有的东西都很大,你不能在屏幕上放超过一两个窗口。色彩准确度也非常糟糕。
如果连接全分辨率的外部显示器更容易,这就不是问题了。
另一方面,Mac 上的迷你 LED 屏幕跨度为 16 英寸,分辨率为 3456x2234。至少可以说,这是白天和黑夜的差别。
结论
英特尔的 i3–6100 u 可以相当好地处理熊猫,它也可以用 TensorFlow 训练深度学习模型,尽管速度很慢。你绝对可以在使用它的同时进入数据科学,甚至在行业内从事专业工作。如今大部分工作都在云端完成,所以笔记本电脑归根结底是被美化了的键盘。
与新事物相比,廉价的旧笔记本电脑的最大问题不是原始性能,而是其他方面的妥协。每天看着可怕的屏幕 8 个多小时,处理劣质电池和一大堆电缆并不好玩。
但另一方面,如果你想进入数据科学领域,并有 200 美元的闲钱,一台旧笔记本电脑就足够了。
你对新旧笔记本电脑的对比有什么看法?您目前使用的笔记本电脑曾经风光过吗?如果是,你每天都在与什么问题作斗争?请在下面的评论区告诉我。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
更多基准
保持联系
原载于 2022 年 11 月 22 日 https://betterdatascience.comhttps://betterdatascience.com/data-science-old-laptop/。
我的新中央供暖锅炉对这些疯狂的高油价有帮助吗?
在能源危机中,我们使用相互作用的线性回归对我的新中央供暖锅炉的性能进行基准测试
简介
随着东欧的冲突愈演愈烈,副作用之一是能源价格飙升。自今年年初以来,我的汽油价格轻松翻了两番,所以像一个好书呆子类型的数据科学家一样,我开始研究我的能源使用数据,以减轻我的担忧。幸运的是,我的能源供应商允许我下载过去几年每天的用气量数据。我首先想到的一件事是,我们今年的天然气用量低于我根据前几年的预期。可能导致这种情况的主要怀疑之一是在 2022 年 4 月底安装了一个新的中央供暖锅炉,因为它是唯一使用天然气的设备。
在这篇文章中,我们探索了我的能源数据,并建立了一个回归模型,其中包括量化我的新中央供暖锅炉对我的燃气用量的影响程度的相互作用。
*注:*本文也可以在 github 上阅读,包含全部代码。
日常用气量和温度
主要数据来源是我的能源供应商 Eneco 的网站。他们允许我从他们的在线系统下载一个 Excel 表格,里面有每天的能源使用情况。请注意,我对我的使用数据(最小-最大比例)进行了标准化,以保护我的数据隐私。此外,我还下载了附近一个气象站的温度数据,因为我怀疑气体的使用会强烈依赖于外部温度。以下代码读取数据,并根据日期合并两个数据集:
每周每日用气量和每周趋势线(自行创建)
其中点是每天的用气量,线是每周的平均值。这清楚地显示了天然气使用的年度趋势,冬季使用量高,夏季使用量低。一个非常显著的特点是日值在横线上累加。这是因为用气量是以整数增量报告的,有效地将数值四舍五入到最接近的最小整数。为了减轻这种整数效应,我们将整个数据集重新采样为每周总用气量和每周平均温度。使用这些重新采样的数据,我们绘制了一个曲线图,显示温度与用气量的关系:
gas_usage = (
energy_usage['gas_usage']
.resample('W').sum().reset_index()
.merge(temperature.resample('W').mean().reset_index(), left_on='Datum', right_on='date')
)
# Some obviously wrong data eliminated
gas_usage = gas_usage.query('gas_usage >= 0.55')
# Eliminate weeks that do not have the full 7 days, because we sum the data over the days
# and for those non-complete weeks this would underestimate the gas_usage, thus skewing the results
non_complete_weeks = (
energy_usage['gas_usage']
.resample('W').count() != 7
)
non_complete_dates = non_complete_weeks.loc[non_complete_weeks].index.values
gas_usage = (
gas_usage.loc[~gas_usage['Datum'].isin(non_complete_dates)]
.reset_index().drop(columns='index') # This is sadly needed as plotnine will throw an error without it. Might be connected to the new index not being contiguous anymore
)
(
ggplot(gas_usage, aes(x='T_gem', y='gas_usage'))
+ geom_point()
+ geom_smooth(method='lm')
)
平均周温度与气体使用量(自行创建)
这清楚地表明温度和气体用量之间有很强的相关性。使用这种影响作为基线,我们现在可以尝试和模拟取代中央供暖锅炉的影响。
对标集中供热锅炉
我们对锅炉进行基准测试的第一步是创建一系列每月燃气用量的箱线图。此外,对于新锅炉安装后的几个月,我们增加了单独的箱子:
锅炉安装前后每月用气量的箱线图(自创)
这向我们显示了新锅炉使用更少燃气的第一个暗示,安装后几个月(5 月-9 月,青色方框)显示燃气使用量更低。下一步是为安装前和安装后的数据重新绘制我们的温度与用气量曲线图:
这证实了我们先前的观察:气体用量较低,并且随着回归线的斜率显著降低,这种影响似乎与温度成比例。然而,安装后的数据仍然非常有限,因为寒冷而触发供暖的周数也不多。但是考虑到目前非常高的油价,这给了我一些希望,我们可以用比去年冬天更少的汽油度过这个冬天。*更新:*文章中包含的新数据(截至 12 月 28 日)进一步证实了斜率已经下降,尽管下降幅度没有旧的有限数据显示的严重。p
为了量化这种影响,我们使用 statsmodels 的公式界面创建了一个回归模型。请注意,我们不能简单地在公式中包含两个变量,就像“T_gem + C(post_install)”,因为这将只允许线与线之间的截距不同。这显然不是我们想要的给定的结果,上面的情节,我们希望不同的斜率后和预安装。解决方案是使用相互作用,即告诉回归模型‘t _ gem’和‘gas _ usage’之间的关系基于‘c(post _ install)’而变化。在公式中,您可以使用 *
: T_gem * C(post_install)
来指定。这意味着“gas_usage”和“T_gem”之间的关系(斜率)对于“C(post_install)”的两个值可以是不同的,导致上面图中的两条线。运行代码会产生:
其中系数需要解释为:
- “拦截”零摄氏度时的(匿名)用气量
C(post_install)[T.True]
在零度时,新锅炉使用的燃气少 0.91(post _ install = True)- 温度从 0 开始每增加一度,用气量下降 0.15,即斜率为-0.15。
T_gem:C(post_install)[T.True]
新锅炉将T_gem
的斜率改变 0.04。这使得斜率(-0.15 + 0.04) = 0.11,这意味着当温度下降时,用气量增加不太明显。
这些系数的附加效应是我们所期望的:新锅炉使用更少的气体,但绝对效应随着温度的升高变得不那么强烈。以下回归公式进一步说明了这一点:
结论
对于我在安装新锅炉后获得的有限数据集,情况看起来不错。它使用更少的汽油,并有望大大影响我的水电费。但是也有一些限制:与多年的安装前数据相比,安装后的数据非常有限,特别是因为安装后的冬季数据不包括在内。今年冬天,我当然会收集更多的数据,这将提供更多的确定性。一个潜在的干扰因素可能是我们把恒温器调低到 18 度,这几乎肯定会影响我们的天然气使用。但这是一个在春天解决的有趣话题。*更新:*截至 12 月 28 日的新数据很好地完善了这篇文章:天然气使用量下降,但没有我们根据早期数据预测的那么严重。
本文也可以在 github 上阅读,包括全部代码。
我是谁?
我叫 Paul Hiemstra,是荷兰的一名教师和数据科学家。我是科学家和软件工程师的混合体,对与数据科学相关的一切都有广泛的兴趣。你可以在 medium 上关注我,或者在 LinkedIn 上关注 T4。
如果你喜欢这篇文章,你可能也会喜欢我的其他一些文章:
Python 还需要 map()函数吗?
有了各种备选方案,Python 的 map()似乎就显得多余了。那么,Python 到底需不需要呢?
Python 需要 map()函数吗?Muhammad Haikal Sjukri 在 Unsplash 上拍摄的照片
别担心,这不会是第一百万篇关于如何在 Python 中使用map()
的文章。我不会告诉你这比列表理解或循环更好或更差。我不打算将它与相应的生成器或列表理解进行对比。我不会说使用map()
会让你看起来像一个高级 Python 开发者…
你可以在 Medium 上发表的其他文章中读到所有这些内容。尽管内置的map()
函数在 Python 开发人员中并不太受欢迎(你很少会在产品代码中发现它),但它在 Python 作者中却很受欢迎(例如,参见这里的、这里的这里的和这里的)。这是为什么呢?也许因为这是一个有趣的函数。类似于函数式编程;或者可以用它的替代品作为基准,而基准通常会引起注意?
大多数关于 Python 的map()
的文章仅仅展示了如何使用,而没有展示为什么:在展示如何使用它的同时,他们通常没有解释为什么应该使用它。难怪,尽管它在 Python 作者中很受欢迎,但在中级开发人员中似乎没有得到应有的重视。
如果你想了解更多关于map()
的知识,这篇文章就是为你准备的。我们将讨论map()
函数在 Python 代码路线图中的位置,以及为什么不管你是否会使用它,了解这个函数都是值得的。
几句关于map()
的话
做一些大多数 Python 开发人员经常做的事情:为 iterable 的每个元素调用一个函数(实际上是一个 callable)。
它接受一个可调用函数作为参数,所以它是一个高阶函数。因为这是函数式编程语言的典型特征,map()
符合函数式编程风格。比如,你会在洛特的《T21 函数式 Python 编程》一书中发现很多map()
的应用。我认为map()
使用了类似的 API,而不是真正的函数式编程。这是因为我们可以把map()
用于不纯的函数,也就是有副作用的函数;这在真正的函数式编程中是不可接受的。
是时候看看map()
的行动了。
>>> numbers = [1, 4, 5, 10, 17]
>>> def double(x):
... return x*2
所以,我们有一个数字列表,我们有一个函数可以将一个数字加倍。double()
适用于单个号码:
>>> double(10)
20
如果我们用double()
代替numbers
会怎么样?
>>> double(numbers)[1, 4, 5, 10, 17, 1, 4, 5, 10, 17]
如果你知道 Python 中列表相乘的工作原理,这不会让你吃惊。虽然这是正常的行为,但这绝对不是我们想要达到的目标。上图,double(numbers)
将函数double()
应用到numbers
整体(作为对象)。这不是我们想要的;我们要将double()
应用到中的每一个元素numbers
。这有很大的不同,这就是map()
的用武之地:当你想对 iterable 的每个元素应用 callable 时,你可以使用它。
警告:一些语言使用名称map
作为哈希映射;在 Python 中,字典是哈希映射。所以,请注意,当你在另一种语言中看到术语“地图”时,首先检查它代表什么。比如 R 中的map()
相当于 Python 的map()
;但是在 Go 中,map()
创建一个哈希映射,工作方式类似于 Python 的dict()
。当我第一次开始学习围棋时,这让我很困惑,但过一段时间后,你就会明白了。
你应该这样使用map()
:
>>> doubled_numbers = map(double, numbers)
如您所见,您向map()
提供了一个 callable 作为第一个参数,一个 iterable 作为第二个参数。它返回一个 map 对象(在 Python 3 中,但是在 Python 2 中,您将获得一个列表):
>>> doubled_numbers #doctest: +ELLIPSIS
<map object at ...>
(请注意,我使用了#doctest: +ELLIPSIS
指令,因为本文档包含在doctest
s 中。它帮助我确保所有示例都是正确的。你可以在文档中读到更多关于 T2 的内容。)
一个map
对象像一个发电机一样工作。所以,即使我们在map()
中使用了一个列表,我们得到的不是一个列表,而是一个生成器。按需评估生成器(延迟)。如果你想把一个map
对象转换成一个列表,使用list()
函数,它将评估所有的元素:
>>> list(doubled_numbers)
[2, 8, 10, 20, 34]
或者,您可以用任何其他方式评估map
的元素,比如在for
循环中。为了避免不愉快的头痛,请记住,一旦这样的对象被评估,它是空的,因此不能再被评估:
>>> list(doubled_numbers)
[]
上面,我们为单个 iterable 应用了map()
,但是我们可以使用多个 iterable。该函数将根据它们的索引来使用它们,也就是说,首先,它将为 iterables 的第一个元素调用 callable(在索引 0 处);然后进行第二次;诸如此类。
一个简单的例子:
>>> def sum_of_squares(x, y, z):
... return x**2 + y**2 + z**2>>> x = range(5)
>>> y = [1, 1, 1, 2, 2]
>>> z = (10, 10, 5, 5, 5)
>>> SoS = map(sum_of_squares, x, y, z)
>>> list(SoS)
[101, 102, 30, 38, 45]
>>> list(map(sum_of_squares, x, x, x))
[0, 3, 12, 27, 48]
map()
的替代品
代替map()
,你可以使用一个生成器,例如,通过一个生成器表达式:
>>> doubled_numbers_gen = (double(x) for x in numbers)
这提供了一个发电机,就像map()
一样。当你需要一个清单时,你会更好地理解相应的清单:
>>> doubled_numbers_list = [double(x) for x in numbers]
哪个可读性更强:map()
版本还是生成器表达式(或者列表理解)?对我来说,没有一秒钟的犹豫,生成器表达式和列表理解更清晰,即使我理解map()
版本没有问题。但是我知道有些人会选择map()
版本,尤其是那些最近从另一种使用类似map()
功能的语言迁移到 Python 的人。
人们经常将map()
与lambda
函数结合使用,当您不想在其他地方重用该函数时,这是一个很好的解决方案。我认为对map()
的部分负面看法来自于这种用法,因为lambda
函数经常会降低代码的可读性。在这种情况下,通常情况下,生成器表达式的可读性会更好。下面比较两个版本:一个是map()
结合lambda
,另一个是对应生成器表达式。这一次,我们将不使用我们的double()
函数,但是我们将在调用中直接定义它:
# map-lambda version
map(lambda x: x*2, numbers)# generator version
(x*2 for x in numbers)
这两行导致相同的结果,唯一的区别是返回对象的类型:返回一个map
对象,而后者返回一个generator
对象。
让我们暂时回到map()
的多次使用:
>>> SoS = map(sum_of_squares, x, y, z)
我们可以按照以下方式使用生成器表达式重写它:
>>> SoS_gen = (
... sum_of_squares(x_i, y_i, z_i)
... for x_i, y_i, z_i in zip(x, y, z)
... )>>> list(SoS_gen)
[101, 102, 30, 38, 45]
这次我投票给map()
版本!除了更简洁之外,在我看来,它更清晰。发电机版本利用zip()
功能;即使这是一个简单的函数,它也增加了命令的难度。
所以,我们不需要 map(),不是吗?
根据以上讨论,不存在我们必须使用map()
功能的情况;相反,我们可以使用生成器表达式、循环或其他东西。
知道了这一点,我们还需要什么吗?
思考这个问题,我得出了我们需要 Python 中的map()
的三个主要原因。
原因一:性能
如前所述,map()
被懒洋洋地评估。然而,在许多情况下,评估map()
比评估相应的生成器表达式更快。尽管相应的列表理解并不一定是这种情况,但是在优化 Python 应用程序时,我们应该记住这一点。
然而,请记住,这不是一个普遍的规则,所以你不应该假设这一点。每次都需要检查map()
在你的代码片段中是否会更快。
只有在性能上的细微差异也很重要时,才考虑这个原因。否则,以牺牲可读性为代价使用map()
将会收获甚微,所以你应该三思而后行。通常,节省一分钟没有任何意义。其他时候,节省一秒钟意味着很多。
原因二:平行度和穿线
当您并行化您的代码或使用线程池时,您通常会使用类似于map()
的函数。这可以包括诸如multiprocessing.Pool.map()
、pathos.multiprocessing.ProcessingPool.map()
或concurrent.futures.ThreadPoolExecutor.map()
之类的方法。所以,学会使用map()
会帮助你理解如何使用这些功能。通常,您会希望在并行和非并行版本之间切换。由于这些函数之间的相似性,您可以非常容易地做到这一点。看:
当然,在这个简单的例子中,并行化没有意义,而且会更慢,但是我想向您展示如何做到这一点。
原因 3:对于来自其他语言的 Python 新人来说简单
这个原因是非典型的,并不涉及语言本身,但它有时很重要。对我来说,生成器表达式几乎总是更容易编写,可读性更好。然而,当我刚接触 Python 时,理解对我来说并不容易,无论是写还是理解。但是自从我在 16 年的 R 编程后来到 Python,我非常熟悉 R 的map()
函数,它的工作方式与 Python 的map()
完全一样。然后,对我来说,使用map()
比使用相应的生成器表达式或列表理解要容易得多。
更重要的是,对map()
的熟悉帮助我理解。我也能够编写 Pythonic 代码;没错,用map()
就是 Pythonic。我们知道,第三种选择是for
循环,但这很少是更好的(甚至是好的)选择。因此,如果有人使用 Python 并且知道这些函数是如何工作的,那么他们编写 Python 代码就容易多了。例如,从 C 语言转向 Python 的人可能会使用一个for
循环,这在这种情况下被认为是非 Python 的。
这意味着map()
是 Python 和其他语言之间的桥梁——一座可以帮助其他人理解语言和编写 Python 代码的桥梁。
原因 4:多个可迭代的情况下的可读性
如上图所示,当你想同时为多个 iterables 使用一个 callable 时,map()
可以比对应的生成器表达式更易读、更简洁。因此,即使在简单的情况下,map()
可读性较差,但在更复杂的情况下,可读性成为了它的优势。
结论
有人说 Python 不需要map()
。回到 2005 年,Guido 自己想把它从 Python 中移除,还有filter()
和reduce()
。但是 17 年后的今天,我们仍然可以使用它,我认为——并真诚地希望——这一点不会改变。这给我们带来了本文所要讨论的两个关键问题:
***map()***
函数是 Python 中必须的吗? 不,不是。你可以用其他方法达到同样的效果。
既然如此,Python 还需要 ***map()***
函数吗? 是的,确实如此。不是因为它是必须的,而是因为它仍然被使用,它服务于各种有价值的目的。
我认为 Python 开发者应该知道如何使用map()
,即使他们不经常使用它。在某些情况下,它可以帮助提高性能,并行化代码,只需很小的改动,或者提高代码的可读性。它有助于理解。它还可以帮助来自其他语言的开发人员使用地道的 Python——因为,是的,map()
仍然是 Python。
正是因为这些原因,我认为map()
值得在 Python 代码库中占有一席之地,即使它并不经常被使用。
资源
- https://medium . com/swlh/higher-order-functions-in-python-map-filter-and-reduce-34299 fee1b 21
- https://towards data science . com/python-map-filter-and-reduce-functions-explained-2b 3817 a 94639
- https://medium . com/forward-data-science/understanding-the-use-of-lambda-expressions-map-and-filter-in-python-5e 03 E4 b 18d 09
- https://docs.python.org/3/library/doctest.html
- *【https://www.artima.com/weblogs/viewpost.jsp?thread=98196 *
- 洛特,S.F. (2018)。函数式 Python 编程。第二版。包装出版公司。
大脑是靠深度学习运行的吗?
播客
JR·金论生物智能与人工智能的异同
编者按:TDS 播客由 Jeremie Harris 主持,他是 Gladstone AI 的联合创始人。每周,Jeremie 都会与该领域前沿的研究人员和商业领袖聊天,以解开围绕数据科学、机器学习和人工智能的最紧迫问题。
深度学习模型——特别是变形金刚——正在定义当今人工智能的前沿。它们基于一种叫做人工神经网络的架构,如果你经常阅读数据科学,你可能已经知道了。如果你是,那么你可能已经知道,正如他们的名字所暗示的那样,人工神经网络是受生物神经网络的结构和功能的启发,就像那些在我们大脑中处理信息的网络一样。
因此,很自然会有人问:这种类比能走多远?如今,深度神经网络可以掌握越来越多的历史上人类独有的技能——如创建图像,或使用语言,规划,玩视频游戏等技能。这是否意味着这些系统也像人脑一样处理信息?
为了探索这个问题,我们将采访 JR·金,他是 Meta AI 附属高等师范学院的 CNRS 研究员,领导着大脑与人工智能小组。在那里,他致力于确定人类智能的计算基础,重点是语言。JR 是一个非常有洞察力的思想家,他花了很多时间研究生物智能,它来自哪里,以及它如何映射到人工智能上。在这一集的 TDS 播客中,他和我一起探索生物和人工信息处理的迷人交集。
以下是我在对话中最喜欢的一些观点:
- JR 的工作重点是研究现代深度神经网络不同层中人工神经元的激活,并将它们与人脑内部细胞簇的激活进行比较。他使用生物细胞群,而不是单个的生物神经元,因为我们根本无法从大脑成像中获得单个神经元水平的分辨率。这些细胞群对应于大脑体积的小像素,称为体素。他的工作包括检测一个大型深度神经网络给定层的神经元激活与大脑中与语言相关部分的体素激活之间的统计相关性,该神经网络经过训练以进行语言建模。
- 众所周知,深度神经网络具有层次结构,其中更简单、更具体的概念(如图像中的角和线,或文本中的基本拼写规则)被网络中的较低层捕获,而更复杂和抽象的概念(如图像中的脸形或轮子,以及文本中的句子级想法)出现在结构的更深处。有趣的是,这种 hirearchy 也倾向于在大脑中出现,这表明深层网络和大脑之间的类比超越了神经元水平,也延伸到了大脑宏观结构的水平。我问 JR,他是否认为这是一种巧合,或者这甚至暗示了一种智力的普遍属性:我们应该期望所有的智力都包括这种等级信息处理吗?
- 最近在人工智能领域存在争议,即人工智能系统是否真正“理解”有意义的概念。我们讨论了是否是这样,以及谈论人工智能系统的“理解”是否具有建设性(我们一致的答案是“是”,和“是”,但你确实是你)。
- 进行大脑<>神经网络比较的一个主要挑战是,大脑是一个非常嘈杂的器官,不断产生和处理与心跳、呼吸、眼球运动、咳嗽等相关的信号。出于这个原因,将大脑行为与神经网络行为相关联具有挑战性:嘈杂的数据,加上较小的影响大小,即使在最好的情况下也是令人沮丧的因素。为了弥补这一点,研究人员倾向于收集大量的数据,这可以使人们对有趣的相关性的存在有很高的信心,尽管这些相关性很弱。
章节:
- 0:00 介绍
- 2:30 JR 的日常工作是什么?
- 5:00 人工智能和神经科学
- 12:15 研究中的信号质量
- 结构的普遍性
- 28:45 大脑是由什么组成的?
- 37:00 扩展人工智能系统
- 人类大脑的成长
- 观察到某些重叠
- 55:30 总结
现代数据栈重视“栈”胜过“数据”吗?
我们听到更多的是关于工具,而不是如何正确使用它们
T 最近有许多关于解绑或再解绑的辩论,这导致了一些关于谁将赢得任何给定数据堆栈的市场的争论。但是这些争论隐藏了一个重要的事实:工具只是工具。最后,你如何使用它们与你的产品和供应商选择同等重要(如果不是更重要的话)。
我们为什么要使用工具
数据工具的主要目的是通过数据驱动的自动化来帮助解决业务问题、做出更好的决策以及改进现有流程。但许多人开始采用新工具,只是为了跟上当前的趋势,如现代数据堆栈。
尤其是在这个时代,当我们每天都受到大量新技术的轰炸时,开发一个决策框架来选择并成功采用新的数据工具并有意识地这样做是非常有用的。
明智地选择您的筹码
从需要解决的问题以及该工具必须支持的业务流程和最终用户开始是一个很好的实践。在以下情况下,应采用堆栈的任何添加:
- 它解决了一个真正的问题,
- 它符合您的工作方式和您组织的需求,
- 它与您的工程工作流程或业务流程配合得很好,并且理想地增强了它们。
一旦澄清了这一点,我们就需要决策和评估标准,最后,我们可以继续进行工具评估、选择和采用。
为什么这种结构化方法对于为您的数据堆栈选择工具非常重要?因为否则,你可能会以意外的复杂而告终。您的数据平台可能会变成一套脱节且难以维护的自托管工具,甚至更糟,变成一堆完全脱节的 SaaS 产品,您为此付出了一大笔钱,但最终却出现了比以前更多的问题。
工程师在数据领域的角色
一旦我们选择了我们的堆栈,我们就可以开始工程。随着现代数据堆栈和新的开源技术的兴起,我们开始看到新职位的出现,如 ML 工程师、分析工程师或数据平台工程师。但是,成为一名优秀的工程师和数据从业者意味着什么呢?
不仅仅是关于编码、优化查询性能或者学习如何挑选 Git 提交。工程师懂得如何处理业务问题,找出问题的根源,然后选择工具和设计不会导致将来后悔的解决方案。为了把它做好,我们需要伟大的工具和伟大的工程师。
良好的工程设计与工具选择
好的工程设计能战胜仔细的工具选择吗?一如既往,真相介于两者之间。
最后,你将如何使用任何给定的工具决定了你能在多大程度上成功使用它。
为了说明这一点,让我们看看有助于解决确保可靠数据流问题的工具。这个领域的产品开始分成两大类。第一类是协调者,重新绑定数据堆栈并努力控制其中的一切。第二个是协调平面,更侧重于观察各种工具(甚至其他协调器)的状态,并允许你根据观察到的状态采取行动。
重新捆绑堆栈的传统协调器容易被供应商锁定,因此协调平面有助于构建一个更能适应变化、更适合为您提供长期服务的数据平台。但是并非一切都是黑白分明的——即使有了完美的数据工具,如果你不小心如何在那个平台上构建你的系统,你仍然可能处于一个糟糕的境地。
在这个前提下,好的工程设计能战胜工具选择吗?在某种程度上,是的。然而,选择错误的工具会迫使你做出单向的决定,不管你在其上设计了多么好的系统。
控制平面:把你所有的(数据流)鸡蛋放在一个篮子里
使用单个 orchestrator 作为可观察性、沿袭和数据流执行的控制平面意味着什么?这意味着,要获得可靠的谱系图并全面了解您的数据平台的状态,您必须在任何地方、任何时候都使用这个平台。
实际上,这意味着从 dbt Cloud 而不是从 central orchestrator 触发您的 dbt 模型会破坏您的工作流血统,并且您的元数据图像是扭曲的。orchestrator 对当前状态感到困惑,而您的数据平台崩溃仅仅是因为您选择从 dbt Cloud 而不是从您的中央 orchestrator 触发运行。
关键的区别在于选择,或者说你选择的工具是否迫使你做出选择
这完全是关于选择以及约束和灵活性的正确平衡。中央控制面板对整个数据堆栈进行重新绑定是极其受限的,但是如果您是一个喜欢这种限制而不是更多灵活性的小团队,这可能是理想的。
相比之下,协调平面被动地观察您的数据堆栈并收集关于它的元数据,而不管哪个工具触发了您的数据流,这给了您选择如何工作的权力。有了这种方法,你可以专注于数据和构建可靠的工程流程**,而不是工具**和如何对抗它们。
后续步骤
如果你喜欢这篇文章,你可能也会喜欢这篇。
感谢阅读!
WDT 会把粉末转移到浓缩咖啡篮的底部吗?
咖啡数据科学
韦斯分布技术(WDT)已经存在几年了。WDT 通常有助于良好的分配,并已被发现改善咖啡提取。人们遇到的一个困难是不知道去 WDT 需要多长时间。如果你 WDT 太多,细颗粒会从混合和运动中向过滤篮底部移动吗?
我的理论是,由于静电,较细的颗粒会粘在较大的颗粒上。我以前的筛分经验表明,简单的振动不足以使颗粒充分分离。我必须仔细筛选,表明研磨咖啡不像干沙。
所有图片由作者提供
为了测试这一点,我在一些 WDT、更多的 WDT 和太多之后观察了几个样品上的研磨分布。对于每一次迭代,我都会在之后夯实圆盘并提取一个核心样本。我也用了现磨咖啡。
然后我会继续 WDT 咖啡。
我得到了这三个核心样本。这些样本是从冰球中心取出的。
使用图像处理,我测量了开始分布、第一次 WDT 应用、第二次和第三次的分布。对于其中的每一个,我测量了圆盘顶部、中部和底部的分布。
我没有看到表明分布变化的分布变化。
我更仔细地观察了精细层,虽然有一些移动,但不符合迁移的预期。例如,我可以通过顶部减少小于 200 微米的颗粒,但随着时间的推移,它会增加。
我也观察较粗的颗粒,我反过来观察它们,这样我就能理解正在发生的任何变化。没有任何明显的趋势。
从这组实验中,我没有发现 WDT 导致更细的颗粒在拍摄准备过程中迁移的证据。由于我的技术,这是很有可能的,但是我没有数据来支持不同的结论。
所以,如果你担心你做了太多的 WDT,深呼吸。
如果你愿意,可以在推特、 YouTube 和 Instagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在中和订阅。
我的进一步阅读:
工作和学校故事集