探索生成性对抗网络
一个生成对抗网络 (GAN)是机器学习(ML)的一种强有力的方法。在高层次上,GAN 就是两个互相反馈的神经网络。一个产生越来越精确的数据,而另一个逐渐提高其分类这些数据的能力。
在这篇博客中,我们将深入探讨这种机制是如何工作的,在哪里可以使用它,以及在现实世界中使用 GAN 的潜在后果。
一言以蔽之
GAN 中的第一个神经网络称为发生器。它从随机输入 1 开始,反复生成接近真实世界数据质量的数据。它通过将其输出发送到另一个神经网络鉴别器来实现这一点,该神经网络逐渐提高其从训练数据中对输出进行分类的能力,并将输出(分类)反馈给生成器。从实现的角度来看,发生器和鉴别器具有它们自己的损失函数,其中鉴别器的损失函数包含发生器的损失函数。输出(分类)包含在损失函数中,其中生成器在训练期间通过反向传播更新其权重。
以下流程图说明了这种设计:
图像由感知实验室提供。
GAN 的对立方面在于,鉴频器的结果可以反馈到自身以进行自我改进,和/或反馈到发电机以提高发电机的输出。从这个意义上来说,生成器提高其输出的能力在某种程度上与鉴别器在训练过程中对数据进行分类的能力相竞争。此外,鉴别器对生成模型(即生成器)的自动训练意味着 GAN 有效地将原本无监督的 ML 过程转变为自动化的、有监督的 ML 过程。
为了支持这种功能,通常使用逆卷积神经网络(有时称为反卷积网络)来构建生成器,因为该神经网络具有生成数据的能力(例如,上采样特征图以创建新图像)。鉴别器通常使用常规 CNN 构建,因为它能够将数据(例如,图像)分解成特征图,并最终对数据进行分类(例如,确定图像是真实的还是伪造的)。注意,也可以使用其他类型的神经网络层来构建 GANs。
在训练过程的最后,一个 ML 实践者可能使用完全训练过的生成器、鉴别器或者两者都使用来进行推理,这取决于他们试图解决的真实世界的问题。
甘斯的“你好世界”
在 GANs 的背景下,一个好的“hello world”2 项目可以围绕 MNIST 数据集创建,数据集是一个图像库,包含从 0 到 9 的手写数字。第一次学习神经网络的用户通常使用该数据集作为输入,以解决对这些图像中表示的数字进行分类的问题。
因此,这个问题可以进一步扩展为学习 GANs 的起点。这里,目标是逐渐生成新的手写数字图像,其质量和风格接近甚至匹配 MNIST 数据集中的图像,同时还增加了对给定图像是由 GAN 生成还是实际上是真实图像进行分类的能力。此类问题的 GAN 如下所示:
由感知器成像。
生成器用随机噪声(数据)播种,并生成手写数字的图像。此时,输出可能非常糟糕,因为随机噪声可能没有很好地反映手写数字。然后,该输出与来自 MNIST 数据集(训练数据)的图像一起被馈送到鉴别器。本例中的鉴别器是二项式,将来自生成器的给定图像分类为真实图像或假(生成)图像。
然后,生成器的输出以及鉴别器的分类被递归地反馈到生成器中,以重复该过程,并有希望改进两个生成器的下一个输出。在这一点上,鉴别器还可以将其输出连同更多的训练数据一起反馈给自身,以进一步提高其对图像进行分类的能力。
训练 GAN 可能需要很长时间,大约几个小时甚至几天,这取决于数据、可用的计算资源以及 ML 从业者试图达到的准确度水平。一种理想化的情况是训练,直到鉴别器在大约 50%的时间内错误地分类图像,此时,ML 实践者通常假设生成器正在输出似是而非的数据。然而,ML 从业者可以根据他们的需要训练到不同的准确度水平。
用途和道德
GANs 的力量打开了一个充满可能性的世界。GANs 可以用于各种各样的图像处理任务,例如将夏天拍摄的照片翻译成冬天拍摄的照片,或者类似地从白天到晚上。它们还可以生成我们许多人会误认为真实的物体、场景和人物的照片。GANs 可用于类似的音频翻译,也可用于帮助识别不同类型的网络威胁。
如果你开始被各种可能性吓到,你并不孤单。由于 GANS 生成的数据与真实世界的数据没有什么区别,所以你不需要思考太多就能意识到它的潜在含义。因此,以有益于社会的方式充分利用氮化镓是很重要的。
结论
GANs 是利用神经网络力量的一种强大而聪明的方式。通过同时训练一个神经网络来创建越来越可信的数据,而另一个神经网络增加了对这些数据进行分类的能力,ML 从业者有效地将两个解决方案合二为一。当很好地使用时,GAN 的生成和分类组件可以潜在地帮助 ML 实践者构建解决方案,这些解决方案执行看起来像魔术一样的技艺。
1。生成器的输入通常被称为潜在空间。
2。hello world项目是在给定的框架或开发环境(如编程语言)中给你的第一个项目起的名字,用来证明你可以构建并运行它。
探索大悉尼郊区
使用 Python Matplotlib、Seaborn、geopandas 和 k-means 聚类
这是我的 IBM 数据科学专业证书的顶点项目的一部分。请继续阅读并跟随源代码的链接,并放心使用。
基思·朱摄于 Unsplash
当我六年前搬到澳大利亚时,肾上腺素和兴奋情绪稍微平静下来后,我面临的第一个挑战是住在哪里!我选择居住地的经历与许多第一代移民没有什么不同。降落在奥兹,在一个朋友家过夜,然后决定在附近找点东西。
现在,我的职业是会计,所以我看到数字会很兴奋。出于这个原因,我决定在数据科学领域提升自己的技能。我选择从 IBM Professional Certificate 开始,对此我将在另一篇博客中详细讨论,但是现在,让我们直接跳到完成认证所需的顶点项目。你猜对了,我选择应用一些新获得的数据技能来探索当地政府区域(LGA)或大悉尼地区的社区,并可能决定在哪个区域居住。
详细演示和源代码的链接在这里:
https://github . com/timun 01/IBM-Capstone-Project/blob/master/Week _ 4/
现在来体验一下我完成这个项目所经历的过程。
数据…当然
与任何数据科学项目一样,最大的挑战是获得一样东西…数据!我花了几个小时浏览了许多网站,最终下载了一些合适的数据,如下所示:
- 2019 年 9 月季度 LGA/街区的住房租赁数据(来源:www.facs.gov.au)
- 2019 年 6 月季度 LGA/街区住宅销售数据(来源:www.facs.gov.au)
- 悉尼地区的形状文件(绘制地理地图,并获得邻近地区的坐标)
- 基于 2016 年人口普查的人口数据(来源:http://stat.data.abs.gov.au/)
- 2019 年 9 月至今 LGA/邻近地区的犯罪数据(来源:https://www.bocsar.nsw.gov.au)
请记住,所有这些数据都是不同的格式,LGA 边界不断变化,因此较新的数据比旧的数据具有不同的 LGA。这导致了一些数据丢失。考虑到这种分析纯粹是出于教育目的,所以我们可以接受它。
在我们继续之前有一个警告。考虑到上述因素和每个人的个人情况,此分析仅用于顶点项目和学术目的,因此不得用于任何其他目的。
方法学
好的。你可能注意到了,我的分析更倾向于租房,假设新移民会租房而不是马上买房。在清理和组合了大量不同的数据后,我使用了方框图、柱状图、散点图和热图的组合来比较不同的街区。
接下来,我使用 Choropleth 和 Folium Open street 地图进行地理空间分析。最后,我使用 Foursquare API 来探索场地,并应用 k-mean 聚类算法来创建共享相似设施的社区组。这将有助于我们通过收集对该地区的一些了解来决定搬到一个新的国家。
结果
接下来是大量数据可视化的部分。
情节
第一个图是使用 Seaborn 的盒图函数绘制的。这为我们提供了每种住宅类型的最低、第一四分位数、中间值、第三四分位数和最高租金的 5 点范围。例如,我们可以看到房屋租金从 400 美元 pw 到 1400 美元 pw 以上不等。叠加在上面的散点图显示了该范围内出租的房产数量。例如,在房屋方面,我们可以看到大部分房产的租金都在最低租金和中等租金之间。对于联排别墅,如果需要,我们可以进一步评估一些异常值,但现在就到此为止。
对于下面的图,我使用 Matplotlib 来说明每个 LGA 的平均租金(实际上是租金中位数的平均值)以及一些可用的物业的简单视图。悉尼作为一个大都市,平均拥有的住宅数量最多。650 美元。整体平均约为 530 美元。
接下来是一组散点图(使用 Seaborn 的 lmplot 函数),显示了每个街区的卧室数量的详细租金分布。再以悉尼为例,我们可以看到 4 居室的房子都是> $1100pw 不到 2500 的房产。另一方面,一居室很丰富,价格在 600 美元以下。这是像悉尼这样的中央商业区所能预料到的。
让我们用一张热图来显示所有郊区按住宅类型划分的租金成本分布。这告诉我们,例如,就个人住宅类型而言,莫斯曼是最昂贵的街区,其次是 Woollahra 等等。
现在是销售分析。下面的柱状图为我们提供了 2019 年 Q2 各街区的摩天大楼中值价格分布。这涵盖了所有物业类型的中值价格。实际上,价格会因型号和大小而异,这不在我们现阶段的考虑范围内。
2019 年 Q2 期间每个街区的销售价格中位数
基于以上所述,正如人们所料,我们可以在下面的散点图(相关系数为 0.84)中看到,平均租金和邻近地区的中值价格之间存在很强的相关性。
地图
该部分包括销售和人口数据的地理空间分布。我首先将 geojson 文件转换为 shape 文件,然后使用 geopandas 的 Choropleth 映射函数来映射这些 shape 文件。老实说,我花了一段时间才明白过来(太棒了!!!我最终绘制它的时刻)。
接下来,我根据 2019 年 9 月至今的犯罪事件下载数据,创建了一个气泡图,以可视化每个街区的犯罪相对规模。这些数据被绘制在 Folium Open street 地图的顶部。
每个街区的犯罪数据
这就结束了我们的数据可视化部分。现在让我们转到机器学习算法部分。
k 均值聚类
在这里,如前所述,我使用 Foursquare API 探索附近的所有场所(咖啡馆、健身房、酒吧、公园等),然后基于这些场所创建数据框。
在生成的数据帧上,应用了一种流行的无监督机器学习算法,称为 k 均值聚类。这将数据分为 4 个不同的组:
集群的图例
第一集群:主要以咖啡馆、海滩和公园为公共场所。
集群 2: 这里有大量的餐馆、咖啡馆和购物中心。
集群 3: 以咖啡馆、公园、餐馆和汉堡店为主
集群 4: 更接近集群 1,以咖啡馆、酒吧和公园为主。
总的来说,正如我们之前所讨论的,对于一个人来说,搬迁到一个全新的国家并不是一个容易的决定。然而,生活在科技时代也有它的好处!我们通过互联网获得大量信息,然后使用数据分析工具,我们可以根据个人偏好做出合理的决定。
这就结束了我的第一个数据科学作业,这是我从头开始做的。它帮助我在这个过程中学到了很多,我期待着在这个领域进一步提高我的技能。
请随时评论源代码或情节,以便我进一步微调。
用 Python 探索 Hulu 数据
Hulu 数据的探索性数据分析
网飞、Prime Video、Hulu 和 Disney Plus 上的电影数据集包含从 reelgood.com 收集的信息,其中包含上述平台上可供流媒体播放的电影列表。在本帖中,我们将对网飞的电影、Prime Video、Hulu 和 Disney Plus* 数据集中的 Hulu 数据进行基本的探索性数据分析。数据可以在这里找到。*
我们开始吧!
首先,让我们将数据读入熊猫数据框:
import pandas as pd
df = pd.read_csv("MoviesOnStreamingPlatforms_updated.csv")
接下来,我们将打印该数据中可用的列:
print(df.columns)
我们不需要“Unamed: 0”列,所以我们可以用“del”关键字删除它:
del d['Unnamed: 0’]
现在,让我们使用“head()”方法打印前五行数据:
print(df.head())
如果我们看一下网飞、Hulu、Prime Video 和 Disney Plus 栏,我们会发现它们包含 1 和 0。1 对应于该电影可在相应平台上流式传输,而 0 对应于该电影在所述平台上不可用。
我们想专门研究 Hulu 数据,所以让我们过滤数据框,使“Hulu”列中的值等于 1:
df_hulu = df[df['Hulu'] == 1]
让我们打印原始数据框和过滤后的数据框的长度:
print("Total Length: ", len(df))
print("Hulu Length: ", len(df_hulu)))
让我们打印新数据框的前五行:
print(df_hulu.head())
我们的数据包含几个分类列。让我们定义一个将数据框、列名和限制作为输入的函数。当被调用时,它打印分类值的字典以及它们出现的频率:
def return_counter(data_frame, column_name, limit):
from collections import Counter print(dict(Counter(data_frame[column_name].values).most_common(limit)))
让我们将函数应用于“语言”列,并将结果限制为五个最常见的值:
return_counter(df_hulu, 'Language', 5)
如我们所见,Hulu 数据中有 607 个英语、27 个英语/西班牙语、19 个日语、18 个英语/法语和 15 个缺失的语言值。
让我们将此方法应用于“流派”列:
return_counter(df_hulu, 'Genres', 5)
现在,让我们来看看最常见的电影类型“纪录片”:
df_d1 = df_hulu[df_hulu['Genres'] =='Documentary']
print(set(df_d1['title']))
我们也可以看看这些国家:
print(set(df_d1['Country']))
接下来,让我们看看电影运行时间值的集合:
print(set(df_d1['Runtime']))
我们看到运行时间值的范围从 30 分钟到 120 分钟。接下来,从数字列(如“Runtime ”)中生成汇总统计数据会很有用。让我们定义一个采用数据框、分类列和数字列的函数。每个类别的数字列的平均值和标准偏差存储在数据框中,并且数据框根据平均值以降序排序。如果您想要快速查看特定类别对于特定数字列是否具有更高或更低的平均值和/或标准偏差值,这将非常有用。
def return_statistics(data_frame, categorical_column, numerical_column):
mean = []
std = []
field = []
for i in set(list(data_frame[categorical_column].values)):
new_data = data_frame[data_frame[categorical_column] == i]
field.append(i)
mean.append(new_data[numerical_column].mean())
std.append(new_data[numerical_column].std())
df = pd.DataFrame({'{}'.format(categorical_column): field, 'mean {}'.format(numerical_column): mean, 'std in {}'.format(numerical_column): std})
df.sort_values('mean {}'.format(numerical_column), inplace = True, ascending = False)
df.dropna(inplace = True)
return df
让我们用分类列“流派”和数字列“运行时”来调用我们的函数:
stats = return_statistics(df_hulu, 'Genres', 'Runtime')
print(stats.head(15))
接下来,我们将使用箱线图来显示基于最小值、最大值、中值、第一个四分位数和第三个四分位数的数值分布。如果您对它们不熟悉,可以看看文章了解 Boxplots 。
与汇总统计函数类似,此函数采用数据框、分类列和数值列,并根据限制显示最常见类别的箱线图:
def get_boxplot_of_categories(data_frame, categorical_column, numerical_column, limit):
import seaborn as sns
from collections import Counter
keys = []
for i in dict(Counter(df[categorical_column].values).most_common(limit)):
keys.append(i)
print(keys)
df_new = df[df[categorical_column].isin(keys)]
sns.set()
sns.boxplot(x = df_new[categorical_column], y = df_new[numerical_column])
让我们在 5 个最常见的“流派”类别中为“运行时”生成箱线图:
get_boxplot_of_categories(df_hulu, 'Genres', 'Runtime', 5)
最后,让我们定义一个函数,它将数据框和数字列作为输入,并显示一个直方图:
def get_histogram(data_frame, numerical_column):
df_new = data_frame
df_new[numerical_column].hist(bins=100)
让我们用数据框调用函数,并从“运行时”生成一个直方图:
get_histogram(df_hulu, 'Runtime')
我就讲到这里,但是请随意处理数据并自己编码。
结论
概括地说,我们回顾了几种分析网飞电影、Prime Video、Hulu 和 Disney Plus 数据集*中 Hulu 数据的方法。*这包括定义用于生成汇总统计数据的函数,如平均值、标准差和分类值的计数。我们还定义了用箱线图和直方图可视化数据的函数。我希望这篇文章有趣。这篇文章的代码可以在 GitHub 上找到。感谢您的阅读!
在边缘探索 AI!
树莓平台上基于 Tensorflow Lite 的图像识别、目标检测和姿态估计
图片来源:作者创作
介绍
什么是边缘(或者雾C计算?
Gartner 将边缘计算定义为:“分布式计算拓扑的一部分,其中信息处理位于靠近边缘的位置,即事物和人生产或消费信息的位置。”
换句话说,边缘计算将计算(和一些数据存储)带到离生成或消费数据的设备更近的地方(尤其是实时),而不是依赖于遥远的基于云的中央系统。使用这种方法,数据不会遇到延迟问题,从而降低了传输和处理的成本。在某种程度上,这是一种“回到最近的过去”,所有的计算工作都在本地的桌面上完成,而不是在云中。
边缘计算是由于连接到互联网的物联网设备的指数增长而发展起来的,这些设备要么从云中接收信息,要么将数据传送回云中。许多物联网(IoT)设备在运行过程中会产生大量数据。
边缘计算为物联网应用提供了新的可能性,特别是对于那些依赖机器学习(ML)来完成诸如对象和姿态检测、图像(和人脸)识别、语言处理和避障等任务的应用。图像数据是物联网的绝佳补充,但也是重要的资源消耗者(如电力、内存和处理)。图像处理“在边缘”,运行经典 AI/ML 模型,是一个巨大的飞跃!
tensor flow Lite——机器学习(ML)在边缘!!
图片来源:机器学习训练对比推理 — Gartner
机器学习可以分为两个独立的过程:训练和推理,正如 Gartner 博客中所解释的:
- **训练:**训练是指创建机器学习算法的过程。训练涉及使用深度学习框架(例如 TensorFlow)和训练数据集(见上图左侧)。物联网数据提供了一个训练数据源,数据科学家和工程师可以使用它来训练各种情况下的机器学习模型,从故障检测到消费者智能。
- **推理:**推理是指使用经过训练的机器学习算法进行预测的过程。物联网数据可以用作经过训练的机器学习模型的输入,实现预测,这些预测可以指导设备上、边缘网关上或物联网系统中其他地方的决策逻辑(见上图右侧)。
TensorFlow Lite 是一个开源的深度学习框架,支持在设备上的机器学习推理,具有低延迟和小二进制大小。它旨在使在网络“边缘”的设备上执行机器学习变得容易,而不是从服务器来回发送数据。
在设备上执行机器学习有助于改善:
- *延迟:*没有到服务器的往返
- *隐私:*没有数据需要离开设备
- *连接:*不需要互联网连接
- *功耗:*网络连接非常耗电
TensorFlow Lite (TFLite)由两个主要组件组成:
- TFLite 转换器,它将 TensorFlow 模型转换成高效的形式供解释器使用,并可以引入优化来提高二进制大小和性能。
- TFLite 解释器在许多不同的硬件类型上运行,包括手机、嵌入式 Linux 设备和微控制器。
图片来源:TensorFlow Lite —在边缘设备部署模型
总之,一个经过训练和保存的 TensorFlow 模型(如 model.h5 )可以使用 TFLite FlatBuffer(如 model.tflite )中的 TFLite Converter 进行转换,该转换器将由 Edge 设备内部的 TF Lite 解释器使用(作为一个 Raspberry Pi),以对新数据进行推理。
例如,我在我的 Mac(上图中的“服务器”)中从头训练了一个简单的 CNN 图像分类模型。最终模型有 225,610 个参数要训练,使用 CIFAR10 数据集作为输入:60,000 个图像(形状:32,32,3)。经过训练的模型( cifar10_model.h5 )的大小为 2.7Mb。使用 TFLite 转换器,在 Raspberry Pi 上使用的模型( model_cifar10.tflite )的大小变为 905Kb(约为原始大小的 1/3)。用两个模型进行推断(h5 在 Mac 和。RPi 处的 tflite)留下相同的结果。两款笔记本都可以在 GitHub 找到。
图片来源:作者笔记本
Raspberry Pi — TFLite 安装
也可以在 Raspberry Pi 从头开始训练模型,为此,需要完整的 TensorFlow 包。但是一旦我们要做的只是推理部分,我们将只安装 TensorFlow Lite 解释器。
仅解释器包的大小是整个 TensorFlow 包的一小部分,并且包括使用 TensorFlow Lite 运行推理所需的最少代码。它只包含用于执行
.tflite
模型的[tf.lite.Interpreter](https://www.tensorflow.org/api_docs/python/tf/lite/Interpreter)
Python 类。
让我们在 Raspberry Pi 打开终端,安装您的特定系统配置所需的 Python wheel 。选项可以在这个链接上找到: Python 快速入门。例如,在我的例子中,我运行的是 Linux arm 32(Raspbian Buster—Python 3.7),所以命令行是:
$ sudo pip3 install [https://dl.google.com/coral/python/tflite_runtime-2.1.0.post1-cp37-cp37m-linux_armv7l.whl](https://dl.google.com/coral/python/tflite_runtime-2.1.0.post1-cp37-cp37m-linux_armv7l.whl)
如果您想仔细检查 Raspberry Pi 中的操作系统版本,请运行以下命令:
$ uname -
如下图所示,如果你得到… arm7l… ,操作系统是 32 位 Linux。
安装 Python wheel 是让 TFLite 解释器在 Raspberry Pi 中工作的唯一要求。可以在终端调用 TFLite 解释器来仔细检查安装是否正常,如下所示。如果没有错误出现,我们是好的。
图像分类
介绍
应用于计算机视觉(CV)的人工智能的更经典的任务之一是图像分类。从 2012 年开始,当一个名为 AlexNet 的卷积神经网络 (CNN)(为了纪念其领先的开发者 Alex Krizhevsky)在 ImageNet 2012 挑战赛中取得了 15.3%的前 5 名误差时,信息架构和深度学习(DL)永远改变了。根据《经济学家》杂志的报道,“人们突然开始关注(人工智能),不仅仅是在人工智能领域,而是在整个技术行业
这个项目,在 Alex Krizhevsk,一个更现代的架构( MobileNet )的近八年后,也使用相同的数据集 ImageNet 对数百万张图像进行了预训练,产生了 1000 个不同的类。这个预先训练和量化的模型是这样的,转换成一个. tflite 并在这里使用。
图片来源:作者创作
首先,让我们在 Raspberry Pi 上移动到一个工作目录(例如, Image_Recognition )。接下来,必须创建两个子目录,一个用于模型,另一个用于图像:
$ mkdir images
$ mkdir models
一旦进入模型目录,让我们下载预先训练好的模型(在这个链接,可以下载几个不同的模型)。我们将使用量化的 Mobilenet V1 模型,用 224x224 像素的图像进行预训练。可以从 TensorFlow Lite 图片分类下载的 zip 文件,使用 wget :
$ cd models
$ wget [https://storage.googleapis.com/download.tensorflow.org/models/tflite/mobilenet_v1_1.0_224_quant_and_labels.zip](https://storage.googleapis.com/download.tensorflow.org/models/tflite/mobilenet_v1_1.0_224_quant_and_labels.zip)
接下来,解压缩文件:
$ unzip mobilenet_v1_1.0_224_quant_and_labels
下载了两个文件:
- mobilenet _ v1 _ 1.0 _ 224 _ quant . TF Lite:tensor flow-Lite 转换模型
- Labels _ mobilenet _ quant _ v1 _ 224 . txt:ImageNet 数据集 1000 个类标签
现在,获取一些图像(例如。png,。jpg)并将它们保存在创建的图像子目录中。
在 GitHub 上,可以找到本教程使用的图片。
Raspberry Pi OpenCV 和 Jupyter 笔记本安装
OpenCV(开源计算机视觉库)是一个开源的计算机视觉和机器学习软件库。在处理图像时,这是一种有益的支持。如果在 Mac 或 PC 上安装它非常简单,那么在 Raspberry Pi 上安装就有点“技巧”了,但是我推荐使用它。
请按照 Q-Engineering 的这个很棒的教程在你的树莓 Pi 上安装 OpenCV:在树莓 Pi 4 上安装 OpenCV 4.4.0。尽管该指南是为 Raspberry Pi 4 编写的,但也可以不做任何修改地用于 Raspberry 3 或 2。
接下来,安装 Jupyter 笔记本。它将成为我们的开发平台。
$ sudo pip3 install jupyter
$ jupyter notebook
此外,在 OpenCV 安装过程中,应该已经安装了 NumPy,如果现在没有安装,与 MatPlotLib 相同。
$ sudo pip3 install numpy
$ sudo apt-get install python3-matplotlib
完成了!我们已经准备好开始我们的人工智能之旅了!
图像分类推理
创建一个新的 Jupyter 笔记本并遵循以下步骤,或者从 GitHub 下载完整的笔记本。
导入库:
import numpy as np
import matplotlib.pyplot as plt
import cv2
import tflite_runtime.interpreter as tflite
加载 TFLite 模型并分配张量:
interpreter = tflite.Interpreter(model_path=’./models/mobilenet_v1_1.0_224_quant.tflite’)
interpreter.allocate_tensors()
获取输入和输出张量:
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
输入细节将为您提供所需的信息,告诉您应该如何为模型输入图像:
(1,224x224x3)的形状通知一个尺寸为:(224x224x3)的图像应该一个一个输入(批量尺寸:1)。dtype uint8 表示这些值是 8 位整数
输出细节显示推理将产生 1001 个整数值(8 位)的数组。这些值是图像分类的结果,其中每个值是特定标签与图像相关的概率。
例如,假设我们想要对形状为(1220,1200,3)的图像进行分类。首先,我们需要将其调整为(224,224,3)并添加一个批处理维度 1,如输入细节中所定义的:(1,224,224,3)。推断结果将是一个大小为 1001 的数组,如下所示:
图片来源:作者创作—图片分类主要步骤
对这些操作进行编码的步骤是:
- 输入图像并将其转换为 RGB (OpenCV 将图像读取为 BGR 图像):
image_path = './images/cat_2.jpg'
image = cv2.imread(image_path)
img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
2.图像预处理、整形和添加批量尺寸:
img = cv2.resize(img, (224, 224))
input_data = np.expand_dims(img, axis=0)
3.指向用于测试的数据并运行解释器:
interpreter.set_tensor(input_details[0]['index'], input_data)
interpreter.invoke()
4.获取结果并将它们映射到类别:
predictions = interpreter.get_tensor(output_details[0][‘index’])[0]
输出值(预测值)从 0 到 255 不等(8 位整数的最大值)。要获得范围从 0 到 1 的预测,输出值应除以 255。与最高值相关的数组索引是这种图像最可能的分类。
有了索引,我们必须找到它指定的类别(如汽车、猫或狗)。与模型一起下载的文本文件有一个与从 0 到 1,000 的每个索引相关联的标签。
让我们首先创建一个函数来加载。txt 文件作为字典:
def load_labels(path):
with open(path, 'r') as f:
return {i: line.strip() for i, line in enumerate(f.readlines())}
创建一个名为标签的字典,并检查其中的一些标签:
labels = load_labels('./models/labels_mobilenet_quant_v1_224.txt')
回到我们的例子,让我们得到前 3 个结果(最高概率):
top_k_indices = 3
top_k_indices = np.argsort(predictions)[::-1][:top_k_results]
我们可以看到,前 3 个指数都与猫有关。预测内容是与每个标签相关联的概率。如前所述,除以 255。,我们可以得到一个从 0 到 1 的值。让我们创建一个循环来检查顶部结果,打印标签和概率:
for i in range(top_k_results):
print("\t{:20}: {}%".format(
labels[top_k_indices[i]],
int((predictions[top_k_indices[i]] / 255.0) * 100)))
让我们创建一个函数,来平滑地对不同的图像执行推理:
def image_classification(image_path, labels, top_k_results=3):
image = cv2.imread(image_path)
img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(img)img = cv2.resize(img, (w, h))
input_data = np.expand_dims(img, axis=0)interpreter.set_tensor(input_details[0]['index'], input_data)
interpreter.invoke()
predictions = interpreter.get_tensor(output_details[0]['index'])[0]top_k_indices = np.argsort(predictions)[::-1][:top_k_results]print("\n\t[PREDICTION] [Prob]\n")
for i in range(top_k_results):
print("\t{:20}: {}%".format(
labels[top_k_indices[i]],
int((predictions[top_k_indices[i]] / 255.0) * 100)))
下图显示了使用该函数的一些测试:
图片来源:作者根据公共数据集创作
整体表现惊人!从你输入内存卡中的图像路径开始,直到打印出结果,整个过程不到半秒钟,精度极高!
该功能可以很容易地应用于视频或现场摄像机的帧。本节讨论的笔记本和完整代码可以从 GitHub 下载。
目标检测
利用图像分类,我们可以检测这样的图像的主要主题是什么。但是,如果几个物体在同一个图像上是主要的和感兴趣的,会发生什么呢?要解决它,我们可以使用一个对象检测模型!
给定图像或视频流,对象检测模型可以识别已知对象集合中的哪一个可能存在,并提供关于它们在图像中的位置的信息。
对于此任务,我们将下载一个使用 COCO(上下文中的公共对象)数据集预训练的 Mobilenet V1 模型。这个数据集有超过 200,000 个带标签的图像,分属 91 个类别。
图片来源:作者创作
下载模型和标签
在 Raspberry 终端上运行命令:
$ cd ./models
$ curl -O [http://storage.googleapis.com/download.tensorflow.org/models/tflite/coco_ssd_mobilenet_v1_1.0_quant_2018_06_29.zip](http://storage.googleapis.com/download.tensorflow.org/models/tflite/coco_ssd_mobilenet_v1_1.0_quant_2018_06_29.zip)
$ unzip coco_ssd_mobilenet_v1_1.0_quant_2018_06_29.zip
$ curl -O [https://dl.google.com/coral/canned_models/coco_labels.txt](https://dl.google.com/coral/canned_models/coco_labels.txt)$ rm coco_ssd_mobilenet_v1_1.0_quant_2018_06_29.zip
$ rm labelmap.txt
在 models 子目录中,我们应该以 2 个新文件结束:
coco_labels.txt
detect.tflite
对新图像执行推断的步骤与图像分类非常相似,除了:
- 输入:图像的形状必须为 300x300 像素
- 输出:不仅包括标签和概率(“得分”),还包括对象在图像上所处位置的相对窗口位置(“边界框”)。
图片来源:作者创作
现在,我们必须加载标签和模型,分配张量。
labels = load_labels('./models/coco_labels.txt')
interpreter = Interpreter('./models/detect.tflite')
interpreter.allocate_tensors()
输入预处理和我们之前做的一样,但是输出应该被处理以获得更可读的输出。以下函数将对此有所帮助:
def set_input_tensor(interpreter, image):
"""Sets the input tensor."""
tensor_index = interpreter.get_input_details()[0]['index']
input_tensor = interpreter.tensor(tensor_index)()[0]
input_tensor[:, :] = imagedef get_output_tensor(interpreter, index):
"""Returns the output tensor at the given index."""
output_details = interpreter.get_output_details()[index]
tensor = np.squeeze(interpreter.get_tensor(output_details['index']))
return tensor
借助上述函数,detect_objects()将返回推理结果:
- 对象标签 id
- 得分
- 边界框,它将显示对象的位置。
我们已经包括了一个“阈值”来避免正确概率低的对象。通常情况下,我们应该考虑 50%以上的分数。
def detect_objects(interpreter, image, threshold):
set_input_tensor(interpreter, image)
interpreter.invoke()
# Get all output details
boxes = get_output_tensor(interpreter, 0)
classes = get_output_tensor(interpreter, 1)
scores = get_output_tensor(interpreter, 2)
count = int(get_output_tensor(interpreter, 3)) results = []
for i in range(count):
if scores[i] >= threshold:
result = {
'bounding_box': boxes[i],
'class_id': classes[i],
'score': scores[i]
}
results.append(result)
return results
如果我们将上述函数应用于一个整形的图像(与分类示例中使用的相同),我们应该得到:
太好了!在不到 200 毫秒的时间内,以 77%的概率,在由“边界框”界定的区域上检测到 id 为 16 的物体:(0.028011084,0.020121813,0.9886069,0.802299)。这四个数字分别与 ymin、xmin、ymax 和 xmax 相关。
考虑到 y 从顶部(ymin)到底部(ymax ), x 从左侧(xmin)到右侧(xmax ),如下图所示:
图片来源:作者创作
边界框有四个值,实际上,我们有左上角和右下角的坐标。有了两边,知道了图片的形状,就有可能画出物体周围的矩形。
接下来,我们应该找出 class_id 等于 16 意味着什么。打开文件 *coco_labels.txt,*作为一个字典,它的每个元素都有一个相关联的索引,检查索引 16,我们得到了预期的’ cat '概率是从分数返回的值。
让我们创建一个通用函数来检测单个图片上的多个对象。第一个函数从图像路径开始,将执行推理,返回调整后的图像和结果(多个 id,每个 id 都有其分数和边界框:
def detectObjImg_2(image_path, threshold = 0.51):img = cv2.imread(image_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
image = cv2.resize(img, (width, height),
fx=0.5,
fy=0.5,
interpolation=cv2.INTER_AREA)
results = detect_objects(interpreter, image, threshold)return img, results
有了整形后的图像和推断结果,下面的函数可用于在对象周围绘制一个矩形,为每个对象指定其标签和概率:
def detect_mult_object_picture(img, results): HEIGHT, WIDTH, _ = img.shape
aspect = WIDTH / HEIGHT
WIDTH = 640
HEIGHT = int(640 / aspect)
dim = (WIDTH, HEIGHT) img = cv2.resize(img, dim, interpolation=cv2.INTER_AREA) for i in range(len(results)):
id = int(results[i]['class_id'])
prob = int(round(results[i]['score'], 2) * 100)
ymin, xmin, ymax, xmax = results[i]['bounding_box']
xmin = int(xmin * WIDTH)
xmax = int(xmax * WIDTH)
ymin = int(ymin * HEIGHT)
ymax = int(ymax * HEIGHT) text = "{}: {}%".format(labels[id], prob) if ymin > 10: ytxt = ymin - 10
else: ytxt = ymin + 15 img = cv2.rectangle(img, (xmin, ymin), (xmax, ymax),
COLORS[id],
thickness=2)
img = cv2.putText(img, text, (xmin + 3, ytxt), FONT, 0.5, COLORS[id],
2) return img
下面是一些结果:
图片来源:作者创作
完整的代码可以在 GitHub 找到。
使用摄像机的目标检测
图片来源:树莓派基金会
如果您有一个连接到 Raspberry Pi 的 PiCam,则可以使用之前定义的相同功能,逐帧捕捉视频并执行对象识别。如果您的 Pi: 中没有可用的相机,请遵循本教程开始使用相机模块。
首先,必须定义相机要捕捉的帧的大小。我们将使用 640x480。
WIDTH = 640
HEIGHT = 480
接下来,你必须校准摄像机:
cap = cv2.VideoCapture(0)
cap.set(3, WIDTH)
cap.set(4, HEIGHT)
并循环运行下面的代码。在按下“q”键之前,摄像机会一帧一帧地捕捉视频,并绘制带有相应标签和概率的边界框。
while True: timer = cv2.getTickCount()
success, img = cap.read()
img = cv2.flip(img, 0)
img = cv2.flip(img, 1) fps = cv2.getTickFrequency() / (cv2.getTickCount() - timer)
cv2.putText(img, "FPS: " + str(int(fps)), (10, 470),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
image = cv2.resize(image, (width, height),
fx=0.5,
fy=0.5,
interpolation=cv2.INTER_AREA)
start_time = time.time()
results = detect_objects(interpreter, image, 0.55)
elapsed_ms = (time.time() - start_time) * 1000 img = detect_mult_object_picture(img, results)
cv2.imshow("Image Recognition ==> Press [q] to Exit", img)if cv2.waitKey(1) & 0xFF == ord('q'):
breakcap.release()
cv2.destroyAllWindows()
下面可以看到树莓 Pi 屏幕上实时运行的视频。注意视频运行在 60 FPS(每秒帧数)左右,相当不错!。
下面是上述视频的一个截屏:
图片来源:作者创作
完整的代码可以在 GitHub 上找到。
姿态估计
人工智能更令人兴奋和关键的领域之一是估计一个人的实时姿势,使机器能够理解人们在图像和视频中做什么。在我的文章使用 TensorFlow2.x 进行实时多人 2D 姿势估计中深入探讨了姿势估计,但在这里的边缘,使用 Raspberry Pi 并在 TensorFlow Lite 的帮助下,可以轻松复制在 Mac 上完成的几乎相同的操作。
我们将在这个项目中使用的型号是 PoseNet 。我们将以与图像分类和对象检测相同的方式进行推理,其中图像通过预先训练的模型输入。PoseNet 带有几个不同版本的模型,对应于 MobileNet v1 架构和 ResNet50 架构的变化。在这个项目中,预训练的版本是 MobileNet V1,它比 ResNet 更小,更快,但不如 ResNet 准确。此外,有单独的模型用于单人和多人姿势检测。我们将探索为一个人训练的模型。
在这个站点中,可以使用现场摄像机实时探索几个 PoseNet 模型和配置。
在 Raspberry Pi 上执行姿态估计的库与之前使用的相同。NumPy,MatPlotLib,OpenCV 和 TensorFlow Lite 解释器。
预训练的模型是posenet _ mobilenet _ v1 _ 100 _ 257 x257 _ multi _ kpt _ stripped . TF Lite,可以从上面的链接或者 TensorFlow Lite —姿态估计概述网站下载。模型应该保存在 models 子目录中。
开始加载 TFLite 模型并分配张量:
interpreter = tflite.Interpreter(model_path='./models/posenet_mobilenet_v1_100_257x257_multi_kpt_stripped.tflite')
interpreter.allocate_tensors()
获取输入和输出张量:
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
与我们之前所做的一样,查看 input_details,可以看到用于姿态估计的图像应该是(1,257,257,3),这意味着图像必须被整形为 257x257 像素。
让我们以一个简单的人形作为输入,这将帮助我们分析它:
第一步是预处理图像。这个特殊的模型没有被量化,这意味着 dtype 是 float32。这些信息对于预处理输入图像至关重要,如下面的代码所示
image = cv2.resize(image, size)
input_data = np.expand_dims(image, axis=0)
input_data = input_data.astype(np.float32)
input_data = (np.float32(input_data) - 127.5) / 127.5
对图像进行预处理后,现在是执行推理的时候了,向张量输入图像并调用解释器:
interpreter.set_tensor(input_details[0]['index'], input_data)
interpreter.invoke()
一篇非常有助于理解如何使用 PoseNet 的文章是 Ivan Kunyakin 教程的姿势估计和与 TensorFlow lite 的匹配。Ivan 评论说,在输出向量中,找到关键点的关键是:
- 热图大小为(9,9,17)的 3D 张量,其对应于 17 个关键点(身体关节)中的每一个在图像的特定部分(9,9)中出现的概率。它用于定位关节的大致位置。
- **偏移向量:**大小为(9,9,34)的 3D 张量,称为偏移向量。它用于更精确地计算关键点的位置。第三维的第一个 17 对应于 x 坐标,第二个 17 对应于 y 坐标。
output_details = interpreter.get_output_details()[0]
heatmaps = np.squeeze(interpreter.get_tensor(output_details['index']))output_details = interpreter.get_output_details()[1]
offsets = np.squeeze(interpreter.get_tensor(output_details['index']))
让我们创建一个函数,该函数将基于热图和偏移量返回包含所有 17 个关键点(或人的关节)的数组。
def get_keypoints(heatmaps, offsets): joint_num = heatmaps.shape[-1]
pose_kps = np.zeros((joint_num, 2), np.uint32)
max_prob = np.zeros((joint_num, 1)) for i in range(joint_num):
joint_heatmap = heatmaps[:,:,i]
max_val_pos = np.squeeze(
np.argwhere(joint_heatmap == np.max(joint_heatmap)))
remap_pos = np.array(max_val_pos / 8 * 257, dtype=np.int32)
pose_kps[i, 0] = int(remap_pos[0] +
offsets[max_val_pos[0], max_val_pos[1], i])
pose_kps[i, 1] = int(remap_pos[1] +
offsets[max_val_pos[0], max_val_pos[1],
i + joint_num])
max_prob[i] = np.amax(joint_heatmap) return pose_kps, max_prob
使用上述函数以及从输出张量提取的热图和偏移向量,图像推断的结果,我们得到:
得到的数组显示了关于关节在 257×257 像素的图像上的位置的所有 17 个坐标(y,x)。使用下面的代码。可以在调整大小的图像上绘制每个关节。作为参考,数组索引已被标注,因此很容易识别每个关节:
y,x = zip(*keypts_array)
plt.figure(figsize=(10,10))
plt.axis([0, image.shape[1], 0, image.shape[0]])
plt.scatter(x,y, s=300, color='orange', alpha=0.6)
img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(img)
ax=plt.gca()
ax.set_ylim(ax.get_ylim()[::-1])
ax.xaxis.tick_top()
plt.grid();for i, txt in enumerate(keypts_array):
ax.annotate(i, (keypts_array[i][1]-3, keypts_array[i][0]+1))
结果,我们得到了图片:
图片来源:作者创作
太好了,现在是时候创建一个通用函数来绘制“骨骼”,这是关节的连接。骨骼将被绘制为线条,这些线条是关键点 5 到 16 之间的连接,如上图所示。独立圆将用于关键点 0 至 4,与头部相关:
def join_point(img, kps, color='white', bone_size=1):
if color == 'blue' : color=(255, 0, 0)
elif color == 'green': color=(0, 255, 0)
elif color == 'red': color=(0, 0, 255)
elif color == 'white': color=(255, 255, 255)
else: color=(0, 0, 0) body_parts = [(5, 6), (5, 7), (6, 8), (7, 9), (8, 10), (11, 12), (5, 11),
(6, 12), (11, 13), (12, 14), (13, 15), (14, 16)] for part in body_parts:
cv2.line(img, (kps[part[0]][1], kps[part[0]][0]),
(kps[part[1]][1], kps[part[1]][0]),
color=color,
lineType=cv2.LINE_AA,
thickness=bone_size)
for i in range(0,len(kps)):
cv2.circle(img,(kps[i,1],kps[i,0]),2,(255,0,0),-1)
通过调用该函数,我们得到了图像中身体的估计姿势:
join_point(img, keypts_array, bone_size=2)
plt.figure(figsize=(10,10))
plt.imshow(img);
图片来源:作者创作
最后但同样重要的是,让我们创建一个通用函数,以图像路径作为起点来估计姿态:
def plot_pose(img, keypts_array, joint_color='red', bone_color='blue', bone_size=1):
join_point(img, keypts_array, bone_color, bone_size)
y,x = zip(*keypts_array)
plt.figure(figsize=(10,10))
plt.axis([0, img.shape[1], 0, img.shape[0]])
plt.scatter(x,y, s=100, color=joint_color)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) plt.imshow(img)
ax=plt.gca()
ax.set_ylim(ax.get_ylim()[::-1])
ax.xaxis.tick_top()
plt.grid();
return img def get_plot_pose(image_path, size, joint_color='red', bone_color='blue', bone_size=1):
image_original = cv2.imread(image_path)
image = cv2.resize(image_original, size)
input_data = np.expand_dims(image, axis=0)
input_data = input_data.astype(np.float32)
input_data = (np.float32(input_data) - 127.5) / 127.5 interpreter.set_tensor(input_details[0]['index'], input_data)
interpreter.invoke()
output_details = interpreter.get_output_details()[0]
heatmaps = np.squeeze(interpreter.get_tensor(output_details['index'])) output_details = interpreter.get_output_details()[1]
offsets = np.squeeze(interpreter.get_tensor(output_details['index'])) keypts_array, max_prob = get_keypoints(heatmaps,offsets)
orig_kps = get_original_pose_keypoints(image_original, keypts_array, size) img = plot_pose(image_original, orig_kps, joint_color, bone_color, bone_size)
return orig_kps, max_prob, img
此时,只需一行代码,就可以检测图像上的姿态:
keypts_array, max_prob, img = get_plot_pose(image_path, size, bone_size=3)
本节开发的所有代码都可以在 GitHub 上获得。
另一个简单的步骤是将该功能应用于来自视频和现场摄像机的帧。我会留给你的!;-)
结论
TensorFlow Lite 是一个在边缘实现人工智能(更准确地说,是 ML)的伟大框架。在这里,我们探索了在 Raspberry Pi 上工作的 ML 模型,但 TFLite 现在越来越多地用于“边缘的边缘”,在非常小的微控制器上,在所谓的 TinyML 中。
一如既往,我希望这篇文章可以激励其他人在 AI 的奇幻世界中找到自己的路!
本文使用的所有代码都可以在 GitHub 项目上下载: TFLite_IA_at_the_Edge。
来自世界南方的问候!
我的下一篇文章再见!
谢谢你
马塞洛
探索新加坡的非法毒品——数据透视
变更数据
用 Python 通过探索性数据分析发现保健品的好、坏、丑
所有的东西都是毒药,因为没有一样东西是没有毒性的。只有剂量才会使一件东西中毒。”― 帕拉塞尔苏斯
micha Parzuchowski 在 Unsplash 上拍摄的照片
执行摘要(又名 TL;博士)
- 新加坡报道的大多数非法保健品都是为了增强性功能和减肥。
- 大多数违禁药品都有中文标签
- 最常见的剂型设计是绿色长方形胶囊
- 最常见的 5 种掺假药物是西地那非、西布曲明、他达拉非、酚酞和利多卡因
背景和动机
贩卖假药和掺假药的有利可图的性质导致了非法保健品全球贸易的扩散。这也是由消费者对这些产品的需求增加以及购买这些产品的低成本所驱动的。明显的缺点是,食用这些来源可疑的保健品可能会给消费者带来严重的健康问题。
这些产品通常含有未声明的成分来增强效果,以吸引消费者服用。这些成分可能是有害的(尤其是在没有医疗监督的情况下服用),过量或不足剂量,被禁止或尚未被评估用于人类的安全使用。
为了更好地了解新加坡的非法保健品贸易,我决定对 HSA 健康科学局公布的非法保健品清单进行分析。
这一分析是由我与第一批企业家伙伴(Shrey Chaturvedi)的一次谈话引发的,当时我们正在讨论东南亚假药市场中需要解决的潜在问题。我决定这是一个很好的机会来做这个与我喜欢的东西相关的迷你项目——药物和数据的交叉。
第一部分—数据来源
HSA 报告的非法保健品数据库在第页可供公众查看。虽然这份清单并不详尽,但它仍然是新加坡所有非法保健品交易的合理样本。
该数据集在网站上以数据表的形式呈现,并于 2020 年 8 月 15 日被访问和检索。
HSA 非法保健品数据库搜索结果截图
可用的功能如下:
数据集特征(列)和各自描述表
网站上的数据库内容被手动转录并保存为 Excel ( )。xlsx )文件的方法。然后将其导入 Jupyter Notebook,以便使用 Python 3 进行进一步分析。
我在国外度假时遇到过街头假药小贩,我最初的假设是这些假药主要是生活用品。特别是,最畅销的可能是针对成年男性的壮阳药。
第二部分—数据清理
在撰写本文时,HSA 非法保健品数据库中记录了总共 245 种报告的非法保健品。
在“剂型”栏中具有空值的所有 54 行都与“产品说明”栏中显示的片剂相关联。因此,这些空值被填入“片剂”作为剂型条目,从而创建了以前未使用的“片剂”新剂型类别。最终数据集中每一列的 null 值的计数如下所示:
每列的空值的频率计数
形状和颜色的零值主要是由于包装成小瓶和瓶子的产品,与通常的口服片剂或胶囊相比,它们更难表征。
鉴于“剂型标记备注”一栏中存在大量缺失数据(即 61.2 % NA ),因此本次分析中省略了对产品标记的审查。
有 13 种产品的剂型注明为“药丸”,这是一个不具参考价值的分类。手动审查产品图像,以确定其确切的剂型(即片剂或胶囊)。
第三部分——探索性数据分析的见解
现在是分析的主要部分,即观察到的模式和发现。
(一)产品描述
产品说明给出了产品的名称,以及一些有用的(尽管是简短的)关于消费者预期用途的信息。鉴于我们在这里处理的是文本字符串,使用单词云生成器(带有 Python WordCloud 库)来说明这些描述中的文本频率是一个好主意。
在删除无信息的停用词(例如,“品牌”、“胶囊”、“胶囊”(中文意思是胶囊)“药片”)后,产生了以下词云:
产品描述全字云
很明显,这些单词大多是汉字的罗马化(即汉语拼音)。这表明许多产品主要以中文标注,解读这种全词云并不是理解这些产品本质的最佳方式。
鉴于单词 cloud 暗示了汉字的高出现率,那么看看在其产品描述中包含汉字的非法保健品的比例将是有趣的。
产品说明中含有汉字的非法保健品比例
可以看出,违禁药品清单以中文标注的产品为主(73.5%)。这可能意味着要么中国人是非法毒贩的主要目标群体,要么这些产品主要来自中国。
The next step is to generate a Chinese character word cloud. This was done by first using regular expression (regex) to extract all Chinese characters from the ‘Product Description’ column, and using Python’s jieba library to tokenize these Chinese characters. With certain Chinese stop words removed (e.g. 胶囊), the WordCloud library was used once again to generate a word cloud:
产品描述中包含汉字的产品词云
有了这个中文词云,就更容易理解非法保健品的本质了。
- Finding 1
The word America (‘美国’) occurs frequently, implying that dealers tend to market their counterfeit drugs as products of USA, likely to falsely augment its appeal, quality and legitimacy towards consumers. This assertion was confirmed when I looked at these specific products containing ‘美国’, and found these products to be branded with phrases like ‘America Warrior’ and ‘America Viagra’. Another country observed in the word cloud is Germany (‘德国’), meaning that drugs produced in Germany are also associated with strong branding. - Finding 2
The two most common claims made by these health products relate to sexual performance and male genitalia (‘牛鞭’, ‘升阳’, ‘延时’, ‘魔根’), and weight loss (‘减肥’, ‘瘦身’). This is further supported by other frequently occurring nuanced words associated with sexual vitality (‘金聖力’, ‘战神’, ‘动力’, ‘天雄’, ‘神威’). - Finding 3
There are several common words associated with dragon (‘龙牌’, ‘天龙’), which is not out of the ordinary since the dragon traditionally symbolizes potent and auspicious powers in Chinese culture. - Finding 4
While drug names are not commonly mentioned in the Chinese descriptions, there is one obvious branded name that appeared frequently, which is Viagra (‘威哥’, ‘伟哥’).
分析不含任何中文字符的其他产品也很重要,这些产品占该非法药物数据集的 26.5%。删除英语和马来语停用词(如 Kapsul、Obat)后,生成的词云如下所示:
产品描述中无汉字的产品的词云
与之前看到的贴有中文标签的产品类似,这些产品似乎也是为男性减肥和增强性功能而销售的。这可以从英语词汇(如“男性”、“性”、“苗条”、“体重”)以及一些暗示性的马来语词汇(如“Kuat”(强壮)、“Untuk Pria”(男性)、“Tongkat Ali”(一种用于治疗勃起功能障碍的草药)中观察到。所有这些见解都支持早期的假设,即性增强药物是正在交易的主要假冒产品。
- 发现 5 单词 cloud exploration 的另一个发现是马来语短语‘Asam Urat’的出现,它在英语中的意思是尿酸。血液中高水平的尿酸是导致痛风的原因,马来语中尿酸的频繁出现表明市场上也有许多用于痛风治疗的假冒产品。
(ii) 剂型
这些非法保健品大多是口服固体剂型,即胶囊和片剂。
剂型的分发
这并不奇怪,因为口服制剂具有许多优点,例如:
- 易于制造、包装和运输
- 良好的化学和物理稳定性
- 生产成本相对较低
- 为消费者提供简单准确的剂量
(iii) 剂型颜色和形状
由于白色通常是口服药物最常见的颜色,所以看到白色也是这些非法保健品的主要颜色也就不足为奇了。接下来最常见的三种颜色恰好是红、蓝、绿的标准原色。
剂型颜色分布
就剂型形状而言,主要形状为长方形。
剂型形状的分布
单独看上面的图表,你可能会认为最常见的设计是白色长方形胶囊。然而,我们应该从剂型、颜色和形状的组合顺序来看待它。由此可见,最常见的剂型设计实际上是一种绿色椭圆形胶囊,而不是白色胶囊。
十大最常见的剂型设计
下面是一个绿色长方形胶囊的例子:
(四)剂型标记
可以看出,这些非法保健品绝大多数(60.6%)上面没有任何标识。这使得确定这些药物的具体身份以及区分假冒产品和真产品变得更加困难。
带有和不带有标记/雕刻的产品分配
(v) 掺杂物
掺假物是保健品中未被发现和未经批准的强效药物成分,这些掺假物是这些假冒产品被视为非法的主要原因。
由于意外误用、过度使用或与其他药物、潜在健康状况或补充剂中的其他成分相互作用,这些掺假物会导致严重的不良健康影响。
下表列出了这些非法产品中最常见的 10 种掺杂物。
非法保健品中发现的 10 大掺假物
现在是时候介绍一些药理学知识了,描述一下上表中的五大掺假者。
***①西地那非
T5 是什么?***西地那非是非法保健品中最常见的掺杂物。西地那非是伟哥(商品名)中发现的活性化合物的通用名,用于治疗勃起功能障碍(ed)。
**趣闻:**西地那非最初是为治疗肺动脉高压和心绞痛(心脏病引起的胸痛)而开发的。然而,在临床试验中,研究人员发现该药物在诱导勃起方面比治疗心绞痛更有效。
副作用:虽然大多数副作用通常是轻微的(如脸红、头痛),但有可能出现更严重的反应,如视力丧失、阴茎异常勃起(持续而痛苦的勃起)和严重低血压。当消费这些假冒产品时,这些风险肯定会被放大,其中所含的剂量监管不力。
***(2)******西布曲明 什么东西?*西布曲明是一种用于减肥的化合物。这支持了之前收集的观点,即减肥是非法保健品的常见目标。西布曲明通过增加产热并使使用者在饭后有饱腹感来发挥作用,它主要在肥胖患者群体中进行研究。
副作用:已知它与心血管事件风险增加有关,如心脏病发作、中风和高血压。**
****(3)******他达拉非 是什么?与西地那非(即磷酸二酯酶-5 酶抑制剂)属于同一药物类别,他达拉非也用于治疗 ed,并与西地那非具有许多相似的特征。它的品牌名是 Cialis。
****(4)酚酞 是什么?酚酞是一种泻药,常见于掺假减肥产品中。多种含有酚酞和西布曲明的膳食补充剂此前已被美国美国食品药品监督管理局(FDA) 召回,原因是它们未经批准包含在补充剂中。
****副作用:接触酚酞与致癌性(即能致癌)有关。
****(5)利多卡因 是什么?利多卡因是一种麻醉剂,这意味着当局部应用于皮肤或粘液表面时,它是一种用于麻醉特定身体区域组织的药物。它通常以局部产品(如喷雾剂、凝胶剂)的形式出现,由于其麻木作用而用于控制早泄。
****副作用:局部使用一般有轻微的副作用,如瘙痒/发红,但非法产品的潜在劣质可能会导致意想不到的过敏/过敏皮肤反应。
从这里,我决定更仔细地观察这些含利多卡因的产品,以证实我的假设,即所有含利多卡因的补充剂都是局部剂型。
含利多卡因产品的剂型分布
尽管最常见的剂型确实是用于局部应用的东西(外用液体),但我惊讶地发现还有相当多的口服胶囊和片剂产品也含有利多卡因,特别是因为利多卡因不是用于口服的。这当然是值得深入研究的事情。
接下来的步骤
该分析对新加坡非法保健品贸易中观察到的趋势和模式提供了一些见解,对 HSA 等当局可能有价值。例如,通过了解常见的营销主张和非法经销商针对的主要人群,可以开展专门策划的教育活动来提高认识,以便公众了解如何更好地保护自己。
能够访问更多数据(如购买地、生产国、报告日期、商贩/消费者概况等)。)也将有助于使这种分析在处理这种非法产品的兜售方面更有见地和有用。
收拾东西
事实上,区分真货和假货是非常具有挑战性的。因此,友好的药剂师会给你一些建议:一定要从注册的医疗机构、诊所和药店购买药物和保健品。如果您确实遇到有人兜售此类非法产品,您可以通过电话(6866 3485)或电子邮件((hsa_is@hsa.gov.sg)向 HSA 执法部门举报
期待听到大家对以上分析的反馈。如果您想要一份代码副本,或者希望讨论更多关于该审查的内容,请随时在 LinkedIn 上给我留言!
特别感谢 辜平雄 分享他对分析的反馈
在你走之前
欢迎您加入和我一起踏上数据科学学习之旅!点击此媒体页面,查看我的 GitHub ,了解更多精彩的数据科学内容。同时,享受探索药物和数据科学的交集吧!
参考书目
- 药物评估和研究中心。(未注明)。假药。检索于 2020 年 8 月 15 日,来自https://www . FDA . gov/drugs/buying-use-medicine-safety/fake-medicine
- 如何识别掺假、假冒、不合格的保健品,你该怎么做。(未注明)。检索于 2020 年 8 月 15 日,来自https://www . HSA . gov . SG/consumer-safety/articles/identify-掺假-假冒-不合格-健康产品
- 新加坡发现非法保健品。(未注明)。检索于 2020 年 8 月 15 日,来自https://www . HSA . gov . SG/在新加坡发现的非法健康产品
- 掺假、假冒伪劣保健品的危害。(未注明)。检索于 2020 年 8 月 15 日,来自https://www . HSA . gov . SG/consumer-safety/articles/launch-health-products
- 用中文创建 wordcloud。(未注明)。2020 年 8 月 15 日检索,来自https://a mueller . github . io/word _ cloud/auto _ examples/word cloud _ cn . html
探索图像处理技术— OpenCV
一些图像处理功能概述—利用 OpenCV-4.2.0 和 Python
由 Matteo Vistocco 在 Unsplash 上拍摄的原始照片
我图像处理是属于计算机视觉的知识领域。机器学习的前提首先由计算机视觉理论奠定,应用一整套技术来处理和分析图像数据,以提取计算机和机器可能用于广泛应用的有价值的信息,例如:
- 拼接:将重叠的照片变成无缝的全景图
- 变形:通过平滑过渡改变或合并不同的图片来创建新的图片
- 3D 建模:将 2D 快照转换成 3D 合成
- 人脸检测:识别数字图像中的人脸
- 视觉认证:坐在网络摄像头前,自动将家人登录到电脑或手机上
Vision 的预期目的是利用可靠的模型,从最简单和最基本的构建块中重建复杂、丰富多彩和生动的三维世界,这些模型有助于以可评估的方式解释图像。
预处理或图像处理是计算机视觉中的前一步,目标是将图像转换成适合进一步分析的形式。曝光校正、色彩平衡、图像降噪或提高图像锐度等操作非常重要,并且在大多数计算机视觉应用(如计算摄影甚至人脸识别)中需要非常小心才能获得可接受的结果。
在本文中,我打算利用一个非常流行的计算机视觉库 OpenCV 来介绍一些常用的图像处理技术。我将尝试简要描述每个操作是如何工作的,并更注重更实际地处理这个主题,为您提供您需要的所有代码,以便您对这些材料有亲身体验。
下面给出的图像将用于我们的实验。
概述:
- 计算机视觉中的图像
- 颜色:RGB 表示
- 像素转换
- 直方图均衡
- 回旋
我在 Google Colab 笔记本中包含了本教程的所有代码,因此您可以继续学习。
编辑描述
colab.research.google.com](https://colab.research.google.com/drive/1c9SsagyzeFWgWzN9O-dpj5m3Ds4qOT2F?authuser=1)
注意:在开始之前,您应该在 windows 中配置一个 Anaconda 环境来使用 OpenCV。对于那些可能没有所需设置的人,我建议你看看我的文章,关于如何在 windows 10 中用 python 安装和配置 OpenCV-4.2.0,否则这个教程的代码将无法使用。
计算机视觉中的图像
计算机视觉中的图像被定义为代表每个图像*像素中离散颜色或强度值的数字矩阵。*每幅图像都被视为可以以多种方式显示的输入数据,无论是像素值数组还是表示像素强度分布的多维图。图像可以用三个通道(蓝色、绿色和红色)的彩色分层、像素值从 0(黑色)到 255(白色)的灰度以及仅描绘黑色或白色值(0 或 1)的二进制渲染。
图像基本上是以不同强度显示的数字块:
像素化图像,致谢:从图像数据中提取特征的技术
颜色:RGB 表示
颜色空间由三个不同的通道红、绿、蓝表示。每个通道都源于人类视觉的所谓三色本性,因为我们有三个独立的感光体,每个感光体都选择性地对色谱的不同部分做出反应。在每通道 8 位的 RGB 系统中,三原色相加产生 16.777.216 种不同的颜色。
在 OpenCV 中,图像被转换成多维数组,这大大简化了它们的操作。例如,灰度图像被解释为像素从 0 到 255 变化的 2D 阵列。
原始图像的灰度版本
彩色图像稍微复杂一些,因为我们处理的是 3D 阵列,其中每个像素都在三个不同的颜色通道中呈现。将原始图像分割成蓝色、绿色和红色分量,以掌握颜色分层结构是如何工作的,这将是很有趣的。我们将使用两个基本的 OpenCV 方法来实现:
split(src, dests)
:分割多维数组。mixChannels(srcs, dest, from_to)
:合并不同的频道。
如果我们打印结果通道的形状,我们会看到尺寸已经缩小到 1。每个通道都包含图像的 RGB 分量,但它是以一维灰度格式渲染的。
将图像拆分到其通道中
将每个通道放大到 3D 阵列将呈现所需的颜色。对于这种用法,mixChannels()
派上了用场。
混合各个通道并显示蓝色、绿色和红色版本
像素转换
应用于图像的最简单类型的操作是那些输入是像素并且对应的输出也是像素的操作。点运算符,也称为点运算符,被建模为获取一个或多个输入图像并产生输出图像的函数。在连续域中,它们可以表示为:
对比度调整
例如,我们可以应用局部点运算符来调整图像的对比度和亮度。将单个图像像素乘以一个常数并添加一个偏差,将使图像或多或少变亮,对比度或多或少变得明显。
对比度调整示例
直方图均衡
绘制像素强度分布,我们可以看到一组图像的亮度值。因此,我们可以识别和纠正图像中最暗的区域,使其看起来更有吸引力。但真正的目标是找到一种自动化的过程,将光线均匀地分布在整个图像上,而不必逐个区域、逐个像素地检查。更明确地说,我们应用一个变换函数来将最频繁的亮度值均匀地分布在整个图像上。
首先,我们需要绘制一个直方图,显示颜色通道的强度分布:
直方图分析图
我们可以在直方图中区分两个区域:
- 区域 1 :连续且相当相等的值,没有突变。我们在 CDF 图中观察到这些值的线性增长。
- 区域 2 :图片达到高亮度值,这意味着范围值[230,250]被超过 62%的像素广泛表示。
直方图揭示了一个有趣的点:我们的图像的像素值被限制在某个特定的强度范围内(在 230 和 255 之间),因此图像在特定的中心区域更亮,而忽略了其他区域。
应用均衡公式有助于将受限区域拉伸到任一直方图边界。这些值被扩展到最大值,以改善图像闪电。
应用于原始图像的直方图均衡化公式
我们还可以查看新均衡图像的累积分布频率:我们观察到强度值的线性趋势增长,这意味着像素强度现在在图像中均匀分布,并且集中高强度 pic 的区域更有可能消失。
最后,我们可以在均衡的输出图像中看到,我们已经启发或揭示了在原始图像中暗淡的新区域。
回旋
卷积是邻域操作符的集合,其操纵局部图像区域以产生预期的变换。从数学上来说,卷积两个函数会产生第三个函数,表示一个函数的形状如何被另一个函数修改。在图像处理中,每个卷积都是用特定变换的特定核来实现的。
卷积例子有:
- 图像过滤应用 2D 卷积,采用各种低通和高通滤波器,帮助去除噪声、模糊图像等。
- 图像梯度使用高斯滤波器和特殊内核进行图像边缘和轮廓检测。这种核的例子是拉普拉斯导数、索贝尔导数、沙尔导数等。
图像渐变
图像梯度技术提供了关于图像组成的非常有用的信息。梯度图像的每个像素测量原始图像中相同像素在给定方向上的强度变化。有了像素信息,我们观察到具有大梯度值的像素成为可能的边缘。因此,一些众所周知的边缘检测算法,如 Canny 边缘检测器,广泛使用梯度图像来提取轮廓。
拉普拉斯衍生物
图像的拉普拉斯算子突出了快速亮度变化的区域,它被广泛用作边缘检测的前一步。操作员通常将单个灰度图像作为输入,并生成另一个灰度图像作为输出。
拉普拉斯导数适用于 2D 函数,在我们的上下文中,我们微分的函数表示输入图像的灰度版本的像素强度值。灰度图像可以被认为是提供像素强度输出的两个输入(x 代表宽度,y 代表高度)的函数。
灰度图像的 3D 绘图,原始照片由米卡在 Unsplash 上拍摄
拉普拉斯导数公式也可以表示为具有特定值的 3×3 核:
拉普拉斯解析公式
应用于原始图像的拉普拉斯滤波器示例:
拉普拉斯变换
灰度和彩色图像输入的拉普拉斯滤波结果
总结想法
在本教程中,我们遇到了一些有趣的图像处理技术,解释了一些背后的理论,并提供了利用领先的计算机视觉库 OpenCV 的实践经验。
OpenCV 有大量的在线 T4 文档,在那里你可以学到更多的计算机视觉技术,比如特征检测和描述、计算摄影、物体检测等等。大部分内容以很酷的教程的形式呈现,鼓励你快速开始编码,并以一种有指导意义的方式体验这些材料。
我还建议你阅读 计算机视觉应用与算法 *,*这是一本全面的书籍,它从科学和数学的角度很大程度上解释了所有关于图像处理和计算机视觉的理论。
另一篇值得一读的有趣文章是什么样的图像处理技术实际应用于 ML 行业在Neptune . aiby**Aigiomawu Ehiaghe。它说明了如何利用图像处理技术来增强现实世界行业中的计算机视觉模型。**
** [## ML 行业实际使用的图像处理技术有哪些?- neptune.ai
处理可用于提高图像质量,或帮助您从中提取有用的信息。这是…
海王星. ai](https://neptune.ai/blog/what-image-processing-techniques-are-actually-used-in-the-ml-industry)
如果你喜欢这篇文章,请随时与你的朋友分享。可以看看 Github 里的项目。
图像处理操作利用 OpenCV-4 . 2 . 0-aymanehachham/Image-Processing-OpenCV
github.com](https://github.com/aymanehachcham/Image-Processing-OpenCV)**
探索基巴纳
可视化和分析 Elasticsearch 索引日志文件的通用工具
这些年来,基巴纳越来越受欢迎。大多数公司依靠 Kibana 强大的可视化和分析功能从每分钟生成的大量日志结果中进行推断。web 服务器生成日志包含有关系统使用、请求时间、请求位置和搜索字符串的重要数据。Kibana 使得实时分析这些数据变得轻而易举。
Kibana 是一个开源的、基于网络的数据可视化和分析工具。它可视化了由 Elasticsearch 框架索引的搜索输出。它是由 Elasticsearch 和 Logstash 以及 Kibana 组成的 ELK 堆栈的一个组成部分。Elasticsearch 是一个开源搜索引擎,用于实时存储、搜索和分析大量数据。它是几个搜索框架的核心。Logstash 用于收集和监控来自不同来源的日志。它充当了 Elasticsearch 的数据管道。这三个工具一起提供了一个伟大的开源平台来监视、可视化和分析日志数据。
本文旨在回答一些关于 Kibana 的常见问题。首先,有没有想过这个独特的名字“基巴纳”是如何产生的?基巴纳的创造者拉什德·卡汉想用外语为他的创新取一个朗朗上口的名字。他想选择一个与该应用程序功能相近的名称,即可视化日志数据。他认为最接近的是“木屋”,谷歌在斯瓦希里语中将其翻译为“基巴纳”。
为什么是基巴纳?
开源且易于设置 : Kibana 的 USP 在于它是开源的且易于设置/使用。每个人都可以轻松设置和访问仪表板和报告。您只需要将您的 web 浏览器指向 Kibana 设置为默认端口 5601 的机器。它也可以托管在其他端口上。
强大的可视化 : Kibana 提供了大量的可视化选项,如图表、直方图、条形图、热图、饼图和区域图,以快速分析数据。Kibana 的数据可视化能力不仅仅局限于数字。它还可以分析不同的数据类型,如文本和地理空间数据。
交互式仪表板:Kibana 的仪表板特性使报告和演示变得非常简单和吸引人。可视化可以根据添加的过滤器进行定制,从而使仪表板具有交互性。对数据的任何更改都会自动反映在仪表板中。
**报告:**使用 Kibana dashboard 可以生成详细而有见地的报告。该报告可以下载并以不同的格式使用,如 PDF 和 DOC。
基巴纳的积木
Kibana 的功能分为两大类:
可视化和探索数据 —在这里您可以找到各种工具来探索、分析和可视化数据中的模式。您也可以创建仪表板和演示文稿。你也可以为分析后的数据建立机器学习模型。Canvas 是 Kibana 中另一个很棒的可视化和表示工具。它可用于可视化来自 Elasticsearch 的实时数据。Data Visualizer 是基本 Kibana 许可的一部分,它为生成的最大 100 MB 的日志提供异常检测功能。这有助于了解更多的数据,并以自动化的方式发现异常。
管理弹性栈 —在这里可以配置安全设置,实时监控和跟踪弹性栈,组织工作空间。通过控制台,用户可以向 Elasticsearch 发送请求,并查看他们的请求历史。您可以使用索引模式 UI 创建新的索引模式和管理现有的模式。基于索引模式,将从 Elasticsearch 检索数据。
基巴纳积木
在数据操作和导出数据方面,Kibana 几乎没有限制。它不能聚合包含嵌套对象的字段。从保存的仪表板或报告中导出数据时存在约束。只能导出屏幕上当前可见的数据。
**总体来说,Kibana 是一个用户友好的工具,可以快速可视化和分析巨大的日志文件。**开源和配置为与 Elasticsearch 合作增加了它的受欢迎程度。Kibana 正在不断升级,并承诺未来会有更多有趣的功能。有关基巴纳的详细信息,请参考,https://www.elastic.co/kibana
探索肯尼亚信用评分农民的机器学习解决方案
图片由 SunCulture 提供,经许可使用。
介绍
本文涵盖了 SunCulture 的一个数据科学项目,SunCulture 是一家位于肯尼亚内罗毕的太阳能初创公司,为小农户提供太阳能水泵、电视和照明。
它强调了我们在项目中探索的不同途径,哪些可行,哪些不可行,以及我们在这个过程中学到了什么。我们希望本文包含的信息能够帮助其他在非洲从事融资工作的公司从我们的经验中学习。
非洲的小规模农民通常被传统银行部门忽视,他们的融资选择非常少。但是怎么知道他们会不会还钱呢?传统的信用评分使用过去的信用记录作为主要指标,但对于许多 SunCulture 客户来说,这些信息是不可用的。
我们提议使用物联网 (IoT)数据源的组合,包括土壤传感器和灌溉系统的水泵使用情况,这些数据已经可供该公司使用。
在 7 个月的时间里,我的任务是探索使用传感器数据进行信用评分的解决方案。这个想法是创建一个算法,根据他或她的物联网传感器数据作为输入,输出农民违约的概率。用专业术语来说,这是一个时间序列分类的问题。
时间序列数据一直是金融服务的主要兴趣,现在随着实时应用的兴起,零售和编程广告等其他领域正在将注意力转向时间序列数据驱动的应用。
在过去的几年中,云服务的几个主要参与者已经发布了用于处理时间序列数据的新产品。因此,理解机器学习在这个新兴领域中的作用和潜力是非常有趣的。
我们将谈论一个失败的第一次尝试,它引导我们探索解决方案空间。随着试验数量的增加,我们发现需要组织试验和管理数据集。最后,在任务结束时,我们意识到处理我们数据的一个显著特征的重要性:低违约率。我将从介绍农民开始,因为我很幸运地认识了他们,并进行了实地考察:
会见农民
当我联系到 SunCulture 的首席技术官和联合创始人查尔斯·尼科尔斯时,我正在网上寻找任务。
由于我当时恰好在肯尼亚,他们邀请我参观他们的总部,并派我去 Nanyuki 山区会见他们的一些客户(农民)。
此次实地考察的目的是了解农民面临的问题,并寻找技术解决方案。
亚历克斯·吉托帮助我了解了农业的基本知识。农场成功的五大标准是:
- 土壤肥力,由存在的养分来衡量
- 水特征
- 水的供应量
- 土地的面积
- 种植作物的市场价值。
我和一位新农民 Patrick Ngetha 聊了聊,他向我解释了他在 T2 的财务问题。市场价格和收获结果都存在很大的可变性和不确定性,例如由虫害造成的。这些收入在一年中不定期地出现几次。
他经营的主要成本是购买种子和支付工人工资。农民需要从公司购买高质量的种子,否则蔬菜找不到市场。
工人们需要在收获季节,也就是销售收入到来之前得到报酬,这进一步增加了现金流的压力。
他有潜力扩大自己的农场,目前农场面积约为半英亩,但他缺乏资金。
他试验了不同的作物,我的向导、阳光栽培农学家亚历克斯·吉陶(Alex Gitau)相信他有必要的技能将他的活动提升到一个新的水平。然而,他无法从银行获得资金。
实地考察并意识到该项目具有巨大的社会影响潜力是非常有意义的。所以我很有动力从我们的第一个模型开始。
我们的第一个架构:TimeNet 有一个艰难的开始
TimeNet 是由 P. Malhotra 等人在论文中介绍的深度学习模型: TimeNet:用于时间序列分类的预训练深度递归神经网络。这是一种无监督的方法,通俗地说,它将原始传感器数据转换成一个更小的汇总向量。然后,我们使用经典的机器学习方法,在我们的情况下是逻辑回归,来基于概要进行最终分类。
听起来很简单?嗯,我们有很多问题。
最主要的一点是让模型真正收敛。经常发生的是,在一次进行得相当顺利的训练中,模特突然“忘记了一切”。这在训练递归神经网络时相当频繁,被称为梯度爆炸**。**
为了解决这个问题,我们尝试扩展我们的计算能力,并开始在多个 GPU 实例上进行培训。在整整一个月和大约 15 次实验后,我们最终放弃了,因为这种设置无法正确地对单个贷款违约进行分类。
为什么 TimeNet 不起作用?这是一个太多的研究模型,没有足够的数据可供学习。
探索解决方案空间
然后,我们继续使用深度神经网络在时间序列分类中提出的模型:来自王志广等人的强大基线。这是一篇更受欢迎的文章(从大约 500 次引用中可以看出),并且附带了它所介绍的三个模型的代码。此外,训练他们的模型非常简单,这意味着收敛会很容易。
我们用他们的 ResNet 模型开始训练。不幸的是,第一次运行没有成功,很可能是由于我们的时间序列的长度。它们比文章中探讨的大多数都要长。
像 ResNet 这样的卷积模型有一个“模式大小”,称为感受域**,这是它们可能检测到的最长模式的长度。例如,如果感受域是 24 小时,那么模型只能检测每天的模式,而不能检测每周的规律性。**
根据我们的数据,默认 ResNet 的感受域出现了几十个小时,我们觉得这是不够的。这被证明是关键的观察。我们最终通过将感受野扩大到大约一周(使用步长卷积**)得到了我们的第一个工作模型。**
该模型比虚拟基线的特异性高 4 倍。
接下来的一段时间,从 11 月到 1 月,我们致力于尝试尽可能多的不同架构。
我们尝试了 14 种不同的模型。有些是最先进的,如《创意时代》中的,有些是更经典的机器学习,如 DTW ,有些是我们自己的,如 ConvRNN。
ConvRNN 的层结构显示卷积输出直接进入 RNN 层。
ConvRNN 是一种深度学习架构,由几个卷积层后跟一个递归层组成。所有图层都按顺序排列,如上图所示。这是一个端到端的模型:输入是传感器数据,最后一层的输出是默认概率。
卷积以时间序列作为输入,也产生时间序列输出。输出长度除以步距,例如步距为 2 将使长度减半。
由于存在多个连续发生的步长,我们可以实现时间序列长度的极大缩减。在某种意义上,卷积充当了智能“子采样器”。
如果传感器数据时间序列很长,这种尺寸的减小是很重要的。递归神经网络只能在短时间序列上有效训练,数量级为几百个点。
如果在原始数据上训练,递归层会发散,因此需要卷积来缩短时间维度。
最后,我们注意到这个模型的感受野理论上是无限的。这是循环层的优势,它能够在长范围内组合和积累信息。正如在 ResNet 模型讨论中提到的,一个大的感受域允许我们在不同的时间范围内理解农民的行为——从几天到几个季节。
该模型在深度学习架构中具有最佳性能。最初的想法来自查尔斯·尼科尔斯。到目前为止,我们还没有在文献中看到这种架构。
选择建筑的教训是不要犹豫尝试新事物。即使有些事情以前没有做过,它仍然可以很好地工作。尽管为了简单起见,您可能仍然希望用现有的模型开始您的项目。****
组织实验
对于 14 种架构中的每一种,我们都有不同的运行和配置。在项目结束时,单独运行的总次数很容易就达到了数千次。数据科学项目的很大一部分是有效地管理它们。
每个实验,不管它是否带来了度量的改进,都需要被记录下来,以便我们可以用它来指导我们未来的决策。其中一些实验非常长,长达几天,因此我们保存了所有的关键信息,以避免以后必须重新运行。
起初,我使用了一个在线维基系统来记录实验。有一次,我们意识到我的很大一部分时间花在了记录实验上,而不是运行它们。然后我们开始使用一个名为重量&偏差的网站,这使得整个过程更有效率。
摘自《重量与偏见》的截图。在这里,我们可以看到不同模型的训练曲线绘制在同一时间线上。适用于绘图比较。
在这一点上还有其他问题:处理不断到来的新数据,版本化数据集,保持高数据质量。主要问题是必须管理两件事:快速做事,这需要固定的流程;探索解决方案空间,这需要灵活性。
管理数据
我们将讨论数据管理的三个主要方面:数据版本化、数据处理和数据调试。
数据版本控制或 DVC 是管理数据集的一个非常有用的工具。它允许控制数据的修订,就像 git 控制代码一样。
每次更新都对应于一个版本**,并保留所有先前版本的可访问记录。如果数据集更新被证明是一个错误,我们可以恢复以前的版本并取消更改。所以这是一种安全网。**
在分布式环境中工作使得数据管理更加困难。在我们的例子中,我们同时使用了多达 5 台不同的机器,包括笔记本电脑和云实例。
这在机器学习中很常见,其中大部分工作是在本地机器上完成的,但训练是在强大的云实例上完成的。挑战在于同步数据。
我们建立了一个 Azure blob 存储,通过 DVC 将所有数据集中起来。这就像一个在线硬盘,只要你有凭证,就可以从任何地方访问。它不仅存储当前数据,还存储所有修订数据。这个 blob 存储器实际上是我们的数据库**。**
这允许机器通过使用推拉系统来同步和修改全局库。通常情况下,一旦工作机处理完一个数据集,就会将其更新推送到中央存储器。然后,一台训练机器可以从中央存储器中取出最新数据。DVC 使这些操作变得非常容易。
我们使用脚本进行数据处理,因为它们比笔记本更容易重用和维护。像任何 UNIX 实用程序一样,可以从命令行直接调用脚本。它们执行“文件到文件”操作,即输入数据集和处理后的数据都写入磁盘,这简化了调试。
对于数据调试**,我们意识到单元测试是不充分的。如果我们不实际绘制数据,很容易遗漏数据错误。同时,数据测试对于检查数据的基本健全性是有用的,比如异常值和格式。因此,我们决定同时使用正式测试和可视化。**
这方面的自然工具是 Jupyter 笔记本。每个都包含一些图,如分布直方图,并显示随机选择的传感器几天的数据。这非常有用,因为许多错误都可以通过视觉检测出来。它还将显示基本的统计数据。从某种意义上说,笔记本可以作为特定资产的快速“身份证”。
笔记本也会包含测试。通常,我们测试数据中的异常值和可能的问题**。如果我们期望数据有一个特定的属性,例如平均值是 0,那么我们为它编写一个测试。**
测试的有趣之处在于它们对整个数据集进行操作,而可视化通常只能显示一些样本的详细信息。
最后,笔记本会自动转换成 Markdown 格式,然后保存到 Wiki 作为文档。通过使用 Papermill 动态执行笔记本,这一过程可以变得非常高效。
处理低违约率
在项目的最后两个月,我们关注的是贷款违约数量的问题。在有数千个客户的测试集中,我们的违约数量只有几十个。这是一个不同于阶级失衡的问题,阶级失衡是一个低的违约比率,这里是一个低的总比率 r。然而,我们有很多非违约可用。
深度学习需要大量高质量的数据。很有可能这些模型在默认数以千计的情况下会做得更好。深度学习大放异彩的所有例子,比如图像识别,都使用了庞大的数据集。
我们探索了日复一日的训练**。该模型的想法是查看一天的数据,并输出它是属于好的还是坏的付款人。在对每一天进行预测之后,我们汇总输出结果,给出一个全局分数。**
这项技术的主要优势在于,对于每个农民,我们都有很多天的数据。本质上,默认值的数量乘以数据集中的天数。所以我们有数千个默认值,非常适合深度学习。
不利的一面是,一天的数据可能包含的信息不足以对一个农民进行评分。
由于优秀支付者的数量很大,该模型在检测优秀支付者方面非常出色。它达到了 99%的特异性(相对于虚拟基线的 95%)和 30%的灵敏度。
该模型显然能够识别大多数严肃农民的日常习惯。
很难“凭直觉”确定谁会还钱,谁不会。
时间序列数据显示,一些用户只用了一次泵,但总是按时还款。类似地,一些人在很长一段时间内非常有规律地使用他们的泵,然而却违约了。
我们与初级数据科学家 Justin Nguyen 一起分析了主要的统计变量,如泵的平均使用量和方差。
我们总共考虑了 36 个特征。令人惊讶的是,这些基本特性带来的分类能力非常小,与虚拟基线相比,性能仅提高了 30%。
这意味着好的付款人和坏的付款人之间的区别来自使用模式,这些模式太微妙了,无法用简单的特征工程来描述。
这里的农民是在特征空间中绘制的。我们很少观察到好的和坏的付款人之间的区别。这两个轴是 PCA 的两个第一主分量。
这项数据研究还揭示了一个有趣的模式:没有填写某些调查字段的农民更有可能违约。换句话说,缺失的信息可以改进预测模型,并且应该明确地添加到模型中。
我们也探索了不同的选择,比如自我监督学习。这是一种使用数据而不使用标签(默认或非默认)的方法。因为标签数量是我们的问题,这是一个很好的选择。它在训练时间和表现稳定性方面给了我们有趣的结果。
最后,自我监督学习和日复一日的训练都有给我们更稳定的指标的优势。由于模型是在数万天内评估的,它们的表现在统计学上非常有意义。这一点非常重要,尤其是当这些模型即将投入生产时。
我们学到了如何处理少量违约:使用专业技术,比如自我监督学习。
结果
我们试图通过与基线比较来解释我们的结果的质量,然后展示如何应用它们。
评估机器学习项目始终是一个挑战。度量值本身没有什么意义,因为它们依赖于所使用的数据。例如,90%的准确率可能是非常好的表现,也可能是非常差的表现,这取决于任务的难度。
为了给出更准确的描述,我们通过将我们的模型结果与总是预测相同类别的简单的基线模型进行对比来讨论我们的模型结果。
这样,我们就有了一个好的付款人基线,无论他的传感器读数如何,都可以预测农民会还钱。类似地,不良付款人基线总是预测违约。
注意,一些读者可能会反对这个基线太简单。然而,仅仅从传感器数据来看,很难检测出“不良付款人”,所以这种天真的基线实际上接近人类水平的表现。此外,我们还尝试了稍微复杂一点的基线,但它们的表现并不明显。
我们根据模型的特异性和敏感性来评估模型。特异性是真阳性与所有阳性的比率。直观上,高特异性(接近 100%)意味着很少有错误警报。
敏感度是所有真实病例中真实阳性的比率。直觉上,高灵敏度意味着很少有假阴性。
不良付款人分类结果
好的付款人分类结果
我们可以将这些结果用于信用归属**。显然,信用归因的风险随着违约概率和贷款金额的增加而增加。**
全球违约概率为 5%,但这些模型能够发现这一概率明显更高或更低的农民群体。然后,我们可以使用这些信息相应地调整贷款金额。
举个例子吧。如果好付款人分类将一个新农民归类为“好付款人”,那么我们知道违约的可能性是 1%(因为特异性是 99%),比其他人低 5 倍。
有了这些信息,我们知道风险很低,给这个农民更多的贷款是有意义的。当然,发放更多的贷款会给公司带来更大的回报。
同样,我们可以使用不良付款人模型来减少被检测为“不良付款人”的农民的贷款金额,这反过来会减少我们的平均损失。
贷款增加或减少会影响多少农民?由于敏感度在 30%左右,我们可以估计它会影响大约三分之一的客户。
结论
给小规模的非洲农民提供贷款将会使他们经济增长并改善他们的生活。这部分人在非洲大陆的总人口中占很大比例,因此很多人可能会从这种解决方案中受益。
根据传感器数据进行信用评分非常困难,因为这些数据只能间接预测金融稳定性。数据的特殊性是时间序列长,违约次数少。建立有效的解决方案必须考虑所有这些方面。从该项目中获得的三个主要的技术成果是:
- 从代码附带的流行研究文章开始
- 花时间找到一种有效的方法来组织实验,从数据准备到记录
- 了解了数据的主要特征后,最先进的模型只能在与它所针对的数据相似的数据上表现良好。
我感谢阳光文化和查尔斯·尼科尔斯给我这个机会,并希望这份报告对其他人有益。我们也对微软 Airband 给予我们免费的 Azure 点数表示感谢。
探索 Python 列表的方法和技巧
Python 列表非常强大💪🏻
上周,我开始复习 Python 的一些基础知识,并决定创建一个存储库,在那里为我修改的所有内容创建一个笔记本。这将使我能够在将来任何时候再次回顾某些话题时,都能够找到一个单独的地方。
上周我浏览了 Python 列表,这篇文章包括了笔记本中的一部分内容,突出了 Python 列表的关键功能。当我有机会浏览其他主题时,我会继续创作这些笔记本。
该库包括一系列 Python 笔记本,描述了 Python 中几种有用的方法和技巧。…
github.com](https://github.com/kb22/python-notebooks)
反转列表— []
我们可以在 Python 列表中使用[]
操作符的第三个参数来返回一个反向列表。当我们将第三个值设置为-1
时,我们指示我们希望在从末尾走一步的同时遍历列表,因此是反转的列表。
long_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]long_list[::-1]# Output
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
数字列表—范围()
我们使用range()
方法来实现这一点。它需要三个参数,起始值、最终值和每个值之后的增量步长。例如,如果第一个值是 0,增量步长是 10,则列表中的下一个数字是 10,然后是 20,依此类推。默认情况下,range()
不生成列表,所以我们使用list()
方法生成完整的列表。
list(range(0, 100, 10))# Output
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
具有相同值的列表— *
假设您想要为每个元素创建一个具有相同值的列表。您可以从单个元素列表开始,然后将列表乘以您希望该元素在新列表中出现的次数。
["element"]*5# Output
['element', 'element', 'element', 'element', 'element']
元素到字符串 join()
我们可以将列表中的各种元素组合成一个字符串。我们使用join()
方法,并向它提供包含我们的元素的列表的名称。接下来是一个字符或一个字符列表,列表中的每个元素都要通过这个字符或列表进行连接。在下面的例子中,我用一个空格组合了各种元素。
separated_list = ["I", "am", "Karan"]
" ".join(separated_list)# Output
'I am Karan'
对列表进行切片— slice()
我们可以使用slice()
方法来分割一个给定的列表,并从原始列表中检索某些元素。它需要起始索引、结束索引和增量步长。它也可以接受负的索引值。这个例子展示了我们如何检索一个 10 元素列表的最后 5 个元素。
long_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]last_five_elements = slice(-5, 10, 1)
long_list[last_five_elements]# Output
[6, 7, 8, 9, 10]
排序—排序()
列表有一个内置的函数,允许我们对列表中的元素进行排序。我们也可以将reverse
参数设置为 True 来反转列表。
long_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]long_list.sort(reverse = **True**)# Output
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
检查列表中的存在性— any()与 all()
假设您有一个布尔列表,您想检查其中的True
值。如果你想检查是否至少有一个True
值存在,你可以使用any()
方法,但是如果你想检查所有的值都是True
,我们使用 all()。
arr = [**False**, **False**, **False**, **True**, **False**, **False**]print("At least one True - **{}**".format(any(arr)))
print("All True - **{}**".format(all(arr)))# Output
At least one True - True
All True - False
使用索引进行迭代—枚举()
enumerate()
方法允许我们迭代 Python 列表,同时提取每一步的值以及元素的索引。
long_list = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]**for** index, value **in** enumerate(long_list):
print("At index **{}**, value is **{}**".format(index, value))# Output
At index 0, value is 10
At index 1, value is 9
At index 2, value is 8
At index 3, value is 7
At index 4, value is 6
At index 5, value is 5
At index 6, value is 4
At index 7, value is 3
At index 8, value is 2
At index 9, value is 1
检查列表中元素的存在性—在
我们使用in
操作符来查看一个元素是否存在于列表中。我们也可以使用not in
来检查一个元素是否不存在。
long_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]1 **in** long_list
1 not **in** long_list# Output
True
False
空列表检查—不是
我们可以在列表前面使用not
操作符,如果它返回 True,我们知道列表是空的。
empty_list = []**not** empty_list# Output
True
对每个元素的操作— map()
我们使用map()
方法在列表的每个元素上应用一个给定的函数。这里,我定义了一个使输入值加倍的函数。
long_list = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]**def** double(x):
**return** 2*x
list(map(double, long_list))# Output
[20, 18, 16, 14, 12, 10, 8, 6, 4, 2]
迭代多个列表— zip
zip()
方法允许我们遍历多个列表,其中返回的对象是一个元组列表,其第一个元素属于第一个列表,第二个元素属于第二个列表。
list_1 = [1, 2, 3, 4]
list_2 = [5, 6, 7, 8]**for** item1, item2 **in** zip(list_1, list_2):
print("**{}** - **{}**".format(item1, item2))# Output
1 - 5
2 - 6
3 - 7
4 - 8
结论
Python 列表非常强大,即使没有其他像numpy
这样的高级库也可以非常有效。
我希望你喜欢我的作品。如果您有任何问题、建议或意见,请告诉我。
探索模型不可知解释的方法
我们预览了几种模型不可知方法背后的思想
米歇尔·卡塔利萨诺在 Unsplash 上的照片
在你的模型中建立信任的一部分可以归结为简单地理解它的工作方式。可解释性允许我们看到模型的结果以及为什么做出预测。很多时候,这些方面可能会因为一些建模策略的复杂性而丢失。
在这里,我回顾了一些关于一些模型不可知的可解释性方法的背景,包括部分依赖图,H 统计量成对交互作用,和 SHAP。这篇文章是对我和他人参考的概念的直觉练习。
讨论的主题:
- 部分依赖图
- H-统计成对相互作用
- SHAP
部分依赖图
低维可视化通常提供了一种获得直观见解的便捷方式。部分相关图(PDP)是一种与模型无关的方法,用于显示特征和预测之间的关系。通过回顾 PDP 的趋势,您可以开始梳理对功能行为的理解。
PDP 通常通过散点图显示连续变量,通过条形图显示分类变量。回归部分相关值是预测结果;对于分类,预测值可以作为概率。
PDP 的工作是将我们感兴趣的特性(通常是一个或两个)的结果在指定的值范围内进行平均。我们本质上忽略了一组特性,以确定我们感兴趣的另一组特性的依赖性。然后,我们通过用平均预测值绘制我们感兴趣的特征值来构建 PDP。
为了更深入地了解 PDP,我们参考了以下内容:
f 是模型。部分相关函数 fxs 是特征集 xs 上的模型。x 是感兴趣的特征, xc 剩余的补码。它们一起构成了完整的特征空间。Pc(xc)是 xc 的概率密度
由于我们无法对 xc 的所有值进行积分,我们可以通过对给定数据集取平均值来进行估算。上面的等式简化为:
从概念上讲,我们基本上保持感兴趣的特征 xs 不变,并在补集 xc 的所有其他组合上找到预测。我们对提供的数据集中的所有预测进行平均,以获得该实例的部分相关值。在我们为 xs 中指定范围的值生成每个实例的平均值之后,我们可以图形化地生成 PDP。
特征 3(绿色)显示,对于等于 0 的基于网格的实例,所提供数据集的平均预测值为 0.11。特征 3 的总体行为显示出与模型预测的负相关,而特征 2(蓝色)通常显示出不相关。
值得注意的是,PDP 本身可能容易产生误导性结果。因为我们在寻找平均值,所以可视化丢失了一些关于构成平均值的数据分布的信息。例如,可能存在可以指示不同依赖行为的看不见的聚类。该信息不会被 PDP 捕获。
为了阐明这种隐藏的信息,个人条件期望(ICE)图通常与 PDP 相结合,以审查每个观察的预测。ICE 图基本上是 PDP 中生成的图平均值的独立分解。
在这里,我们看到两组观察结果:一组为负相关,另一组为正相关 PDP(蓝色)没有捕捉到的行为,这本身表明没有相关性
成对相互作用
特征交互效应是模型解释的另一个重要方面。某些变量对模型输出的影响会随着其他变量的值而变化。
我们有办法捕捉这类信息。例如,在线性回归、实验设计和方差分析的背景下,采用特征多项式是检测交互作用的一种常见且强大的方法。
这里的线性模型有两个特点:
通过包括第四项多项式作为成对相互作用,我们可以考虑模型对相互作用效应的依赖性。β 系数的大小和符号提供了相对重要性和贡献的指示。
线性模型具有很强的可解释性,您可以通过系数了解输出相对于要素的变化情况。然而,当模型性能不足且不满足严格的线性模型假设时,我们可以寻找替代的、模型不可知的方法来研究成对相互作用。
下面,我们讨论双向 PDP、SHAP 依赖图和交互作用的 H 统计量。
**部分依赖图(双向)😗*视觉上,上面讨论的 PDP 可以扩展到单个特征之外。相反,如果我们对两个特征感兴趣,我们可以在三维空间中显示多变量图,或者使用如下所示的等高线图来检测相互作用。这些视觉效果很好地表现了特征和模型预测方面的行为。
双向交互的样本图,其中一个特性值的变化会影响另一个特性值的变化
SHAP 依赖图:基于 SHAP 的依赖图类似于 PDP 的依赖图,因为我们能够使用视觉来显示特征和预测之间的行为。每个点都是一个观测值,相应的 SHAP 值是该特征对给定预测的附加贡献。
关于 SHAP 的更多细节将在下一节讨论。
左边的图显示了特征 1 对预测的总体正关系,具有轻微的相互作用传播。右边的图显示了特征 1 和 4 之间的相互作用(或缺乏相互作用)。在某些情况下,我们看到特征 4 中较低的值通常会产生稍强的信号
H-statistic :对比视觉方法,我们也可以使用 Friedman H-statistic 通过计算找到特征交互的实际度量。这种与模型无关的方法建立在 PDP 奠定的基础之上。统计量基本上是通过将特征对的部分相关(PD)函数(其中 PD 方程中的 xs 由相互作用对组成)与其各自的单独 PD 函数进行比较来确定的。
H 统计量为每个特征对产生一个介于 0 和大约 1 之间的分数。值 0 对应于没有交互作用,值 1 意味着输出变化仅强烈依赖于感兴趣的特征之间的交互作用。
使用上面的部分相关性 f 可以将 H 统计检验描述如下:
一个比较直观的观点是,如果该对的 PD 函数完全由各个 PD 函数部分的总和来解释,则不存在相互作用。相反,如果 PD 函数的某些部分不能用和来解释,那就表明了两者之间某种程度的相互作用。因此,该方法观察特征 A 的贡献、特征 B 的贡献以及 A 和 B 之间交互的任何附加贡献
一旦我们将上面的测试应用于我们感兴趣的特征的每个组合,输出可以帮助显示交互的相对强度。如果我们有合理数量的这些特性,我们可以选择使用表格或图表(如条形图和带注释的热图)来更直观地表示这些指标。
H-统计得分表:我们看到特征 1 和 3 具有很强的相对相互作用
虽然 H-统计量适合于检测特征关系,但是它的一个局限性是这些度量不能提供关于感兴趣特征的重要性的很多信息。强相互作用不一定意味着对模型的全面影响。作者 Friedman 和 Popescu 在第 8.1 节中提到了通过修改上述方程来解释这一点的方法。
SHAP(沙普利加法解释)
最后,SHAP 是一个更突出的和新兴的工具来处理模型解释。SHAP 值将重要性和贡献描述为要素对结果贡献的总和。特别是核 SHAP 方法产生了一种模型不可知的方式来解释为什么模型会做出某种预测。下面是一个温和的讨论 SHAP 的概念。
Shapley 值: SHAP 以 Shapley 值为基础,它起源于合作博弈论。Shapley 值寻求按特征公平分配预测贡献。其应用的一个常见例子是想象一个需要容纳不同飞机的机场。因为每架飞机需要不同长度的跑道,Shapley 值将是一个解决方案,根据相对要求在它们之间合理分配总成本。
Shapley 值可描述如下:
n 是特征的数量。s 是没有感兴趣特征 I 的子集特征。f 是模型
要打破上面的等式:
- A 项是当引入 i 时,每个个体特征 i 对总体预测的贡献。
- 术语 B 通过在引入特征 i 之前和之后将特征组合在一起的不同方式来衡量术语 A。B 中的第一项是出现在 i 之前的特征排列,第二项是出现在之后的排列。
- C 项是所有特性的平均值。
最终值是感兴趣的要素对结果的平均边际贡献。
在高层次上,Shapley 值背后的概念是通过比较将感兴趣的特征引入模型时所有可能排序的结果变化来捕捉重要性和贡献。
SHAP: 耦合特征独立性和局部模型线性的进一步条件,可以使用核 SHAP 来近似 Shapley 值。这种模型不可知的方法建立在 Shapley 值和在 LIME(局部可解释模型不可知解释)方法中发现的局部线性回归替代模型的基础上。
LIME 使用以下方法获得模型的局部解释:
l 是最小化损失函数。 f 是型号。 g 是训练好的线性解释模型。g 是更简单的、潜在可解释的模型族。πx 是局部加权核,ω是惩罚项
内核 SHAP 使用经典 Shapley 值的属性来生成 l、ω和πₓ的特定形式,作为恢复 Shapley 值的一种方式。非常整洁!
值得注意的是,SHAP 中的加权核πₓ将重点放在具有很少特征的子集以及具有几乎所有特征的子集上。直观地说,这是有意义的,因为您通过排除该特性(几乎完整的子集)或排除除该特性之外的所有特性(小子集)来捕获关于特定特性行为的更多信息。有关更深入的回顾,请参考第 3 节和第 4.1 节。
从概念上讲,每个特征的贡献通过采样特征子集来近似。基于所提供的训练数据,那些不存在于子集中的值被普遍的值所替代——基本上是每个特征的值的扰动。从不同特征子集的这些值中生成模拟目标数据,并如上所述进行加权。局部和加权线性回归模型在该数据上被训练为解释模型,其中最终系数对应于 SHAP 值。
总的来说,这种近似试图提供一种将上述概念应用于任何模型或函数的方法,这种方法在计算上比经典的 Shapley 值更实用,同时也减轻了使用 LIME 时出现的不太直观的行为。从这里开始,SHAP 值可用于描述行为的各种方式,使用相关的可视化,如下例所示。
力图:我们可以局部看到每个特征对单个预测的每个附加贡献。如果我们把每一个观察看作是整体总结的一部分,我们就可以在每一个预测中汇总这些观察。
SHAP 值汇总:图表显示了按重要性排序的特征,以及为每个观察点绘制的所有 SHAP 值。这可以给出特征效果的更全面的感觉。在下面的示例图中,要素 1 的高值通常对预测有相当积极的影响,而低值则有负面影响。
摘要
最后,我们看了几个模型不可知的策略,这些策略有助于解释模型,否则这些模型会太复杂而无法直接解释。这些方法包括:
- 部分依赖图(PDP)与个别条件期望图相结合,有助于提供对功能行为的一些视觉理解。我们研究了单个和成对 PDP,以了解依赖性趋势
- 接下来,我们在 PDP 奠定的基础上计算 H 统计量,这是一种检测和探索成对交互的方法
- 最后,我们讨论了 SHAP 及其整合的 Shapley 值和石灰方法,以了解功能的重要性和贡献
参考文献
[1] Lundberg,s .,& Lee,S.-I. (2017)。“解释模型预测的统一方法.”
[2]杰罗姆·弗里德曼(1999 年)。“贪婪函数逼近:梯度推进机.”
[3]弗里德曼、杰罗姆 H .和波格丹一世 E .波佩斯库(2008).“通过规则集合的预测学习.”
[4]克里斯托弗·莫尔纳尔。(2019)“可解释的机器学习。让黑盒模型变得可解释的指南。”
探索移动互联网:从纸张到 Keras
拆卸 MobileNets,看看它们是如何轻便高效的。
MobileNets 是流行的架构,用于图像分类、人脸检测、分割等等。它们在移动和嵌入式设备上的延迟是众所周知的。你可以从名字*“MobileNet”*中推断出这一点。由于使用了可分离卷积,它们的可训练参数数量少得多。如果你正在运行一个使用相机实时帧的图像分类模型,你可能需要一个快速、准确、占用移动设备更少内存的模型。
移动互联网的使用。来源。
今天,我们将从它的研究论文到 TensorFlow ( Keras!).在进一步阅读这个故事之前,你可以确保以下几点,
- 如果你对可分卷积的概念不感兴趣,请阅读“ 可分卷积基础介绍 ”。
- Colab 笔记本 仅包含 MobileNet V1 的 TF 实现。
- 所有用粗体和斜体书写的术语,如 【示例】 都可以直接在研究论文中找到。
- 你可能会在 GitHub 的tensor flow/modelsrepo 上看到 MobileNet 的实现。
我建议您在另一个选项卡中打开 MobileNet 的 TensorFlow 实现,
colab.research.google.com](https://colab.research.google.com/drive/1uUYdZk7EbOESRP7JFwHfsR7b9gUjxU75#scrollTo=K17Opyz2XLeN&forceEdit=true&sandboxMode=true)
MobileNets 使用可分离的卷积。但是什么是可分卷积呢?它们有什么“可分”之处?
可分离盘旋由下面的两个(分离的)盘旋组成。它们是深度方向卷积和点方向卷积。深度方向卷积接受一个特征图,在每个输入核上运行一个核。逐点卷积增加了输出通道的数量。
解释空间可分离卷积,深度可分离卷积,以及在一个简单的。
towardsdatascience.com](/a-basic-introduction-to-separable-convolutions-b99ec3102728)
在本文中,所有的卷积都被认为是填充的。所以在卷积之后,输入和输出特征图的大小是相同的。所以在下面两张图中, Df — Dₖ + 1 只等于 Df 。
深度方向回旋
深度方向的回旋。
假设,我们有尺寸为 Df 的 M 正方形特征地图。使用大小为 Dₖ 的内核,我们正在生成大小为 Df — Dₖ + 1 的输出特征图(假设没有填充,步长为 1)。我们对所有 m 个输入特征图重复这一过程,最后,我们剩下一个维度的特征图,*df—dₖ+1×df—dₖ+1×m。*注意,我们将对输入特征图的 M 个通道使用 M 个不同的核。这就是我们的 【深度方向卷积】 。
乘法次数(或文中提到的 【计算成本】 )将为,
当你只为一个时期训练了你的模型并且你用完了内存的时候!
逐点卷积
逐点卷积。
上面生成的输出特征图有 M 个通道,而我们需要 N 个输出通道。因此,为了增加输出维度,我们使用了一个 1 × 1 卷积。这些被称为 【点态卷积】 。我们使用大小为 1 × 1 × M 的核,并产生大小为df-dₖ+1×df-dₖ+1×1 的单一特征图。我们将此重复 N 次并且我们留下大小为df-dₖ+1×df-dₖ+1×n 的输出特征图。
对于一个标准卷积,计算成本应该是,
对于可分离卷积(深度方向+点方向),计算成本将是,
文中还计算了参数的缩减量。
宽度和分辨率乘数
尽管 MobileNet 的可训练参数有了相当大的减少,但你仍然希望它快,为此我们引入了一个名为 【宽度乘数】 的参数。用 α 表示。因此,模型中的任何层都将接收 αM 特征图,并产生 αN 特征图。它使 MobileNet 型号更薄并增加了延迟。为简单起见,我们将设置α = 1.0,其中α ∈ ( 0,1)。
如文中所述的宽度倍增器。来源。
为了进一步降低计算成本,他们还引入了一个 【分辨率乘数】 记为ρ。它减少了输入图像以及每一层的内部表示。
如论文中所述的分辨率倍增器。来源。
TensorFlow 实现(带 Keras)
首先,我们将从论文本身来看一下架构。它看起来像这样,
MobileNet 架构。来源
这里, " Conv /s2" 表示步长为 2 的卷积层(非深度方向)。 " Conv dw /s1" 表示步长为 1 的可分卷积。所有层之后是批处理规范化和 LeakyReLU 层。
具有 BatchNorm 和 LeakyReLU 的标准卷积层(右)。具有深度方向和 1 × 1 卷积(点方向)的可分离卷积(左)。来源
Keras 中的实现如下所示,
可分卷积。
最后,模型由 29 层包装而成,
组装模型。
我们将在劳伦斯·莫罗尼的石头剪刀布数据集上训练我们的模型。为了方便起见,它托管在 TensorFlow 数据集上。最初的 MobileNet 是在包括 ImageNet 在内的许多数据集上进行评估的。
你会在 Colab 笔记本中找到培训部分。恭喜你。您刚刚从头开始创建了一个 MobileNet。
进一步探索…
结束了
来源。
我希望你喜欢 MobileNet 的概念。如果你想在大数据集上训练模型,你可以通读研究论文。他们还包括一些超参数,这将有助于你的训练。谢谢,再见。*
探索移动平均线以在 Python 中构建趋势跟踪策略
使用 Plotly 在 Python 中生成
移动平均线能提高投资组合相对于基准的表现吗?
“低买高卖”是金融界每个人都想实现的共同目标。然而,这比看起来更困难,因为我们不知道顶部或底部在哪里。我们只知道我们现在在哪里,以及我们去过哪里。许多投资者坐着等待下一次衰退,以获得一个良好的进场点,错过了牛市带来的巨大回报,而那些坐在指数中的投资者获得了牛市的好处,但也面临着熊市的严峻现实。
跟随趋势比预测顶部和底部更容易。因此,在这篇文章中,我们将介绍一些跟踪市场趋势的基本方法,并试图在牛市和熊市中获得稳定的回报。
简单移动平均线
100 天简单移动平均线
简单移动平均(SMA)是一种平滑函数,用于计算过去观察值的平均值。这是一个常用的技术指标,用来表示价格反转。
如果我们有 1000 天的每日定价数据,则 100 天移动平均值的计算方法是平均前 100 天,然后通过一次移动一天的范围,一次平均 100 天。
用乳胶写的
简单的基于移动平均线的策略是利用资产价格和移动平均线之间的关系来捕捉趋势。我们将使用价格和移动平均线的交叉点来确定我们的位置。
导入数据
我们可以从雅虎财经等网站下载数据,并将其加载到数据框架中,或者使用 API 直接从外部服务器读取数据到 Python 内存中。
import pandas as pd
df = pd.read_csv("your_data.csv")
Quandl 是一个在线金融数据数据库,有一个免费账户和一个付费账户。非用户的请求数量是有限的,而用户每 10 秒钟可以获得 300 个呼叫。基本使用绰绰有余。
我们使用我们最喜欢的股票 choice Apple,并下载 2009 年至 2018 年期间的价格数据。
myquandlkey = "myquandlkey"
import quandlaapl = quandl.get("WIKI/AAPL", start_date="2009-01-01",
end_date="2018-12-31", api_key=myquandlkey)
另一个流行的选择是 pandas_datareader,这是这次将使用的一个。
import pandas_datareader as web
aapl = web.get_data_yahoo('AAPL',
start=datetime.datetime(2009, 1, 1),
end=datetime.datetime(2018, 12, 31))
数据准备
导入数据后,让我们看一下数据框,检查一下是否一切正常
aapl.head()
现在,只保持调整后的收盘,但在进一步的分析中可以有趣地看看成交量。删除第一行,因为我们不小心包括了 2008 年的除夕。
# New DataFrame only containing the Adj Close column
aapl = aapl[["Adj Close"]]# Rename the adjusted close column to Price
aapl.rename(columns={"Adj Close":"Price"}, inplace=True)# Remove the first row,
aapl = aapl.iloc[1:]aapl.head()
使用 pandas rolling 函数计算移动平均值,并将结果作为新列添加到 DataFrame 中。
aapl["100MA"] = aapl["Price"].rolling(window=100).mean()
aapl
前 99 个移动平均值显示为 NaN。这是因为我们需要前 100 个价格观察值来计算 MA。
绘制价格和移动平均线
# import plotting packages
import matplotlib.pyplot as plt
import matplotlib as mpl# Set color style
plt.style.use('seaborn-dark')
plt.style.use("tableau-colorblind10")fig = plt.figure(figsize=(20,12))
ax1 = plt.plot(aapl["Adj Close"])
ax1 = plt.plot(aapl["100MA"])
ax1 = plt.title("Apple daily ajusted close from 2009 through 2018", fontsize=22)
ax1 = plt.xlabel("Date", fontsize=18)
ax1 = plt.ylabel("Price", fontsize=18)
ax1 = plt.legend(["Price", "100 day SMA"],prop={"size":20}, loc="upper left")
plt.grid(True)
plt.show()
100 日均线是一种通过平滑价格来观察价格趋势的方法。由于它是一个滞后指标,它告诉我们过去发生了什么,尽管看一看它很有趣,但它本身可能不是一个好的策略。让我们探索一下,看看结果。
战略和实施
我们将使用两种不同的策略
- 当价格超过移动平均线时买入,当价格低于移动平均线时卖空。
- 作为一种替代方案,我们可以用现金头寸代替空头头寸,使策略只做多。
我们可以通过持有债券等低风险证券而不是现金来改进策略 2,波动性略有增加,但为了简单起见,现在现金为王。
我们希望在资产趋势上升时搭上顺风车,获得类似的回报。当它趋势向下时,选择卖出等待,或者卖空以从下跌中获利。
像这样一个简单的策略可能会有几个问题。如果趋势没有表现出来,而是给出了一个假阳性,我们的策略可能会以错误的方向买入或卖出而告终,从而亏损。短时间内的大量交叉会堆积交易成本。解决这个问题的方法是在交易前设定价格和移动平均线之间的最小距离,这可以在以后进一步探讨。
让我们按照策略 1 创建一个包含“Long”或“Short”的新列。
Position = []
for i in range(0,apple.shape[0]):
if apple[“Adj Close”].iloc[i] > apple[“100MA”].iloc[i]:
Position.append(“Long”)
else:
Position.append(“Short”)apple[“Position”] = Position
apple.head()
创建价格回报的新列,并将其添加到数据框架中
apple[“return”] = (apple[“Adj Close”] — apple[“Adj Close”].shift())/apple[“Adj Close”].shift()# The first return is defined from the 2\. row, so drop the first
apple.dropna(inplace=True)
apple.head()
在第一天,设定每种策略的价值等于苹果的价格,这样就很容易比较策略的价值如何与苹果的价格相比较。将价格与移动平均线进行比较,建立“多头”或“空头”头寸。第 1 天的回报将使用第 0 天的百分比变化来设置。我们还不能建仓,因为我们只有每日收盘,需要等到明天。
在第 2 天,我们根据第 1 天的头寸做多或做空,因为我们没有增加或删除第 1 天到第 2 天的任何值,策略将具有与第 1 天相同的值。
在第 3 天,我们乘以收益,或者从第 2 天到第 3 天苹果价格的变化,这是第 nr 行的收益。3、按每种策略。
编辑:请记住,长期持有策略是持有现金的策略 2。更好的名字应该是朗凯什。
LongShort = [0]*apple.shape[0]
LongHold = [0]*apple.shape[0]LongShort[0] = apple[“Adj Close”].iloc[0]
LongShort[1] = apple[“Adj Close”].iloc[0]
LongHold[0] = apple[“Adj Close”].iloc[0]
LongHold[1] = apple[“Adj Close”].iloc[0]for i in range(0, apple.shape[0]-2):
if apple[“Position”].iloc[i] == “Long”:
LongShort[i+2] = LongShort[i+1]*(1+apple[“return”][i+2])
else:
LongShort[i+2] = LongShort[i+1]/(1+apple[“return”][i+2])
for i in range(0, apple.shape[0]-2):
if apple[“Position”].iloc[i] == “Long”:
LongHold[i+2] = LongHold[i+1]*(1+apple[“return”][i+2])
else:
LongHold[i+2] = LongHold[i+1]apple[“LongShort”] = LongShort
apple[“LongHold”] = LongHold
apple.drop(apple.tail(1).index,inplace=True)
apple.head()
现在把策略和 100 天的 SMA 画在一起
fig = plt.figure(figsize=(20,12))
ax1 = plt.plot(apple[“Adj Close”])
ax1 = plt.plot(apple[“100MA”])
ax1 = plt.plot(apple[“LongShort”], color=”green”)
ax1 = plt.plot(apple[“LongHold”], color=”brown”)
ax1 = plt.title(“Apple daily ajusted close and strategy from 2009 through 2018”, fontsize=18)
ax1 = plt.xlabel(“Date”, fontsize=18)
ax1 = plt.ylabel(“Price”, fontsize=18)
ax1 = plt.legend([“Price”, “100 day SMA”, “SMA crossover long/short strategy”,
“SMA crossover long/cash strategy”],prop={“size”:22}, loc=”upper left”)
plt.grid(True)
plt.show()
在过去的 10 年里,苹果经历了非常健康的增长。因为 SMA 是滞后的,所以空头最终会伤害我们。导致 SMA 交叉的轻微下跌很快变成急剧上涨,而 SMA 图形没有足够快地跟上,当资产价格上涨时,我们做空。可以看出,多空策略被价格过程碾压,而多空/持有策略停留在中间。
2012/2013 年,做空策略发挥了作用,在苹果价格下跌时增加了价值。价格平稳,容易体现趋势。在 2015/2016 年,该战略效果不佳。价格趋势本身并不明显,表现出高度的波动性。
指数移动平均线
代替 SMA,更合适的加权函数将给予更近的观察更高的投票。这种方法的一个流行版本是指数移动平均线(EMA),它使用指数衰减权重。因为旧的观察很少有发言权,我们可以使用整个数据集作为回望期来计算均线。
该公式由下式给出
用乳胶写的
使用与前面相同的策略实施 EMA。简单地用均线而不是均线来比较价格。
至少对苹果来说,均线表现比均线好。这可能是因为它对资产价格的突然变化反应更快。仅仅持有这种资产仍然不会表现过度。
MACD
我们可以不用单一的均线,而是比较两个不同时间段的均线之间的关系。这就是所谓的移动平均收敛发散,或 MACD。
假设是根据 MACD 指标捕捉到的趋势买入或卖出资产。当短期移动平均超过长期移动平均时,我们做多,当长期移动平均超过短期移动平均时,我们做空。在这个分析中,我使用了 21 日均线和 126 日均线,代表一个月和六个月的交易日。由于回溯测试是在一个很长的时间范围内进行的,我尽量避免使用太短的回溯期。要写代码,计算 26 和 126 日均线,比较它们而不是和均线比较价格。
类似于均线,我们画出图来看结果
MACD 的这个特殊版本在这个数据上表现不佳。我们将很快研究各种数据集和时间框架上的所有策略,看看它们在不同情况下的表现。
结合一切
为了从上面的分析中进行归纳,我使用了函数来更容易地选择我们想要分析的数据,以及我们想要包含的模型。
我还加入了一个汇总统计脚本,以查看每个策略在最大提款、夏普、波动性和年化回报 VaR 等方面的表现。
现在编写一个将使用上述函数的主函数,以及一个绘制结果的函数。
让我们运行主函数 strategy()和绘图函数 plot_strategy(),并显示汇总统计表。我们计算并绘制 3 毫安变化以及所有的多头/空头和多头/持有。
2 个牛市案例
- 苹果从 2009 年到 2018 年,和之前一样
- 同一时期的标准普尔 500
start = datetime.date(2009, 1, 1)
end = datetime.date(2018, 12, 31)
ticker = “AAPL”
days = 100df, table, df_return_of_strategy, df_positions, df_price_of_strategy = strategy(ticker, start, end, days=days, MA=True, EMA=True, MACD=True, LongHold=True, LongShort=True)plot_strategy(ticker=ticker, start=str(start), end=str(end), df=df_price_of_strategy)
table
移动平均线策略在牛市中表现不佳。在市场上涨的时候做空,同时还能产生超额回报,这很难。正如我们所预期的那样,最大提款和波动性在现金投资组合中是最低的,这实际上给了苹果比在该期间持有股票更高的夏普比率。让我们来看看两次市场崩盘,分别发生在下跌前和下跌中。
3 熊案例
- 标准普尔 500 从 2006 年到 2010 年,“全球金融危机”
- 纳斯达克综合指数从 1998 年到 2001 年,“科技泡沫”
- 20 世纪 90 年代的日经 225 指数,“失去的十年”
在熊市期间做空会产生好的结果,这并不奇怪,但我们在顶部之前很久就建仓了,这不会在上涨期间损害太多的回报。然而,在下跌过程中,空头头寸受益匪浅。这种策略的一个缺点是我们无法察觉价格的突然下跌。在 1987 年的“黑色星期五”,道琼斯工业平均指数下跌了 22.6%,标准普尔 500 下跌了 18%。这些策略不能保护我们免受这种跌倒,应该与其他方法结合使用。
2 横向资产
- 通用汽车从 2014 年到 2019 年
- 从 2002 年到 2017 年的微软
横盘从来不代表趋势,当落后的 MA 赶上价格时,已经太晚了。趋势太短,这表明较短的均线可能是更好的选择。需要注意的一点是,除了最后一张图,所有均线都有更高的回报,波动性相似或更低。这个版本的 MACD 不能给出很好的结果,尽管最常见的版本快速均线用 12 天,慢速均线用 26 天。
结论和进一步的工作
趋势跟踪的移动平均交叉策略是一种众所周知的简单方法,在测试期间和使用的数据中,在熊市中比牛市中表现更好。尽管高度不稳定和停滞的市场会损害其质量。
我们做了几个假设。没有滑点或交易费用。我们只使用调整后的每日收盘,并且只能在那时进行交易。我们不考虑分红或做空限制。
我们每天重新平衡,这对于一些必须转移大量资金的基金来说并不总是可行的。他们可能来不及参加聚会了。我们也简单地根据交叉来买卖,不考虑价格和 MA 在背离之前的高振荡。
为了进一步分析,比较各种具有相反行为的资产类别是很有趣的,例如,高波动性对低波动性。每天运行一组资产,查看价格和 MA 之间的百分比距离,看看交易那些距离较大的资产是否可以减少误报的数量,并更好地捕捉更可靠的趋势,这将是很有趣的。
使用适当的时间序列交叉验证方法进行更严格的回溯测试也是有用的。模型的稳定性非常重要。如果我们改变了一些参数,结果发生了巨大的变化,我们应该放弃这个模型,重新开始。回溯测试并不能证明什么,但是使用交叉验证和限制前瞻偏差可以提高我们的信心。
免责声明: 如果您发现任何错误,请告诉我。另外,请注意,这只是对一些历史数据的一些方法的探索。过去的表现不能保证未来的结果。
更新(19/02/2020): 这是一个重写的版本。我已经修改了第一版的一个计算错误。
Github 库:
https://github.com/RunarOesthaug/MA-crossover_strategies
感谢阅读!
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。