使用 TensorFlow 和 Keras 在 Google Colab 中构建性别分类器
使用 TensorFlow 和 Keras 在 Google Colab 中从头开始构建一个基于 CNN 的性别分类器。使用 TensorBoard 可视化模型的训练
在这篇文章的结尾你将会得到什么。(来源: Unsplash )
你好朋友们。在本文中,我们将使用 TensorFlow 和 Keras 提供的 API 来构建一个基于 CNN 的性别分类器。我们将在 Google Colab 上编写和执行代码。Colab 提供免费的 GPU 服务。我们将使用这些来快速训练我们的模型。
我将使用来自 IMDB-WIKI 数据集的维基百科图片。我已经对数据集进行了预处理,从图像中裁剪出所有的脸,并将它们作为对象存储在。pickle 格式在我的 Google Drive 里。如果你已经准备好数据集,你可以继续,如果你想准备一个和我在这篇文章中使用的数据集一样的数据集,你可以阅读我的另一篇文章,在那里我解释了如何创建一个 Colab 笔记本,处理数据并存储它们。
苦苦寻找数据集?,让我们使用…从 IMDB-WIKI 数据集为 CNN 模型准备一个现成的人脸数据集
medium.com](https://medium.com/@nachi2muthu13/preprocess-and-prepare-a-face-dataset-ready-for-cnn-models-885867907eb0)
在这篇文章中,我解释了每一行代码,最后,你会有信心自己构建一个多类分类器。
让我们编码
在 Google Colabs 上创建一个新的笔记本,并安装您的 Google drive。不要错过在您的笔记本中将您的运行时类型更改为 GPU。
如果你是 Google Colab 的新手,不知道怎么做,你可以在我的以前的文章中学习。
首先,让我们导入这段代码中需要的所有包。在一个新的单元格中导入所有的包。
代码块 1:导入所需的包。
现在我们已经导入了所有需要的包,让我们加载我存储在 Google Drive 中的数据集(PS:我已经在我的另一篇文章中展示了如何预处理数据并存储它。如果你没有自己的数据集,请阅读它。我已经在前面的部分添加了链接)。
代码块 2:导入数据集。
**第 1 行:**读取存储的数据。二进制模式的 pickle 格式。
**第 2 行:**使用 pickle 包将其转换为 python 对象。
**第 4–5 行:**从加载的数据集中单独提取图像和性别数据。
**第 6 行:**显示图像数组的形状,单个图像的形状和性别数组的形状。
图 1:显示输入的形状
在上面的输出中,我们可以看到有 42252 个图像,每个都是 32x32 的 RGB 图像。我们还有一个 2D 数组格式的每张图片的性别标签,每个子数组包含性别值(1.0:男性,0.0:女性)。为了更好地理解,您可以打印数组并进行检查。
接下来,让我们看看我们将使用什么样的图像来训练模型。
代码块 3:查看我们拥有的图像
**第 4–11 行:**从我们的数据集中打印 4 张随机图像并显示它们。
正如您在上面的输出中看到的,我们将用来训练我们的模型的图像只包含人脸及其性别。
我们现在将可视化我们的数据分布,以深入了解我们的数据。
代码块 4:绘制数据集的直方图。
**第 1–12 行:**我创建了男性和女性两个类别,并将其添加到“性别绘图”变量中。使用这些分类数据,我们可以使用 Plotly 绘制直方图。
图 2:我们数据集的直方图。
我们有 30k 男像,10k 女像。这些图像足够了,因为我们正在执行二元分类。
我们现在开始登船。执行下面的代码
%tensorboard --logdir "logs"
在“logdir”参数中,您应该给出保存模型日志数据的目录的路径。TensorBoard 将在给定的目录中搜索日志并显示出来。如果您想将日志文件保存在 Google Drive 中,您可以给出相应的路径。在几个步骤中,我将向您展示如何向我们的模型添加回调来保存日志文件。
是时候创建我们的 CNN 模型了。首先,我们将模型所需的所有层添加到一个列表中,然后使用 Keras 中的顺序类创建模型。虽然这是一个二进制分类问题,而不是在最后一层使用 sigmoid 激活函数,但我使用了“二进制交叉熵”作为损失函数。我已经把这个问题当作一个只有两类的多类分类问题。我这样做是为了让你知道如何使用 CNN 实现多类分类。
代码块 5:创建我们的 CNN 模型。
**第一行:**模型中的第一层是输入层。我们将图像的形状作为参数传递给它。
**第 3 行:**创建一个元组,其中包含我们模型中特定 Conv2D 层将分别拥有的过滤器数量。
**第 5–11 行:**遍历上面创建的元组,创建 Conv2D 层,Conv2D 层具有“Relu”激活功能。然后为每个 Conv2D 函数添加一个“BatchNormalization”函数和 MaxPooling2D。MaxPooling2D 的池大小为 2x2,每步 2 步。您可以更改这些值。
**第 13–26 行:**每当在 Conv2D 层之后添加一个密集层时,应该在它们之间添加一个展平层。该图层会将 Conv2D 图层的权重从 2D 格式转换为密集图层可以使用的一维格式。每个密集层被赋予一个“Relu”激活函数和“BatchNormalization”函数。添加了 Dropout 函数,以防止模型过拟合。函数内部传递的参数是丢弃概率,如果传递 0.1,则意味着 10 个输入中有 1 个将被随机丢弃。
**第 27–28 行:**当我们使用 2 个类进行分类时,我们使用输出大小为 2 的密集层。最后一个密集图层中输出大小的数量与您希望模型进行分类的类的数量相同。我们还将 softmax 层用于分类问题,因为它为每个预测返回一个列表,该列表包含图像属于相应类别的概率,并且该图像被分配到具有最高概率的类别。
第 31 行:然后,我们将所有创建的图层添加到一个序列模型中。向顺序模型添加层还有其他方法,您可以参考相关文档。
**第 32–34 行:**我们通过指定优化函数和损失函数来编译模型。因为它被视为多类分类问题,所以我们使用“分类交叉熵”作为损失函数。当针对二元分类问题创建模型时,我们必须使最后一个密集层中的输出为 1,并且仅对最后一层使用 sigmoid 激活函数。与此同时,所使用的损失函数应该是“二元交叉熵”
**第 35 行:**我们为每个模型训练指定文件夹名。我们使用模型被训练的时间作为文件夹名称。
如果你想你的模型日志存储在你的 Google Drive 中,那么你必须以这样的格式指定路径。
例如log_dir = "drive/My Drive/Colab Notebooks/Tutorial/Gender Classifier/logs/" + datetime.datetime.now().strftime("%Y%m%D-%H%M%S")
启动 TensorBoard 时,应提供相同的路径,直到“日志”文件夹。log_dir = "drive/My Drive/Colab Notebooks/Tutorial/Gender Classifier/logs
。
**第 36 行:**在一个名为 callbacks 的变量中,我们创建了一个所有回调的列表,并将其传递给模型。这里我们只创建一个回调来保存日志供 TensorBoard 使用,我们从“keras.callbacks”调用 TensorBoard 类,并将保存日志的路径作为参数传递。
是时候训练我们的模特了。我们将使用 80:20 的比例进行培训和测试。训练数据被进一步分成 80:20 用于训练和验证数据。
代码块 6:训练模型并评估它。
**第 1–3 行:**我们将数据分为训练数据和测试数据。
第 4 行:我们将只使用 80%的训练数据来训练模型,其余的将用作验证数据。因此将“数量 _ 训练 _ 示例”设置为可用于训练的图像数量的 80%。
**第 5 行:**设置批量为 64。出于训练目的,我们的训练数据将被分成多个批次,每个批次包含 64 幅图像。
**第 6–8 行:**我们正在从模型对象中调用 font 函数,以便我们的模型可以使用我们的数据进行训练。
Epoch 设置为 10,这意味着我们为训练提供的全部数据将用于训练模型 10 次。一个历元意味着使用给定的数据执行一次迭代训练,所提供的全部数据将被我们的模型使用一次。
steps_per_epoch 是提供给我们模型的数据批次数。当达到 steps_per_epoch 计数时,模型知道特定的 epoch 已经结束。
batch_size 是一批数据中出现的图像数量。
如果设置为 false,则模型将按照提供给它的顺序获取数据。建议将此设置为 true,这样模型就不会被训练成特定的类。
validation_split 是所提供的训练数据中可用于验证的部分。
回调是在每个时期或每个步骤结束时必须执行的功能或操作。这是我们的 TensorBoard 回调将通过的地方。
**第 11 行:**使用我们之前分割的测试数据,我们评估我们的模型的准确性,并检查是否没有发生过度拟合。
上述单元的执行为我们提供了每个时期的精确度和损失值。
图 3:模型精度和损失
正如您在上面的输出中所看到的,该模型对于我们的训练数据具有很好的准确性。验证准确性也很好,这表明我们的模型没有过度拟合数据。评估结果表明,我们的模型对未知数据也有很好的表现。现在是我们使用 TensorBoard 可视化模型训练的时候了。
向上滚动到执行命令以启动 TensorBoard 的单元格。现在,您可以看到那里显示的图表。将有一个图表用于绘制模型的准确性,另一个用于绘制损失。
图 4:可视化我们模型的训练。
如果您看不到此图表,请检查您是否为日志文件提供了正确的目录路径。TensorBoard 是一个非常强大的可视化工具,你可以用它做很多事情。如果你有兴趣了解更多信息,我推荐你观看 2017 年 TensorFlow dev 峰会。
让我们进入下一步。我们现在将从我们的计算机摄像头获取输入,并让我们的模型对图像中的人进行分类。在我们编写获取相机输入的代码之前,让我们定义一个将 RGBA 格式的图像转换为 RGB 的函数。
代码块 7:将 RGBA 图像转换为 RGB 图像。
**第 1–20 行:**该函数对图像执行数学运算以进行转换。感谢汪锋,我从 StackOverflow 的回答中获得了这段代码。
让我们定义一个函数来使用计算机中的网络摄像头捕捉图像。由于整个笔记本都在您的浏览器上运行,我们需要编写 JavaScript 代码来使浏览器访问网络摄像头,捕捉图片并将其发送到服务器进行处理。我简单解释一下代码。除非您对使用 JavaScript 通过网络摄像头捕捉图像感兴趣,否则可以跳过这一部分。
代码块 8:通过网络摄像头捕捉图像。(来源: advanced_outputs.ipynb )
**第 1 行:**我们定义了一个函数,它将我们想要的图像的大小和质量作为参数。
**第 2 行:**向 Ipython.display 包中的 JavaScript 函数传递我们用 JavaScript 编写的要执行的 set 代码。
**第 3–16 行:**我们正在创建两个元素。一个 div 来显示来自我们相机的输入流,一个 capture 按钮来点击捕获图像,“stream”包含负责视频流的类的对象。
**第 22–31 行:**代码等待点击捕获按钮。一旦它被点击,显示在输出屏幕上的视频的特定帧被捕获并转换成像素字典,然后被返回。您可以在 MDN Web 文档中了解更多关于数据返回格式的信息。
**第 35 行:**执行里面写的 JS 代码。这里我们调用上面定义的 JS 函数。
代码块 8:将捕获的图像转换成所需的格式。
**第 5 行:**我们调用 python 函数“take_photo ”,它反过来调用 JS 函数并捕获一张照片。
**第 7–9 行:**我们根据关键字对 JS 函数返回的像素进行排序,然后将图像调整为 3D 数组格式。
**第 15 行:**由于获得的图像是 RGBA 彩色格式,我们要将其转换为 RGB 彩色格式并显示。
图 5:捕获的图像。
我们已经收到了需要分类的输入数据。我们将从图像中检测和裁剪人脸,将裁剪后的图像传递给模型,并获得其预测和显示输出。让我们定义两个函数,一个用于提取人脸,另一个用于显示带有标签的结果。
代码块 9:定义函数来处理我们的图像并显示输出。
**第 1–21 行:**此函数使用 Dlib 的基于 CNN 的人脸识别模型获取图像参数并检测其中的所有人脸,然后将它们从图像中裁剪出来。这个函数返回一个字典,包含所有的脸及其在图像上各自的坐标。
**第 23–33 行:**该函数将原始图像、裁剪后的人脸以及模型对每个人脸的预测作为其参数。它在所有面上绘制矩形框,并在这些框上添加各自的标签,然后显示图像。
是时候调用这些函数并获得输出了。
代码块 10:对图像进行分类。
**第 1 行:**我们从图像中提取人脸。
**第 3–6 行:**我们正在创建一个包含图像中所有人脸的图像数组。
**第 9–10 行:**我们预测每张人脸图像的标签。
**第 12 行:**我们调用显示分类输出的函数。
图 6:最终的分类图像。
你也可以试着把一个有多张脸的图像分类。
到此,我结束这篇文章。我希望你已经从这篇文章中学到了很多,现在可以用任何类型的对象和任何数量的类构建一个多类分类器。
我在我的笔记本上附上了一个链接供你参考。
性别分类器. ipynb](https://colab.research.google.com/drive/1d_mJjJJgdx_zqEBK4WgG0f7tQ1vsMaJB)
如有任何疑问,请随时联系我。
Instagram: nachijr4
电子邮件:nachi2muthu13@gmail.com
中:纳奇穆图
领英:纳奇穆图
构建一个在 Kubernetes 上运行的高度可伸缩的仪表板
使用 Python,Docker 和 Google 云平台
目前,一些出版物中提到了许多技术发展。这些关键词通常包括云、开源、自动化部署、高度可扩展、容器化应用。
在这种背景下,具体的技术如 Python 、 Docker 、云平台和类似 Kubernetes 的容器编排平台 s 经常被命名。
为了更好地理解提到的技术,我决定基于 Flask 和 Dash by Plotly 构建一个仪表板,它运行在 Google 云平台上的 Kubernetes 集群上,因此具有高度的可伸缩性。
你会在 GitHub 上的我的资源库中找到完整的代码和相关文件。
这篇文章是一个更大项目的一部分。如果你也对可扩展的 web 抓取感兴趣,或者对我们如何基于 Spark 用自然语言处理准备数据,在 Docker 中打包代码并通过 Kubernetes 部署感兴趣,你会在文章末尾找到相应的链接。
架构概述
Kubernetes 上可扩展仪表板的架构
数据存储在 BigQuery 表中,由 python 脚本读取,该脚本在 Docker 容器中执行。Kubernetes 集群由一个节点组成,该节点包含 3 个执行应用程序逻辑(dashboard-app)的 pod。为了能够从 web 访问 dashboard-app,我们创建了一个公开部署的服务对象。我们在当前集群中提供一个负载平衡器,并为服务分配一个固定的外部 IP 地址。因此,最终用户可以通过互联网浏览器使用仪表板。
仪表板的实施
在我描述 Kubernetes 中仪表板的部署过程之前,我想向您简要介绍一下仪表板的实现。如果您对应用程序本身不感兴趣,可以跳过这一部分,专注于 Kubernetes 中的部署过程。
仪表板本身是在 Flask 和 Dash by Plotly 的基础上构建的。
Flask 是用 Python 编写的 web 框架,它提供了 Jinja2 、模板引擎和 Werkzeug 作为构建 Web 服务器网关接口(WSGI)应用程序的库。Flask 的核心功能保持简单,优点是易于集成额外的库。
Dash 是一个开源 Python 框架,允许您创建最先进的仪表板。他们提供了许多演示应用,你可以用它们作为自己实现的灵感。
他们提供了许多鼓舞人心的想法来实现它们。
Dash 为后端使用了 Flask web 框架。这使得在现有 Flask 应用程序的特定路径上嵌入 Dash 应用程序变得相当简单。在这个仪表盘中,Dash app 挂载在 Flask app 的“/dash”路由(http://localhost:8080/Dash)下。
Kubernetes 中的部署流程
因为这是我在这个环境中的第一次实现,所以我将一步步地向您介绍我所做的事情。我希望这些解释能帮助你在这个话题上有一个好的开始!开始了…😃:
- 首先,我向 Google 云平台申请了免费试用订阅。(也可以使用运行 Kubernetes 的任何其他平台)
- 创建一个 Kubernetes 集群
进入“Kubernetes 引擎”→“创建集群”→选择“您的第一个集群”
创建一个 Kubernetes 集群
将参数中的名称更改为 dashboard-web ,可以应用其余设置。然后点击创建来构建名为 dashboard-web 的 Kubernetes 集群。
现在创建了一个具有一个节点的集群,在其上可以创建多达 110 个pod。
通过 REST 或命令行创建 Kubernetes 集群
**提示:**您所做的集群配置以脚本的形式存储和提供,可以通过 REST-API 或命令行来执行。因此,可以自动创建集群。下面是一个可以在云 Shell 中使用的命令行代码示例:
gcloud beta container --project "<YOUR-PROJECT>" clusters create "dashboard-web" --zone "us-central1-a" --no-enable-basic-auth --cluster-version "1.15.4-gke.22" --machine-type "g1-small" --image-type "COS" --disk-type "pd-standard" --disk-size "30" --scopes "https://www.googleapis.com/auth/devstorage.read_only","https://www.googleapis.com/auth/logging.write","https://www.googleapis.com/auth/monitoring","https://www.googleapis.com/auth/servicecontrol","https://www.googleapis.com/auth/service.management.readonly","https://www.googleapis.com/auth/trace.append" --num-nodes "1" --no-enable-cloud-logging --no-enable-cloud-monitoring --enable-ip-alias --network "projects/<YOUR-PROJECT>/global/networks/default" --subnetwork "projects/<YOUR-PROJECT>/regions/us-central1/subnetworks/default" --default-max-pods-per-node "110" --addons HorizontalPodAutoscaling,HttpLoadBalancing --enable-autoupgrade --enable-autorepair
3.容器化一个 Python 应用
要获得 dashboard——它是用 Python 开发的——在 docker 容器中运行,需要 3 个文件:
- index.py (包含仪表盘 python 代码的文件)
- docker file*(*通过读取该文件中的指令自动构建图像)
- requirements . txt(包含应用需要运行的依赖项列表)
4.创建一个 Docker 镜像,推送到 google 容器注册表 启动云壳,然后启动编辑器。
云壳编辑器
我已经在云外壳提供的浏览器中创建了我的仪表板结构。你会在我的 git 库中找到完整的代码。
将文件复制到云外壳编辑器后,可以创建 Docker 映像。为此,您必须转到仪表板目录。
***cd dashboard***
从这个目录中,您可以运行 bash 脚本buildpushdockerimage . sh,
***bash BuildPushDockerImage.sh***
它依次执行以下命令:
构建 Docker 映像并将其推送到 Google 容器注册表
Docker 映像现在是基于 3 个文件(index.py、Dockerfile、requirements.txt)构建的,可以在 Google 容器注册表中找到。
谷歌容器注册
5.创建一个部署,该部署创建一个复制集以显示三个“dashboard-app”窗格
要开始部署,可以执行以下 bash 脚本:
***bash DeployDashboardPod.sh***
它依次执行以下命令:
部署三个吊舱
第一步是确保我们被允许为我们的项目运行ku bectl。Kubectl 是一个命令行界面,用于对我们的 Kubernetes 集群运行命令。
之后,我们使用 DashboardPod.yaml 模板创建我们的“dashboard-app”窗格。
部署仪表板应用程序以调出 3 个窗格
创建窗格后,我们可以使用以下命令详细检查它们:
*****kubectl get pods -o wide*****
命令的输出
部署中有 3 个 pod(Status = container creating),所以让我们再等几秒钟,然后再次执行命令:
再次执行该命令后的输出
我们看到容器现在处于就绪状态(1/1)和状态=正在运行。此外,它们都被分配了一个内部 IP 地址。如上所述,我们可以在这个节点上运行的最大 pod 数量是 110,这意味着如果仪表板上的负载增加,我们可以在这个节点上再创建 107 个容器。如果我们需要更多的容器,我们可以订购一个包含更多节点的更大的集群,每个集群运行 110 个容器。这种方法使我们有可能实现一个高度可伸缩的环境。
6.创建一个暴露外部 IP 地址的服务对象,以允许通过互联网浏览器访问仪表板
为了能够从 web 访问 dashboard-app,我们创建了一个公开部署的服务对象。“type=LoadBalancer”在当前集群中创建一个负载平衡器,并为该服务分配一个固定的外部 IP 地址。该服务在端口 80 上运行,并连接到端口 8080 上的容器。
要公开部署,可以执行以下 bash 脚本:
*****CreateExtWebService.sh*****
它执行以下命令:
显示有关仪表板应用程序服务的信息:
*****kubectl get service dashboard-app*****
命令的输出
现在可以通过外部 IP 地址访问“dashboard-app”服务。
要获得更多服务信息,您可以执行以下命令:
*****kubectl describe services dashboard-app*****
命令的输出
记下 dashboard-app 服务公开的外部 IP 地址(LoadBalancer Ingress
)。在本例中,外部 IP 地址是 35.202.15.18。此外,您可以看到该服务有几个端点:10.44.0.10:8080,10.44.0.11:8080,10.44.0.12:8080。这些是运行仪表板应用的 pod 的内部地址。**
您可以使用外部 IP 地址(LoadBalancer Ingress
)访问 dashboard-app 应用。由于应用程序运行在“/dash”目录中,因此这种情况下的调用如下:
***http://<LoadBalancer Ingress IP>/dash/***
仪表板现在显示在互联网浏览器中:
仪表板-通过外部 IP 调用的应用程序
仪表板分为四个区域:
- ****热门词汇(文章):对应到数据科学文章的“前 10”或“前 100”词汇显示为词云。
- ****作者:提供了一个包含所有已爬网作者的下拉列表。
- ****来自…的热门文章:根据所选作者,将显示热门文章及其元数据(点击次数、阅读时间等)。如果没有选择作者,则显示所有作者的“前 10 篇”文章。
- ****数据科学趋势(标签):显示文章中使用的标签,以便您可以随着时间的推移分析哪些术语在何时被频繁使用,以及是否可以从中得出趋势。
****提示:云的优势在于,只要你使用它,你就只需要为基础设施付费。在一次成功的测试运行之后,为了节省成本,我删除了我的“dashboard-web”集群;)
结论:
这样一来,我们现在就有了一个在云平台上运行的仪表盘,它是用 Python (Flask & Dash)开发的,打包在 Docker 中,部署在 Kubernetes 上。整个过程已经公开,可以通过网络浏览器浏览。
我觉得很棒,希望你也是:)!
相关文章:
如果你想知道我们如何从向数据科学爬取数据,那么查看 Philipp Postels 的文章:通过使用 python 用 Selenium 为向数据科学爬取构建一个可伸缩的网络爬虫
如果你想知道我们如何使用基于 Spark 的自然语言处理来准备数据,如何在 Docker 中打包代码,如何通过 Kubernetes 部署它,请查看 Jürgen Schmidl 的文章:Spark in Docker in Kubernetes:A practical approach for scalable NLP
制作自制电流/电压绘图仪
设计和编程您自己的数据采集硬件
最近我在思考二极管的非线性行为。在大学里,我了解到通过二极管的电流可以用施加在其上的电压的指数函数来近似表示。我想知道指数函数如何描述真实二极管的电流/电压行为。
我认为这将是一个有趣的项目,建立一些测量和绘制二极管的行为。我有一个备用的 Arduino Uno,还有一块试验板和一些基本的电子零件。
现在,三天后,我有了一个比我想象的更好的系统。它捕获数据并根据数据制作图表,如下所示。以下是如何制作自己的电流/电压分析仪。我提供源代码和硬件设计作为开源。
零件清单
- 一个小试验板和电线。
- Arduino Uno 或兼容板,带 USB 电缆。
- 产生 12 至 15 伏 DC 的电源,电流至少为 100 毫安。您可以为这部分寻找一个旧的“墙砖”电源适配器。
- LM7808 稳压器。这就产生了一个稳定的 8 伏 DC 信号。(你可以用产生 9 伏信号的 LM7809 调节器来代替;它将同样有效。)
- LM324N 四路运算放大器芯片。
- 各种电阻器,如下面更详细解释的。
电路设计
对于这个项目,我需要一种方法来产生从 0V 到 5V 的不同电压,并精确控制增量。将数字数据转换成模拟电压电平的设备称为数模转换器,简称 DAC。虽然更先进的 Arduino Due 带有内置 DAC,但 Due 的成本几乎是 Arduino Uno 的两倍。我决定从头开始构建自己的 DAC,并与 Uno 一起使用。这本身就具有教育意义和回报。
DAC 设计如下所示:
关于硬件设计的一些注意事项:
- LM324N 芯片上有四个运算放大器。仅使用其中一个,在原理图中标为 IC1A。三个未使用的运算放大器如图所示进行布线,以防止它们因不必要的振荡而产生干扰。
- DAC 本身由左侧的电阻网络和运算放大器 IC1A 组成。(有关 DAC 工作原理的更多信息,请参见下文。)
- 原理图上标有
X
的端子表示电路连接到 Arduino Uno 板(未显示)的位置。 - Uno 板由主机的 USB 连接供电。您将需要此连接来通过 Arduino IDE 串行监视器捕获数据,因此没有必要为 Uno 提供另一个电源。
- 电阻 R0 和 R1 连接到 PB0 和 PB1,但其余的电阻 R2-R7 连接到 PD 引脚,而不是 PB 引脚。(PD0 和 PD1 不能使用,因为串行通信需要它们。)
- 原理图中的二极管 D1 是您想要测试的任何元件的占位符。我已经成功测试了一个 1K 电阻(一种验证整个系统的好方法)、不同种类的 led 和各种其他二极管,包括肖特基二极管、齐纳二极管、整流器和锗检测二极管。
这是我的最终版本的样子:
DAC 理论
我使用来自 Uno 的 8 个数字输出来选择 256 个不同电压电平中的一个。九个电阻组成的网络和运算放大器共同实现 DAC。
每个输出(PB0、PB1、PD2、… PD7)产生 0V 或 5V 的独立电压,由 Uno 上运行的固件控制。因为这是 8 个独立的输出,所以有 2⁸=256 独特的输出组合。
电阻选择为 2 的幂,从 1K 一直到 128K。输出 PD7 通过相连的 1K 电阻 R7 对正运算放大器电压的贡献是 8 路输出中最大的,因为电阻最小。每个电阻值增加一倍,到达运算放大器的电压就会减半。
为了证明这是真的,考虑这张图,重新绘制以使当前的流动更清晰。
X₀至 X₇代表 8 个 Uno 输出引脚的电压。在给定时间,每个可以是 0V 或 5V。通过电阻器 R₀和 R₇流入 V₀节点的总电流必须等于通过 R₈.流出的电流(我们可以忽略任何进入运算放大器正输入端的电流,因为运算放大器设计具有极高的输入阻抗。)因此:
根据欧姆定律,每个电流等于每个电阻上的压降除以相应的电阻值:
拆分分数后,收集等式一边的 V₀项,求解 V₀,我们发现:
分母可以用常数 R’≈500ω代替,它代表所有九个电阻的并联等效电阻:
最后一个等式表明,运算放大器正输入端的电压 V₀是八个输入电压的线性加权和。
通过将运算放大器的输出连接到其负输入,它充当一个电压缓冲器,输出与其输入相同的电压 V₀。这被称为单位增益配置。运算放大器向测试元件提供高达 20 mA 的电流,同时将电阻网络与该元件隔离。
电阻调谐
为了使自制 DAC 尽可能线性,需要选择接近 2 的幂的电阻。这有一个小问题。电阻器通常不以 2K、4K 等值出售。
然而,有可能找到串联或并联的电阻对,以接近匹配目标值。电阻器的实际值很少与其标称值相符。你必须测量它们!我坐下来,用我的数字欧姆表试着组合,直到我得到正确的数值。仅仅这个电阻调谐步骤就花了我几个小时。
例如,对于我的 4K 电阻,我发现一个标称值为 180ω的电阻测量值为 176.8ω。我还发现了一个标称值为 3.9K 的电阻,测量值为 3.83K。将它们串联焊接在一起,我的欧姆表测得的总电阻为 4.00K。我发现,在试验板上串联(或并联)不同的电阻并测量它们,直到得到足够接近的结果,这比做数学计算更容易。
你不需要为了获得完美的阻力而把自己逼疯。这不会影响最终电流/电压图的准确性。正如您将看到的,成品器件将使用 Uno 的两个模拟输入来测量电压,因此结果图的准确性不涉及任何计算或猜测。重点是尽量平滑地覆盖可能的电压范围,不要让 DAC 产生的电压出现任何大的缺口。每次阻力在 1%以内就足够好了。
如果你觉得用这种方式组合固定电阻太麻烦,你可以买一组八个微调电阻,用螺丝刀调整它们,直到它们测量正确。
这里有一个特写镜头,可以更好地观察试验板上的 DAC。你可以看到我的一些复合电阻,大部分是串联的,一对是并联的。
安装和测试固件
您需要将这个项目的 GitHub 库克隆到您的计算机上。我已经验证了一切都可以在 Debian Linux 和 Windows 10 上运行。我没试过 Mac,但应该可以用。
https://github.com/cosinekitty/diodeplot
固件是一个名为DiodePlotFirmware.ino
的 Arduino 草图。它设计用于 Arduino IDE 的串行监视器。一旦您构建了硬件并用固件对其进行了编程,您就可以执行快速诊断。我建议您第一次运行时使用 1K 电阻作为测试元件。将其放在电路中,而不是二极管 D1 中。打开 Arduino IDE 串行监视器。当您引导系统时,您应该会看到以下提示:
# READY
要验证 DAC 是否正常工作,可以将数字万用表连接到 DAC 输出,并在输入一系列命令的同时测量其电压。将您的万用表负极探针连接到 GND(我喜欢将其夹在 LM7808 的金属标签上)。将正极探针连接到运算放大器输出。它的测量值应该非常接近 0V。
接下来,输入一系列命令来手动更改电压水平。每个命令将是一个从 0 到 255 的数字,后跟字母v
。按 enter 键提交命令。让我们从告诉电路板达到最大电压开始:
255v
您应该会在串行监视器中看到如下内容:
# READY
**255 1020 767**
第一个数字确认值 255(二进制 1111111)已经馈入进入 DAC 的 8 个引脚。第二个数字是 DAC 输出的模拟读数。它应该接近 Arduino 模拟输入的 10 位范围的高端,即 0…1023.你的万用表应该跳到+5.0 伏附近。
假设你安装了一个 1K 的电阻而不是 D1,第三个数字应该接近显示的值。它是测试组件顶部的电压。
如果一切检查完毕,您就可以继续了。否则,就是出问题了;拔掉你的电源和 USB 电缆,然后花些时间检查你的线路。在继续之前,您需要诊断并修复任何错误。
一旦你的最大电压检查出来,尝试几个中间值。例如,通过输入以下命令尝试 50%的电压。
128v
现在,串行监视器应该打印出与这些数字相近的数字(新输出以粗体显示):
# READY
255 1020 767
**128 507 380**
第一个数字应该正好是 128,剩下的两个数字应该大约是之前255v
测试值的一半。此外,您的万用表读数应该约为+2.5V。
捕捉电压/电流曲线数据
现在是时候找点乐子了!让我们验证 DAC 的线性行为,仍然使用 1K 电阻代替 D1。
首先,按下显示屏右下角的“清除输出”按钮,清除串行监视器输出。然后输入以下命令:
1000m
m
命令运行自动数据收集程序,扫描每个 DAC 输入,从 0 到 255。对于每个 DAC 值,m
前面的1000
告诉固件对每个 DAC 设置进行 1000 次过采样。固件连续读取 1000 次两个电压:DAC 输出本身和测试元件顶部的电压。
测试将需要几分钟时间来运行。完成后,您将得到如下所示的输出:
0 0 [1000] 0 [1000]
1 3 [2 231 766 1 0] 1 [8 755 237]
2 6 [1 153 846] 5 [590 409 1]
3 10 [1 119 880] 7 [0 5 491 498 6]
4 15 [41 957 2] 11 [3 247 748 2 0]
5 19 [60 934 6] 13 [0 1 145 849 5]
...
250 1000 [0 1 832 166 1] 751 [0 999 1]
251 1005 [3 790 205 2 0] 754 [0 984 16]
252 1009 [1 604 393 2 0] 757 [0 940 60]
253 1012 [0 2 567 430 1] 760 [0 931 69]
254 1017 [451 548 1] 763 [0 935 65]
255 1021 [360 630 10] 766 [0 838 162]
# FINISHED
数据格式的解释
此部分是可选的。如果你只是想用我提供的软件来绘制数据,你可以跳到下一节。但是如果你想编写自己的软件来处理数据,这里我解释一下所有这些数字的含义。
典型的一行如下所示:
2 6 [1 153 846] 5 [590 409 1]
第一个数字2
是馈入 DAC 的 8 位值。就像我们之前看到的v
命令一样,这是一个从 0 到 255 的值。
第二个数字 6 是 DAC 测得的模拟电压的中间值。就像在v
命令中一样,它是一个从 0 到 1023 的值,从 0V 到 5V 线性缩放。
现在情况不同了。您将看到方括号[]
中的数字列表。括号中的值总是奇数。这些值是读取不同电压值的次数。因为我们对每个值进行了 1000 次过采样(在1000m
命令中),所以这些数字的总和总是 1000。也就是 1+153+846=1000。
中间的数字,在本例中为 153,表示测量的电压是 6 的多少倍(括号左边的数字)。所以对6 [1 153 846]
的解读方式是:
- 1000 次中有 1 次,电压读数为 6–1 = 5。
- 1000 次中有 153 次,电压读数是 6。
- 1000 次中有 846 次,电压读数是 6+1=7。
类似地,线的剩余部分5 [590 409 1]
表示在测试部件处测量的电压的过采样结果。
准备绘图仪软件
现在,使用剪贴板将这些数据复制并粘贴到您喜欢的文本编辑器中。在 GitHub repo 的克隆版本的data
目录中,将文件保存为resistor.txt
。打开命令提示符,转到克隆的目录。
如果您的系统上还没有 Python3,您需要安装它。验证方式:
$ **python3 --version**
Python 3.5.3
您还需要安装 Python 包matplotlib
来渲染图形。在 Linux 上,该命令是:
sudo pip3 install matplotlib
要确认绘图仪软件准备就绪,请输入以下命令:
$ **./plot.py**USAGE: plot.py x y datafile.txt [outfile.png]Where x and y are each one of the following: n = the value 0..255 that is fed into the resistor network.
v1 = measured voltage coming out of the op-amp.
v2 = measured voltage fed into the test component.
i = deduced current calculated as (v1-v2)/R.If outfile.png is provided on the command line, the image
is saved directly to that file.
Otherwise, the graph is shown interactively.
虽然数据文件只包含n
、v1
、v2
,但是在这个帮助文本中可以看到plot.py
可以利用欧姆定律推导出电流i
。
绘制电流/电压曲线
让我们将电流i
绘制成元件电压v2
的函数。输入以下命令:
./plot.py v2 i data/resistor.txt
这将产生一个显示线性关系的图形。毕竟电阻遵守欧姆定律,所以电流应该和电压成正比。这是我的样子:
现在,您可以测试非线性元件了。从 LED 开始很有趣,因为你也可以看到随着电压增加超过其照明阈值,它会慢慢变亮。用 LED 替换电阻器,确保将其阳极连接到电阻器 R9,阴极连接到 GND。如果你把它弄反了,就不会有坏事发生;它就是不亮,你会得到一个平坦的零电流曲线。把它翻过来再做一次测试。
确保在每次测试之前清除串行输出,这样就不会在输出文件中混淆不同测试的数据。遵循与电阻测试相同的步骤,运行1000m
命令,将输出保存到文件中,并运行plot.py
绘制数据。
以下是我测试随机放置的红色 LED 时得到的结果:
绘制正负电压响应
请看下图,您会注意到它涵盖了齐纳二极管在正负输入电压下的电流/电压响应。但是这个电路只产生 0V 到+5V 的电压。我是如何制作这个图表的?
这需要一点手工操作,但是绘图仪程序plot.py
自有妙招。您将运行两个测试,一个测试组件(如齐纳二极管)正向偏置。将数据复制并粘贴到一个文本文件中,并在该数据前单独手动键入单词FORWARD
。
在所有的测试数据之后,在一行中单独输入REVERSE
这个词。
然后在试验板上翻转元件,使其反向偏置。清除您的串行输出,再次运行测试,并将结果数据粘贴到单词REVERSE
之后。在data/zener3v.txt
中,您可以在克隆的 repo 中看到一个示例数据文件。为简洁起见,它看起来像这样:
# [1n5225b Zener Diode @ 3.0V]
**FORWARD**
0 0 [1000] 0 [1000]
1 2 [0 4 309 685 2]
2 [6 260 734]
...
253 1012 [0 3 756 236 5] 156 [4 695 173 88 40]
254 1016 [0 1 670 326 3] 156 [5 728 148 84 35]
255 1021 [553 442 5] 156 [4 685 184 90 37]
**REVERSE**
0 0 [1000] 0 [1000]
1 3 [5 316 677 2 0] 3 [6 288 703 3 0]
2 6 [5 215 780] 6 [0 6 187 806 1]
...
253 1012 [0 1 640 351 8] 525 [0 36 634 94 44 79 49 8 35 20 1]
254 1017 [533 462 5] 526 [6 241 507 67 52 64 14 16 27 6 0]
255 1021 [2 437 554 7 0] 526 [36 619 128 54 64 45 12 24 18]
FORWARD
是可选的,但是它明确表示下面的数据是正向偏置的。缺省值是向前偏移的数据。REVERSE
关键字表示其后的任何数据都应被视为反向偏置的负电压。
还要注意第一行中显示的语法,它允许您在图形的顶部放置一个标题:井号#
后跟方括号内的任何文本[]
。这个标题便于记录您测试了什么组件。
有趣的实验
探索不同二极管和晶体管结的行为会很有趣。尝试绘制几个同类 led 的数据,看看它们的行为有多匹配。或者你可以试着绘制不同颜色的 led 曲线,看看它们的“开启”电压有什么不同。
这个项目也可以是创建给定组件的数学模型的基础。我打算自己做这件事,看看如何用指数回归、切比雪夫多项式或其他技术最好地拟合实验曲线。看看软件是否可以通过与已知回归模型的数据库进行匹配来推断出哪个设备产生了曲线,这将是一件有趣的事情。
我希望这个项目能激发你思考创造性的改编。也许你可以将其中的一些想法用于你自己的发明和实验。
用 Spacy、Flask 和 FuzzyWuzzy 构建一个关键词提取 API
如何在 python 中构建一个轻量级的关键字提取和模糊词匹配服务?
通常,在处理长文本序列时,您会希望将这些序列分解开来,并提取单个关键字来执行搜索或查询数据库。
如果输入文本是自然语言,您很可能不希望使用每个单词来查询数据库,相反,您可能希望从输入中选择一组唯一的关键字,并使用这些单词或单词短语来执行有效的搜索。
这项任务被称为关键词提取,多亏了像 Spacy 这样的生产级 NLP 工具,只需几行 Python 代码就可以完成。在本文中,我们将涵盖:
- 如何使用 Spacy 构建一个简单而健壮的关键词提取工具
- 如何使用 fuzzyWuzzy 处理拼写错误并找到给定关键字(令牌)的模糊匹配
- 如何用 Flask 将这两个函数包装成 REST API 端点
这个轻量级 API 旨在成为许多用例的通用关键字服务。当然,您也可以使用相同的通用结构将 Spacy 众多 NLP 函数中的任何一个构建到这个 API 中。
在我们开始之前,确保运行:pip install flask flask-cors spacy fuzzywuzzy
来安装所有需要的包。
基于空间的关键词提取
对于关键字提取功能,我们将使用 Spacy 的两个核心思想——核心语言模型和文档对象。
Spacy 核心语言模型有:
通用预训练模型,用于预测命名实体、词性标签和句法依赖性。可以开箱即用,并针对更具体的数据进行微调。
空间文档对象为:
用于访问语言注释的容器……(和)是一个标记结构数组
因此,随着通过模型创建的文档对象的创建,我们可以访问大量非常有用(且强大)的 NLP 派生属性和功能,包括词类标签和名词块,它们将是关键字提取器功能的核心。
下载语言模型
使用 Spacy,我们必须首先下载我们想要使用的语言模型。到今天为止,Spacy 目前的version 2.2.4
已经有了 10 种不同语言的语言模型,都有不同的大小。我将使用英文核心模型的小版本。我选择了小模型,因为对于 Heroku 部署来说,大模型的内存大小有问题。根据您部署该模型的位置/方式,您可能能够使用大型模型。要使用 Spacy 的 CLI 下载语言模型,请在终端中运行以下命令:
python -m spacy download en_core_web_sm
当我们构建 flask API 时,我们将使用 python 内置的子进程包,在服务启动后在应用程序本身中运行这个命令。但是现在,我们可以在命令行中这样做。
现在下载好模型后,你可以加载它并创建nlp
对象:
import spacynlp = spacy.load("en_core_web_sm”)
我们的语言模型nlp
将作为参数传递给下面的extract_keywords()
函数,以生成doc
对象。
关键字提取函数有 3 个参数:
- 语言模型
nlp
sequence
我们要从中提取关键字的字符串。- 以及可选的字符串列表
special_tags
。此参数允许用户指定一个特殊的单词/短语列表,如果它们存在于序列中,则默认情况下会添加到输出中。
下面的代码片段显示了该函数是如何工作的:
- 通过语言模型传递字符串序列来创建
doc
对象。 - 如果特殊记号出现在序列中,则将它们添加到最终结果中。
- 迭代文档的名词组块,如果所有组块的标记都在期望的
pos_tag
列表中,则向结果添加一个名词组块。 - 最后,我们迭代所有的单个标记,并添加那些在期望的
pos_tag
集合中的标记,而不是语言模型的默认停用词列表的一部分。如有需要,可将自定义停用词追加到该列表中。
然后,该函数返回结果变量中结束的所有唯一单词的列表。
使用 Spacy 的关键字提取代码
带有 FuzzyWuzzy 的模糊字符串匹配
图片来源 u/lawlesskenny
当人类打字时,错别字和错误是不可避免的。在关键字搜索/匹配的环境中,这是一个问题,但是使用模糊匹配算法可以很好地解决这个问题。
Python 包 FuzzyWuzzy 实现了一个非常有效的模糊匹配算法: Levenshtein 距离。
Levenshtein Distance 是一个公式,用于计算将源单词 S 转换为目标单词 T. 的成本。该算法会惩罚需要进行许多更改才能转换为目标单词的源单词,并支持需要较小转换的单词。关于 FuzzyWuzzy 如何实现这种检查的详细而直观的解释,请参见 Luciano Strika 的文章。
[## FuzzyWuzzy:如何在 Python 上测量字符串距离
FuzzyWuzzy 是一个 Python 库,用于测量两个字符串之间的相似性。以下是您开始使用它的方法…
towardsdatascience.com](/fuzzywuzzy-how-to-measure-string-distance-on-python-4e8852d7c18f)
使用 FuzzyWuzzy
模糊匹配实现起来非常快。从包中导入ratio
会导入默认的 Levenshtein 距离评分机制,而process.extractBests()
允许我们计算一系列目标的 Levenshtein 距离,并返回高于定义的截止点的结果。为了获得最佳匹配结果,您可能需要对score_cutoff
参数进行微调。
模糊匹配实现
烧瓶应用
差不多了,现在剩下要做的就是把所有东西都打包到两个非常简单的烧瓶端点中。对于下面的要点,请确保导入模糊匹配器和关键字提取服务,或者在 app.py 本身中声明它们。
如果你是 Flask 的新手,我建议看看他们的文档快速入门指南。它们提供了如何启动和运行的简单、最小的示例。
该应用程序有两个端点:
api/keywords
api/fuzzy-matches
两个端点都接收 POST 请求,因此参数通过请求体传递给每个端点。
您可能还注意到,我们使用前面提到的子流程模块在应用程序内部以编程方式调用 Spacy CLI。如果您需要将它部署到云服务,并且忘记通过 CLI 手动下载模型,这将非常有用。
我们在两个端点之外加载语言模型,因为我们希望这个对象在我们的服务运行时无限期地持久,而不必在每次发出请求时都加载它。这使得添加使用 Spacy 功能的新端点变得容易,因为它们可以共享相同的语言模型,该语言模型可以作为参数提供。
应该就是这样了,在项目目录的命令行中运行下面实现的代码flask run
,这应该会在您的本地主机上启动 API。
您可以在 Postman 中测试端点,以确保它们的行为符合预期。
两个端点的邮递员请求
如果您想将 API 部署到云服务,如 Heroku,请查看:
[## 在 Heroku 上部署 Python Flask 应用程序
在我之前的文章中,我描述了我是如何使用 Flask 开发 API 的。我简单讲了一下我是如何主持…
medium.com](https://medium.com/the-andela-way/deploying-a-python-flask-app-to-heroku-41250bda27d0)
上面的文章是几年前的了,原理还是一样的,你也可以直接在 heroku 的网站上设置一个应用程序,然后通过 CLI 推送。
结论
这应该有望帮助您启动并运行这个简单的 API。我发现在很多情况下,我需要这样一个简单的服务来处理文本输入或执行某种 NLP 任务。这个设置希望有所帮助的是,它应该很容易允许添加额外的空间 NLP 服务作为端点,而无需任何重大更改。
如果你有任何问题或者在我提供的任何代码中发现了一个错误,请告诉我,感谢阅读!
[1]空间文档。模特。https://spacy.io/models
[2]空间文档。文档对象。https://spacy.io/api/doc
用最少的代码行构建一个 LIME explainer 仪表板
实践教程
Flask、Plotly Dash 和 Streamlit 的比较,以构建仪表板,为分类结果提供石灰解释
图片来源:Pixabay 上的奇摩诺
在早先的一篇文章中,我描述了如何使用 LIME(LocalIinterpretableMmodel-agnosticEexplanations)来解释一个细粒度情感分类器的结果。概括地说,以下六个模型用于在斯坦福情感树库(SST-5)数据集上进行细粒度情感类别预测。
- 基于规则的模型:文本块和 VADER
- 基于特征的模型:逻辑回归和支持向量机
- 基于嵌入的模型: FastText 和 Flair
使用线性工作流来分析和解释使用每种方法的情感分类结果。每个模型被训练成 5 类情绪(1 到 5),1 是“强烈负面”,3 是“中性”,5 是“强烈正面”。
这篇文章的目标是展示如何构建一个解释器仪表板(使用三个框架中的任何一个),它接受一个经过训练的模型,并为模型做出的预测输出及时的解释。
示例石灰说明
简而言之,LIME 生成一个包含可视化效果(作为嵌入式 JavaScript)的解释对象,该对象可以输出到一个 HTML 文件,然后可以在任何浏览器中打开。LIME 的典型输出如下所示。
为什么要构建交互式仪表板应用程序?
要使用 LIME 解释分类器的结果,每次需要解释时都必须写出单独的 HTML 文件,这可能很麻烦。接受用户输入的交互式仪表板是实时快速迭代多个测试样本的非常有效的手段,为用户提供即时反馈。此外,拥有一个仪表板允许非技术用户(他们可能知道也可能不知道如何执行 Python 脚本)能够按需做出自己的时间解释。
以下部分展示了我们如何使用三种不同的框架构建 LIME explainer 仪表板:Flask、Dash 和 Streamlit。
解释器类
为了方便地引用每个分类器的预测方法,下面的面向对象的模板被应用来支持代码重用,在项目的 GitHub repo 中可用。简而言之,定义了一个 Python 类,它接受由 LIME 生成的变量列表(标记空白的随机文本样本),然后我们将每个样本的类概率输出为一个 Numpy 数组。
一旦每个变化的类概率被返回,这可以被馈送到LimeTextExplainer
类(如下所示)。启用词包(bow
)意味着 LIME 在生成变体时不考虑词序。然而,FastText 和 Flair 模型分别考虑 n 元语法和上下文排序进行训练,因此为了模型之间的公平比较,SST-5 上的所有解释都禁用了bow
标志选项。
LIME 解释器返回的exp
对象是通过 LIME 内部的explain_instance
方法,将本地线性模型的预测(以数字形式)转换成可视的、可解释的形式。这可以输出为 HTML。
以下部分描述了如何将所有这些功能封装到一个交互式仪表板应用程序中。
选项 1:烧瓶
LIME explainer 仪表板的烧瓶版本如下所示。用户输入一段文本,选择要为 LIME 生成的随机样本的数量,然后使用下拉菜单从给定的分类器列表中进行选择。点击Explain results!
按钮,然后生成一个 LIME 解释 HTML 对象,它呈现在一个 HTML Iframe 中。
虽然 Flask 不是一个数据仪表板工具(它是一个 WSGI web 框架,最初是围绕 Werkzeug 和 Jinja 的包装器),但它提供了一个简单的基于插件的架构,开发人员可以从这个架构中为复杂的应用程序构建和扩展接口。Flask 的关键优势在于它在生产环境中的健壮性,以及在 Python 生态系统中围绕它的大量扩展。
要使用 Flask 构建 LIME explainer 仪表板,需要了解以下技术:
- HTML/JavaScript :页面的整体结构和内容是用 HTML 定义的。任何需要基于字段值或用户输入触发的操作都需要使用 JavaScript 来定义,要么通过 HTML 文件本身,要么从外部源加载。
- CSS :使用 CSS 文件定义页面的样式和布局。
- Jinja2 :这是一个模板引擎,从 Python 中动态生成页面的 HTML。出于安全原因,这是必要的(不使用模板和传递未转义的静态 HTML 会导致跨站点脚本攻击)。模板引擎由 Python 控制,最终的 HTML 使用 Flask 方法呈现。
Flask 应用程序:目录结构
Flask 应用程序使用的目录结构如下所示。所需的样式在static/style.css
文件中用 CSS 配置,要渲染的 HTML 模板在templates/index.html
中定义。任何经过训练的情感分类器模型都会进入models
目录。解释器类在lime_explainer.py
中定义,烧瓶路径在app.py
中定义。
烧瓶石灰解释器应用程序的目录结构
对于这个用例,Flask 中的应用程序代码是用最简单的方式编写的。定义了两条路线(默认路线'/'
和时间结果路线'result'
)。请注意,结果路由使用了一个POST
请求,这意味着它只在用户向应用程序输入一些信息并与之交互时才生成 HTML(通过 Jinja)。
Flask LIME explainer 应用程序的代码可从 GitHub 获得:
这个报告包含一个用 Flask 编写的交互式应用程序的初始原型,它解释了…
github.com](https://github.com/prrao87/fine-grained-sentiment-app)
选项 2:破折号
设计 LIME 仪表板的另一种方法是使用 Plotly 的 Dash 库。Dash 是一个用 Python 构建分析性 web 应用程序的框架。使用 Dash 的好处是双重的:开发人员可以只使用 Python(不需要 JavaScript)设计应用程序,并且他们可以通过 CSS 完全控制应用程序的设计和结构。下面演示了一个使用 Dash 编写的 LIME explainer 应用程序。就像 Flask 应用程序的情况一样,单击Explain results
按钮生成一个 LIME 解释 HTML 对象,该对象通过 Dash 的 HTML Iframes 包装器呈现。
Dash 应用程序:目录结构
Dash 应用程序使用的目录结构如下所示。使用 CSS 在assets/style.css
文件中配置所需的样式。与 Flask 示例不同,在app.py
中,应用程序的 HTML 布局和路线/交互是使用纯 Python 定义的。任何经过训练的情感分类器模型都会进入models
目录。解释器类在lime_explainer.py
中定义。
Dash LIME 解释器应用程序的目录结构
Dash 应用程序代码的关键组件如下所述。首先是应用程序布局,它是使用 Dash 的 HTML 包装器用 Python 编写的。
常见的 HTML 对象,如标题、标签、文本输入和 Iframes,可以使用 Python 结构轻松添加,如图所示。
下一个组件是回调,这是一段反应式的功能性代码,允许开发人员观察、修改和更新 UI 中任何组件的属性。在 Dash 中,回调是使用 Python 的装饰语法定义的。回调是编码应用程序交互性的非常强大的方法,因为它们跟踪按钮点击和数据更新的状态*。LIME 解释器中使用的主要回调如下所示。*
按照上面的构造,我们传递用户的按钮点击(或者是“提交”或者是“清除”按钮),以及下拉菜单的状态,在文本输入中输入的样本数量,以及我们想要预测其情感的文本样本。这里跟踪每个文本字段的状态(而不仅仅是值)是很重要的——它允许我们将回调的执行与按钮点击联系起来,而不是每次文本输入中的值更新时都执行它。
Dash LIME explainer 应用程序的代码也可以在 GitHub 上找到:
这个报告包含了一个现有的交互式应用程序的 Plotly Dash 等价物,它解释了…
github.com](https://github.com/prrao87/fine-grained-sentiment-app-dash)
选项 3:简化
另一种选择是使用 Streamlit 来构建解释器应用程序。这是目前最快的方法,需要非常基础的 web 开发知识和最少的代码行。与 Flask 和 Dash 方法不同,Streamlit 应用程序使用自己的样式和布局(不可能通过 CSS 进行定制,至少不能使用传统方法)。Streamlit LIME explainer 应用程序如下所示。
Streamlit LIME 解释器应用程序的目录结构
因为 Streamlit 是为帮助快速创建和共享 web 应用程序而从头设计的,所以 LIME dashboard 应用程序有一个非常简单的目录结构。web 应用程序的所有代码都写在一个文件中,app.py
-这包括小部件、结构、交互和所有用户输入来进行时间预测。这种设计的显著之处在于,即使将所有这些功能都塞进了一个文件中,它仍然非常简洁(大约 40 行代码!).因此,我们只对特定于应用程序的实体使用单独的目录,比如模型和数据。
Streamlit 仪表板的完整代码如下所示。
标题是用 markdown 语法编写的。请注意,因为 Streamlit 的 API 是为速度和易用性而设计的,所以它不像 Flask 和 Dash 那样允许轻松访问底层 HTML。因此,在这种情况下,我们需要显式地使用原始 HTML 字符串的不安全呈现(使用unsafe_allow_html
关键字)来获得居中对齐的标题。然后使用一个 Streamlit HTML 组件呈现 LIME explainer 的 HTML 输出,这是一个定制组件,在 Iframe 中显示 HTML 字符串。
和往常一样,Streamlit LIME explainer 应用程序的代码可以在 GitHub 上获得:
此回购包含现有交互式应用程序的 Streamlit 等价物,它解释了…
github.com](https://github.com/prrao87/fine-grained-sentiment-app-streamlit)
部署
出于本文的目的,这三个应用都是使用 Heroku 部署的,这是一个 PaaS 系统,允许开发者在云上构建、运行和操作应用。用于构建应用程序的三个框架都有很好的 Heroku 部署指南,如下所示。
- Flask: 将 Flask 应用程序部署到 Heroku
- Dash: 部署 Dash 应用
- Streamlit: 如何在 Heroku 上部署 Streamlit
然而,使用生产级 WSGI web 服务器,如 gunicorn 和负载平衡器,如 Nginx ,部署 Flask 或 Dash 应用程序也相对简单。在部署期间,Nginx 充当位于 web 服务器前面的反向代理,以高度的可靠性处理大量请求。类似地,Streamlit 还提供了一种使用 Docker 和/或 Nginx 的组合来手动部署应用的方法。
关于 Python web 框架中可伸缩性的一个注释
web 服务器网关接口(WSGI)是作为 Python web 框架与 Web 服务器交互的标准而开发的一个 Python 规范。有了这样的系统,开发人员可以轻松地将 Nginx 之类的服务放在 Python web 应用程序的前面(例如,用 Flask 或 Dash 编写),作为将所有请求转发给 web 应用程序的反向代理。兼容 WSGI 的服务器的一个关键特性是它们是同步的。这意味着每个请求阻塞服务器,直到它收到来自应用程序的响应,称为阻塞操作。WSGI 服务器合并可伸缩性的典型方式是通过使用多线程,其中为每个请求创建一个新线程,以便能够同时处理多个请求。然后,这与运行 web 服务器的多个工作进程相结合,这确实具有很好的伸缩性,但是受到给定机器上可用的物理内核数量的限制。
像 Flask 和 Dash 这样的框架所使用的 WSGI 方法,如果有非常大量的请求进入,就可以在伸缩方面达到极限。在生产系统中解决这个问题的方法是水平扩展*,即添加越来越多的服务器,并使用类似 Nginx 的负载平衡服务,这些服务可以在高请求量期间在所有服务器之间平均分配负载。*
最近的 Python web 应用框架,如 Streamlit,完全依赖于不同的并发系统。Streamlit 在幕后使用了一个 Tornado web 服务器,它被从头设计为使用异步事件循环。在这个系统中,使用了一个单线程,它实现了按照到达顺序执行的非阻塞功能。这种方法可以很容易地在 web 应用程序中实现非常高的并发度,这在很大程度上依赖于 I/O 绑定操作的应用程序中,可以真正帮助扩展系统,以同时处理大量请求。
和往常一样,没有单一的规则来决定哪种并发方法更好。根据确切的用例以及手边的应用程序,基于 WSGI 或异步事件循环驱动的服务可能是合适的选择。
延伸阅读: WSGI 已经不够用了 —第一、二、三部
什么时候使用每个框架最有意义?
本节讨论每个框架最适合当前任务的情况。
瓶
Flask 的强大之处在于它允许开发人员使用任何前端工具的组合来创建 web 应用程序。这包括表单输入扩展,如 WTForms 和 Flask-Login ,以及 JavaScript 可视化库( Highcharts 或 D3 )。此外,Flask 通过 HTML、CSS、jQuery 和 Bootstrap 为开发人员提供了对底层页面结构和用户交互的完全访问,允许基于项目需求构建非常复杂的应用程序的巨大灵活性。
关于本文中显示的 LIME explainer 仪表盘,Flask 是以下任何一种场景的绝佳选择:
- 该应用程序由一个在 JavaScript、HTML 和 CSS 以及不同的 HTTP 请求方法(GET、POST 等)方面拥有专业知识的团队编写。)
- 该应用是一个更大的仪表板的一部分,由前端的 JavaScript 框架提供支持
- 处理和服务数据的多个 RESTful 端点已经存在(在这之后,LIME explainer 可以作为另一个端点被写入前端)
破折号
对于主要在 Python 环境中工作的应用程序需要高度可定制性的开发人员来说,Dash 是一个很好的选择。许多强大的前端可视化工具(来自 Plotly.js JavaScript 库)都是现成的,允许开发人员主要关注应用程序的样式和添加用户交互。因为 Dash 构建在 Flask 之上,所以它遵循与 Flask 相似的部署策略,这使得已经有在生产中实现 Flask 应用的经验的团队可以非常容易地使用它。
一般来说,在以下任何一种情况下,Dash 都是构建 LIME explainer 仪表板的绝佳选择:
- 需要编写应用程序来与现有的 Flask 应用程序集成
- 开发和部署该应用程序的团队在 Python 方面经验丰富(但在 JavaScript 方面不太精通)
- 仪表板需要很好的定制程度(Dash 允许开发者访问底层 CSS)
细流
作为生态系统的相对新人,Streamlit 在数据科学家需要通过交互式应用程序与更大的团队快速分享他们的工作的情况下大放异彩。对于数据科学家个人来说,它也非常有用,可以快速、交互式地探索数据集或模型对单个样本的性能。
对于 LIME explainer 仪表板,Streamlit 是在以下任何场景中使用的一个很好的替代方案:
- 该应用程序是由一个团队(或个人)编写的,只有很少的 web 开发经验
- 应用程序需要在最短的时间内快速构建,并尽可能使用最少的代码行
- 开发人员希望花更多的时间构建交互式工具,尽可能少的时间定制应用程序的外观
结论
这篇文章强调了构建 LIME explainer 交互式仪表板应用程序的三种不同方法。Streamlit 是所有选项中最简单、最容易学习的。Flask 需要最大的前期时间投入,学习各种组合在一起的部分(HTML、CSS、jQuery/JavaScript、Jinja2 和 HTTP 请求方法)。Plotly Dash 很好地介于 Flask 和 Streamlit 之间,就复杂性和启动并运行仪表板的初始工作而言。根据承担此类项目的团队的组成和技能,这三个选项中的任何一个都可能是最合适的。
构建如图所示的 LIME 仪表板的主要原因是允许非数据科学家的人检查 NLP 分类器的结果。至少在某种程度上,提供一种交互式方法来动态测试单个样本的结果可以帮助诊断 NLP 模型的问题,并提高模型的可解释性。为您自己的用例从每个回购(如下所示)中体验和定制代码的乐趣吧!
原载于 2020 年 10 月 24 日https://prrao 87 . github . io*。*
用 Dash 构建机器学习模拟工具
了解如何使用 Plotly/Dash 库构建交互式仿真工具,并动态探索您的机器学习模型行为以提高其可解释性。
丹·洛马尔在 Unsplash 上拍摄的照片
你做到了!这一切都始于您的同事提出的一个业务问题,您经历了数据整合、清理、特性工程和建模的黑暗山谷。您的模型的稳健性已经过检查,您希望分享您的发现。
然而,并不是每个人都熟悉 RMSE、混淆矩阵或 Shapey 值…
例如,行业中的运营团队每天都面临着优化生产或供应链的挑战,机器学习提供了一种新的方式来理解复杂的流程行为……只要它可以转化为可理解的分析。当分享人工智能带来的一些见解时,Python 笔记本可能不是最佳选择!
除了模型预测能力本身,团队总是热衷于了解在众多相关生产参数中最重要的因素是什么,以及每个因素如何影响模型的行为。
Dash libraries 为您提供了一种创建动态仪表板的方法,可通过具有交互功能的 Web 浏览器访问。
这个概念非常简单:
- 您创建一个. py 脚本,其中您的“经典”机器学习任务与编码的网页布局设计结合在一起,从 HTML 代码中关闭。
- 当执行时,python 脚本在本地地址( http://127.0.0.1:8050/ )上生成一个动态 web 页面,组件在此进行交互。
图标由 像素佛 和Pixelmeetup*(来源:flaticon.com)*
你想得到一个快速演示吗?开始了。
首先,我们需要设计一个“看起来像”的工业用例!Scikit-learn 的“make_regression”将生成我们的数据源。我们将微调一些功能,以获得示例中的真实人物:
数据生成和建模阶段
一旦“机器学习”任务完成,我们仍然需要准备要在我们的网络浏览器上显示的动态信息。对于此示例,我们将创建:
- 显示模型特征重要性的条形图
- 三个滑块允许用户更改三个最重要特征的值,并了解它们对模型预测的影响
您可能已经注意到条形图的创建与 Plotly 中使用的相似,除了。未调用 show()函数。原因是图表将在“布局”部分呈现。
“布局”一节(下面)要求不熟悉 Python 和 HTML 的用户进行一些练习。
这里的理念是将 HTML 元素(由 htm.xxx 调用)与动态破折号组件(dcc.xxx)结合起来。
我们将保持这个例子非常简单:
**<H1> Title
<DCC> Features Importance Chart
<H4> #1 Importance Feature Name
<DCC> #1 Feature slider
<H4> #2 Importance Feature Name
<DCC> #2 Feature slider
<H4> #2 Importance Feature Name
<DCC> #2 Feature slider
<H2> Updated predictions**
接下来是脚本中“最棘手”的部分: app.callback 函数允许您封装一个标准的 python 函数,这样它就可以与上面设计的 web 页面组件进行交互。其机理可以简化如下:
[**@app**](http://twitter.com/app)**.callback**(Output_on_webpage,
Input_1_from_webpage,
Input_2_from_webpage,
Input_3_from_webpage)**python_function**(Input_1, Input_2, Input_3): Output = model_evaluation([Input_1, Input_2, Input_3]) return Output
您可能已经注意到,我们在这里认为只有三个最重要的特性值得更新。因此,当模型评估阵列时,我们考虑了所有其他特征的平均值。这显然是出于简化目的的设计选择。
然后“瞧”!
我们打开命令提示符并键入" python dashboard.py "
(base) PS C:\Users\...\> **jupyter dashboard.py**
* Serving Flask app "__main__" (lazy loading)
* Environment: production
* Debug mode: off
* Running on [**http://127.0.0.1:8050/**](http://127.0.0.1:8050/) (Press CTRL+C to quit)
现在,我们只需在 打开我们最喜欢的网络浏览器 http://127 . 0 . 0 . 1:8050/并检查结果:
每次滑块移动,app.callback 函数都会运行 python 脚本来重新评估预测!
完整的脚本存储在 GitHub 上的这里。
正如您所想象的,让非数据科学家团队与模型进行交互并理解其行为成为了一个非常强大的工具。这也是测试模型准确性的一个非常好的平台:运营团队将能够检查并确认模型的行为确实符合他们的现场经验。
在我们结束这篇文章之前,有几个建议:
- 如果您不熟悉 Plotly 和 Dash,您应该从标准 Plotly 图表开始训练自己(步骤解释得非常清楚):
Plotly 的 Python 图形库制作出交互式的、出版物质量的图形。如何制作线图的示例…
plotly.com](https://plotly.com/python/)
- 下面的在线课程设计得非常好,它将指导你学习 Python 基础知识(Pandas / NumPy)、Plotly 图表语法以及 Dash 设计。我不推荐它:
[## 互动课程:使用 Python 仪表盘和 Plotly & Dash
本课程将教授您使用 Python 创建交互式仪表盘所需的一切知识
www.udemy.com](https://www.udemy.com/course/interactive-python-dashboards-with-plotly-and-dash/) [## 皮埃尔-路易·贝斯康德关于媒介的文章
数据科学、机器学习和创新
pl-bescond.medium.com](https://pl-bescond.medium.com/pierre-louis-besconds-articles-on-medium-f6632a6895ad)**
使用 OpenCV 和 Python 构建运动热图视频
OpenCV 是一个强大的图像和视频操作库,在这个故事中,我想创建一个运动热图,用于检测运动,物体或人的流动方向,例如,在投影公共区域时为建筑师提供帮助。
本指南中描述的最终结果的屏幕截图。
简介:
OpenCV,或(开源计算机视觉)是英特尔在 1999 年开发的主要针对计算机视觉和实时视频操作的库,它是用 C++编写的,但它支持不同的语言,其中包括 Python。
工作流程:
这个程序是基于一种被称为高斯背景减法的技术。这种技术广泛用于用稳定的摄像机检测运动物体。
背景减法创建一个表示帧背景(图像的静态部分)的遮罩,并且对于每一帧,它减去前一帧。
让我们对该算法如何工作的两个主要步骤有一个简单的概述:
- 背景初始化:在这个第一步中,通过冻结第一帧来计算背景的模型。
- 更新:在该第二步骤中,从前一帧中减去下一帧,因此,如果在两帧之间发生了变化(运动),则帧的差异将反映该变化,这可以通过应用过滤器来实现。
以下是应用于从城市摄像机录制的短视频的背景遮罩示例:
代码:
代码从读取输入视频文件开始,并初始化一些需要的变量:
capture = cv2.VideoCapture('input.mp4')background_subtractor = cv2.bgsegm.createBackgroundSubtractorMOG()
length = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
然后,for
循环开始遍历帧:
for i in range(0, length):
ret, frame = capture.read()
# If first frame
if first_iteration_indicator == 1:
first_frame = copy.deepcopy(frame)
height, width = frame.shape[:2]
accum_image = np.zeros((height, width), np.uint8)
第一个if
块检查该帧是否是视频的第一帧,这样做是为了初始化我们的背景,以便进行背景减法,然后用对应于该帧尺寸的尺寸初始化accum_image
阵列。
filter = background_subtractor.apply(frame) # remove the background
threshold = 2
maxValue = 2
ret, th1 = cv2.threshold(filter, threshold, maxValue, cv2.THRESH_BINARY)
accum_image = cv2.add(accum_image, th1)
color_image_video = cv2.applyColorMap(accum_image, cv2.COLORMAP_HOT)
为了去除少量的运动,如风、小鸟飞翔或一些噪音,一个threshold
和maxValue
一起应用到遮罩上。
屏蔽的结果然后被添加到accum_image
阵列,该操作对每一帧执行。结果由accum_image
数组组成,用于存储视频中发生的每个动作。
此外,因此,在最后,当已经对每一帧完成了先前描述的操作时,色彩映射表被应用于遮罩,并且遮罩与当前帧合并。
从上到下,从左到右:当前帧、当前最终帧、过滤后的当前帧、应用了从帧 0 开始的所有遮罩的帧。
更进一步,可以制作一个视频,显示热图逐帧淡入淡出。为了实现这一点,每一帧都被导出,并且再次使用cv2
,通过将所有帧合并在一起来生成视频:
video = cv2.VideoWriter('output.avi', fourcc, 30.0, (width, height))for image in images:
video.write(cv2.imread(os.path.join(image_folder, image)))
cv2.destroyAllWindows()
最终结果:
**I have a newsletter 📩.**Every week I’ll send you a brief findings of articles, links, tutorials, and cool things that caught my attention. If tis sounds cool to you subscribe.*That means* ***a lot*** *for me.*
**[## 米尔斯形式
编辑描述
无情-创造者-2481.ck.page](https://relentless-creator-2481.ck.page/68d9def351)**
用 Python 构建运动检测报警系统
使用 OpenCV 进行运动检测的初学者友好指南
在我之前的一篇关于 Open-CV 的文章中,(10 行中的人脸检测 ) ,我们探讨了一幅图像或视频中人脸检测的基础知识。在此基础上,我们将看到如何检测帧中的任何移动对象。当你外出度假时,你可以使用这种运动检测逻辑编写一个程序,然后发出警报或向你的手机发送消息。在本文中,我们将重点关注使用open-cv识别和跟踪来自网络摄像头的视频提要中的任何移动对象,并使用离线文本到语音模块pyttsx 3库播放音频消息。让我们看看它是否足以吓跑窃贼!
概观
这个程序中有两个独立的模块,你可以根据你要实现的想法单独使用:
1.检测运动(使用 OpenCV)
2。播放音频/文本到语音(使用 pyttsx3)
我们将从使用 pip 安装以下 Python 库开始。
pip install pyttsx3
pip install pywin32
pip install numpy
pip install opencv-python
方法
方法很简单。当程序启动时,我们将捕获一个名为 *baseline_image 的图片。*这是没有任何物体/入侵者的图像。我们的程序将不断比较新的帧和这个基线图像。如果没有人进入或退出框架,将不会有任何区别。然而,当有人进入画面时,图像的内容会有所不同,如果这种差异超过某个阈值,我们的程序会将其视为入侵者并播放音频。
1.检测运动
为了检测运动,我们将使用 Open-CV 模块。我们从 baseline_image 开始,它是在没有任何移动对象的情况下捕获的帧。摄像机一启动,第一张图像就被设置为我们的 baseline_image,这意味着当我们的程序第一次启动时,我们不希望有移动的物体。接下来,当有人进入该帧时,该帧中的某些像素将会不同。我们使用“cv2.absdiff”方法推导出这种差异。
我们将采取几个步骤来实现这一目标。
1 。捕捉基线 _ 帧(无对象)
1.1 将帧转换为灰色
1.2 平滑帧以去除噪声2 。捕捉新帧(带对象)
2.1 将帧转换为灰色
2.2 平滑帧以去除噪声3 。计算两帧之间的差异
3.1 如果差异大于阈值,则假定检测到运动
3.2 否则假定没有检测到运动
首先,我们将图像转换为灰度,并使用低通滤波器(LPF)柔化(模糊)图像。LPF 通常用于图像处理以平滑图像(例如:-用于皮肤平滑、背景模糊),而高通滤波器(HPF)用于锐化图像(例如:-锐化眼睛和其他细节)。如果你曾经使用过像 Photoshop/GIMP 等图片编辑工具。,你一定很熟悉这个。这有助于通过消除噪声来提高精度。为此,我们使用了高斯布鲁。GaussianBlur 之前和之后的图像如下所示。
如果你仔细观察图像的底部中心,你会注意到在“之前”图像中我手指的较暗一侧,我的手指有不同程度的灰度(一些区域没有其他区域暗)。然而,在“后”图像中,整个较暗的一面几乎是灰色的一种变体。模糊的程度由我们自己决定。这里我有高斯内核大小宽度和高度为(25,25)和一个标准偏差 0。宽度和高度应该是正奇数。可以把它想象成改变图像编辑工具中模糊量的滑块。执行此操作的代码如下所示。
**#Gray conversion and noise reduction (smoothening)**
gray_frame=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
gray_frame=cv2.GaussianBlur(gray_frame,(25,25),0)
下一步是推导基线和新帧之间的差异。我们将这两个图像传递给 cv2.absdiff()函数。这是使用一种叫做 图像阈值 的方法转换成的二进制图像,也就是说,如果一个特定的像素值大于某个阈值(我们在这里指定为 35),它将被赋予白色的值(255)或者黑色的值(0)。现在我们有一个只有两种像素的图像(纯黑或纯白,中间没有)。这样做是为了使我们能够识别检测对象周围的轮廓区域。我们将用它在框架中的对象周围画一个绿色的方框。
现在我们将在我们的二值图像中找到所有的轮廓。 轮廓 简单来说就是沿着具有相同颜色或强度的周界或边界绘制的曲线。本质上,它会在黑色背景上的白色区域周围绘制一条曲线。它期望背景为黑色,前景对象为白色。使用 cv2.findContours()方法,我们将识别图像中的所有轮廓。该方法期望 3 个参数, (a) 图像, (b) 轮廓检索模式和 © 轮廓逼近方法。
该方法返回已识别轮廓的列表。我们使用 cv2.contourArea()方法过滤掉我们不感兴趣的小轮廓。cv2.boundingRect()返回左上角的(x,y)坐标以及包含特定轮廓的矩形的宽度和高度。然后我们画一个矩形在屏幕上显示它。
**#Calculating the difference and image thresholding**
delta=cv2.absdiff(baseline_image,gray_frame)
threshold=cv2.threshold(delta,35,255, cv2.THRESH_BINARY)[1]**# Finding all the contours**
(contours,_)=cv2.findContours(threshold,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)**# Drawing rectangles bounding the contours (whose area is > 5000)**
for contour in contours:
if cv2.contourArea(contour) < 5000:
continue
(x, y, w, h)=cv2.boundingRect(contour)
cv2.rectangle(frame, (x, y), (x+w, y+h), (0,255,0), 1)
2.播放音频(文本到语音)
为了播放音频,我们将使用"pyttsx 3" python 库来将文本转换为语音。您可以选择自己的声音(男/女)、讲话速度和音量。下面分享了我在这里使用的一段示例代码。
import pyttsx3engine = pyttsx3.init()
voices = engine.getProperty('voices')
engine.setProperty('voice', voices[1].id)
engine.setProperty('rate', 150)
engine.say("Object Detected")
engine.runAndWait()
最终的输出程序存在于 Github 库中以供参考。您可能会注意到,播放音频的功能是在一个单独的线程中执行的。这样做是为了在播放音频警报消息时,视频馈送没有延迟。
让我们看看我们最终的工作版本是什么样的。
阿林多姆·巴塔查尔吉
在结论中,本文演示了如何使用 Python OpenCV 库来检测帧之间的差异,从而检测视频馈送中的运动对象。我们还探索了另一个 Python 库 pyttsx3,它可以将文本转换为语音,以及如何通过结合这两者来构建一个程序,在新对象进入框架时发送语音警报。我希望您发现本文中的信息很有用,并以自己的方式使用它。感谢阅读!
使用 Python OpenCV 在图像和视频中检测人脸的介绍。
towardsdatascience.com](/face-detection-in-10-lines-for-beginners-1787aa1d9127) [## 用 Python 生成 5 行二维码
也许在你的简历中使用它来链接到你的网站或 LinkedIn 个人资料
towardsdatascience.com](/generate-qrcode-with-python-in-5-lines-42eda283f325) [## 在 5 分钟内制作一个声控鼠标
Python 中语音识别和 GUI 自动化的初学者指南
towardsdatascience.com](/build-a-voice-controlled-mouse-keyboard-in-5-minutes-952bc8f101fc)
参考资料:
[1]图像过滤,https://docs . opencv . org/2.4/modules/imgproc/doc/Filtering . html
[3]了解轮廓,https://docs . opencv . org/trunk/D4/d73/tutorial _ py _ Contours _ begin . html
使用 Scikit-Learn、Flask 和 Heroku 构建一个电影推荐 API
创建您自己的电影推荐 API,供其他开发者在他们的应用程序或网站中使用
Noom Peerapong 在 Unsplash 上拍摄的照片
推荐系统无处不在。一些最好的例子是 YouTube、网飞、Spotify 。网飞和 YouTube非常依赖他们的推荐系统来让用户在他们的平台上停留更长时间。 Spotify 根据用户的兴趣向用户推荐精选的歌曲列表。推荐系统发挥关键作用的例子还有很多。
但是等等,什么是推荐系统?
简而言之,推荐系统是一种算法,旨在基于用户过去给出的偏好或评级向用户建议相关数据,或者基于项目的属性向用户建议相关数据。
要了解更多关于推荐系统的信息,请点击下面的链接
目标:-
建立一个基本的基于内容的电影推荐系统,用 Flask 做一个 API,部署到 Heroku。
随着本文的深入,我们将详细讨论基于内容的推荐系统、API 和 Heroku 。
现在,我们来看看这篇文章的内容。
内容:-
- 数据集描述
- 建立一个基于内容的推荐系统
- 使用 Flask 构建一个 REST API
- 在本地主机— 127.0.0.1 上测试
- 部署到 Heroku — API 上线
我们将逐步介绍这些主题,最后,我们将构建一个电影推荐 API ,任何人都可以使用它在应用程序或网站中向他们的用户提供相关的电影建议。
那么,让我们开始吧。
杰瑞米·毕晓普在 Unsplash 上的照片
1。数据集描述
弗兰基·查马基在 Unsplash 上拍摄的照片
关于数据集
我们首先需要的是数据。关于电影的数据,比如类型、演员阵容、剧情等等。在这个任务中,我从 Kaggle 获取了数据。我们有两个数据来源。
现在,由于我们想要一个非常基本的推荐系统来查看 API 在应用程序或网站中使用时的外观,因此我们根据我们的需要组合并修改数据集。
数据集的修剪和预处理部分是在我的一个 Jupyter 笔记本上单独完成的,这将在以后讨论。但是,如果您仍想了解修剪数据集所涉及的步骤,请访问下面的链接。
笔记本可能有点不整洁,因为它从来没有打算上网。因此,如果任何人都难以理解的话,我道歉。
下面是数据集的链接,该数据集将用于构建我们的**基于内容的推荐系统。**下载这个数据集,我们就可以开始了。
数据集概述
因此,我们的数据集中有 6477 部电影,具有以下属性:-
- 演员阵容:电影前三名男女演员
- **流派:**电影的前 3 大流派
- 好莱坞和宝莱坞电影的 movie_id: TMDb 和IMDbid
- **原创 _ 标题:**电影的标题
- **剧情:**电影基本概况
我们已经准备好了数据集。让我们进入下一部分,即构建基于内容的推荐系统。
2。构建基于内容的推荐系统**
好了,现在什么是基于内容的推荐系统?
基于内容的推荐的工作原理是,如果用户喜欢某个项目,那么我们根据项目的特征或属性向用户推荐类似的项目。所以在我们的例子中,如果一个用户喜欢一部特定类型的电影或者一个演员,那么我们就向用户推荐一部类似的电影。因此,如果用户已经观看了电影小丑,那么我们的推荐系统将预测与小丑相似的电影,或者如果我们考虑电影的演员阵容,与小丑具有相同演员阵容的电影。
现在我们对什么是基于内容的推荐系统以及它是如何工作的有了一个基本的概念,让我们来编码一下。
文件 1 — recommendation.py
导入所需的包
recommendation.py 中有 5 个函数。让我们逐一讨论它们。
i) get_data()
***get _ data()***用于获取电影的相关数据,并将数据集及其属性作为结果返回,以便进一步预处理。
- **第二行:**我们使用 pandas.read_csv() 读取 movie_data.csv.zip 文件。
- **第 3 行:**将所有电影的标题转换成小写字母。
- **第 4 行:**返回数据集作为函数的结果。
get _ data()的返回值:-
ii)合并数据()
combine _ data()删除特征提取不需要的列,然后合并cast和 流派 列,最后返回combine列作为该函数的结果。
**第 2 行:**删除特征提取不需要的属性。
第 3 行:将两列演员和流派合并成一列。
第 5 行:我们有一个带有造型和流派值的组合列,因此我们从数据集中删除了单独存在的造型和流派列。
第 6 行:返回带有合并列的数据集。
combine _ data()的返回值:-
演员表和流派栏合并
在继续下一步之前,请浏览下面提到的主题及其链接。在我们进入下一部分,即 transform_data()之前,对这些主题有一个基本的了解将会很有帮助。
iii)转换数据()
transform _ data()取combine _ data()和plotcolumn from***get _ data()***并应用 计数矢量器 和
- ***第 2 行:*为 CountVectorizer 创建一个对象,并使用 stop_words 参数开始删除英文停用词。
- 第 3 行:将计数矢量器对象计数拟合到 combine_data() 返回的值上,即 cast 和流派的组合列值。在这之后,我们得到一个稀疏矩阵*,如我们在关于单词袋的讨论中所示,带有每个单词的计数值。*
计数矢量器稀疏矩阵输出
- ***第 5 行:*为 TfidfVectorizer 创建一个对象,并使用 stop_words 参数开始移除英文停用词。
- 第 6 行:将tfidf 矢量器对象 tfdif 拟合到我们从 get_data()得到的 plot 列上。在此之后,我们得到一个稀疏矩阵,如我们讨论的 Tf-Idf 矢量器所示,其中包含每个单词的值。
tfidf 矢量器稀疏矩阵输出
- 第 8 行:我们将通过计数矢量器和 TfidfVectorizer 得到的两个稀疏矩阵组合成一个单一的稀疏矩阵*。*
- 第 10 行:我们现在对我们组合的稀疏矩阵应用余弦相似度。
- ***第 12 行:*返回作为 transform_data() 的结果生成的余弦相似度矩阵
transform _ data()的返回值:-
余弦相似性
我们将Tf Idf 矢量器 用于 图 列,因为Tf-Idf**将较低的值分配给特定文档中具有较高频率的词,而将较高的值分配给具有较低频率的词。
例如
在星际军阀灭霸瓦解了半个宇宙后,复仇者们必须重新团结起来,重振他们被击败的盟友,恢复平衡。
在这部来自 复仇者联盟 4:终局之战 的电影情节中,我们将赋予诸如 灭霸 和 复仇者联盟 等词语更高的价值,因为它们出现的次数更少,但在确定电影的主旋律方面具有更高的意义。
四)推荐 _ 电影()
***推荐 _ 电影()*需要四个参数。
-
***标题:*电影名称
-
***数据:*返回值为 get_data()
-
***combine:****combine _ data()*的返回值
-
***transform:****transform _ data()*的返回值
-
第 3 行:创建一个熊猫系列,包含我们数据集中所有电影的索引。
熊猫系列
- 第 4 行:在标题参数中获取传递给我们的*推荐 _ 电影()*函数的输入电影的索引。
例如,我们通过电影 Logan 作为我们的输入电影。这一行给了我们熊猫系列电影Logan*的索引。*
我们输入电影的索引
- 第 6 行:这里我们存储每部电影相对于输入电影的余弦值。
比如我们输入的电影是 黑暗骑士。 这行代码的作用是计算所有电影相对于我们输入的电影的余弦值。
关于黑暗骑士的余弦值
- ***第 7 行:*得到余弦值后,我们以逆序排序。正如我们从余弦相似性主题的简介中读到的,文档离源越近,余弦值越高。
相对于黑暗骑士排序的余弦值
在上面的图像中,我们看到了输入电影的所有余弦值。输入的电影将是最相似的,所以它的值是0.99。之后我们看到对于 索引 3 我们的余弦值是 0.530。 现在如果你把它和我们之前的熊猫系列输出 【黑暗骑士崛起】 处于 指数 3 不出所料这是最类似于 黑暗骑士 的电影。
- ***第 8 行:*我们需要输入电影的前 20 部电影。因此,我们存储了与输入电影最相似的 20 部电影
关于黑暗骑士的前 20 个余弦值
- ***第 10 行:*我们将按照余弦值排序的前 20 部电影存储在一个列表中。
20 大电影指数
- ***第 12–14 行:*在这几行中,我们将电影索引存储在它们各自的列中。
movie_id 及其索引
电影标题及其索引
电影类型及其索引
- 第 16 行:我们创建一个熊猫数据帧,以电影 Id、名称、类型为列。
- 第 18 — 20 行:我们在刚刚创建的熊猫数据帧中存储了所有 20 部与我们的输入电影相似的电影,即黑暗骑士。
- ***第 22 行:*返回推荐前 20 部电影的熊猫数据帧。
***推荐 _ 电影()*返回值为 黑暗骑士 :-
五)结果()
****result()**取一个 电影的片名 作为输入,返回前 20 个推荐。
- ***第 2 行:*将 movie_name 转换为小写,因为在我们的数据集中所有的电影都是小写。我们这样做是作为预防措施。如果用户同时用小写字母和大写字母输入电影名称,这不会有问题,因为我们的函数仍然会返回结果。
例如: 如果输入的是Logan或Logan或Logan**我们还是得到我们的推荐。**
- ***第 4–6 行:*我们存储 get_data()、combine_data()和 transform_data()返回的值。
- ***第 8–9 行:*检查输入电影是否存在于我们的数据集中。如果在我们的数据集中没有找到,那么我们返回没有找到电影。
- ***第 11–12 行:*如果我们的电影出现在数据集中,那么我们调用我们的 recommend_movies()函数并传递 get_data()、combine_data()和 transform_data() 的返回值以及电影名称作为函数的参数。
- ***第 13 行:*我们以 Python 字典格式返回电影结果。
结果的返回值():-
既然我们的推荐系统已经准备好了,让我们进入下一部分,即使用 Flask 构建一个 REST API。
3.使用 Flask 构建一个 REST API
Marvin Meyer 在 Unsplash 上拍摄的照片
烧瓶:-
为了理解文章的这一部分,我建议你对 Flask 有一个基本的概念。对于我们的任务,我们只需要了解一些初级功能。
要在您的系统上安装 Flask,请打开终端/命令提示符,键入 pip install Flask。 就这样,Flask 现在已经安装在你的系统上了。
要了解一个简单的 Flask 应用程序的基本知识,请点击下面的链接。
如果你喜欢视频讲座,请仔细阅读视频中关于烧瓶的简要说明。
现在,我们对 Flask 有了一个基本的概念,让我们进入下一个主题,即REST API。
REST API:-
我强烈建议你们对API和 API 的设计原则之一(即 REST API)有一个基本的了解。
浏览下面提到的链接。它将为您提供关于API和REST API 的基本概述。
如果你喜欢视频讲座,请仔细阅读 Telusko 提供的关于API和REST API 的简要说明。
要了解如何创建一个 REST API ,如果我们先编写一些基本的应用程序,并了解更多关于 GET 方法及其工作原理,这将非常有用。
下面提到的链接使用 Flask 和 GET 方法构建了一个 REST API 来向客户端显示数据。把这些代码写出来,理解基础知识,然后继续我们的 app.py 文件中的代码。
文件 2 — app.py
在这个文件中,我们将编写 Flask 应用程序,并使用我们之前构建的推荐系统。
导入所需的包
第 1 行:我们导入烧瓶类,然后请求*库发送 HTTPS 请求,最后我们导入 jsonify 以 JSON 格式返回我们的结果。*
***第 2 行:*我们导入 flask_cors 来为我们的 API 启用跨来源请求。
什么是跨来源请求?
跨源资源共享 ( CORS )是一种机制,允许从提供第一资源的域之外的另一个域请求网页上的受限资源。
要了解更多关于 CORS 的政策,请点击下面的链接。它解释了你需要知道的关于 CORS 政策的一切。
第 3 行:我们导入我们的推荐. py 文件作为一个模块,在我们的 app.py 文件中使用它。
烧瓶代码:-
- 第 1 行:我们创建了这个类的一个实例。第一个参数是应用程序的模块或包的名称。
- ***第 2 行:*我们使用 CORS() 方法在我们的 API 上启用 CORS 策略。
- ***第 4 行:*然后我们使用 route() decorator 告诉 Flask 哪个 URL 应该触发我们的函数。在这种情况下,我们使用带有基本 URL 的 /movie 端点。
- ***第 5 行:*现在,我们定义一个名为 recommend_movies() 的函数,它将用于返回前 20 个推荐。
- 第 6 行:在这一行中,我们从 recommendation.py 文件中调用 results() 函数,并将推荐存储在一个名为 res. 的变量中。使用request . args()将电影名称作为查询字符串传递给我们的 results() 函数,参数名称为 title。
- ***第 7 行:*最后我们将从 recommendation.py 接收到的结果以字典格式返回到 app.py 并转换成 JSON 格式返回结果。
- ***第 9 行:*这一行表示如果我们直接从终端/命令提示符调用我们的 app.py 文件,那么它将执行后面的内容。
- 第 10 行:我们在终端/命令提示符下直接调用我们的 app.py 文件后运行 app。当在localhost**上运行时,我们将端口号设置为 5000,并设置 debug=True 来追溯运行我们的应用程序时发生的任何错误。**
4。在 localhost — 127.0.0.1 上测试
现在,我们已经完成了编码部分,让我们在 localhost 上测试我们的应用程序,看看它是否工作。
如果你想用 Postman 测试我们的 API,那么从下面的链接下载。
如果你喜欢用浏览器而不是邮递员,你也可以用浏览器来测试。我们将对他们两个进行测试,你将会看到结果。
测试我们的 API:-
步骤— 1: 如果在 Windows 中,请打开命令提示符;如果使用 Linux,请打开终端。
步骤— 2: 使用命令行导航到存储数据集、 recommendation.py 文件和 app.py 文件的文件夹。
我们将文件存储在名为建议 2.0 的文件夹中。下面是我们的目录结构。****
我们的目录结构
开发应用程序时,所有文件和数据集都应该放在一个文件夹中,以便于使用。
步骤 3: 当我们在建议 2.0 文件夹中时,在命令行中键入以下命令。
****set FLASK_APP=app.py****
运行应用程序:-
****flask run****
执行完这两个命令后,我们将看到我们的应用程序运行在 localhost 上。
烧瓶应用程序启动并运行
步骤 4: 使用 Postman 或任何浏览器在 localhost 上测试我们的 API。
当我们将一部电影传递给我们的 API 时,让我们看看我们的结果。
邮递员:-
对洛根的建议
浏览器:-
对逝者的推荐****
我们已经在 localhost 上完成了对我们的 API 的测试,它工作得非常好。
让我们进入最后一部分,即将我们的 API 部署到 Heroku。
5。部署到 Heroku — API 上线
莱昂·塞伯特在 Unsplash 上拍摄的照片
GitHub 存储库:-
在我们进入部署部分之前,我们需要对 GitHub 有一个基本的了解。您应该能够创建一个新的存储库,添加文件,删除文件和在存储库中创建文件夹如果需要的话。
- **步骤 1: 创建一个存储库,可以使用您喜欢的任何名称。我已经把我的库命名为 **BioScope,因为我和我的朋友们认为这是一个很酷的名字。
- 第二步: 创建过程文件
Heroku 应用程序包括一个 Procfile ,它指定了应用程序在启动时执行的命令。您可以使用 Procfile 来声明各种进程类型,包括:您的应用程序的 web 服务器。
**要创建一个,在工作目录中打开一个名为 **Procfile(无扩展名)的新文件,并粘贴以下内容。
****web: gunicorn app:app --max-requests 2****
我们添加了 max requests 来确保我们的服务器在每第二次请求我们的 API 后重启。这样做是为了确保我们在使用 API 时不会超过 Heroku 分配的 512 MB 的 RAM 限制。
- 第三步: 创建需求. txt
requirements.txt 文件将包含 flask 应用程序的所有依赖项。如果您不是在一个新的环境中工作,这个文件将包含您当前环境中的所有需求。
对于这个项目,你的 requirements.txt 至少应该包含:
****Flask==1.1.2
Flask-Cors==3.0.8
Flask-RESTful==0.3.7
gunicorn==20.0.4
joblib==0.13.2
jsonschema==2.6.0
pandas==0.25.1
pickleshare==0.7.5
requests==2.23.0
requests-file==1.4.3
scikit-learn==0.22.2
scipy==1.4.1
wcwidth==0.1.7
webencodings==0.5.1
Werkzeug==1.0.1****
将上述内容复制粘贴到您的 requirements.txt 文件中,并提交到存储库。
第四步:提交推荐. py,应用. py.
****第五步:在你的存储库中创建一个名为 dataset/ 的文件夹,提交 movie_data.csv.zip 文件。
我们的存储库结构应该是这样的:-
Jupyter 笔记本包含制作我们的 movie_data.csv.zip 文件所需的预处理部分。所以,这个文件夹是可选的。除此之外,所有其他文件和文件夹都是必要的。
- re commendation . py
- app . py
- requirements . txt
- Procfile
Note:以上所有文件都应该在工作目录级别,而不是在另一个文件夹中。
部署到 Heroku:-
我们可以使用 Heroku CLI 或 GitHub 部署我们的应用程序。在本文中,我们将讨论如何使用 GitHub 部署我们的应用程序。
步骤— 1: 在www.heroku.com创建自由账户。
****第二步:选择一个名称,点击“创建应用”,创建一个新的应用。这个名称并不重要,但它必须是唯一的。
****第三步:点击下面的 GitHub 图标,连接您的 GitHub 账户。
步骤 4: 搜索正确的存储库并单击连接。
第五步:滚动到页面底部,点击“部署分支”。
如果一切正常,您应该会看到这条消息。
如果出现问题,检查你的 requirements.txt ,删除给你带来问题的依赖项,然后再试一次。
部署后测试我们的 API
- 注意我们发送 GET 请求的链接。
最后,我们的电影推荐 API 现已上线。
任何拥有 Heroku 链接的人现在都可以访问电影推荐 API,并向他们的用户显示电影建议。
就这样,我们到了这篇文章的结尾。我希望你们已经学到了一些新的东西,我当然希望你们都使用这个 API 或者构建类似的东西。
我们刚刚构建了一个基本的 基于内容的推荐系统 。可以做的比这多得多,比如构建一个 协同推荐系统 或者甚至构建一个 混合推荐系统。我们会详细讨论这个问题,但那是另一篇博文的内容了。
API 链接:-
完整代码:-
你可以打:- 找到我
快乐阅读!!!
构建一个 MSSQL Docker 容器
使用容器数据库提升开发水平
轻巧、快速、方便
您可以不再等待数据库管理员允许您访问开发服务器,或者在您的计算机的锁定工作环境中筛选关于设置 MS SQL 数据库的无尽文档,并获得您真正需要的基本信息。可以接受请求并返回数据的 sql server。就是这样。你的 API 需要测试,你需要一个数据库。
在开发的开始阶段,您不希望花费数小时来配置 sql server 或处理原型项目的托管数据库。这是一个本地码头集装箱可以改变你的生活。
在这篇文章中
我们将基于mcr.microsoft.com/mssql/server图像构建一个 Docker 容器,以创建一个定制的 Docker 文件,该文件启动一个 SQL Server、创建一个数据库、创建一个表并用数据填充该表。本文是在 为角斗士之旅 app 构建. NET 核心 API 的背景下撰写的。
对于完成的 MSSQL Dockerfile 文件和脚本,请 访问我的 GitHub 。
非常感谢 : twright-msft 给他的明星码头工人形象。我的很多图片都是直接从他的回购中复制的。
测试图像
在我们疯狂地构建自定义 docker 文件之前,让我们下载并运行映像以确保它能够工作。确保您的系统上安装了 Docker 。
从命令行运行:
docker pull mcr.microsoft.com/mssql/server:latest
通过运行下面的代码运行图像并构建容器。指定环境变量,如密码和 SQL Server Express 的版本。
docker run \
-e 'ACCEPT_EULA=Y' \
-e 'SA_PASSWORD=Password1!' \
-e 'MSSQL_PID=Express' \
--name sqlserver \
-p 1433:1433 -d mcr.microsoft.com/mssql/server:latest
如果您运行docker container ls
,您应该看到 sqlserver 容器在端口 1433 上运行。
进入容器
运行以下命令进入您的容器,并显示一个 bash 终端:
docker exec -it sqlserver "bash"
从容器命令行启动 sqlcmd。请注意,我们使用的密码与用docker run
创建容器时使用的密码相同。
/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "Password1!"
您应该会看到一个1>
,通知您现在在 sqlcmd 命令行上,并且可以与您的服务器进行交互!让我们编写一些 SQL 来创建一个数据库和一个表。
CREATE DATABASE heroes
GO
USE heroes
CREATE TABLE HeroValue (id INT, name VARCHAR(50))
INSERT into HeroValue VALUES (1, "Wonder Woman")
GO
现在,查询数据库以确保数据被正确写入:
SELECT * FROM HeroValue;
GO
成功!
写文档
我们可以从 bash 终端创建数据库并插入条目,这很好,但感觉还是有点“手动”让我们通过编写 3 个脚本来实现自动化,Dockerfile 将在创建容器时运行这些脚本。这些脚本是:
- entrypoint.sh —在启动时运行的脚本,它只是运行 import-data.sh 并启动 SQL Server。
- import-data.sh —运行一个调用 *setup.sql、*的 sqlcmd 和一个导入. csv 文件的 bcp 命令。
- setup.sql —创建数据库和表的 sql 脚本。
下面是引用这三个脚本的最终 Dockerfile 文件。不要担心,我们将更详细地介绍这一切是如何工作的!
Dockerfile 文件
FROM microsoft/mssql-server-linux:latest# Create work directory
RUN mkdir -p /usr/work
WORKDIR /usr/work# Copy all scripts into working directory
COPY . /usr/work/# Grant permissions for the import-data script to be executable
RUN chmod +x /usr/work/import-data.shEXPOSE 1433CMD /bin/bash ./entrypoint.sh
根据 MSSQL docker 文档,您必须在启动 SQL Server 的最终命令之前运行数据库定制。在我们的 entrypoint.sh 文件中,我们在启动服务器之前调用 import-data.sh。
entrypoint.sh
/usr/work/import-data.sh & /opt/mssql/bin/sqlservr
在创建容器时,entrypoint 将运行 import-data.sh 并启动服务器。Import-data.sh 为我们提供了两个 SQL Server 命令,以及一个非常必要的命令,用于在数据库启动时休眠 90 秒。在你等待或冥想的时候,喝杯茶。
导入-数据. sh
# wait for the SQL Server to come up
sleep 90s#run the setup script to create the DB and the schema in the DB
/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "Password1!" -i setup.sql# import the data from the csv file
/opt/mssql-tools/bin/bcp heroes.dbo.HeroValue in "/usr/work/heroes.csv" -c -t',' -S localhost -U SA -P "Password1!" -d heroes
第二个命令 sqlcmd ,运行 setup.sql,搭建我们的数据库。创建数据库后,它使用 bcp 从. csv 文件导入数据。
setup.sql
CREATE DATABASE heroes;
GO
USE heroes;
GO
CREATE TABLE HeroValue (id INT, name VARCHAR(50));
GO
使用您的 docker 文件构建图像
Randy Fath 在 Unsplash 上拍摄的照片
通过运行以下命令,标记您的映像 mssql:dev 并进行构建:
docker build -t mssql:dev .
完成后,使用docker run
运行映像。这几乎与我们的第一张docker run
完全相同,除了我们已经将图像更改为我们新创建并标记的mssql:dev
图像。
docker run \
-e 'ACCEPT_EULA=Y' \
-e 'SA_PASSWORD=Password1!' \
-e 'MSSQL_PID=Express' \
--name sqlserver \
-p 1433:1433 \
-d mssql:dev
当创建了映像并且我们收到确认映像 ID 时,访问容器以确认它正在工作。
docker exec -it sqlserver "bash"
从容器命令行,访问英雄数据库。
/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "Password1!" -d heroes
查询数据以确保其插入正确。出于某种原因,当我运行它时,它返回一个空白值块,以及最后一个值。但是我已经通过从 C#运行查询确认了所有的值都在那里。如果有人知道为什么没有显示所有的值,我很想知道。
SELECT * FROM HeroValue;
GO
就是这样!现在,您已经拥有了一个 SQL Server 数据库,可以随时轻松配置和启动!
您可以通过运行以下命令来终止容器:
docker kill sqlserver
用您的docker run
命令重启您的容器,您可以很容易地将它存储在一个脚本中,比如start-docker-SQL . sh或类似的文件。
docker run \
-e 'ACCEPT_EULA=Y' \
-e 'SA_PASSWORD=Password1!' \
-e 'MSSQL_PID=Express' \
--name sqlserver \
-p 1433:1433 \
-d mssql:dev
推送至 Docker Hub 存储库
现在您已经有了一个工作映像,您肯定希望将它保存在一个托管的存储库中,这样您就可以根据需要将映像拉到不同的机器上。
在 https://hub.docker.com/的创建一个 Docker Hub 账户。
然后创建一个新的存储库,并给它一个名称和描述。
从命令行运行docker login
。系统会提示您输入创建帐户时设置的凭据。
现在,标记您的图像:
docker tag local-image:tagname username/new-repo:tagname
接下来,将您的映像推送到您的新存储库:
docker push username/new-repo:tagname
完成后,您将能够通过运行以下命令提取您的映像:
docker pull username/new-repo:tagname
希望这能让你的生活轻松一点!您可以阅读本系列的下一篇文章,通过. NET API 使用这个数据库。
额外收获:C#连接字符串
"ConnectionStrings": {
"DockerDB": "Server=localhost,1433;Database=heroes;User ID=SA;Password=Password1!"
}
感谢您的阅读!
用 SciKit-Learn 构建 NLP 管道:火腿还是垃圾邮件?
使用 Scikit-Learn 的自然语言处理库制作简单的垃圾邮件检测器模型的初学者指南。
在我们的 GitHub 库中查看代码!
安娜·阿尔尚蒂在 Unsplash 上的照片
问题是
像许多 NLP 问题一样,决定哪些电子邮件属于垃圾邮件,哪些是你可能想要阅读的是一个分类问题。
我们可以问,电子邮件属于哪一类,是垃圾邮件还是非垃圾邮件?因此,我们有两个类来分类我们所有的电子邮件。从哪里开始?
为了解决这个问题,我们将使用监督机器学习。这意味着我们有一个已经被标记为垃圾邮件的电子邮件集合。被认为是非垃圾邮件的电子邮件。大致上,我们的模型将通过查看电子邮件的内容并将该内容与任一标签相关联来学习对这些电子邮件进行分类。
经过训练后,我们的模型可以接收一封未标记的电子邮件,并对其进行分类。
数据
在我们开始之前,让我们看一下我们的数据。
用于监督学习的标记电子邮件数据
我们从包含垃圾邮件和非垃圾邮件的语料库或文本集合开始。数据包括标签列(v1)和文本列(v2)。这是我们的模型将读取的数据,用于将消息与类相关联。
听起来很简单,但是计算机不能像我们一样阅读和学习!然而,机器是优秀的计算器。所以让我们把我们的文本转换成一种我们的机器可以进行数学运算的结构,向量。
在完成一些基本的文本清理以处理大小写和非字母字符之后,我们可以开始了。我们将讨论的矢量化的第一步称为标记化。
标记化
给定一个字符序列和一个定义的长度,标记化就是将文档分割成指定长度的片段,称为标记。
令牌不一定是术语或单词。一个标记是一些特定文档中的一个字符序列的实例,这些字符被分组为一个有用的语义单元用于处理。换句话说,标记是我们希望模型学习的有意义的语言。
示例:句子的标记化,将标记长度限制为五个字符。
**IN:** “moon river wider than a mile i am crossing you in style”**OUT:** [moon, river, wider, than, a, mile, i, am, cross, you, in, style]
通过标记化,我们已经从包含超过五个字符长的单词和空格的字符串变成包含五个字符或更少字符且没有空格的单词或标记的数组。
好了,我们现在有了一个字符串数组,我们如何把这些数组转换成向量呢?
矢量化:将单词映射为数值
Scikit-learn 的 CountVectorizer 提供了一种简单的方法来标记一组文本文档并构建已知单词的词汇表。
我们看到的任何文档都可以被编码为一个固定长度的向量,其长度为已知单词的词汇表的长度。向量中每个位置的值可以用文档中每个单词的计数或频率来填充。
用 CountVectorizer 将句子转换成向量
对我们来说,这给了每封邮件一个整数向量。向量将是语料库中不同单词数量的长度。给定一个冗长的语料库,这些向量在计算过程中会变得相当长且麻烦。
这就引出了我们的下一个想法,如何在不损失太多意义的情况下缩小语料库?
一旦我们用 CountVectorizer 将语料库转换成一个整数数组,我们就进入停用词。
停用词:模型中包含什么词汇?
在帮助确定文档是否是垃圾邮件方面似乎没有什么价值的常见单词被完全从词汇表中排除。这些词被称为停止词。决定停止列表的一般策略是根据收集频率(每个术语在文档集合中出现的总次数)对术语进行排序,然后将最常见的术语作为*停止列表,这些术语有时根据它们相对于被分析文档的领域的含义进行手动过滤。*然后在索引过程中丢弃该非索引列表的成员。
可以手动或自动将停用词添加到停用列表中。在 Scikit-learn 的计数矢量器中,有一个针对特定语料库的停用词的选项。您可以传递一个非索引词数组,或者使用最小和最大文档频率参数自动完成这个过程。
这个简化的矩阵将训练得更快,甚至可以提高你的模型的准确性。
缩小语料库对于减少模型的内存使用非常有用。这样会节省计算成本!
计数矢量器
CountVectorizer 标记、矢量化并创建我们的非索引列表!所以我们要做的就是在模型管道中调用它。这个函数返回一个稀疏矩阵,我们将使用它来训练我们的 ML 模型。
构建模型管道
机器学习管道用于帮助机器学习工作流程自动化。这些管道的运行方式是,在一个模型中对一系列数据进行转换和关联,只需一个代码步骤就可以对这些数据进行测试和评估。
例如,看看我们的朴素贝叶斯管道。
nb = make_pipeline(
CountVectorizer(binary=**True**),
MultinomialNB()
)
我们只需调用make_pipline
,添加 CountVectorizer 和我们的模型,然后调用nb.fit
使用管道训练一个模型!
机器学习算法通常涉及一系列任务,包括预处理、特征提取、模型拟合和验证阶段。管道帮助我们将这些任务整合在一起。
模型度量:精确度和召回率
我们如何判断哪种模型最有效地解决了这个问题?首先,让我们看看我们的一些模型分数,精度和召回率。
Precision 试图回答以下问题:
实际上有多少比例的肯定认同是正确的?
Precision 测量被标记为垃圾邮件的邮件被正确分类的百分比。
回想一下试图回答以下问题:
正确识别实际阳性的比例是多少?
召回衡量被正确分类的实际垃圾邮件的百分比。
要全面评估一个模型的有效性,你必须同时检查和精度和召回率。不幸的是,精确度和召回率经常处于紧张状态。也就是说,提高精度通常会降低召回率,反之亦然。
所以你需要知道,对于你的问题,精确和召回哪个更重要?
让我们来看看我们的两个模型。这里我们有朴素贝叶斯模型的分数。
朴素贝叶斯
这是逻辑回归模型的分数。
逻辑回归
请注意,除了垃圾邮件精确度这一项之外,NB 模型在所有方面都优于 LR 模型。我们不能说一个模型总是比另一个好,但我们可以更多地选择正确的那个。
保存您的模型
一旦你决定了一个模型,把它保存为一个. joblib,就像下面我们对朴素贝叶斯模型所做的那样。
dump(nb, "clf.joblib")
现在您已经有了一个保存为 joblib 的垃圾邮件检测模型!
结论
在这里,我们已经涵盖了基本的 NPL 概念,记号化,向量化和停用词。然后,我们在模型管道中使用这些概念和 CountVectorizer 来比较模型之间的度量。在决定了朴素贝叶斯分类器之后,我们将它保存为一个 joblib,供以后在我们的应用程序中使用。
查看我们的 GitHub Repositor y 中的代码,获取一个模板开始使用!
要为您的模型提供应用程序并进行部署,请查看下面的进一步阅读资料!
本文讨论构建一个应用程序来服务我们的模型,以及如何将它部署到云中。我们涵盖了应用程序的构建和 docker 化,在 Google Container Registry 上存储 docker 映像,以及将应用程序部署到 Cloud run。
[## 使用 Docker、GCP 云运行和 Flask 部署 Scikit-Learn NLP 模型
构建一个服务于自然语言处理模型的应用程序,将其容器化并部署的简要指南。
towardsdatascience.com](/deploy-a-scikit-learn-nlp-model-with-docker-gcp-cloud-run-and-flask-ba958733997a)
用 Python 构建个性化智能闹钟
让我们建立一个基于人工智能技术的个性化智能闹钟,它将为你设置最佳的闹钟铃声,帮助你早点醒来。
布兰迪·里德在 Unsplash 上拍摄的照片
你好,读者,你经常会看到或使用 Python 语言创建闹钟,它会帮助你醒来或提醒你一个重要的会议。
几乎所有这些都很简单,没有任何智能,它们所做的只是播放你设置的闹钟或选择一个随机的 YouTube 视频或歌曲来播放。
所以,让我们更上一层楼,建造一些智能的东西,一些更个性化的东西,理解你,帮助你以更好更快的方式醒来。
我们将在本文中构建的警报将从过去的事件中学习并理解它们,以便在下一个警报中有更好的性能。一次比一次好用。它会记录用户关闭闹铃所用的时间(用户醒来所用的时间),并推荐有助于你更快醒来的闹铃。
所以,事不宜迟,让我们开始建立我们的警报。下面我们将一步一步来构建。
导入所需的包
第一步是将所需的包导入到我们的 Python 代码中,以便在构建警报时使用它们。
如果没有安装,您需要使用 pip 安装方法进行安装。完成安装步骤后,继续将它们导入代码。
import datetime
import os
import time
import random
import csv
from pygame import mixer
import pandas as pd
import numpy as np
设置音乐文件夹
下一步是建立一个闹铃音乐文件夹,用户可以在其中存储自己喜欢的闹铃音乐。
您可以为警报曲调选择任何路径,我更喜欢在与 Python 脚本相同的文件夹中创建文件夹。我们只需要创建该文件夹一次,因此我们需要检查该文件夹是否存在。如果该文件夹不存在,我们将创建一个。
# Getting the current path of the script
path = os.getcwd()
# Setting up the alarm path
alarm_path = path + '\Alarm_Tunes'
# If no directory present, create one.
if not os.path.isdir(alarm_path):
os.makedirs(alarm_path)
在我们的文件夹被创建后,当且仅当文件夹当前为空时,我们会要求用户向文件夹添加一些闹钟铃声。
# Ask user to add some alarm tunes to the folder.
while len(os.listdir(alarm_path))==0:
print("No Alarm Tunes Present. Please add some tunes to the folder before proceeding.")
confirm = input("Have you added songs? Press Y or N:\t")
if(confirm=="Y"):
print("Good! Let's continue!")
continue
else:
continue
因此,如上所述,我们要求用户至少添加一个闹钟铃声。如果没有报警音,则发出警告并再次询问用户。
创建 CSV 文件并定义助手函数
在进入 CSV 文件创建部分之前,让我们定义一个助手函数。
这个帮助函数帮助我们计算两个 Python 列表之间的差异。这将在我们的程序中用到。
def List_diff(list1, list2):
if len(list1)>=len(list2):
return (list(set(list1) - set(list2)))
else:
return (list(set(list2) - set(list1)))
现在,既然我们已经编写了帮助函数来计算两个列表之间的差异,那么让我们继续创建一个 CSV 文件(如果它还不存在的话)。
# If no csv file, create the lists with parameters as zero
if not os.path.isfile("tune_parameters.csv"):
tune_list = os.listdir(alarm_path)
tune_time = [60]*len(tune_list)
tune_counter = [1]*len(tune_list)
tune_avg = [60]*len(tune_list)
tune_prob_rev = [1/len(tune_list)]*len(tune_list)
tune_prob = [1/len(tune_list)]*len(tune_list)
所以,上面的代码检查我们是否有一个 CSV 文件;如果没有,我们将创建列表,正如您在上面看到的。我们将在程序结束时将这些保存在一个 CSV 文件中。
现在,让我们解释代码中每个列表的意义。让我们一个一个来看。
- tune_list: 它存储报警声音的名称,从代码中可以明显看出,因为它在 alarm_path 中存储文件列表。
- tune_time: 它存储了用户关闭特定闹铃所花费的总时间,即用户醒来所花费的时间。
- tune_counter: 记录到目前为止每首闹钟铃声播放的次数。
- tune_avg: 它会找出用户在每一个闹钟铃声中唤醒和关闭闹钟所花费的平均时间。
- tune_prob_rev: 它根据用户每次闹钟调谐所需的平均时间来计算一种反向概率。
- tune_prob: 是闹钟铃声每次播放的概率。它根据之前的结果进行自我更新,并使用 tune_prob_rev 进行计算。
这里要注意的一点是,我已经为所有这些列表设置了一些默认值,而不是提供零,因为这将对模型产生负面影响,因为从未玩过的人将永远不会有机会,因为概率为零。
所以,我更倾向于假设这些游戏每个都玩过一次,平均时间是 60 秒。因此它使我们的工作更容易。
现在,如果 CSV 文件已经存在,我们需要从 CSV 文件加载数据。
此外,我们需要注意,如果有任何改变,以报警曲调文件夹。用户可能已经添加了新的歌曲或者删除了一些当前的歌曲。因此,我们需要通过添加新的歌曲到我们的列表中或者删除那些已经从文件夹中删除的歌曲来进行更新。
因此,我们使用前面定义的 helper 函数来找出从文件夹获得的列表和从 CSV 文件获得的列表之间的任何差异。因此,我们可以对代码执行所需的操作,并使用各自的公式分别更新 tune_prob_rev 和 tune_prob,。
# If csv file is present, read from csv file
else:
tune_df = pd.read_csv("tune_parameters.csv")
tune_list_os = os.listdir(alarm_path)
tune_list = list(tune_df['Tunes'])
tune_diff = List_diff(tune_list_os, tune_list)
tune_time = list(tune_df['Delay Times'])
tune_counter = list(tune_df['Count'])
tune_avg = list(tune_df['Average'])
tune_prob_rev = list(tune_df['Reverse Probability'])
tune_prob = list(tune_df['Probability'])
if len(tune_list_os)>=len(tune_list):
for i in range(0,len(tune_diff)):
tune_list.append(tune_diff[i])
tune_time.append(60)
tune_counter.append(1)
tune_avg.append(60)
tune_prob_rev.append(0.1)
tune_prob.append(0.1)
else:
for i in range(0,len(tune_diff)):
tune_diff_index = tune_list.index(tune_diff[i])
tune_list.pop(tune_diff_index)
tune_time.pop(tune_diff_index)
tune_counter.pop(tune_diff_index)
tune_avg.pop(tune_diff_index)
tune_prob_rev.pop(tune_diff_index)
tune_prob.pop(tune_diff_index)
avg_sum = sum(tune_avg)
for i in range(0,len(tune_prob_rev)):
tune_prob_rev[i] = 1 - tune_avg[i]/avg_sum
avg_prob = sum(tune_prob_rev)
for i in range(0,len(tune_prob)):
tune_prob[i] = tune_prob_rev[i]/avg_prob
设置闹钟并核实时间
现在,我们需要定义另一个助手函数来检查用户输入的时间是否正确。因此,我们定义了函数 verify_alarm 来实现这一点。
# Verify whether time entered is correct or not.
def verify_alarm(hour,minute,seconds):
if((hour>=0 and hour<=23) and (minute>=0 and minute<=59) and (seconds>=0 and seconds<=59)):
return True
else:
return False
现在,我们已经准备好了助手函数。所以,我们需要向用户询问闹铃时间。我们将使用一个循环来请求警报,一旦我们验证时间有效,我们将中断。如果无效,我们将再次询问用户,直到他输入一个有效的时间。
# Asking user to set alarm time and verifying whether true or not.
while(True):
hour = int(input("Enter the hour in 24 Hour Format (0-23):\t"))
minute = int(input("Enter the minutes (0-59):\t"))
seconds = int(input("Enter the seconds (0-59):\t"))
if verify_alarm(hour,minute,seconds):
break
else:
print("Error: Wrong Time Entered! Please enter again!")
现在,在接受用户输入后,我们将找出当前时间,并将这两个时间转换为秒,并找出两个时间之间的差异。如果差值为负,则意味着闹钟是第二天的。
然后,我们将让 python 代码休眠几秒钟,以便警报只在需要的时间响起。
# Converting the alarm time to seconds
alarm_sec = hour*3600 + minute*60 + seconds
# Getting current time and converting it to seconds
curr_time = datetime.datetime.now()
curr_sec = curr_time.hour*3600 + curr_time.minute*60 + curr_time.second
# Calculating the number of seconds left for alarm
time_diff = alarm_sec - curr_sec
#If time difference is negative, it means the alarm is for next day.
if time_diff < 0:
time_diff += 86400
# Displaying the time left for alarm
print("Time left for alarm is %s" % datetime.timedelta(seconds=time_diff))
# Sleep until the time at which alarm rings
time.sleep(time_diff)
响起闹铃
现在,我们将敲响我们的闹钟,我们需要根据概率列表随机选择闹钟曲调。为了播放闹铃,我们将使用 pygame.mixer.music 库。我们将无限循环闹钟铃声,直到用户停止它。
print("Alarm time! Wake up! Wake up!")
# Choose a tune based on probability
tune_choice_np = np.random.choice(tune_list, 1, tune_prob)
tune_choice = tune_choice_np[0]
# Getting the index of chosen tune in list
tune_index = tune_list.index(tune_choice)
# Play the alarm tune
mixer.init()
mixer.music.load(alarm_path+"/"+tune_choice)
# Setting loops=-1 to ensure that alarm only stops when user stops it!
mixer.music.play(loops=-1)
# Asking user to stop the alarm
input("Press ENTER to stop alarm")
mixer.music.stop()
列表的计算和更新
现在,我们将根据用户停止警报所需的时间来更新列表的值。
我们将找到报警和当前停止报警时间之间的时间差。我们将把它转换成秒,然后相应地更新它。
# Finding the time of stopping the alarm
time_stop = datetime.datetime.now()
stop_sec = time_stop.hour*3600 + time_stop.minute*60 + time_stop.second
# Calculating the time delay
time_delay = stop_sec - alarm_sec
# Updating the values
tune_time[tune_index] += time_delay
tune_counter[tune_index] += 1
tune_avg[tune_index] = tune_time[tune_index] / tune_counter[tune_index]
new_avg_sum = sum(tune_avg)
for i in range(0,len(tune_list)):
tune_prob_rev[i] = 1 - tune_avg[i] / new_avg_sum
new_avg_prob = sum(tune_prob_rev)
for i in range(0,len(tune_list)):
tune_prob[i] = tune_prob_rev[i] / new_avg_prob
合并列表并保存为 CSV 文件
现在,我们将所有列表合并成一个多维列表,然后我们将它转换为 pandas 数据框并保存为 CSV 文件。
#Create the merged list of all six quantities
tune_rec = [[[[[[]]]]]]
for i in range (0,len(tune_list)):
temp=[]
temp.append(tune_list[i])
temp.append(tune_time[i])
temp.append(tune_counter[i])
temp.append(tune_avg[i])
temp.append(tune_prob_rev[i])
temp.append(tune_prob[i])
tune_rec.append(temp)
tune_rec.pop(0)
#Convert merged list to a pandas dataframe
df = pd.DataFrame(tune_rec, columns=['Tunes','Delay Times','Count','Average','Reverse Probability','Probability'],dtype=float)
#Save the dataframe as a csv (if already present, will overwrite the previous one)
df.to_csv('tune_parameters.csv',index=False)
使用 Python 的智能警报的完整代码是:
我们终于完成了智能闹钟的制作。关于更新,请访问我的 Github 资源库,如果您有一些改进或新想法,请对资源库做出贡献。
我希望你觉得这篇文章很有见地。尝试建立你的版本,并在评论中分享你的想法。感谢阅读!
这篇文章之后还有更多的文章可以阅读:
[## 使用 Django Rest 框架构建博客网站——概述(第 1 部分)
让我们使用 Django Rest 框架构建一个简单的博客网站,以了解 DRF 和 REST APIs 是如何工作的,以及我们如何添加…
towardsdatascience.com](/build-a-blog-website-using-django-rest-framework-overview-part-1-1f847d53753f) [## 用 Django 构建求职门户——概述(第 1 部分)
让我们使用 Django 建立一个工作搜索门户,允许招聘人员发布工作和接受候选人,同时…
shubhamstudent5.medium.com](https://shubhamstudent5.medium.com/build-a-job-search-portal-with-django-overview-part-1-bec74d3b6f4e) [## 使用 React 构建一个简单的 Todo 应用程序
让我们用 React 构建一个简单的 Todo 应用程序,教你 CRUD 的基本原理(创建、读取、更新和…
medium.com](https://medium.com/javascript-in-plain-english/build-a-simple-todo-app-using-react-a492adc9c8a4) [## 使用 Django 构建一个社交媒体网站——设置项目(第 1 部分)
在第一部分中,我们集中在设置我们的项目和安装所需的组件,并设置密码…
towardsdatascience.com](/build-a-social-media-website-using-django-setup-the-project-part-1-6e1932c9f221)