TowardsDataScience-博客中文翻译-2022-三十五-
TowardsDataScience 博客中文翻译 2022(三十五)
原文:TowardsDataScience
协议:CC BY-NC-SA 4.0
MacBook M1 Pro 与谷歌 Colab 在数据科学方面的对比——你应该从苹果公司购买最新产品吗?
原文:https://towardsdatascience.com/macbook-m1-pro-vs-google-colab-for-data-science-should-you-buy-the-latest-from-apple-f6da806b1eb0
苹果 2499 美元的 16 英寸 beast 与完全免费的环境——哪个对 TensorFlow 更好?
杰里米·贝赞格在 Unsplash 上的照片
苹果连续第二年彻底改变了笔记本电脑行业。自 2020 年 M1 发布以来,专业用户一直在等待新的 14 英寸和 16 英寸。送货吗?当然,但是一个完全免费的 Google Colab 能胜过它吗?这就是我们今天要回答的。
想看看 2020 年的 M1 基地在数据科学方面与谷歌实验室相比如何吗?我掩护你:
https://betterdatascience.com/macbook-m1-vs-google-colab/
今天,我们将使用 TensorFlow 制作两个数据科学基准,并比较 MacBook Pro M1 Pro 和 Google Colab。我们将忽略拥有闪电般速度的笔记本电脑的明显优势,而只关注模型训练速度。
下表总结了两者之间的硬件规格:
图 1 —硬件规格对比(图片由作者提供)
M1 Pro 有更多的内存和更新的 CPU,但我们关心的是 GPU。我测试的苹果的那个是 16 核,但是你可以配置 24 或者 32 核。分配给我的 Colab 环境是完全随机的。您可能会得到不同的结果,因此基准测试结果可能会有所不同。
不想看书?请观看我的视频:
MacBook M1 Pro 与 Google Colab —数据科学基准测试设置
如果您继续学习,您将需要安装 TensorFlow。这里有一整篇文章致力于在苹果 M1 上安装 TensorFlow:
https://betterdatascience.com/install-tensorflow-2-7-on-macbook-pro-m1-pro/
此外,您还需要一个影像数据集。我使用了来自 Kaggle 的狗和猫的数据集,它是在知识共享许可下授权的。长话短说,你可以免费使用。
有关如何组织和预处理它的详细说明,请参考以下文章:
https://betterdatascience.com/top-3-prerequisites-for-deep-learning-projects/
我们今天会做两个测试:
- 具有定制模型架构的 tensor flow—使用我在 CNN 文章中描述的两个卷积块。
- 带迁移学习的 tensor flow—使用 VGG-16 预训练网络对图像进行分类。
让我们检查一下测试中使用的代码。
自定义张量流模型—代码
我将这个测试分为两部分——一个有数据增强和没有数据增强的模型。一次仅使用一对train_datagen
和valid_datagen
:
import os
import warnings
from datetime import datetime
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
warnings.filterwarnings('ignore')
import numpy as np
import tensorflow as tf
tf.random.set_seed(42)
# COLAB ONLY
from google.colab import drive
drive.mount('/content/drive')
####################
# 1\. Data loading
####################
# USED ON A TEST WITHOUT DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
# USED ON A TEST WITH DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
train_data = train_datagen.flow_from_directory(
directory='data/train/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
valid_data = valid_datagen.flow_from_directory(
directory='data/validation/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
####################
# 2\. Model
####################
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), input_shape=(224, 224, 3), activation='relu'),
tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), activation='relu'),
tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(2, activation='softmax')
])
model.compile(
loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(),
metrics=[tf.keras.metrics.BinaryAccuracy(name='accuracy')]
)
####################
# 3\. Training
####################
time_start = datetime.now()
model.fit(
train_data,
validation_data=valid_data,
epochs=5
)
time_end = datetime.now()
print(f'Duration: {time_end - time_start}')
接下来我们来看一下转移学习代码。
迁移学习张量流模型——代码
大部分导入和数据加载代码是相同的。同样,一次只使用一对train_datagen
和valid_datagen
:
import os
import warnings
from datetime import datetime
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
warnings.filterwarnings('ignore')
import numpy as np
import tensorflow as tf
tf.random.set_seed(42)
# COLAB ONLY
from google.colab import drive
drive.mount('/content/drive')
####################
# 1\. Data loading
####################
# USED ON A TEST WITHOUT DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
# USED ON A TEST WITH DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
train_data = train_datagen.flow_from_directory(
directory='data/train/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
valid_data = valid_datagen.flow_from_directory(
directory='data/validation/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
####################
# 2\. Base model
####################
vgg_base_model = tf.keras.applications.vgg16.VGG16(
include_top=False,
input_shape=(224, 224, 3),
weights='imagenet'
)
for layer in vgg_base_model.layers:
layer.trainable = False
####################
# 3\. Custom layers
####################
x = tf.keras.layers.Flatten()(vgg_base_model.layers[-1].output)
x = tf.keras.layers.Dense(128, activation='relu')(x)
out = tf.keras.layers.Dense(2, activation='softmax')(x)
vgg_model = tf.keras.models.Model(
inputs=vgg_base_model.inputs,
outputs=out
)
vgg_model.compile(
loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(),
metrics=[tf.keras.metrics.BinaryAccuracy(name='accuracy')]
)
####################
# 4\. Training
####################
time_start = datetime.now()
vgg_model.fit(
train_data,
validation_data=valid_data,
epochs=5
)
time_end = datetime.now()
print(f'Duration: {time_end - time_start}')
最后,让我们看看基准测试的结果。
MacBook M1 Pro 与 Google Colab —数据科学基准测试结果
我们现在将比较 M1 专业版和谷歌 Colab 在定制模型架构上每个时期的平均训练时间。请记住,训练了两个模型,一个有数据增强,一个没有数据增强:
图 2 —定制模型的基准测试结果(Colab:87.8 秒;Colab(增强):286.8sM1 Pro:71s;M1 专业版(增强版):127.8 秒)(图片由作者提供)
M1 专业版在 TensorFlow 测试中无疑更快。在没有增强的情况下,M1 Pro 比谷歌 Colab 快 23%左右。如果我们谈论的是使用增强图像数据集的模型,差异会飙升至 124%。
但是现在谁会从头开始写 CNN 的模型呢?如果您的数据有限,并且您的图像不是高度专业化的,则始终推荐迁移学习。以下是迁移学习模型的结果:
图 3 —迁移学习模型的基准结果(Colab:159s;Colab(增强):340.6sM1 Pro:161.4s;M1 专业版(增强版):162.4 秒)(图片由作者提供)
结果令人惊讶。在没有增强的模型上,谷歌 Colab 和 M1 Pro MacBook 几乎相同——只有 2.4 秒的差异有利于 Colab),这可以忽略不计。当比较使用增强图像数据集的模型时,我们看到略高于 100%的训练时间差。
您现在知道了这些数字,但是您是否应该仅仅根据这些数字来做出购买决定呢?大家讨论一下。
离别赠言
我在 16 英寸的 M1 Pro MacBook Pro 上花了很多钱,事实是,你可以在价格减半甚至免费的笔记本电脑上获得相同的性能。我没有遭受买家后悔,因为 M1 专业版有很多其他的东西。它速度快,反应灵敏,重量轻,屏幕精美,电池续航时间长达一整天。见鬼,它在比大多数 15 英寸笔记本电脑更小的机箱中装了一个 16.2 英寸的屏幕!
M1 Pro 或苹果的任何东西都不是为数据科学家和机器学习工程师设计的,但他们从 2021 年开始的 Pro 阵容绝对可以处理繁重的工作负载。
您对最佳便携式数据科学环境有什么想法?是全押 M1 Pro / M1 Max,还是买一台更便宜的笔记本电脑,把剩下的钱花在云积分上?或者介于两者之间?请在下面的评论区告诉我。
了解更多信息
- Python If-Else 语句一行—三元运算符讲解
- Python 结构模式匹配—入门的三大用例
- Dask 延迟—如何轻松并行化您的 Python 代码
保持连接
- 注册我的简讯
- 在 YouTube上订阅
- 在 LinkedIn 上连接
原载于 2022 年 1 月 20 日 https://betterdatascience.comhttps://betterdatascience.com/macbook-m1-pro-vs-google-colab/。
MacBook M1 与采用 RTX3060Ti 的定制 PC——哪个对数据科学更有利?
原文:https://towardsdatascience.com/macbook-m1-vs-custom-pc-with-rtx3060ti-which-is-better-for-data-science-1a10cdb05558
苹果 2020 年售价 1299 美元的 beast 与同等价格的 PC 配置相比,TensorFlow 的速度更快?
卢卡斯·凯普纳在 Unsplash 上的照片
早在 2020 年,苹果的 M1 芯片就是一项惊人的技术突破。它没有支持科学家们在发布时日常需要的许多工具数据,但自那以后发生了很多变化。我们甚至为专业用户定制了新的 M1 Pro 和 M1 Max 芯片。
但它真的能和一台配备专用 GPU 的定制电脑相比吗?这就是我们今天要回答的问题。
在今天的文章中,我们将只比较数据科学使用案例,忽略笔记本电脑与 PC 的其他差异。在测试中,我们有一台 2020 年的基本型号 MacBook M1 Pro 和一台由 AMD 锐龙 5 和 Nvidia RTX 显卡驱动的定制 PC。以下是规格:
图 1 —硬件规格对比(图片由作者提供)
两台机器的价格几乎相同——我只多花了 50 美元买了这台定制电脑。就预算而言,我们可以认为这种比较是公平的。
不想看书?请观看我的视频:
MacBook M1 与定制 PC——geek bench
综合基准不一定描绘真实世界的使用情况,但它们是一个很好的起点。先来看看苹果 M1 和 AMD 锐龙 5 5600X 在单核部门的对比:
图 2 — Geekbench 单核性能(图片由作者提供)
苹果 M1 在综合单核测试中快了大约 8%,这是一个令人印象深刻的结果。请记住,我们是在比较内置于超薄笔记本电脑中的移动芯片和台式机 CPU。
接下来我们来对比一下多核的性能。M1 有 8 个内核(4 个性能和 4 个效率),而锐龙有 6 个内核:
图 3 — Geekbench 多核性能(图片由作者提供)
M1 快得可以忽略不计——大约 1.3%。我们可以得出结论,两者的表现应该差不多。
这是他们渐行渐远的地方。定制 PC 有一个专用的 RTX3060Ti GPU,内存为 8 GB。轻薄的笔记本电脑毫无胜算:
图 4 — Geekbench OpenCL 性能(图片由作者提供)
RTX3060Ti 在 OpenCL 基准测试中的得分比苹果 M1 芯片高出约 6.3 倍。这些结果是意料之中的。见鬼,光是 GPU 就比 MacBook pro 大。
总体而言,M1 在 CPU 方面与 AMD 锐龙 5 5600X 相当,但在 GPU 基准测试方面有所欠缺。我们将会看到这些结果如何转化为 TensorFlow 性能。
MacBook M1 与 RTX3060Ti —数据科学基准测试设置
如果您继续学习,您将需要安装 TensorFlow。这里有一整篇文章致力于为苹果 M1 和 Windows 安装 TensorFlow:
- 在 MacBook M1 上安装 tensor flow 2.7
- 在 Windows 上安装支持 GPU 的 TensorFLow】
此外,您还需要一个影像数据集。我使用了来自 Kaggle 的狗和猫的数据集,它是在知识共享许可下授权的。长话短说,你可以免费使用。
有关如何组织和预处理它的详细说明,请参考以下文章:
https://betterdatascience.com/top-3-prerequisites-for-deep-learning-projects/
我们今天会做两个测试:
- 具有定制模型架构的 tensor flow—使用我在 CNN 文章中描述的两个卷积块。
- 带迁移学习的 tensor flow—使用 VGG-16 预训练网络对图像进行分类。
让我们检查一下测试中使用的代码。
自定义张量流模型—代码
我将这个测试分为两部分——一个有数据增强和没有数据增强的模型。一次仅使用一对train_datagen
和valid_datagen
:
import os
import warnings
from datetime import datetime
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
warnings.filterwarnings('ignore')
import numpy as np
import tensorflow as tf
tf.random.set_seed(42)
####################
# 1\. Data loading
####################
# USED ON A TEST WITHOUT DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
# USED ON A TEST WITH DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
train_data = train_datagen.flow_from_directory(
directory='data/train/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
valid_data = valid_datagen.flow_from_directory(
directory='data/validation/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
####################
# 2\. Model
####################
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), input_shape=(224, 224, 3), activation='relu'),
tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), activation='relu'),
tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(2, activation='softmax')
])
model.compile(
loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(),
metrics=[tf.keras.metrics.BinaryAccuracy(name='accuracy')]
)
####################
# 3\. Training
####################
time_start = datetime.now()
model.fit(
train_data,
validation_data=valid_data,
epochs=5
)
time_end = datetime.now()
print(f'Duration: {time_end - time_start}')
接下来我们来看一下转移学习代码。
迁移学习张量流模型——代码
大部分导入和数据加载代码是相同的。同样,一次只使用一对train_datagen
和valid_datagen
:
import os
import warnings
from datetime import datetime
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
warnings.filterwarnings('ignore')
import numpy as np
import tensorflow as tf
tf.random.set_seed(42)
####################
# 1\. Data loading
####################
# USED ON A TEST WITHOUT DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
# USED ON A TEST WITH DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
train_data = train_datagen.flow_from_directory(
directory='data/train/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
valid_data = valid_datagen.flow_from_directory(
directory='data/validation/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
####################
# 2\. Base model
####################
vgg_base_model = tf.keras.applications.vgg16.VGG16(
include_top=False,
input_shape=(224, 224, 3),
weights='imagenet'
)
for layer in vgg_base_model.layers:
layer.trainable = False
####################
# 3\. Custom layers
####################
x = tf.keras.layers.Flatten()(vgg_base_model.layers[-1].output)
x = tf.keras.layers.Dense(128, activation='relu')(x)
out = tf.keras.layers.Dense(2, activation='softmax')(x)
vgg_model = tf.keras.models.Model(
inputs=vgg_base_model.inputs,
outputs=out
)
vgg_model.compile(
loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(),
metrics=[tf.keras.metrics.BinaryAccuracy(name='accuracy')]
)
####################
# 4\. Training
####################
time_start = datetime.now()
vgg_model.fit(
train_data,
validation_data=valid_data,
epochs=5
)
time_end = datetime.now()
print(f'Duration: {time_end - time_start}')
最后,让我们看看基准测试的结果。
MacBook M1 与 RTX3060Ti —数据科学基准测试结果
现在,我们将比较 M1 和定制 PC 在定制模型架构上每个时期的平均训练时间。请记住,训练了两个模型,一个有数据增强,一个没有数据增强:
图 5 —定制模型在几秒钟内得出的结果(M1:106.2;M1 增广:133.4;RTX 3060 ti:22.6;RTX3060Ti 增强版:134.6)(图片由作者提供)
有一点是肯定的——这些结果是意料之外的。不要误会我的意思,我预计 RTX3060Ti 整体上会更快,但我无法解释它在增强数据集上运行如此缓慢的原因。
在非增强数据集上,RTX3060Ti 比 M1 MacBook 快 4.7 倍。两者在扩充数据集上大致相同。
但是现在谁会从头开始写 CNN 的模型呢?如果您的数据有限,并且您的图像不是高度专业化的,则始终推荐迁移学习。以下是迁移学习模型的结果:
图 6 —几秒钟内迁移学习模型结果(M1:395.2;M1 增广:442.4;RTX 3060 ti:39.4;RTX3060Ti augmented: 143)(图片由作者提供)
这次的结果看起来更真实。在非增强图像数据集上训练迁移学习模型时,RTX3060Ti 每历元快 10 倍。对于扩充的数据集,这种差异下降到 3 倍快,有利于专用 GPU。
尽管如此,对于一台最初不是为数据科学设计的超轻笔记本电脑来说,这些结果还是相当不错的。我们从一开始就知道 M1 没有机会。
离别赠言
苹果的 M1 芯片是非凡的——这一点毋庸置疑。如果我们比较每瓦特的计算能力,没有什么能与之相比。尽管如此,如果你需要像样的深度学习性能,那么定制桌面配置是强制性的。唯一的解决方法是在云中租用 GPU,但这不是我们今天探讨的选项。
NVIDIA 的 RTX3060Ti 是一款中间层 GPU,非常适合初级到中级深度学习任务。当然,你不会很快在上面训练高分辨率风格的 GANs,但这主要是由于 8 GB 的内存限制。24 GB 内存的 RTX3090Ti 绝对是更好的选择,但前提是你的钱包能撑那么远。
在大多数情况下,中端产品将让您受益匪浅。
*你对这个基准有什么想法?能不能在更强大的 GPU 上运行一下,分享一下结果?*请在下面的评论区告诉我。
更多基准
- MacBook M1 vs M1 Pro
- M1 MacBook vs 谷歌 Colab
- MacBook M1 Pro vs 谷歌 Colab
保持联系
- 注册我的简讯
- 在 YouTube上订阅
- 在 LinkedIn 上连接
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
原载于 2022 年 1 月 27 日https://betterdatascience.com。
MacBook M1 与 Google Colab 在数据科学领域的较量——意想不到的结果
原文:https://towardsdatascience.com/macbook-m1-vs-google-colab-for-data-science-unexpected-results-f50127e72d1e
苹果 2020 年售价 1299 美元的 beast 与完全免费的环境相比,TensorFlow 的速度更快?
在 Unsplash 上由 Anh Nhat 拍摄的照片
早在 2020 年,苹果的 M1 芯片就是一项惊人的技术突破。它没有支持科学家们在发布时日常需要的许多工具数据,但自那以后发生了很多变化。我们甚至为专业用户定制了新的 M1 Pro 和 M1 Max 芯片。
但是如果你可以在云中做任何事情,那么昂贵的笔记本电脑又有什么意义呢?
今天,我们将只比较数据科学使用案例,而忽略笔记本电脑比云环境更实用的其他场景。在测试中,我们有一个来自 2020 年的基本模型 MacBook M1 和带有 GPU 环境的 Google Colab。以下是规格:
图 1 —硬件规格对比(图片由作者提供)
Google Colab 环境在纸面上看起来更好——这一点毋庸置疑。我们将不得不看看它如何转化为用 TensorFlow 训练图像分类模型。
请记住:分配给我的 Colab 环境是完全随机的。您可能会得到不同的结果,因此基准测试结果可能会有所不同。
不想看书?请观看我的视频:
MacBook M1 vs. Google Colab —数据科学基准设置
如果您继续学习,您将需要安装 TensorFlow。这里有一整篇文章致力于在苹果 M1 上安装 TensorFlow:
https://betterdatascience.com/install-tensorflow-2-7-on-macbook-pro-m1-pro/
此外,您还需要一个影像数据集。我使用了 Kaggle 的狗和猫的数据集,它是在知识共享许可下授权的。长话短说,你可以免费使用。
有关如何组织和预处理它的详细说明,请参考以下文章:
https://betterdatascience.com/top-3-prerequisites-for-deep-learning-projects/
我们今天会做两个测试:
- 具有定制模型架构的 tensor flow—使用我在 CNN 文章中描述的两个卷积块。
- 带迁移学习的 tensor flow—使用 VGG-16 预训练网络对图像进行分类。
让我们检查一下测试中使用的代码。
自定义张量流模型—代码
我将这个测试分为两部分——一个有数据增强和没有数据增强的模型。一次仅使用一对train_datagen
和valid_datagen
:
import os
import warnings
from datetime import datetime
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
warnings.filterwarnings('ignore')
import numpy as np
import tensorflow as tf
tf.random.set_seed(42)
# COLAB ONLY
from google.colab import drive
drive.mount('/content/drive')
####################
# 1\. Data loading
####################
# USED ON A TEST WITHOUT DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
# USED ON A TEST WITH DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
train_data = train_datagen.flow_from_directory(
directory='data/train/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
valid_data = valid_datagen.flow_from_directory(
directory='data/validation/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
####################
# 2\. Model
####################
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), input_shape=(224, 224, 3), activation='relu'),
tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), activation='relu'),
tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(2, activation='softmax')
])
model.compile(
loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(),
metrics=[tf.keras.metrics.BinaryAccuracy(name='accuracy')]
)
####################
# 3\. Training
####################
time_start = datetime.now()
model.fit(
train_data,
validation_data=valid_data,
epochs=5
)
time_end = datetime.now()
print(f'Duration: {time_end - time_start}')
接下来我们来看一下转移学习代码。
迁移学习张量流模型——代码
大部分导入和数据加载代码是相同的。同样,一次只使用一对train_datagen
和valid_datagen
:
import os
import warnings
from datetime import datetime
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
warnings.filterwarnings('ignore')
import numpy as np
import tensorflow as tf
tf.random.set_seed(42)
# COLAB ONLY
from google.colab import drive
drive.mount('/content/drive')
####################
# 1\. Data loading
####################
# USED ON A TEST WITHOUT DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
# USED ON A TEST WITH DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
train_data = train_datagen.flow_from_directory(
directory='data/train/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
valid_data = valid_datagen.flow_from_directory(
directory='data/validation/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
####################
# 2\. Base model
####################
vgg_base_model = tf.keras.applications.vgg16.VGG16(
include_top=False,
input_shape=(224, 224, 3),
weights='imagenet'
)
for layer in vgg_base_model.layers:
layer.trainable = False
####################
# 3\. Custom layers
####################
x = tf.keras.layers.Flatten()(vgg_base_model.layers[-1].output)
x = tf.keras.layers.Dense(128, activation='relu')(x)
out = tf.keras.layers.Dense(2, activation='softmax')(x)
vgg_model = tf.keras.models.Model(
inputs=vgg_base_model.inputs,
outputs=out
)
vgg_model.compile(
loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(),
metrics=[tf.keras.metrics.BinaryAccuracy(name='accuracy')]
)
####################
# 4\. Training
####################
time_start = datetime.now()
vgg_model.fit(
train_data,
validation_data=valid_data,
epochs=5
)
time_end = datetime.now()
print(f'Duration: {time_end - time_start}')
最后,让我们看看基准测试的结果。
MacBook M1 vs. Google Colab —数据科学基准测试结果
我们现在将在定制模型架构上比较 M1 和 Google Colab 每个时期的平均训练时间。请记住,训练了两个模型,一个有数据增强,一个没有数据增强:
图 2 —基准测试结果—定制模型(图片由作者提供)
事实证明,MacBook M1 在非增强图像集上的速度慢了 20%左右,但在增强图像集上,它与 Colab 完全不相上下。M1 总体上更加稳定。
但是现在谁会从头开始写 CNN 的模型呢?如果您的数据有限,并且您的图像不是高度专业化的,则始终推荐迁移学习。以下是迁移学习模型的结果:
图 3 —基准结果—迁移学习模型(图片由作者提供)
认为不看好 M1 MacBook。由于专用的 GPU,Google Colab 的速度明显更快。M1 有一个 8 核 GPU,但它远没有 NVIDIA 的 TESLA 强大。
尽管如此,我不得不承认,对于一台不是为数据科学和机器学习而设计的轻薄笔记本电脑来说,看到这些结果令人印象深刻。2021 年的 M1 Pro 车型处理这些任务要好得多。
离别赠言
是的,你绝对可以将 2020 年的 13 英寸 M1 Macbook Pro(甚至 Air)用于数据科学和机器学习——至少是较轻的任务。如果你只关心这两个地区,那么 M1 可能不是最划算的选择。Google Colab 是完全免费的,在今天的大多数测试中,它的表现都超过了 M1。
如果你在寻找一台能够处理典型数据科学工作负载的笔记本电脑,并且没有廉价的塑料和不必要的红色细节,M1 可能是最佳选择。它速度快,反应灵敏,重量轻,屏幕精美,电池续航时间长达一整天。另外,你绝对可以用它来研究数据科学。当然,它的价格不菲,但它们提供了许多 Windows 笔记本电脑无法比拟的东西。
*您对最佳便携式数据科学环境有什么想法?像 M1 这样的多面手+云,还是更便宜的笔记本电脑+更多时间在云中?或者介于两者之间?*请在下面的评论区告诉我。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
- 注册我的简讯
- 订阅 YouTube
- 在 LinkedIn 上连接
原载于 2022 年 1 月 14 日https://betterdatascience.comT22。
MacBook M1 与 M1 Pro 在数据科学领域的较量——新芯片真的更好吗?
原文:https://towardsdatascience.com/macbook-m1-vs-m1-pro-for-data-science-is-the-new-chip-radically-better-3d6098ef3694
从 8 到 16 个 GPU 核心——这是它在 TensorFlow 中产生的巨大差异
安东尼·乔伦在 Unsplash 上的照片
早在 2020 年,苹果的 M1 芯片就是一项惊人的技术突破。它还不支持科学家们在发布时日常需要的许多工具数据。从那以后,很多事情都变了。我们甚至为专业用户定制了新的 M1 Pro 和 M1 Max 芯片。这些对数据科学有好处吗?绝对的。请继续阅读,了解更多信息。
苹果将专业用户视为内容创作者和电影制作者,而不是数据科学家。尽管如此,新的 M1 Pro 芯片有两个额外的 CPU 核心,两倍的 GPU 核心,并配备两倍的内存。作为数据科学家,我们应该感受到不同。
今天,我正在测试 2020 年的 M1 和 2021 年的 M1 Pro 16”的基本型号,规格如下:
图 1 —硬件规格对比(图片由作者提供)
2020 年的 M1 起价为 1299 美元,但如果你想让它配备 16 GB 内存和 512 GB 固态硬盘,它会跳到 1699 美元。如果你能多花 800 美元,你将获得 2 个额外的 CPU 和 8 个额外的 GPU 核心。哦,还有额外的 3 英寸屏幕空间。
首先,我们将检查综合基准,看看我们在处理什么。
不想看书?请观看我的视频:
M1 vs M1 专业版——极客 bench
综合基准不一定描绘真实世界的使用情况,但它们是一个很好的起点。我们不应该看到单核性能的任何差异,因为单个内核是相同的。M1 专业版只是有更多不同的排列方式。
图 2 — Geekbench 单核性能(图片由作者提供)
正常的 M1 多得了 13 分,但这一差距在误差范围内。接下来让我们比较一下多核性能——2020 年的 M1 有 8 个内核(4 个性能和 4 个效率),而 2021 年的 M1 Pro 有 10 个内核(8 个性能和 2 个效率):
图 3 — Geekbench 多核性能(图片由作者提供)
M1 专业版在多核测试中的得分高出约 60%。这是意料之中的,因为它有更多的内核,可以更好地组织高要求的任务。
最后,让我们比较一下 GPU。你可以用 Geekbench 进行金属测试。它很有用,因为 macOS 上的 TensorFlow 可以使用 Metal 插件来加速模型训练。新的 M1 Pro 拥有 16 个 GPU 核心,比基本的 2020 型号增加了一倍:
图 4 — Geekbench 金属性能(图片由作者提供)
它显示——新的 M1 Pro 在金属测试中快了大约 95%。如果你的钱包允许,你可以指定 2021 年的型号具有 24 和 32 个 GPU 核心。如果你选择了这些版本,让我知道你能得到什么样的金属乐谱。
总的来说,M1 Pro 芯片前景看好。让我们进行几个真实世界的测试,看看基准测试中显示的性能提升是否转化为数据科学。
M1 vs M1 Pro—数据科学基准设置
如果您继续学习,您将需要安装 TensorFlow。这里有一整篇文章致力于在苹果 M1 上安装 TensorFlow:
https://betterdatascience.com/install-tensorflow-2-7-on-macbook-pro-m1-pro/
此外,您还需要一个影像数据集。我使用了来自 Kaggle 的狗和猫的数据集,它是在知识共享许可下授权的。长话短说,你可以免费使用。
有关如何组织和预处理它的详细说明,请参考以下文章:
https://betterdatascience.com/top-3-prerequisites-for-deep-learning-projects/
我们今天会做两个测试:
- 具有定制模型架构的 tensor flow—使用我在 CNN 文章中描述的两个卷积块。
- 带迁移学习的 tensor flow—使用 VGG-16 预训练网络对图像进行分类。
让我们检查一下测试中使用的代码。
自定义张量流模型—代码
我将这个测试分为两部分——一个有数据增强和没有数据增强的模型。一次仅使用一对train_datagen
和valid_datagen
:
import os
import warnings
from datetime import datetime
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
warnings.filterwarnings('ignore')
import numpy as np
import tensorflow as tf
tf.random.set_seed(42)
####################
# 1\. Data loading
####################
# USED ON A TEST WITHOUT DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
# USED ON A TEST WITH DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
train_data = train_datagen.flow_from_directory(
directory='data/train/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
valid_data = valid_datagen.flow_from_directory(
directory='data/validation/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
####################
# 2\. Model
####################
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), input_shape=(224, 224, 3), activation='relu'),
tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), activation='relu'),
tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(2, activation='softmax')
])
model.compile(
loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(),
metrics=[tf.keras.metrics.BinaryAccuracy(name='accuracy')]
)
####################
# 3\. Training
####################
time_start = datetime.now()
model.fit(
train_data,
validation_data=valid_data,
epochs=5
)
time_end = datetime.now()
print(f'Duration: {time_end - time_start}')
接下来我们来看一下转移学习代码。
迁移学习张量流模型——代码
大部分导入和数据加载代码是相同的。同样,一次只使用一对train_datagen
和valid_datagen
:
import os
import warnings
from datetime import datetime
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
warnings.filterwarnings('ignore')
import numpy as np
import tensorflow as tf
tf.random.set_seed(42)
####################
# 1\. Data loading
####################
# USED ON A TEST WITHOUT DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
# USED ON A TEST WITH DATA AUGMENTATION
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
train_data = train_datagen.flow_from_directory(
directory='data/train/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
valid_data = valid_datagen.flow_from_directory(
directory='data/validation/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
####################
# 2\. Base model
####################
vgg_base_model = tf.keras.applications.vgg16.VGG16(
include_top=False,
input_shape=(224, 224, 3),
weights='imagenet'
)
for layer in vgg_base_model.layers:
layer.trainable = False
####################
# 3\. Custom layers
####################
x = tf.keras.layers.Flatten()(vgg_base_model.layers[-1].output)
x = tf.keras.layers.Dense(128, activation='relu')(x)
out = tf.keras.layers.Dense(2, activation='softmax')(x)
vgg_model = tf.keras.models.Model(
inputs=vgg_base_model.inputs,
outputs=out
)
vgg_model.compile(
loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(),
metrics=[tf.keras.metrics.BinaryAccuracy(name='accuracy')]
)
####################
# 4\. Training
####################
time_start = datetime.now()
vgg_model.fit(
train_data,
validation_data=valid_data,
epochs=5
)
time_end = datetime.now()
print(f'Duration: {time_end - time_start}')
最后,让我们看看基准测试的结果。
M1 vs M1 Pro—数据科学基准测试结果
我们现在将比较 M1 和 M1 专业版在定制模型架构上每个时期的平均训练时间。请记住,训练了两个模型,一个有数据增强,一个没有数据增强:
图 5 —基准测试结果—定制模型(图片由作者提供)
M1 Pro 比默认型号快 1.5 倍左右。奇怪的是,对于使用数据扩充的模型,结果几乎是相同的。
但是现在谁会从头开始写 CNN 的模型呢?如果您的数据有限,并且您的图像不是高度专业化的,则始终推荐迁移学习。以下是迁移学习模型的结果:
图 6 —基准结果—迁移学习模型(图片由作者提供)
M1 Pro 大幅度淘汰了旧的 2020 M1 MacBook。在没有增强的模型上大约快 2.45 倍,在增强的模型上大约快 2.72 倍。
我不得不承认,在 MacBook 上看到这些结果令人印象深刻,这不是数据科学家的想法。这是不是意味着你应该买一个?大概不会。
离别赠言
有一点是肯定的——在我们今天进行的所有数据科学测试中,M1 Pro 的表现都优于普通 M1。尽管如此,如果你只关心数据科学和机器学习,这并不是最好的选择。你可以买一台 500 美元的笔记本电脑,在云中完成所有繁重的工作。您可以购买一个带有一个或多个专用 GPU 的桌面工作站。在这种情况下,苹果公司没有任何机会。
如果你在寻找一台能够处理典型数据科学工作负载的笔记本电脑,并且没有廉价的塑料和不必要的红色细节,M1 可能是最佳选择。它速度快,反应灵敏,重量轻,屏幕精美,电池续航时间长达一整天。另外,你绝对可以用它来研究数据科学。当然,这可能不是最划算的选择,但它们提供了许多 Windows 笔记本电脑无法比拟的东西。
你对用于数据科学的新 M1 Pro 和 Max 芯片有何看法?你能在你的机器上运行我的基准测试并分享结果吗?请在下面的评论区告诉我。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
- 注册我的简讯
- 订阅 YouTube
- 在 LinkedIn 上连接
原载于 2022 年 1 月 7 日https://betterdatascience.com。
大规模服务的机器学习模型
原文:https://towardsdatascience.com/machine-learned-model-serving-at-scale-86f6220132c8
Jukan Tateisi 在 Unsplash 上拍摄的照片
假设您有一个机器学习模型,希望在某些应用程序中使用,例如,一个从文本生成矢量表示的 transformer 模型。您测量单个模型评估所需的时间。然后,对模型能够足够快地被评估感到满意,您在某个模型服务器中将这个模型部署到生产环境中。流量增加,模型执行速度突然变慢,根本无法维持预期的流量,严重违反了 SLA。会发生什么事?
你看,大多数用于评估机器学习模型的库和平台在默认情况下被调整为使用机器上所有可用的资源进行模型推断。这意味着并行执行利用的线程数量等于机器上可用内核或 CPU 的数量。这对于单个模型评估来说非常好。
不幸的是,这在并发评估中会失效。这是一个未被充分沟通的重要点。
让我们来看看会发生什么。在下面,我们使用 Vespa.ai 服务一个变压器模型。Vespa 是一个高性能和 web 可伸缩的开源平台,适用于在大型数据集上执行实时数据处理的应用程序。Vespa.ai 使用 ONNX 运行时进行模型加速。我们将使用最初的 BERT-base 模型,一个 12 层、1.09 亿参数的变压器神经网络。我们在 32 核 Xeon Gold 2.6GHz 机器上测试了该模型的性能。最初,这个模型可以在大约 24 毫秒内在这个特定的机器上被评估。
并发性与延迟和吞吐量。图片作者。
这里,蓝线是 95%的延迟,这意味着 95%的请求的延迟低于这个值。红线是吞吐量:机器每秒可以处理的请求数。横轴是并发连接数(客户端)。
随着并发连接数的增加,延迟会急剧增加。大约在 10 个并发请求时达到最大吞吐量。此时,第 95 百分位的延迟约为 150 毫秒,与预期的 24 毫秒相差甚远。结果是高度可变的和差的体验。
应用程序的类型决定了低延迟和高吞吐量之间的最佳平衡。例如,如果该模型用于终端用户查询,对于给定的预期流量水平,(可预测的)低延迟是重要的。另一方面,如果模型在某些数据存储中摄取之前生成嵌入,那么高吞吐量可能更重要。两者的驱动力都是成本:需要多少硬件来支持所需的吞吐量。举一个极端的例子,如果您的应用程序每秒处理 10 000 个查询,95%的延迟要求为 50 毫秒,那么按照上面的设置,您将需要大约 200 台机器。
当然,如果你期望只有少量的流量,这可能完全没问题。然而,如果您正在扩展到每秒数千个请求,这是一个真正的问题。因此,我们将在下文中看看我们能做些什么来扩大规模。
模型的并行执行
我们需要解释在模型推断过程中使用的线程模型,看看这里发生了什么。一般来说,有 3 种类型的线程:推理(应用程序)、操作间和操作内线程。这是多个框架中的一个常见特性,比如 TensorFlow、PyTorch 和 ONNX Runtime。
推理线程是主应用程序的线程。每个请求都在自己的推理线程中处理,推理线程最终负责交付给定请求的模型评估结果。
内部操作线程使用多线程实现评估单个操作。这对于许多操作都很有用,比如对大型张量的元素操作、一般的矩阵乘法、嵌入查找等等。此外,许多框架将几个操作组合成一个更高级的操作,可以并行执行以提高性能。
互操作线程用于并行评估模型的独立部分。例如,一个包含两个不同路径的模型可能会从这种形式的并行执行中受益。例子是宽和深模型或双塔编码器架构。
推理中的各种线程池。图片作者。
在上面使用 ONNX 运行时的例子中,默认情况下禁用操作间线程。但是,内部操作线程的数量等于机器上 CPU 的数量。在这种情况下,32。因此,每个并发请求都在自己的推理线程中处理。但是,有些操作是通过使用操作内线程池中的线程来并行执行的。因为这个池是在请求之间共享的,所以并发请求需要等待可用的线程在执行中前进。这就是延迟增加的原因。
该模型包含顺序运行和并行运行的操作。这就是为什么即使延迟增加,吞吐量也会增加到一定水平。然而,在那之后,吞吐量开始下降,因为我们遇到了这样一种情况,更多的线程执行 CPU 绑定的工作,而不是机器中的物理核心。由于过多的线程交换,这显然对性能有害。
按比例放大
为了避免这种线程过度订阅,我们可以确保每个模型在其自己的推理线程中顺序运行。这避免了操作内线程的并发求值之间的竞争。不幸的是,它也避免了使用并行执行来加速单个模型评估的好处。
让我们看看当我们将操作内线程的数量设置为 1 时会发生什么。
1 个操作内线程的并发性与延迟和吞吐量。图片作者。
正如所见,延迟相对稳定,直到并发性等于机器上的内核数量(大约 32)。之后,由于线程数量超过了执行线程的实际内核数量,因此延迟会增加。此时,吞吐量也增加了,达到每秒大约 120 个请求的最大值,提高了 40%。然而,第 95 百分位的延迟现在约为 250 毫秒,与预期相差甚远。
因此,最初看起来很有前途的模型可能并不适合高效的服务。
第一代变压器模型,如上面使用的 BERT-base,相对较大,评估速度较慢。因此,已经开发出了更有效的模型,可以使用相同的标记器和词汇表作为替代。xtremedithtransformers 系列就是一个例子。这些是从 BERT 中提取的,在许多不同的任务上具有与 BERT-base 相似的精度,而计算复杂度显著降低。
在下文中,我们将使用xtremediate-l6-h256模型,与 BERT-base 的 109M 相比,它只有大约 13M 的参数。尽管只有 12%的参数计数,但该模型的准确性与完整的 BERT-base 模型相当:
提炼模型的准确性。来自https://hugging face . co/Microsoft/xtremediate-l6-h256-un cased。
使用默认的线程数量(与系统上可用的相同),该模型可以在大约 4 毫秒的 CPU 上进行评估。但是,对于多个并发请求,它仍然会遇到与上面相同的伸缩问题。因此,让我们看看这如何随着单线程执行的并发请求而扩展:
在提取模型上使用 1 个操作内线程时并发性与延迟和吞吐量的关系。图片作者。
正如预期的那样,在我们达到与机器上的内核数量相等的并发水平之前,延迟要稳定得多。这提供了更好和可预测的体验。吞吐量现在达到每秒大约 1600 个请求,大大优于另一个模型,后者达到每秒大约 120 个请求。这使得实现所需性能水平所需的硬件更少。
实验细节
为了衡量扩展的效果,我们使用了 Vespa.ai,这是一个开源平台,用于构建对大型数据集进行实时数据处理的应用程序。它被设计成高性能和网络可扩展的,用于各种任务,如搜索、个性化、推荐、广告、自动完成、图像和相似性搜索、评论排名,甚至寻找爱情。
Vespa.ai 有许多集成的特性,并且支持开箱即用的多种用例。因此,它提供了在生产中部署的简化途径,而没有维护许多不同子系统的复杂性。在本文中,我们将 Vespa.ai 作为一个易用的模型服务器。在 Vespa.ai 中,很容易将线程模型调整为对每个模型使用:
<model-evaluation>
<onnx>
<models>
<model name="reranker_margin_loss_v4">
<intraop-threads> number </intraop-threads>
<interop-threads> number </interop-threads>
<execution-mode> parallel | sequential </execution-mode>
</model>
</models>
</onnx>
</model-evaluation>
此外,很容易横向扩展以使用额外的节点进行模型评估。在这篇文章中,我们没有探讨这个问题。
本文中的数据是使用 Vespa 的 fbench 工具收集的,该工具将负载驱动到系统进行基准测试。Fbench 提供了关于系统管理工作负载情况的详细而准确的信息。
摘要
在这篇文章中,我们看到默认的线程设置不适合生产中的模型服务,特别是对于具有高度并发请求的应用程序。并行模型评估之间对可用线程的竞争会导致线程超额订阅,从而影响性能。等待时间也变得高度可变。
问题在于共享的内部操作线程池。也许应该考虑一种不同的线程模型,它允许在低流量情况下使用多线程,但在需要高并发性时会退化为顺序评估。
然而,目前的解决方案是确保模型在它们自己的线程中运行。为了管理增加的延迟,我们转向了模型提取,这在不牺牲准确性的情况下有效地降低了计算复杂性。还有一些我们在这里没有提到的优化,比如模型量化。对于变压器模型来说,另一个重要因素是限制输入长度,因为评估时间是输入长度的平方。
这里我们没有考虑 GPU 求值,它可以显著加速执行。然而,规模成本也是一个真正的问题。
这里没有充分沟通的一点是,承诺推理延迟非常低的平台只是讲述了故事的一部分。例如,考虑一个平台承诺给定模型 1 毫秒延迟。天真地说,这可以支持每秒 1000 个查询。然而,考虑一下如果 1000 个请求几乎同时到达会发生什么:最后一个请求在返回之前必须等待将近 1 秒。这与预期的 1 毫秒相差甚远。
机器学习 101:线性回归
原文:https://towardsdatascience.com/machine-learning-101-linear-regression-72ba6c04fbf1
回到数据科学的基础
自从被称为“21 世纪最性感的工作”以来,机器学习和数据科学已经取得了很大进展——我们现在有非常强大的深度学习模型,能够自动驾驶汽车,或者在不同语言之间无缝翻译。所有这些强大的深度学习模型的基础是一个不起眼的模型,我今天想深入探讨它的细节。那个模型是线性回归!
尽管简单,但对线性回归的良好理解是理解更高级模型如何工作的先决条件!毕竟,一个人必须先学会走,然后才能跑!今天,我将在这篇文章中详细探讨:
- 线性回归背后的基本数学。
- 损失函数。
- 普通最小二乘回归的解析解。
- 如何创建 Python 普通最小二乘求解器🐍
如果你刚刚开始学习机器学习和数据科学,或者你只是想修改一些你在高级职业生涯中留下的基本概念,请继续阅读,直到本文结束!
新西兰埃格蒙特国家公园塔拉纳基山。由大卫·比林斯在 Unsplash 上拍摄的照片。
数据中的数学线性关系
线性回归模型假设因变量 d 与 m 解释变量 g ₁、 g ₂、… gₘ 呈线性关系:
d=c₁g₁+c₂g₂+…+cₘgₘ,
其中 c ₁、 c ₂、… cₘ 为与解释变量相关的系数。例如,因变量 d 可以是房价,解释变量 g ₁、 g ₂、… gₘ 可以是地理坐标、年龄、建筑面积等。那栋房子。
上面的线性方程可以简洁地写成下面的内积:
d=σᵢcᵢgᵢ=mᵀg,
其中 m 是长度为m:m=[c₁、 c ₂、… cₘ ]ᵀ、 g 也是长度为 m 的向量:g=gg*,g*
由于 m 包含允许我们使用 g 对 d 进行预测的系数向量, m 也被称为定义 d 和 g 之间线性关系的模型参数向量。
在数据分析中,我们通常不得不处理一组因变量,而不是单个值。因此,我们通常使用一个包含 n 因变量的向量:d=【d₁、 d ₂、… dₙ ]ᵀ,其中 d 中的 u th 元素与相应的一组 m 解释变量 Gᵤ ₁ 、Gᵤ
dᵤ=gᵤ₁c₁+gᵤ₂c₂+…+gᵤₘcₘ=σg \u\u g \u\u c。**
向量d**=【d₁、 d ₂、… dₙ ]ᵀ中所有 n 元素的这种线性关系可以简洁地写成以下矩阵乘积:**
d = Gm ,
其中 G 是大小为 n × m 的矩阵,结构如下:
g=[g₁₁, G ₁₂,… G ₁ ₘ ,
。…….【 G ₂₁、 G ₂₂、… G ₂ ₘ 、
。…….…
。…….[ Gₙ ₁、 Gₙ ₂、… Gₙₘ ]],
其中 G 的第 u 行包含 d 中第 u 元素的解释变量向量。和上面一样, m 是一个长度为 m 的向量:m=[c₁, c ₂,……cₘ]ᵀ包含模型参数。****
因此,给定一组解释变量 G 和相应的因变量 d ,线性回归的目标是确定最能描述 d 和 G 之间线性关系的 m 。
上下确定线性系统
**现在,如果 n,d,中的数据点数大于 m 中的模型参数数 m ,那么数据提供了足够的信息来唯一地确定所有的模型参数。我们说这个系统被过度决定了。
然而,如果 m 大于 n ,那么不幸的是,数据没有提供足够的信息来唯一地确定所有的模型参数。我们说这个系统是不确定的。
对于本文的其余部分,我们将只处理超定线性系统,这通常是大多数数据科学项目中的情况。对于确定系统下的,需要拉格朗日乘子来获得解,使得所涉及的数学稍微复杂一些。
预测误差和损失函数
为了最好地确定 m ,我们需要最小化 d 和 Gm 之间的预测误差(或损失函数):
e=d-Gm。
如果 m 完美地描述了 d 和 G 之间的关系,那么 Gm 将等于 d ,预测误差 e 将为 0 。
为了进一步量化跨越 d 中所有 n 元素的整体预测误差,我们将误差向量 e 减少到一个标量。这通常是通过使用向量的某种范数来完成的。常用的规范有:
- L1 常模:σᵤ|eᵤ|**
- L2 范数:√(σᵤeᵤ)**
L1 规范假设 d 中的数据来自指数分布,而 L2 规范假设数据来自高斯分布。
如下图所示,指数分布比高斯分布有更长的尾部。这意味着数据点远离平均值的概率在指数分布中比在高斯分布中更高。这个结果反过来意味着指数分布对于远离平均值的异常数据点比高斯分布更稳健。
指数分布比高斯分布有更长的尾部,这使得它对异常值更稳健。作者创建的图像。
当确定使用哪个范数时,理解哪个概率分布控制 d 很重要,因为这将极大地影响 m 的估计值。然而,令人欣慰的是,由于中心极限定理,高斯分布非常频繁地出现,并且在大多数情况下,使用 L2 范数应该会导致模型参数 m 的合理的良好估计。
另一方面,更复杂的损失函数可以通过使用 L1 和 L2 规范的组合来构建,例如胡伯损失,或者通过引入正则化项使最优解唯一。可能性是无限的!
超定普通最小二乘解
假设 d 中的数据由高斯分布控制,我们希望最小化与预测误差e=d-Gm的 L2 范数相关的损失函数 L ( m ):
l(m)=σᵤeᵤ=eᵀe**=(d-GM)ᵀ(d-GM)=(dᵀ-mᵀg =dᵀd-dᵀGM-****mᵀgᵀd+mᵀgᵀGM。****
由于 L ( m )是由误差的平方组成的,这个问题也被称为普通最小二乘问题。我们通过对 L ( m )相对于 m 求微分并将结果等于零,来最小化相对于模型参数 m 的损失函数 L ( m ):
∂l(m)/∂m= 0-gᵀd-gᵀd+2gᵀGM**= 0,
gᵀGM-g****
(如果你不太熟悉向量的微分,你可以参考杨、曹、钟和莫里斯的这组笔记。)
于是, d = Gm 的超定普通最小二乘解就简单了:m=【gᵀg]⁻gᵀd!
普通最小二乘 Python 求解器
数学够了!现在让我们用 Python 实现上面导出的普通最小二乘解!
**import numpy as npdef least_squares(G, d):
"""
Over determined ordinary least squares solver. Inputs
G: np.array of nxm kernel matrix of explanatory variables.
d: np.array of n observations.
Returns
M: np.array of m estimated model parameters
"""
M = np.dot(np.linalg.inv(np.dot(G.T, G)), G.T)
M = np.dot(M, d)
return M**
让我们也创建一个一维线性系统来解决使用我们的代码。我们假设有 10 个无噪声测量值,由一个非常简单的线性系统生成,该系统有两个模型参数:
d=c₀+c₁g,**
这里我们定义c₁=π*和 c ₀ = exp(1)。***
***g = np.arange(0, 10)
d = np.exp(1) + np.pi * g***
我们现在需要准备解释变量的矩阵 G 。为了做到这一点,我们注意到上面的等式相当于 c ₀是一个解释变量的系数,其值始终为 1:
d=c₀g⁰+c₁g =1c₀+c₁g .
矩阵 G 因此将具有对应于 2 个模型参数 c ₀和 c ₁.的 2 列第一列包含 g 的值,而第二列仅包含 1。该矩阵 G 可以使用二阶范德蒙矩阵创建,使用np.vander
:
***# The 2 here means: create a 2nd order Vandermonde matrix from g.
G = np.vander(g, 2)
print(G)[[0 1]
[1 1]
[2 1]
[3 1]
[4 1]
[5 1]
[6 1]
[7 1]
[8 1]
[9 1]]***
我们现在需要做的就是将 G 和 d 传递给最小二乘解算器来求解模型参数:
***print(least_squares(G, d))[3.14159265, 2.71828183]***
它正确地返回了c₁=π和 c ₀ = exp(1)的值!
虽然这可能是一个非常简单的例子(使用我们事先已经知道模型参数的数据),但它准确地显示了线性回归是如何工作的,并且可以应用到更复杂的问题中。
更高级的线性回归
我们今天做了大量的数学和一些编码,最终达到这一点!然而,线性回归并没有完全完成!还有其他更复杂的方法来获得上面获得的最小二乘解,例如使用矩阵的奇异值分解或者通过最大似然法!每种方法都有适合不同场景的特点。********
此外,我们还没有探索欠定的情况下,需要一个稍微复杂的数学来获得解决方案!我们也没有试图为 L1 范数损失函数创建求解器,这需要更高级的数值方法来求解,例如线性规划方法或随机梯度下降方法。
线性回归可能是最简单的回归模型,但它也非常深奥,充满了复杂的细节!一定要花时间探索这些更高级的主题,以便更好地理解线性回归是如何工作的!
摘要
今天我们探索了线性回归背后的数学,并用 Python 创建了一个普通的最小二乘解算器。今天探讨的概念,如最小化损失函数可能是基本的,但适用于所有其他更高级的模型,如神经网络!我希望您能够从这篇文章中更好地理解回归是如何工作的。感谢您的阅读!
参考
[1] W. M. Menke (2012),地球物理数据分析:离散逆理论 MATLAB 版,Elsevier。
【2】杨,曹,钟,莫理斯(2007),利用 MATLAB 应用数值方法,Wiley .
机器学习 102:逻辑回归
原文:https://towardsdatascience.com/machine-learning-102-logistic-regression-9e6dc2807772
回到分类的基础
在之前的一篇文章中,我探索了线性回归——机器学习和数据科学中使用的所有其他高级模型的基础。线性回归模型连续因变量,如股票价格。
然而,在数据科学和机器学习中,我们通常也必须处理布尔或分类相关变量。典型的布尔因变量包括像债务人的贷款违约状态——完全偿还/违约,或分类猫的图片——是猫/不是猫🐈!
用于模拟布尔因变量的最基本模型之一是逻辑回归模型。在本文中,我将深入探讨以下细节:
- 逻辑回归模型背后的基本数学。
- 布尔因变量的损失函数。
- 随机梯度下降法。
- 如何使用 Python 创建逻辑回归模型🐍。
如果您刚刚开始学习数据科学,或者尽管您已经有了很高的职业生涯,但只是想修改一些基本概念,请继续阅读,直到本文结束!
英国斯凯岛。由罗伯特·卢克曼在 Unsplash 上拍摄的照片。
布尔因变量、概率和赔率
在本节中,我们将探索逻辑回归背后的数学,从机器学习中最基本的模型开始— 线性回归。
在线性回归中,因变量 d 连续无界,与解释变量mg₁、 g ₂、… gₘ 成线性关系:
d=c₁g₁+c₂g₂+…+cₘgₘ,
其中 c ₁、 c ₂、… cₘ 为与解释变量相关联的 m 模型参数。
然而,在二进制分类问题中,因变量 d 是布尔型的: d 只有 0 或 1 的值。由于上述线性回归方程为 d 返回连续且无界的值,因此不能直接用于模拟布尔因变量。
我们可以尝试将 d 的连续输出视为一种概率。然而,我们不能有负概率或大于 1 的概率!因此, d 不能作为概率来对待。
也许我们可以试着用几率来处理它,而不是把 d 当作一种概率。赔率通过以下关系与概率密切相关:
O = P / (1 - P ),
其中 O 是赔率, P 是概率。
当概率 P 的范围为[0,1]时,赔率 O 的范围为[0,∞)使得其对于正值是无界的。我们仍然需要考虑负值。为此,我们取几率的对数 O 得到几率的对数 l :
l = log( O ),
其中 l 是连续的,有值域(-∞,∞),并且没有任何一般性损失,log 是以 e 为底的对数。
这正是我们需要的解决方案,以便使用上面的线性回归方程,使用赔率和概率对布尔因变量进行建模!我们需要做的就是将 d 视为对数比 l :
l= log(o)= log(p/(1-p)=c₁g₁+c₂g₂+…+cₘgₘ.
我们可以重新排列这个等式,用 P 代替:
p/(1-p)= exp(c₁g₁+c₂g₂+…+cₘgₘ)
p= 1/(1+exp(-(c₁
可以更简洁地写成:
p= 1/(1+exp(-mᵀg),
其中 m 是长度为 m 的向量,包含模型参数:m=[c₁, c ₂,… cₘ ]ᵀ, g 也是长度为 m 的向量,包含解释变量: g 该方程也被称为逻辑函数,因此被称为“逻辑回归”!
线性回归模型d=mᵀg已经转化为逻辑回归模型p*= 1/(1+exp(-mᵀg**)),将概率 P 建模为 m 和 g 的非线性函数!请注意,原始布尔因变量 d 没有出现在逻辑回归模型中——我们只处理概率!d’稍后将再次出现在损失函数中。***
等式p= 1/(1+exp(-mᵀg))可能很难理解,所以让我们用图表来看看最简单的可能情况。最简单的情况,P= 1/(1+exp(-g*))只有 1 个解释变量 g 如下图所示。当 g 增大时, P 趋于 1,当 g 减小时, P 趋于 0。图表中还包括一些合成布尔数据点,用于 d ' = 0 和 d ' = 1。***
蓝色表示 P = 1 / (1 + exp(- g )),橙色点表示 d' = 0,绿色点表示 d' = 1。作者创造的形象。
当 d ' = 1 时, P 理想情况下应具有接近 1 的对应值,而当 d ' = 0 时, P 应具有接近 0 的对应值。由逻辑回归模型定义的分类问题被简化为寻找一组模型参数m*=[c₁、 c ₂、… cₘ ]ᵀ ,这导致了这里描述的行为。***
布尔因变量的向量
在上一节中,我们算出了单布尔因变量 d 的逻辑回归方程。在现实生活中,我们更可能需要处理一个布尔因变量的向量,它对应于一组不同的测量值: d ' = [ d' ₁, d' ₂,… d'ₙ ]ᵀ.
在这种情况下,对应于 d 中第 u 个元素的概率由逻辑回归模型用一组对应的 m 解释变量 Gᵤ ₁ 、Gᵤ ₂、… Gᵤₘ 来建模:
pᵤ= 1/(1+exp(-(gᵤ₁c₁+gᵤ₂c*₂+…+gᵤₘcₘ))。***
注意, Pᵤ 只是向量中的第 u 个元素: P = [ P ₁, P ₂,… Pₙ ]ᵀ.对于 P ,该等式也可以向量形式写成:
P= 1/(1+exp(-Gm)),****
其中 G 是一个大小为n×m 的矩阵,结构如下:
g=【g₁₁、 G ₁₂、… G ₁ ₘ 、
。…….[ G ₂₁、 G ₂₂、… G ₂ ₘ 、
。…….…
。…….[ Gₙ ₁, Gₙ ₂,… Gₙₘ ]],
其中ugg第一行包含 m 解释变量的向量 Gᵤ ₁ 、Gᵤ ₂、… Gᵤₘ 、和m=**[c₁、******
最小化二元交叉熵损失函数
为了量化我们的逻辑回归模型的表现,我们需要最小化模型的概率预测 P 和关于模型参数 m 的布尔基础真值 d 之间的误差。
用于量化分类问题中的预测误差的最常用损失函数之一是二元交叉熵。对于第 u 个数据点,二元交叉熵为:
lᵤ(m)=-d'ᵤ原木(pᵤ(m)-(1-d'ᵤ)原木(1 - Pᵤ ( m )。****
Lᵤ ( m )中的第一项仅在 d'ᵤ = 1 时有效,第二项仅在 d ' ᵤ = 0 时有效。要获得所有 n 个数据点的损失,我们只需求和:
l(m)=σᵤlᵤ(m)。**
我们希望最小化关于模型参数 m 的损失函数 L ( m )。为此,我们将 Lᵤ ( m )相对于 m 进行区分:
∂lᵤ(m)/∂m=∂/∂m(-d'ᵤ原木(pᵤ(m)-(1-d'ᵤ)原木(1 - Pᵤ ( m ))****
这个导数看起来令人生畏,但是我们可以使用乘积法则将∂lᵤ(m)/∂m分解成更简单的成分:**
∂lᵤ(m)/∂m=∂lᵤ(m)/∂pᵤ(m∂pǐ(m)/∩m**。******
术语∂lᵤ(m)/∂pᵤ(m)可以评估为:****
∂lᵤ(m)/∂pᵤ(m)*=-d*'ᵤ/pᵤ+(1-d'ᵤ)/(1******
而∂pᵤ(m)/∂m可以由p= 1/(1+exp(-mᵀg**))计算为:******
∂pᵤ(m)/∂m=-1/(1+exp(-mᵀgᵤ)(-gt118】ᵤt121】exp(-mᵀg******
其中gt140】ᵤ=[gₙ₁、 Gₙ ₂、… Gₙₘ ]ᵀ是包含矩阵 G 的第 u 行的向量。
使用这些结果,导数∂lᵤ(m)/∂m**现在可以被评估为:****
∂lᵤ(m)/∂m**=(pᵤ-**d'ᵤ)/(pᵤ(1-pᵤ)gᵤ**p(1-p)****
理想情况下,我们将设置∂lᵤ(m)/∂m**= 0,并求解最佳模型参数 m 。不幸的是,我们无法求解出 m ,因为它们没有出现在∂lᵤ(m)/∂m的表达式中!因此,看起来我们可能需要求助于数值方法来最小化相对于 m 的 Lᵤ ( m )。****
随机梯度下降
可以使用各种数值方法,例如牛顿法或随机梯度下降法来最小化逻辑回归模型的损失函数。对于本文,我们将探索随机梯度下降法,其变体仍在更高级的模型中积极使用,如神经网络或联邦学习框架。
随机梯度下降是一种一阶迭代优化算法,用于通过沿最陡负梯度方向迭代移动来搜索可微函数的局部最小值。如下图所示,从绿点开始,如果我们沿着蓝色曲线朝着最陡负梯度的方向继续在 x 中小步前进,最终应该会到达红点——最小点。
从绿点到红点向最陡的负梯度方向移动。作者创造的形象。
记住,我们希望相对于 m 最小化 L ( m )。利用随机梯度下降算法,我们沿着最陡负梯度的方向在 m 中采取多个小步骤:-∂lᵤ(m)/∂m**向 L ( m )的最小值点前进。****
因此,我们使用∂lᵤ(m)/∂m**对 d ' = [ d' ₁、 d' ₂、… d'ₙ ]ᵀ:中的所有 n 元素迭代更新模型参数 m****
m:=m-r∂lᵤ(m)/∂m,******
其中 R 是某个学习率,它控制我们所走的步数的大小。
学习率的大小非常重要:如果 R 太小,步长几乎不会改变 m 的值,另一方面,如果 R 太大,那么我们可能会超过最小点!为 R 选择一个好的值取决于所涉及的数据。
随机梯度下降算法可以用伪代码编写:
****# Initialize the model parameters with some initial value.
m = initialize_m()# Iterate the algorithm until some termination condition is reached.
while termination_condition == False:
# Shuffle the rows the training data.
shuffle_training_data() # Iterate through all the n elements in the data.
for i in range(n):
# Update the model parameters m using the derivative of the
# loss function for that element.
m = m - R * dLdm[i] # Check if the termination condition is reached.
check_termination_condition()****
概述
我们已经讨论了很多细节,所以在继续用 Python 编码之前,让我们回顾一下我们已经讨论过的内容!
- 我们希望对布尔因变量 d ' ∈ {0,1}进行建模。
- 我们没有直接对布尔值建模,而是使用:p= 1/(1+exp(-(c₁g₁+c₂g₂+…+cₘgₘ))对概率建模。**
- 我们用二元交叉熵来衡量模型的预测误差:
(m)=-d'ᵤlog(pᵤ(m)-(1-d'ᵤ)log(1-【t74**** - 为了找到最佳的模型参数 m ,我们相对于 m 最小化 Lᵤ ( m )。不幸的是,这个最小化问题必须使用数值方法来解决,例如随机梯度下降。
使用 Python 进行逻辑回归
我们现在已经做了足够的数学!在本节中,我们将使用 Python 创建一个逻辑回归模型求解器!
首先,我们定义实现逻辑回归模型的logistic_regression
:
**import numpy as npdef logistic_regression(G, m):
"""
Logistic regression model. Inputs
G: np.array of shape nxm containing the explanatory variables.
m: np.array of length m containing the model parameters. Returns
P: np.array of length n containing the modeled probabilities.
"""
return 1 / (1 + np.exp(-np.dot(G, m)))**
接下来我们定义binary_cross_entropy
和binary_cross_entropy_grad
,它们计算二元交叉熵及其梯度。
**def binary_cross_entropy(d, P):
"""
Calculates the mean binary cross entropy for all n data points. Inputs
d: np.array of length n containing the boolean dependent
variables.
P: np.array of length n containing the model's probability
predictions. Returns
bce: float containing the mean binary cross entropy.
"""
# For d = 1:
d_1 = d[d == 1] * np.log(P[d == 1])
# For d = 0:
d_0 = (1 - d[d == 0]) * np.log(1 - P[d == 0])
return -np.mean(d_1) - np.mean(d_0)def binary_cross_entropy_grad(g, d, p):
"""
Calculates the gradient of the binary cross entropy loss for a
single data point. Inputs:
g: np.array of length m containing the vector of explanatory
variables for 1 data point.
d: integer containing the boolean dependent variable for 1 data
point.
p: float containing the model's probability predictions for 1
data point. Returns:
bce_grad: np.array of length m containing the gradients of the
binary cross entropy.
"""
return g * (p - d)**
最后,我们定义SGD
,它执行随机梯度下降算法,使用所有数据点逐一迭代更新模型参数。
**def SGD(d, G, m, R = 0.01):
"""
Stochastic gradient descent. Updates the model parameters m
iteratively using all individual data points in d.
Inputs
d: np.array of length n containing the boolean dependent
variables.
G: np.array of shape nxm containing the explanatory variables.
m: np.array of length m containing the initial model parameters.
R: float containing the SGD learning rate.
Returns
m: np.array of length m containing the updated model parameters.
"""
# Shuffle the data points.
indices = np.arange(len(d))
np.random.shuffle(indices)
d = d[indices]
G = G[indices] # Iteratively update the model parameters using every single
# data point one by one.
for i in range(len(d)):
P = logistic_regression(G[i], m)
m = m - R * binary_cross_entropy_grad(G[i], d[i], P) return m**
现在我们只需要一些数据来测试我们的逻辑回归求解器!我们使用从sklearn
获得的鸢尾花分类数据集。原始数据集提供了 3 个不同的类:0、1 和 2。我们将数据集仅限于 0 和 1 的类。
**from sklearn.datasets import load_iris# Load the iris flower dataset from sklearn.
data = load_iris()
G = data["data"] # Explanatory variable matrix.
d = data["target"] # Dependent variable array.# The dataset has 3 classes. For the time being, restrict the data
# to only classes 0 and 1 in order to create boolean dependent
# variables.
want = (d == 0) | (d == 1)G = G[want]
d = d[want]**
因为我们的逻辑回归模型
p= 1/(1+exp(-(c₁g₁+c₂g₂+…+cₘgₘ))**
没有明确包括偏差项,我们需要在矩阵 G 的第一列添加一列 1。这列 1 将扮演偏置项的角色。
**# Because we do not explicitly account for a bias term in our
# logistic regression model, we need to add a column of 1s to the
# matrix G. This will play the term of the bias term.
G = np.hstack([np.ones([len(G), 1]), G])# Take a look at the first 3 data points in the dataset.
for i in range(3):
print(G[i], d[i]) [1\. 5.1 3.5 1.4 0.2] 0
[1\. 4.9 3\. 1.4 0.2] 0
[1\. 4.7 3.2 1.3 0.2] 0**
现在我们已经准备好了数据结构 G 和 d ,是时候对模型参数 m 进行初步猜测了!由于每个数据点有 5 个解释变量,因此在 m 中将有 5 个模型参数。我们猜测模型参数的值为 1。
**m = np.ones(5)**
接下来,我们运行随机梯度下降解算器 20 次迭代,使用学习率 R = 0.01 来防止算法超过最小点。
**# Run the SGD algorithm 20 times.
for i in range(20):
# Use a learning rate of 0.01.
m = SGD(d, G, m, 0.01) # Calculate the mean loss for all data in the dataset.
P = logistic_regression(G, m)
bce = binary_cross_entropy(d, P) print("Step {:3d} : m = [ ".format(i+1), end = "")
for j in range(len(m)):
print("{:6.3f}".format(m[j]), end = " ")
print("], loss = {:.3f}.".format(bce)) Step 1 : m = [ 0.692 -0.506 -0.080 0.640 0.961 ], loss = 0.548\. Step 2 : m = [ 0.674 -0.517 -0.207 0.854 1.050 ], loss = 0.417\. Step 3 : m = [ 0.643 -0.609 -0.355 0.985 1.110 ], loss = 0.322\. Step 4 : m = [ 0.629 -0.620 -0.440 1.124 1.168 ], loss = 0.258\. Step 5 : m = [ 0.616 -0.638 -0.514 1.234 1.214 ], loss = 0.218\. Step 6 : m = [ 0.608 -0.635 -0.568 1.337 1.257 ], loss = 0.187\. Step 7 : m = [ 0.598 -0.652 -0.626 1.417 1.291 ], loss = 0.165\. Step 8 : m = [ 0.592 -0.652 -0.668 1.495 1.324 ], loss = 0.148\. Step 9 : m = [ 0.579 -0.691 -0.726 1.545 1.348 ], loss = 0.135\. Step 10 : m = [ 0.573 -0.699 -0.765 1.605 1.373 ], loss = 0.123\. Step 11 : m = [ 0.568 -0.697 -0.796 1.665 1.398 ], loss = 0.113\. Step 12 : m = [ 0.563 -0.702 -0.828 1.716 1.420 ], loss = 0.104\. Step 13 : m = [ 0.556 -0.716 -0.862 1.759 1.439 ], loss = 0.097\. Step 14 : m = [ 0.551 -0.723 -0.891 1.802 1.457 ], loss = 0.091\. Step 15 : m = [ 0.547 -0.729 -0.918 1.843 1.475 ], loss = 0.086\. Step 16 : m = [ 0.542 -0.736 -0.944 1.880 1.491 ], loss = 0.081\. Step 17 : m = [ 0.538 -0.741 -0.968 1.916 1.506 ], loss = 0.077\. Step 18 : m = [ 0.535 -0.741 -0.987 1.953 1.522 ], loss = 0.073\. Step 19 : m = [ 0.531 -0.748 -1.009 1.985 1.536 ], loss = 0.069\. Step 20 : m = [ 0.528 -0.748 -1.027 2.018 1.549 ], loss = 0.066.**
从上面的结果来看,平均二进制交叉熵似乎一直在下降,这意味着该模型应该朝着一组令人满意的参数收敛,所以接下来让我们检查该模型的准确性。由于这是一个小数据集,我们将简单地直接打印所有 n 个数据点的结果,并直观地检查它们。
**# Print the ground truth.
print(d)# Print the modeled probabilities of the logistic regression model,
# rounded to the nearest integer value.
print(np.round(logistic_regression(G, m)).astype(int)) [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1][0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]**
似乎我们的模型已经完美地分类了数据集中的每一个数据点!接下来让我们仔细看看模型的实际非舍入输出,包括 d ' = 1 和 d ' = 0 两种情况。
**# Get the probability predictions from the model.
P = logistic_regression(G, m)# d' = 0
print("d' = {}, P = {:.3f}.".format(d[0], P[0]))
# d' = 1
print("d' = {}, P = {:.3f}.".format(d[-1], P[-1])) d' = 0, P = 0.023\.
d' = 1, P = 0.975.**
我们看到,当 d ' = 1 时, P 非常接近 1,当 d ' = 0 时, P 非常接近 0——这正是我们预期的行为!
提醒一句
在上面的例子中,使用的数据是非常容易分离的。也就是说, d ' = 1 和 d ' = 0 之间的解释变量存在明显差异。这就是我们如何在预测中获得完美结果的。
在实际的数据科学项目中,数据通常没有这么干净——我们需要设计数据中的特征,考虑 d 中 0 和 1 的数量之间的比例,以及微调模型训练过程。根据数据,其他损失函数,如聚焦损失或正则化损失函数可能比二元交叉熵更好。
在本文中,我们使用随机梯度下降来最小化损失函数。随机梯度下降并不总是适用于所有数据集,在其他数据集中,其他更高级的优化算法,如有限内存 BFGS 或 Adam 可能适用。
指标也是另一个需要考虑的重要因素。在上面的例子中,我们简单地比较了模型的输出。在现实生活中,对于包含数千个数据点的数据集,这种方法显然是行不通的!使用哪种指标将取决于所涉及的数据。分类中使用的常用指标包括准确度、接收器工作特性或精确度和召回率。
此外,我们在用于训练模型的数据上测试了我们的模型——实际上,整个数据集应该分成训练和验证数据集!该模型应在训练数据集上进行训练,然后在验证数据集上进行测试-这是为了确保该模型不只是记住了训练数据集,而且能够将其预测推广到以前没有见过的数据!
数据科学和机器学习是极其深入和广阔的领域!一旦你完全理解了基本原理是如何工作的,那么就开始花些时间探索更高级的话题吧!
摘要
在本文中,我们探索了逻辑回归模型背后的数学,并使用 Python 创建了一个基于随机梯度下降的求解器!今天探索的概念,如使用数值方法最小化损失函数可能是基本的,但适用于所有其他高级模型,如深度学习图像分类或分割模型!我希望您能够更好地理解分类模型今天是如何工作的。一如既往的感谢您的阅读,下篇文章再见!
参考
[1] W. M. Menke (2012),地球物理数据分析:离散逆理论 MATLAB 版,Elsevier。
【2】艾伦·b·唐尼(2014),Think Stats Python 中的探索性数据分析,绿茶出版社。
机器学习 103:损失函数
原文:https://towardsdatascience.com/machine-learning-103-loss-functions-37b84f3e9b73
为什么机器学习是一个优化问题
在之前的两篇文章中,我介绍了机器学习中使用的两个最基本的模型——线性回归和逻辑回归。在这两种情况下,我们感兴趣的是搜索产生被观察目标 d 的最佳模型预测**d’的一组模型参数 m ,并且在这两种情况下,这是通过最小化一些损失函数 L ( m )来完成的,该损失函数测量d’**和 d 之间的误差。
很大一部分机器学习——从简单的线性回归到深度学习模型,本质上都涉及某种损失函数的最小化——然而,许多数据科学或机器学习书籍/教程/材料往往更强调模型本身,而不是损失函数!在这篇文章中,我们将继续前两篇文章的内容,并在以后的文章中探索更高级的模型之前,重点关注损失函数!
美国肯塔基州观测到的美丽日食。照片由李宗盛在 Unsplash 拍摄。
简而言之,训练机器学习模型
很大一部分训练机器学习模型可以简化如下:
- 我们有一组观察目标 d 和一组相应的解释变量 G 。我们希望使用相应的新值 G 来预测新值 d 。 d — G 对的例子包括信用分析中的贷款违约状态—信用数据,物理实验中的摆的周期—摆长的平方根,或者猫狗图像分类中的图像标签— RGB 图像🐈/🐕。
- 我们想要创建一个机器学习模型 f ( G , m ),该模型使用一组模型参数 m 在给定一些解释变量G:d’=f(G, m )的情况下对 d 进行预测。
- 训练模型 f ( G , m )本质上是寻找能够给出最佳预测的 m 。使用某种损失函数 L ( m )来衡量模型预测的质量; d ,d’),为了方便起见我们写成 L ( m )。损失函数本质上是测量**d’**和 d 之间的误差,一般来说 L ( m )的值越小,模型的预测越好。
- 当我们最终找到导致最小 L ( m )值的 m 时,我们就结束了我们的搜索——对于给定的一组 d 和 G ,我们已经最小化(或优化) L ( m )!
现在,正如“最佳”是一个非常主观的词,损失函数也是如此!在某些情况下,一些损失函数会比其他函数更好,使用好的损失函数可以显著提高模型性能!不幸的是,对于某些任务,找到一个好的损失函数可能是一个不小的问题。
在许多情况下,我们通常从众所周知的损失函数开始,然后再转向更复杂的损失函数。通过使用不同的损失函数重新创建模型,我们通常最终会找到产生最佳性能模型的东西!
在接下来的两节中,我们将讨论回归和分类问题中使用的一些损失函数。
线性回归的损失函数
对于这里的回归损失函数,我们将预测值和观测值之间的误差定义为: eᵤ ( m )是向量的第 u 个元素:
e(m)=d-f*(G, m ),*
而 mᵥ 是矢量 m 的第 v 个元素。
为了进一步显示不同回归损失函数的效果,我们对以下问题执行线性回归:通过由等式产生的 19 个点找到最佳拟合曲线:
y(x)= 3x-x*+2、*
在原始数据集的右侧添加了两个异常点。
从 y(x)= 3x-x*+2 中抽取的一些数据点,有两个离群点。作者创作的人物。*
- 均方误差(L2 范数)
l(m)=σᵤ*(eᵤ(m))。
这可能是回归问题最广泛使用的损失函数,假设数据中的噪声来自高斯分布。由于误差的平方,该损失函数受到异常值的强烈影响,如下图所示。*
使用 L2 范数误差(均方差)训练的模型的最佳拟合曲线。L2 范数误差受离群数据点的影响。作者创作的人物。
- 平均绝对误差(L1 范数)
l(m)=σᵤ*|eᵤ(m)|。
该损失函数不像均方误差那样广泛使用,因为它假设数据中的噪声来自指数分布。由于使用了误差的绝对值,该损失函数不会受到异常值的强烈影响,如下图所示——最佳拟合曲线完全忽略了两个异常点!*
使用 L1 范数误差(平均绝对误差)训练的模型的最佳拟合曲线。L1 范数误差不受离群数据点的强烈影响。作者创作的人物。
- Lasso 正则化均方误差(L2 范数带 L1 正则化)
l(m)=σᵤ*(eᵤ(m)+λσᵥ|mᵥ|,其中λ为待指定的超参数。如果λ = 0,这个损失函数就变成了均方误差。
如果数据包含许多异常值,则使用 lasso 正则化均方误差,但如果假设数据中的噪声来自高斯分布。实质上,套索正则化迫使模型参数尽可能接近零。lasso 误差损失函数受异常数据点的影响不如正则化项导致的均方误差大,但比误差项平方导致的平均绝对误差大,如下图所示。*
使用套索误差训练的模型的最佳拟合曲线。lasso 误差受异常数据点的影响不如均方差大,但比平均绝对误差大。作者创作的人物。
- 岭正则化均方误差(L2 范数加 L2 正则化)
l(m)=σᵤ*(eᵤ(m)+λσᵥmᵥ,其中λ为待指定的超参数。如果λ = 0,这个损失函数就变成了均方误差。
岭正则化均方误差用于数据中存在多重共线性。理想情况下,解释变量中的任何两个特征都不应该相互关联。然而,这有时是无法避免的,可以使用岭正则化来确保模型参数具有相同的大小。下图显示了岭正则化线性回归模型的最佳拟合曲线。*
使用岭误差损失函数训练的模型的最佳拟合曲线。如果数据中存在多重共线性,则岭误差会变得有意义,但这里的情况并非如此。作者创作的人物。
逻辑回归的损失函数
在机器学习 102:逻辑回归中,我们展示了对于分类问题,我们建模概率而不是实际类别。因此,分类问题中使用的损失函数直接处理来自模型的概率输出:p(m)=f(G, m )。
为了进一步显示不同损失函数的影响,我们对用 scikit-learn 打包的乳腺癌数据集进行了逻辑回归。原始数据集有 357 个良性(1 级)和 212 个恶性(0 级)数据点。虽然原始数据集已经不平衡,但我们通过将恶性数据点的数量减少到 50 个来进一步扭曲数据,导致 1 类和 0 类之间的比例为 87:12。
- 二元交叉熵
L(m)=-(ylog(p(m)+(1-y*)log(1-p(m))。
这是最广泛使用的二元分类损失函数。对于多类分类,存在一个等价的分类。然而,由于二进制交叉熵平等地对待类 0 和类 1,因此它通常不适合不平衡的数据集,这可以从下面的分类报告中看出-训练的模型无法检测类 0 的任何实例。*
*precision recall f1-score support 0 0.00 0.00 0.00 15
1 0.85 1.00 0.92 87 accuracy 0.85 102
macro avg 0.43 0.50 0.46 102
weighted avg 0.73 0.85 0.79 102*
- 焦十字熵
l(m)=-(y**α(1-p*(m)ᵞlog(p(m)+(1-y如果 α = 1 且 γ = 0,则该焦交叉熵损失转化为二元交叉熵损失。默认情况下, α = 0.25, γ = 2。
焦点交叉熵损失是对二元交叉熵损失的扩展,它考虑了具有比例因子的两个类别的比例: α 和(1 - α ,并迫使模型关注具有调制因子的错误分类预测:(1-p(m)ᵞ和p(【)对于不平衡数据集,焦点交叉熵损失往往优于二进制交叉熵损失,如下面的分类报告所示-训练模型现在更能够检测类 0 的实例。*
*precision recall f1-score support 0 1.00 0.87 0.93 15
1 0.98 1.00 0.99 87 accuracy 0.98 102
macro avg 0.99 0.93 0.96 102
weighted avg 0.98 0.98 0.98 102*
复杂模型的损失函数
在前两节中,我们介绍了回归和分类模型中一些常用的损失函数。上面的列表并不详尽,还有许多其他更复杂的损失函数!此外,某些机器学习算法使用特别定制的损失——例如铰链损失被广泛用于支持向量机,而基尼杂质被广泛用于基于决策树的模型。
此外,深度学习模型等高度复杂模型的损失函数往往具有多个局部最小值。在这种情况下,在模型训练期间,优化过程很可能陷入局部最小值,而不是收敛到损失函数的全局最小值。因此,对于复杂模型,通常使用不同的随机生成的初始模型参数同时训练几个模型,以增加模型收敛的机会,并使用随机优化器,如随机梯度下降,以随机将优化器踢出局部最小值。
摘要
在本文中,我们仔细研究了损失函数在训练机器学习模型中所起的作用,以及使用不同的损失函数如何导致同一组数据的非常不同的模型性能。使用适当的损失函数将确定回归模型是否过度拟合离群数据点,或者分类模型是否过度拟合多数类。数据科学家和机器学习工程师应该注意使用适合他们正在处理的问题的损失函数!
参考
[1] W. M. Menke (2012),地球物理数据分析:离散逆理论 MATLAB 版,Elsevier。
[2] C. M. Bishop (2006),模式识别与机器学习,Springer
[3]宗-林逸,普里亚·戈亚尔,罗斯·吉尔希克,明凯·何和皮奥特·多勒(2018), 密集物体探测的焦损失 , arXiv:1708.02002 。
机器学习:科学的朋友还是敌人?
原文:https://towardsdatascience.com/machine-learning-a-friend-or-a-foe-for-science-9c0b421eabd8
机器学习如何影响科学再现性以及如何解决它
图片由作者使用 DALL-E 2 拍摄
再现性是科学进步的基础,但机器学习的日益使用正在影响它。为什么再现性很重要?为什么机器学习的使用有一个有问题的副作用?我们如何解决它?
并非所有闪亮的东西都是钻石
复制另一篇文章的结果。图片由作者使用 DALL-E 2
2016 年,科学杂志《自然》发表了一项调查的结果。他们要求 1576 名研究人员回答一份关于研究中可重复性的简短问卷。结果显示,超过 70%的科学家未能重现另一位研究员的实验。调查中超过 50%的研究人员宣称存在再现性危机。
这个问题涉及到所有的科学学科,从医学到生物学,从经济学到物理学,从心理学到化学。这位科学家回答说,这背后的主要原因是两个因素:发表的压力(“发表或灭亡”)和选择性报道。其他人指出,统计能力低和技术困难也可能是一个原因。事实上, p 值和其他统计方法正在接受审查以找到更好的方法来分析数据。
研究人员宣称,当试图复制学术发现时,不到 40%的尝试是成功的。此外,实验室中的许多本科生对复制失败感到沮丧(导致他们精疲力竭)。此外,通常当一位科学家能够复制这些发现时,其结果远不如最初的论文热情(效应的大小远小于宣称的)。事实上,通常他们所寻求的突破性发现对结果的影响要小得多。
"疯狂的定义是一遍又一遍地做同样的事情,却期待不同的结果."归功于爱因斯坦
然而,再现性危机的影响超出了学术界。该行业经常试图重现研究人员的发现。例如,制药公司正在寻找有前景的研究,以开发新的潜在疗法。然而,他们在复制结果时也遇到了困难。这是导致 II 期临床试验成功率(尤其是肿瘤学)较低的原因之一。
你能信任这台机器吗?
在过去的十年里,机器学习已经在许多不同的领域产生了影响(从政治学到心理学,从物理学到生物医学)。例如,欧洲核子研究中心的实验或新的韦伯望远镜正在产生大量的数据。在医学领域,有成千上万的电子病历、庞大的患者数据图像数据集等等。生物学也是如此,由于组学革命,我们正在积累数据。
越来越多的政治学文章使用机器学习(这里的术语“内战”和“机器学习”用于数据库中的研究)。图片来源(原文)。
因此,数据科学和机器学习为许多研究应用找到了空间。然而,最近发表的一篇文章对机器学习在科学中的应用投下了阴影。
“机器学习正在作为一种工具出售,研究人员可以在几个小时内学会并自行使用。但是你不能指望一个化学家能够通过网络课程学习如何管理实验室”——萨亚什·卡普尔在《自然》杂志上说
作者在不同的科学领域使用机器学习方法时发现了不同的错误。事实上,作者对 20 篇科学文章(17 个研究领域的 20 篇综述)进行了元分析,发现了 329 篇研究论文的结果无法重复,他们确定了机器学习错误应用的原因。该图显示,大多数论文中的突出错误是某种形式的数据泄漏。
对 20 篇科学文章的调查,这些文章确定了在不同领域采用机器学习方法的陷阱。在表中,代表某种形式的数据泄漏的标题被作者用粗体突出显示。图片来源(原创文章)。
用胶带封好你的管子(管线)
在 Unsplash.com,米凯拉·帕兰特拍摄的照片
数据泄露,在机器学习中,是指模型在训练期间访问了不应该被允许看到的信息。例如,它应该是测试集和训练集之间的信息泄漏,因为模型将在其上进行评估。事实上,由于模型已经看到了答案,它会比现实中表现得更好。
虽然防止某些形式的数据泄漏(如缺少测试集、重复条目)很容易,但其他形式的泄漏则更加微妙。数据集预处理过程中的一个常见错误是缺乏训练和测试的清晰分离。事实上,标准化、缺失值插补和过采样/欠采样步骤应该在训练集和测试集上分别执行。此外,即使不太明显,特征选择应该为训练集和测试集分别执行(否则模型将知道哪些特征在集合上执行得更好)。
事实是,作为一名数据科学家,你随时都有可能出现数据泄露的情况,比如准备、清理数据、估算缺失值、删除异常值等。——做数据科学:直话直说
一个常见但更难注意到的错误是时间泄漏,即来自较晚时间的数据点包含在测试数据中。例如,如果用于预测股票价值的模型是用未来的数据点训练的。例如,2011 年,研究人员声称他们的模型可以利用 Twitter 用户的情绪预测股市。然而,令人难以置信的准确性(87%)是由于时间泄漏误差。要处理的其他复杂错误是训练和样本之间的非独立性(例如,训练和测试集包含来自相同患者的图像)和测试分布中的采样偏差。
有趣的是,作者正确地表明了数据泄漏错误对许多模型的性能有很大的影响。
该图表明,通过纠正不同政治学文章中的数据泄漏错误,已发布模型的性能正在急剧下降。图片来源(原创文章)。
此外,研究人员经常声称他们的模型比其他模型表现更好。作者表明,通过纠正数据泄漏错误,复杂模型(如随机森林、 AdaBoost 、梯度增强树等)实际上比简单模型(如逻辑回归)表现更差。下表中有几个例子:
由于某些情况下的数据泄漏,复杂模型比逻辑回归表现得更好。图片来源(原文)。
结论
再现性是科学的基础。随着机器学习和人工智能在科学领域的使用越来越多,再现性面临着额外的变化。事实上,这种重复性的缺乏正在影响从科学论文中获得应用的可能性。
传统上,科学论文在发表之前必须通过一个被称为同行评议的过程(论文由该领域的专家进行修改)。然而,如今的评论者更难找到(因为他们没有报酬),而且他们往往对机器学习知之甚少。因此,文章中的方法错误很容易在审查过程中被忽略。
“到目前为止,人工智能最大的危险是,人们过早地断定他们理解它。”—埃利泽·尤德科夫斯基
作者在文章中提出了一个模型信息表,以确保科学论文检测/防止数据泄漏。然而,许多科学文章呈现不完整的方法部分,并且经常不发布代码。此外,代码通常写得不好或者没有适当的文档,因此很难重用。因此,科学期刊的编辑在选择审稿人时应该更加小心,并要求根据标准指南发布和记录代码。
例如,心理学从统计学的使用中获得了有益的效果,但是它的粗心使用造成了再现性问题。同样,机器学习(和人工智能)在科学研究中具有变革力量,但应该由专家来处理(或至少与知道如何使用它的人合作)。
其他资源
- 关于再现性危机(此处,此处)
- 关于数据泄露(此处,此处)
- 机器学习中的再现性(这里,这里)
如果你觉得有趣:
你可以寻找我的其他文章,你也可以 订阅 在我发表文章时获得通知,你也可以在**LinkedIn**上连接或联系我。感谢您的支持!
这是我的 Github 知识库的链接,我计划在这里收集代码和许多与机器学习、人工智能等相关的资源。
**https://github.com/SalvatoreRa/tutorial
或者随意查看我在 Medium 上的其他文章:
**
机器学习算法备忘单
原文:https://towardsdatascience.com/machine-learning-algorithms-cheat-sheet-2f01d1d3aa37
5 种常见算法的快速参考指南
凯勒·琼斯在 Unsplash 上拍摄的照片
机器学习的“ 没有免费的午餐” 定理指出,没有单一的机器学习算法可以解决所有类型的机器学习问题。
机器学习任务可能会有很大差异,算法的选择将取决于数据的大小、维度和稀疏性等因素。目标变量、数据质量以及特征内部和特征与目标变量之间存在的相互作用和统计关系。
因此,不可能简单地选择一种算法用于“一刀切”的方法。不同的算法将更适合特定的任务,这取决于它们的具体工作方式。数据科学家通常会选择最终使用的算法,首先确定适合特定问题的算法子集,然后对这些算法进行实验以找到最佳选择。
在本文中,我将提供一个快速参考指南,介绍机器学习中最常用的五种算法。这将介绍算法的内部工作原理,以及使每个算法更适合某些任务的考虑因素。
这将包括对线性回归、逻辑回归、随机森林、XGBoost 和 K-means 的简要介绍。对于每个算法,我将涵盖以下内容:
- 算法如何工作。
- 一个示例代码实现。
- 在适当情况下使用算法的指南。
- 优缺点。
1.线性回归
线性回归是一种受监督的机器学习算法,用于预测连续的目标变量。对于简单的线性回归,其中有一个自变量(特征)和一个因变量(目标),该算法可由以下等式表示。
y = a + bX
其中 y 为因变量, X 为解释变量,b为直线的斜率, a 为截距。
简单的线性回归可视为散点图,其中 x 轴包含因变量,y 轴包含自变量。线性回归算法通过数据点绘制一条最佳拟合线,使预测输出和实际输出之间的差异最小化。
线性回归。作者图片
代码示例
应该在什么时候使用?
- 线性回归只能用于解决基于回归的问题。
- 因变量和自变量之间必须有线性关系。
- 残差必须形成正态分布。
- 特征之间必须没有相关性。
- 该算法假设训练数据是随机采样的。
- 最适合基于回归的问题,其中数据中的关系既线性又简单。
优势
- 高度可解释性和快速训练。
- 在线性可分数据上表现非常好。
不足之处
- 对异常值不稳健。
- 非常简单,因此它不能很好地模拟现实世界数据中的复杂性。
- 这种算法也容易过拟合。
2.逻辑回归
逻辑回归本质上是为适应分类问题而建模的线性回归。逻辑回归不是拟合直线,而是应用逻辑函数 来压缩 0 和 1 之间的线性方程的输出。结果是穿过数据点的 S 形曲线而不是直线,如下图所示。
逻辑回归曲线。作者图片
选择介于 0 和 1 之间的阈值来分隔类别,通常为 0.5。本质上,我们在 0.5 处画一条横过 S 曲线的水平线。这条线以上的任何数据点都属于 1 类,线以下的任何数据点都属于 0 类。
代码示例
应该在什么时候使用?
- 这种算法只能用于解决分类问题。
- 特征和目标变量之间必须有线性关系。
- 观察值的数量必须大于特征的数量。
- 最适合于数据关系既线性又简单的分类问题。
优势
- 与线性回归一样,该算法可解释性强,训练速度快。
- 它在线性可分数据上表现非常好。
不足之处
- 容易过度拟合。
- 与线性回归一样,它不能很好地模拟复杂的关系。
3.随机森林
随机森林算法构建了一个决策树的“森林”。森林中的每棵树都根据一组给定的特征生成一个预测。一旦生成了所有预测,就进行多数投票,并且最常预测的类别形成最终预测。
随机森林生成的单一决策树。作者图片
决策树是一种非常直观的算法。它有一个类似流程图的结构,包含一系列代表测试的节点。每个测试的结果导致一个分裂,并且一个或多个叶节点被创建,直到实现最终预测。超参数决定了树增长的深度和使用的节点分裂函数。
随机森林算法遵循以下步骤序列:
- 根据森林中树木的数量,将训练数据集随机分成多个样本。树的数量通过超参数设置。
- 使用其中一个数据子集并行训练决策树。
- 评估所有树的输出,并将最常发生的预测作为最终结果。
代码示例
应该在什么时候使用?
- 该算法可用于解决基于分类和回归的问题。
- 由于该算法固有地执行特征选择,因此它特别适合于具有高维数的大型数据集。
优势
- 它可以模拟线性和非线性关系。
- 它对异常值不敏感。
- 随机森林能够在包含缺失数据的数据集上运行良好。
不足之处
- 随机森林很容易过度适应,虽然这可以通过修剪得到一定程度的缓解。
- 它不像线性回归和逻辑回归那样具有可解释性,尽管可以提取特征重要性来提供某种程度的可解释性。
4.XGBoost
XGBoost 是一种基于梯度推进决策树的算法。它与 Random Forest 相似,都是构建一个决策树集合,但 XGBoost 不是并行训练模型,而是顺序训练模型。每个决策树从先前模型产生的错误中学习。这种顺序训练模型的技术被称为增强。
XGBoost 中的梯度指的是使用弱学习者的特定类型的增强。弱学习者是非常简单的模型,仅仅比随机机会表现得更好。该算法从一个初始的弱学习者开始。每个随后的模型都以先前决策树产生的错误为目标。这种情况会持续下去,直到无法取得进一步的改进,并最终形成一个强学习者模型。
代码示例
应该在什么时候使用?
- 它可用于解决基于分类和回归的问题。
- XGBoost 通常被认为是对结构化数据进行监督学习的最佳和最灵活的算法之一,因此适用于广泛的数据集和问题类型。
优势
- XGboost 非常灵活,在小型和大型数据集上都能很好地工作。
- 与其他复杂算法相比,该算法计算效率高,因此训练模型更快。
不足之处
- 它不能很好地处理非常稀疏或者非结构化的数据。
- 它被认为是一个黑盒模型,比其他一些算法更难解释。
- 由于模型从其前辈的错误中学习的机制,XGBoost 可能对异常值很敏感。
5.k 表示
K-means 是最流行的聚类算法之一,这是一种无监督的机器学习形式,旨在找到训练数据集中的相似示例组。
该算法首先初始化随机聚类质心。然后,对于每个数据点,通常使用距离度量 欧几里德 距离或 余弦 相似度来将其分配给最近的质心。一旦分配了所有数据点,质心就移动到所分配数据点的平均值。重复这些步骤,直到质心分配停止变化。
k-意味着具有可视化质心的簇。作者图片
代码示例
应该在什么时候使用?
- K-means 只适合于非监督聚类。
- 对于这类问题,它通常被认为是一个很好的全能算法。
优势
- 这是一个实现起来相对简单的算法。
- 它可以用于大型数据集。
- 由此产生的聚类很容易解释。
不足之处
- k 均值对异常值很敏感。
- 该算法没有找到最佳的聚类数。这必须在实现之前通过其他技术来确定。
- 聚类的结果不一致。如果 K-means 在数据集上运行多次,每次都会产生不同的结果。
本文涵盖的算法是机器学习中最广泛使用的一些算法。还有许多算法可供使用,每一种算法都有自己适合特定问题的地方。正如本文开头提到的,目前没有一种算法可以解决所有问题,尽管 XGBoost 是解决结构化数据问题的最接近的算法。
这一介绍将提供一些关于数据科学家如何以及为什么选择一种算法而不是另一种算法的见解。下面是每种算法对特定类型问题的适用性的简要总结:
- **线性回归:**最适合解决基于回归的数据集问题,其中存在线性关系,且关系相对简单。
- **逻辑回归:**最适合解决数据线性可分、数据集维度低的分类问题。
- **随机森林:**最适合具有复杂关系的大型高维数据集。
- XGBoost: 适用于广泛的结构化数据集和问题。与随机森林相比,计算效率更高。
- K-means: 最适合解决无监督聚类问题。
感谢阅读!
承认
iris 数据集用于生成本文中的决策树图: 该数据集用于 r . a . Fisher 1936 年的经典论文, 分类问题中多重测量的使用 。它是在 CCO 1.0 通用版(CCO 1)许可下使用的。
机器学习与 Rust(第四部分):Torch 中的神经网络
原文:https://towardsdatascience.com/machine-learning-and-rust-part-4-neural-networks-in-torch-85ee623f87a
我们可以在 Rust 中使用 PyTorch 吗?什么是锈绑定?tch-rs 是什么?Rust 中的神经网络研究
乔尔·菲利普在 Unsplash 上拍摄的图片
自从我们上次看到 Rust 及其在机器学习中的应用已经有一段时间了——请向下滚动到底部,查看以前关于 ML 和 Rust 的教程。今天我将向大家介绍 Rust 中的神经网络。存在一个铁锈火炬,它允许我们创建任何我们想要的神经网络。捆绑是焊炬落地的关键。绑定允许创建外部函数接口或 FFI,这在 Rust 和用语言编写的函数/代码之间建立了一座桥梁。在 Rust nomicon 中可以找到很好的例子
要用 C 和 C创建绑定,我们可以使用 bindgen,一个由自动生成 Rust FFI 的库。从绑定到 PyTorch 的 C api, Laurent Mazare 已经帮助 Rust 社区拥有了一个 Rustacean 版本的 PyTorch。正如 GitHub 页面所说,tch 在 C++ libtorch 周围提供了薄薄的包装。最大的好处是,这个库和原来的严格相似,所以没有学习障碍需要克服。核心代码相当易读。
初始化和线性:让我们学习巨人肩膀上的铁锈
首先,我们来看一下代码。这是进一步了解 Rust 基础设施的最佳起点。
首先,为了对 Rust FFI 有所了解,我们可以查看这些文件。其中大部分是自动生成的,而 Laurent 和他的同事们已经编写了大量代码,将 c++ Torch API 与 Rust 连接起来。
下面,我们就可以开始阅读src
中的核心代码了,具体来看一下[init.rs](https://github.com/LaurentMazare/tch-rs/blob/main/src/nn/init.rs:)
。在定义了一个enum Init
之后,有一个公共函数pub fn f_init
,它匹配输入初始化方法并返回一个权重张量和一个偏差张量。我们可以学习 C 中反映switch
的match
和 Python 3.10 中的match
的用法。权重和偏差张量通过随机、统一、明凯或正交方法初始化(图 1)。
图 Rust 中的匹配大小写,它反映了 C 中的 switch 和 Python 3.10 中的 match
然后,对于类型enum Init
,我们有了方法实现 [impl Init](https://github.com/LaurentMazare/tch-rs/blob/a022da9861efbe66a4920d318166341c3a60be9e/src/nn/init.rs#L82)
。实现的方法是一个 setter pub fn set(self, tensor: &mut Tensor)
,这是一个很好的例子来进一步理解 Rust 中所有权和借用的概念:
图 init 的实现。注意&mut 张量,这是解释 Rust 中借力的一个很好的例子。
我们在第一个教程中谈到了借贷。现在是更好地理解这个概念的时候了。假设我们可以有一个类似的set
函数:
pub fn set(self, tensor: Tensor){}
在主代码中,我们可以调用这个函数,传递一个张量Tensor
。Tensor
会被设定,我们会很开心。但是,如果我们再次在Tensor
上呼叫set
呢?嗯,我们会遇到错误value used here after move
。这是什么意思?这个错误告诉你你把Tensor
移到了set
。 A *move*
表示您已经将所有权转让给了set
中的self
,当您再次调用set(self, tensor: Tensor)
时,您希望将所有权归还给Tensor
以便再次设置。幸运的是,在 Rust 中这是不可能的,而在 C++中则不同。在 Rust 中,一旦一个 *move*
已经完成,分配给该进程的内存将被释放。因此,我们在这里要做的是将Tensor
的值借用给set
,这样我们就可以保留所有权。为此,我们需要通过引用调用Tensor
,因此tensor: &Tensor
。因为我们预计Tensor
会发生变异,所以我们必须添加mut
以便:tensor: &mut Tensor
接下来,我们可以看到另一个重要的元素,它很简单,使用了Init
类:[Linear](https://github.com/LaurentMazare/tch-rs/blob/main/src/nn/linear.rs)
,即一个完全连接的神经网络层:
图 3:定义线性结构并为其实现默认配置
图 3 显示了建立一个完全连接的层是多么容易,它由一个权重矩阵ws_init
和偏置矩阵bs_init
组成。重量的默认初始化是通过super::Init::KaimingUniform
完成的,这是我们在上面看到的功能。
然后可以使用功能linear
创建主全连接层。正如您在函数签名中看到的,也就是在<...>
之间,有一些有趣的事情(图 4)。其一, 一生注释 ['a](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html)
。如上所述,Rust 会自动识别变量何时超出范围并被释放。我们可以注释一些变量,让它们有一个特定的生命周期,这样我们就可以决定它们能活多久。标准注释是'a
,其中'
表示寿命参数。需要记住的一件重要事情是,这个签名不会修改函数中的任何内容,但是它告诉函数借用者识别所有那些其生存期可以满足我们所施加的约束的变量。
图 4:实现完全连接的神经网络层的功能。在函数签名中,您可以注意到一个生存期注释和一个通用变量 T,它从 nn::Path 借用了一个值
第二个参数是T: Borrow<super::Path<'a>
这个注释的意思是:取 [var_store.rs](https://github.com/LaurentMazare/tch-rs/blob/2c2b4545966be04e8377ffa7f34fe01b9a20acd0/src/nn/var_store.rs#L40)
中指定的nn::Path
,把这个类型借用到T
。Rust 中的任何类型都可以自由借用为几种不同的类型。该类型将用于定义输入硬件(如 GPU),如您在vs:T
中所见。最后,网络的输入和输出维度与LinearConfig
一起被指定为整数in_dim: i64, out_dim: i64
,用于初始化权重和偏差c: LinearConfig.
让我们与巨人同行:你在 Rust 的第一个神经网络
是时候弄脏我们的手玩火炬锈了。让我们使用 MNIST 数据集建立一个简单的线性神经网络,然后是序列网络,最后是卷积神经网络。一如既往,你可以在我的 ML ❤生锈回购上找到所有的材料。 Yann LeCun 和 Corinna Cortes 拥有 MNIST 数据集的版权,并已根据知识共享署名-类似共享 3.0 许可证的条款提供。
Rust 中的一个简单神经网络
和往常一样,新 Rust 项目的第一步是cargo new NAME_OF_THE_PROJECT
,在这里是simple_neural_networks
。然后,我们可以开始用我们需要的所有包设置Cargo.toml
:我们将使用mnist
、ndarry
,显然还有tch
——图 5。我决定使用mnist
提取原始的 MNIST 数据,这样我们可以看到如何转换和处理数组和张量。请随意使用tch.
中已经存在的vision
资源
图 5: Cargo.toml 用于建立一个简单的线性神经网络。
我们将使用mnist
下载 MNIST 数据集,使用ndarray
对图像向量执行一些转换,并将它们转换成tch::Tensor
。
让我们跳到main.rs
代码。简而言之,我们需要:
- 下载并提取 MNIST 图像,并返回用于训练、验证和测试数据的向量。
- 从这些向量中,我们必须执行一些到
Tensor
的转换,这样我们就可以使用tch
。 - 最后,我们将实现一系列时段,在每个时段中,我们将输入数据乘以神经网络权重矩阵,并执行反向传播来更新权重值。
mnist
自动从下载输入文件到这里。我们需要在Cargo.toml
中添加features = ['download']
来激活下载功能。下载文件后,提取原始数据download_and_extract()
,并细分为训练集、验证集和测试集。注意,主函数不会返回任何东西,所以您需要在代码末尾指定-> Results<(), Box<dyn, Error>>
和Ok(())
(图 6)
图 6:从 mnist::MnistBuilder 下载、提取和创建训练、验证和测试集。
现在,代码的第一件事:将一个数组转换成Tensor.
,mnist
的输出数据是Vec<u8>
。训练向量结构具有TRAIN_SIZE
个图像,其尺寸是HEIGHT
乘以WIDTH
。这三个参数可以指定为usize
类型,与输入数据向量一起,可以传递给image_to_tensor
函数,如图 7 所示,返回Tensor
图 7: image_to_tensor 函数,给定输入数据向量、图像数量、高度和宽度,我们将返回 tch::Tensor
输入的Vec<u8>
数据可以用from_shape_vec
整形到Array3
,数值被归一化并转换到f32
,即.map(|x| *x as f32/256.0)
。从一个数组很容易建立一个火炬张量,如第 14 行所示。对于我们的训练数据,输出张量大小为dim1 x (dim2*dim3)
,设置TRAIN_SIZE=50'000
、HEIGHT=28
和WIDTH=28
,输出训练张量大小为50'000 x 784
。
类似地,我们将标签转换为张量,其大小将为dim1
—因此对于训练标签,我们将有一个50'000
长张量https://github . com/ste boss/ML _ and _ Rust/blob/aa7d 495 C4 a2 C7 a 416d 0b 03 Fe 62 e 522 b 6225180 ab/tutorial _ 3/simple _ neural _ networks/src/main . RS # L42
我们现在准备开始处理线性神经网络。在权重和偏差矩阵的零初始化之后:
let mut ws = Tensor::zeros(&[(HEIGHT*WIDTH) as i64, LABELS], kind::FLOAT_CPU).set_requires_grad(true);let mut bs = Tensor::zeros(&[LABELS], kind::FLOAT_CPU).set_requires_grad(true);
类似于 PyTorch 实现,我们可以开始计算神经网络权重。
图 8:主要训练功能。对于 N _ EPOCHS,我们在输入数据和权重及偏差之间执行 matmul。计算每个历元的精确度和损失。如果两个连续损失之间的差异小于三,我们停止学习迭代。
图 8 示出了运行线性神经网络训练的主例程。首先,我们可以用'train
给最外层的 for 循环命名,在这种情况下,撇号不是生命期的指示器,而是循环名的指示器。我们正在监控每个时期的损失。如果两个连续的损失差小于THRES
,当我们达到收敛时,我们可以停止最外面的循环——你可以不同意,但目前让我们保持它:)整个实现非常容易阅读,只是在从计算的logits
中提取精度时需要注意一点,工作就完成了:)
当你准备好了,你可以直接在我的 2019 年 MacBook Pro 上用cargo run
运行整个main.rs
代码,2.6GHZ,6 核英特尔酷睿 i7,16GB RAM,计算时间不到一分钟,在 65 个周期后达到 90.45%的测试准确率
顺序神经网络
现在我们来看顺序神经网络实现https://github . com/ste boss/ML _ and _ Rust/tree/master/tutorial _ 3/custom _ nnet
图 9 解释了顺序网络是如何建立的。首先,我们需要导入tch::nn::Module
。然后我们可以为神经网络fn net(vs: &nn::Path) -> impl Module
创建一个函数。该函数返回Module
的实现,并接收作为输入的nn::Path
,该输入是关于用于运行网络的硬件的结构信息(例如 CPU 或 GPU)。然后,时序网络被实现为输入大小为IMAGE_DIM
和HIDDEN_NODES
节点的线性层、relu
和具有HIDDEN_NODES
输入和LABELS
输出的最终线性层的组合。
图 9:顺序神经网络的实现
因此,在主代码中,我们将神经网络创建称为:
// set up variable store to check if cuda is available
let vs = nn::VarStore::new(Device::cuda_if_available());// set up the seq net
let net = net(&vs.root());// set up optimizer
let mut opt = nn::Adam::default().build(&vs, 1e-4)?;
还有一个 Adam 优化器— 记住 [opt](https://www.google.com/search?client=safari&rls=en&q=question+mark+in+Rust&ie=UTF-8&oe=UTF-8)
https://www.google.com/search?client=safari&rls=en&q=question+mark+in+Rust&ie=UTF-8&oe=UTF-8末尾的 [?](https://www.google.com/search?client=safari&rls=en&q=question+mark+in+Rust&ie=UTF-8&oe=UTF-8)
,否则你会返回一个Result<>
类型,它没有我们需要的功能。在这一点上,我们可以简单地按照 PyTorch 的过程来做,所以我们将设置一些 epochs,并用优化器的backward_step
方法和给定的loss
来执行反向传播
图 10:针对给定的历元数 N_EPOCHS 训练序列神经网络,并使用 opt.backward_step(&loss)设置反向推进;
卷积神经网络
我们今天的最后一步是处理卷积神经网络:https://github . com/ste boss/ML _ and _ Rust/tree/master/tutorial _ 3/conv _ nnet/src
图 11:卷积神经网络结构
首先,你可以注意到我们现在使用的是nn::ModuleT
。这个模块特征是附加训练参数。这通常用于区分训练和评估之间的网络行为。然后,我们可以开始定义网络Net
的结构,它由两个 conv2d 层和两个线性层组成。Net
的实现陈述了网络是如何构成的,两个卷积层的步幅分别为 1 和 32,填充为 32 和 64,膨胀分别为 5 和 5。线性层接收 1024 的输入,最后一层返回 10 个元素的输出。最后,我们需要为Net
定义ModuleT
实现。这里,前进步骤forward_t
接收一个额外的布尔参数train
,它将返回一个Tensor
。前一步应用卷积层,以及max_pool_2d
和dropout
。dropout 步骤只是出于训练目的,所以它与布尔值train
绑定在一起。
为了提高训练性能,我们将从输入张量中分批训练 conv 层。为此,您需要实现一个函数来将输入张量分成随机批次:
图 12:为从图像输入池创建批次生成随机索引
generate_random_index
获取输入图像数组和我们想要分割的批次大小。它创建一个随机整数的输出张量::randint
。
图 13:卷积神经网络的训练时期。对于每个时期,我们通过输入数据集进行批处理,并训练计算交叉熵的模型。
图 13 显示了训练步骤。输入数据集被分成n_it
批,其中let n_it = (TRAIN_SIZE as i64)/BATCH_SIZE;
。对于每一批,我们计算网络损耗并用backward_step
反向传播误差。
在我的本地笔记本电脑上运行卷积网络需要几分钟,实现了 97.60%的验证准确率。
结论
你成功了!我为你骄傲!今天我们来了解一下tch
以及如何设置一些计算机视觉实验。我们看到了初始化和线性层代码的内部结构。我们回顾了 Rust 中关于借用的一些重要概念,并了解了什么是终生注释。然后,我们开始实现一个简单的线性神经网络、一个顺序神经网络和一个卷积神经网络。在这里,我们学习了如何处理如何输入图像并将其转换为tch::Tensor.
,我们看到了如何使用模块nn:Module
作为一个简单的神经网络,来实现一个向前的步骤,我们还看到了它的扩展nn:ModuleT
。对于所有这些实验,我们看到了两种执行反向传播的方法,要么使用zero_grad
和backward
,要么直接将backward_step
应用于优化器。
希望你喜欢我的教程:)敬请期待下一集。
支持我的写作:
通过我的推荐链接加入 Medium 来支持我的写作和项目:
https://stefanobosisio1.medium.com/membership
如果有任何问题或意见,请随时给我发电子邮件,地址是:stefanobosisio1@gmail.com,或者直接在 Medium 这里。
以前关于 Rust 和 ML 的教程
https://levelup.gitconnected.com/machine-learning-and-rust-part-1-getting-started-745885771bc2 https://levelup.gitconnected.com/machine-learning-and-rust-part-2-linear-regression-d3b820ed28f9 https://levelup.gitconnected.com/machine-learning-and-rust-part-3-smartcore-dataframe-and-linear-regression-10451fdc2e60
什么是正则化:偏差-方差权衡
原文:https://towardsdatascience.com/machine-learning-bias-variance-tradeoff-and-regularization-94846f945131
用看不见的数据改进预测的良好实践
马库斯·温克勒在 Unsplash 上的照片
当我们谈论使用机器学习模型的预测时,理解预测误差(即偏差和方差)很重要。任何机器学习模型的目标都是找到一个最小化对看不见的数据的预测误差的模型。在模型最小化偏差和方差之间的预测误差的能力上有一个权衡。理解这些概念将有助于我们解决过拟合和欠拟合的问题。
图 1(作者图片)
什么是偏见?
当我们开发一个模型时,我们可以用训练好的模型对目标进行单独的预测。例如,贝叶斯线性回归可以使用遵循多元高斯分布的所有可能的回归权重进行重复预测。因此,对于给定的模型,我们可能有一组不同的预测值。
在图 1 中,我们假设红色目标的中心是真实的输出值。使用训练好的模型,我们可以在蓝色圆圈内生成预测,因此蓝色圆圈的中心代表平均预测值。
偏差是我们模型的平均预测值和我们试图预测的真实值之间的差异。
偏差告诉我们训练模型预测真实目标的能力。偏差越低,给定的训练模型就越有能力。
什么是方差?
在图 1 中,假设蓝色圆圈的半径代表预测值的方差。
方差是对训练模型给定输入的预测值的可变性(又名分布)的度量。
方差越低,经过训练的模型做出的预测就越精确。
什么是不可约误差?
除了偏差和方差之外,第三种误差称为不可约误差。
不可约误差代表数据中的噪声无法被训练好的模型解释。不管训练出来的模型有多好,它总是存在的。
预测误差是方差、偏差和不可约误差的总和
让我们从数学上证明**。**
****
什么是不合身?
当模型无法捕捉和概括数据的潜在趋势时,就会出现欠拟合。直觉上,它与数据拟合得不够好,因此,它会在训练和测试数据上产生很高的误差(即高偏差**)。**
不适合的原因可能是
- 缺乏开发模型的数据(例如,少量的训练数据,关键解释变量不可用)。
- 基础模型不能捕捉数据中的模式(例如,使用具有非线性数据的线性模型)
什么是过度拟合?
另一方面,当模型太好地拟合训练并且它开始对训练数据的噪声建模时,发生过拟合。它在训练数据中具有低误差(即低偏差**,但在测试数据中具有高误差(即高方差)**
过度拟合的原因可能是
- 由于模型过于复杂,包括太多变量或包括有问题的变量**,如高阶多项式变量、无关变量、高度相关的变量或同时受响应变量影响的变量。**
- 模型的过度训练。例如,在没有显式停止标准的情况下训练决策树容易过度拟合。
偏差-方差权衡
理想情况下,我们希望减少模型的偏差和方差。然而,这是非常困难的,有时是不可能实现的。当您试图减少定型数据的预测误差时,测试数据的预测误差可能会增加。偏差和方差经常朝着相反的方向移动。
当使用训练数据开发模型时,很容易拟合复杂的模型来进行偏差较小的预测,但由于测试数据的预测方差较高,可能不会产生最佳结果。
或者,我们可以探索偏差和方差之间的权衡,在这种情况下,接受一些偏差以减少方差可能会更好。因此,我们对未知数据的预测误差会更小。
图 2(作者图片)
什么是正规化?
正则化是一种实现偏差和方差的折衷的概念,有助于减少预测误差。先说一些常用的正则化技术。
类型 1:修改成本函数
在线性回归模型中,我们可以改变 代价函数 来构建实现正则化的不同模型。
例如,在一个岭回归模型中,我们将成本函数修改为
在一个拉索回归模型中,我们将成本函数修改为
在弹性网回归模型中,我们将成本函数修改为
线性回归的这三个变量与正则化相关联,正则化会损害模型的灵活性和复杂性,以防止过度拟合的风险。
这里λ是一个超参数,它决定了模型的灵活性将受到多大的惩罚。λ的值越高,对每个特征的权重施加的约束就越多,因此,它可以防止权重变得太大,从而避免过度拟合。然而,如果λ的值太高,可能会产生欠拟合的问题,因为关键解释变量的权重变得太小,它不能准确地解释响应变量。
我们可以使用 K 倍交叉验证找到λ的最佳值(我们将在后面介绍)。
类型 2: K 倍交叉验证
K-Fold 交叉验证是另一种用于训练机器学习模型的流行工具。这种方法的思想很简单,我们从观测数据中创建多组训练数据,然后训练模型并基于验证数据评估模型,这类似于在看不见的数据上评估模型。它包括以下步骤,
图 3(作者图片)
步骤 1:我们将观察到的数据拆分成训练数据和测试数据(例如,1:9 拆分,有时需要分层)。然后,我们将训练数据随机放入训练文件夹和验证文件夹。例如,在 5 重交叉验证中,我们将有 5 组训练数据和验证数据。
步骤 2:在每组中,我们使用来自训练文件夹的数据训练模型,并在验证文件夹中评估模型性能。使用验证折叠评估模型模拟了 ML 模型的实际应用(即样本外预测或对未知数据的预测)。然后我们得到 K 个性能结果。
步骤 3:对于给定的训练模型,我们通过平均 K 个性能结果来计算总体性能分数。
步骤 4:挑选具有最佳总体性能得分的训练模型,并将该模型应用于测试数据以计算性能得分。
测试数据中的性能分数应该接近整体性能分数。如果情况更糟,那么我们需要更深入地研究数据,调查差异。
类型 3:修改最大似然算法
另一种避免过度拟合的方法是简单地改变机器学习算法。例如,一棵过度生长的决策树容易过度拟合。为了解决这个问题,我们可以使用一个具有预定义最大树深度的随机森林。在训练神经网络的情况下,我们可以应用 dropout 方法来确保模型可以用所选节点的随机子集来概括模式。
最终注释
为了训练一个预测误差最小的机器学习模型,我们需要确保我们探索了偏差和方差之间的权衡。正则化是我们在开发模型时需要考虑的一个重要步骤。
如果你对线性回归和因果推断感兴趣,这里有一些相关的帖子可以浏览。
- 因果推断:计量经济模型 vs. A/B 检验
- 线性回归与逻辑回归:OLS、最大似然估计、梯度下降
- OLS 线性回归:无偏、一致、蓝色、最佳(有效)估计量
- 线性回归因果推断:省略变量和无关变量
- 用线性回归进行因果推断:内生性
- 与 OLS 的线性回归:异方差和自相关
感谢您的阅读!!!
如果你喜欢这篇文章,并且想请我喝杯咖啡,请点击这里。****
您可以注册一个 会员 来解锁对我的文章的完全访问,并且可以无限制地访问介质上的所有内容。如果你想在我发表新文章时收到电子邮件通知,请订阅。
机器学习猫狗品种分类器
原文:https://towardsdatascience.com/machine-learning-cats-and-dogs-breeds-classifier-b26a9df45000
实践教程
机器学习猫狗品种分类器
了解如何使用 Python 中的机器学习模型来创建猫狗品种分类器,以预测您宠物的品种
遇见卡罗莱娜(没错,是一个人的名字;不,不是我选的)——图片作者
谁不喜欢动物呢,对吧?就我个人而言,如果我能把它们放在我所有的项目中,我就能享受生活了。如果你有点像我,想探索机器学习技术,那么这个教程可能就是你正在寻找的。
我们要做什么?
在本教程中,我将向您展示如何获取一个充满可爱动物图像的数据集,使用一些黑魔法,最终得到一个甚至可以对您的个人宠物图片进行分类的模型。这个模型可以用在你的个人应用程序中,这样你就可以向你的朋友和家人展示你学到的很酷的东西。
这篇教程是基于我和同事 Pedro Silva 一起为大学课程做的一个项目。我们一起设法在测试集上创建了一个准确率为 92%的模型。也就是说,它正确地分类了未用于训练的数据集部分中 92%的图像。这不是一个糟糕的分数,但它可以被改进——我也将告诉你如何改进!
在我们开始之前,你应该知道这些技术在计算上非常昂贵,并且很可能需要很长时间来执行。但是不要因此而气馁!在等待的时候,你可以做很多事情,比如遛狗……或者喝杯咖啡,或者小睡一会儿。事实上,如果你想拖延,这是一个很好的借口;)
数据集
为了实现我们的目标,我们将使用 Kaggle 上的猫狗品种分类牛津数据集(可以根据知识共享署名-共享 4.0 国际许可证下载用于商业/研究目的)。这个数据集总共有 7384 张宠物的图片,可以分为 37 个品种(或者,在这个上下文中,类)。
数据集中的图像示例—数据集中的图像
这些图片都是非正式的宠物图片,就像我们通常在手机上看到的一样,这很重要,因为这意味着我们的模型将更加通用。否则,只有当照片具有某种特征或以特定方式拍摄时,它才能理解宠物品种。
我们需要验证的另一件事是每个类的图像分布。我们需要知道这一点,因为如果类别中的图像数量不完全相同,那么该模型就不太擅长预测某些品种。为了了解这种情况,让我们绘制一个图表,并开始分析数据集!
绘制数据集中每个类别的图片数量
首先,我们需要导入一些依赖项,并将所有图像文件名加载到一个列表中。我们发现有些图像打不开,所以我们把它们的文件名放在一个集合中,从一开始就把它们排除在外。
加载所有图像的名称
数据集包括一个文本文件,其中包含一些关于信息组织方式的信息。我们可以使用这些信息来填充 2 个字典:info_by_name
和info_by_id
。尽管该文件也给了我们关于图像数量的信息,但我决定不信任它,并在代码中对其进行计数。我知道这是可选的,我只是展示我是如何做的。
将相关数据保存到字典中,以便于访问
现在我们已经有了字典中提到的所有信息,我们可以继续绘制条形图。
用每个品种的图片分布绘制图表
并且输出应该如下!
每个品种图片分布的条形图—按作者分类的图片
正如我们所见,每个品种的图像数量变化不大,这很好!尽管猫和狗的图片数量差异很大,但这应该不是问题,因为分类器并不专注于确定图片是猫还是狗。
基本原则
现在我们已经看了数据,是时候考虑我们将要构建的基础了。因为这个问题被认为是细粒度图像分类的一个问题——它旨在区分具有非常相似特征的类别——经典的机器学习方法不会成功。
一般来说,在解决这类问题时,一个好的做法是从查看以前的类似作品开始。在研究这个问题时,我们发现一些作品特别有趣和有用:“使用深度学习识别狗的品种”(z . rádully,C. Sulyok,Z. Vadászi 和 a . zlde),[“了解你的狗品种:用深度学习识别狗的品种”(P. Borwarnginn,W. Kusakunniran,S. Karnjanapreechakorn,和
- Thongkanchorn)](https://link.springer.com/article/10.1007/s11633-020-1261-0) 和“狗分类成 12
普遍的看法是,卷积神经网络(CNN)对于涉及大量复杂数据的问题是最可行的,从这些数据中必须获得抽象特征,如形状和颜色,以获得准确的结果。我不会详细介绍什么是 CNN 在 Medium 上有很多关于它的文章——但我会在必要时给出一个概述。
迁移学习
许多被分析的文献提到迁移学习是一种好的方法,不仅可以提高模型的性能,还可以降低其训练的计算复杂度。
该技术包括:使用预先训练的模型作为起点;冻结这些图层,以避免在拟合阶段破坏它们已经拥有的信息;给它添加一些新的图层。
基本模型被用作理解图像分类任务中的共同特征的框架,例如形状和颜色。前提是这些特征对于每幅图像都是相似的,因此我们可以节省一些时间来重新使用它们之前计算的权重。另一方面,新层的目的是使模型适应新的更具体的特征,从而允许模型对我们新的和具体的数据集进行预测。
这甚至可以通过解冻先前训练好的模型的最后几层来改善,这个过程叫做微调。我们一会儿就来看看这个。
模型架构
由于在 imagenet 数据集上预训练的 InceptionV3 架构在文献中多次被提及,被认为是解决类似问题的迁移学习基础模型的良好架构,我们将使用它。
该架构由不同层的多种组合组成,包括卷积层、批量归一化层、和池层。这是一个非常深刻和复杂的网络,我不打算详述。
在基本模型的末尾,我们将添加 5 层:
- 池层:该层将直接与 InceptionV3 层的输出交互。通过应用将形成像素组的过滤器并对其应用操作,池化图层用于对输入进行缩减采样。在我们的例子中,使用了平均池,它计算那些像素组的平均值。
- 密集层:具有特定数量神经元的单一连接层——在我们的例子中是 256 个。选择的激活函数是 ReLU ,它简单地将任何负输入映射到 0,将任何正输入映射到自身。它被广泛使用,因为它是实际输入的线性函数,没有任何饱和风险。
- 丢弃层:其目的是根据正则化参数随机忽略前一层输出的一定百分比。这一层的目标是防止数据过度拟合。
- 批量标准化层:用于按顺序缩放输出,使其平均值为 0,标准差为 1。
- 密集层:这是输出层,全连接的一层,有 37 个神经元,每个对应我们的一个分类类。这里使用了 SoftMax 激活功能。与逻辑函数(如 sigmoid )相比,对于多分类问题,这是一个更好的激活函数。这是因为 SoftMax 输出一个概率向量,每个类对应一个值,使我们能够轻松理解哪些类具有更高的值,以及更大的概率是正确的。
我们还需要向我们的模型添加一个新的输入层,以正确的格式准备要传递给 InceptionV3 模型的图像。该图层直接与输入数据交互,并将接收到的每个值重新调整为-1 和 1 之间的值。
优化器选择为 Adam ,并实现随机梯度下降,其默认学习率为 0.001,我们将在稍后更改。准确性是我们将在培训期间用来评估模型性能的指标。
使用 Keras 库,很容易使用这个架构组装模型。
函数创建具有指定配置的模型
准备图像
为了让这些图像可以在模型中使用,我们必须对它们进行预处理。这包括将它们重新整形为(299,299,3)张量,因为这是 InceptionV3 模型输入的推荐形状。
一个常见的预处理任务是将图像的彩色图变为黑白。我们不打算这样做,因为颜色实际上是这个问题所必需的,因为它们对区分品种有特殊的重要性。此外,我们的模型需要一个三维输入。
这里我们将使用之前填充的info_by_breed
字典来获取每张图片的品种标识符,这样我们就可以将图片数据保存在其 ID 中。
创建两个数组:一个包含正确格式的图像,另一个包含各自的 ID
现在我们有了数据,我们需要把它分成两大部分:训练和测试数据。为此,我们要做一个叫做 的分层拆分 。这意味着每一类中图像数量之间的比例在测试集中保持相等。
使用分层分离来分离训练和测试数据
超参数优化
尽管我们已经有了一个(希望)可行的模型,但是它的开发过程还没有结束!为了改进当前的设置,我们可以改变很多变量,以了解哪些变量可以产生最好的结果。我们将迅速探索改变学习率和下降值如何影响模型的能力。
如果您想立即测试这个模型,您可以向下滚动以了解如何。我建议您继续阅读本节,了解超参数优化的工作原理及其重要性,从我们如何发现某个配置是否适合我们的问题开始。
k 倍交叉验证
K-Fold 交叉验证是一种众所周知的优化超参数的技术。这是一种将训练数据分为 K 部分(或倍)的技术。然后,该模型将在 K-1 个零件上进行训练,并在其余零件上进行验证。这要做 K 次,因为每个折叠都有机会扮演验证的角色。然后对结果进行平均。这试图解决所使用的验证数据不能很好地代表模型性能的问题。因为所有数据都间接用于验证,所以这种情况不会发生。
尽管最常见的折叠次数似乎是 5 次或 10 次,但我们决定只用 3 次,因为这个过程需要时间。由于时间限制,我们使用了 32 和 15 个时期的批量大小。我鼓励你尝试其他的价值观!
以下函数返回一个字典,其中包含一些指标随时间的变化:训练数据准确性和丢失,以及验证准确性和丢失。
在这里,您可能注意到了代码中的一些奇怪之处——函数onehotencode_func
!其定义如下:
它有一个非常重要的目标,将我们在y_train
数组中使用的品种 id 的十进制表示转换为另一种表示——只使用 1 和 0。每个值都将被转换成一个包含 37 个元素的数组,这些元素是我们的数据集中的类的数量,除了在与被转换的值相等的位置之外,都用 0 填充。例如,数字 0 将被转换为[1,0,0,0,0,0,...]
,数字 3 将被转换为[0,0,0,1,0,0,...]
。
还记得我们的密集层有 37 个神经元,每个神经元输出一个概率值吗?我们可以把每一个看作是数组中的一个元素。如果我们将最大值设置为 1,并将所有其他值设置为 0,我们将最终得到一个数字一个 - 热编码。该数字是模型对该输入的预测!
学习率
下一个要点中的代码对学习率的不同值执行这个过程,并将提到的度量保存在一个名为lr
的字典中。
现在我们可以使用存储在这本字典中的数据来可视化信息。
输出应该是两个图表——每个学习率值一个图表,每个图表都有一条针对每个指标的曲线。
不同学习率指标的演变—图片由作者提供
可以清楚地看到较低的学习率如何有益于该模型。训练和验证的准确性显示出相似的曲线和值,甚至训练损失也是相似的,尽管随着学习率的降低,训练损失会有所降低。最大的区别在于验证损失。随着更大的学习速率,该曲线没有收敛到一个值,可能是因为由于更大的步长,它“跳跃”通过局部最小值。在这次经历中,我们得出结论,0.001 是所有比较中最好的学习。
下降值
改变其他变量的过程非常相似。下一个代码片段重复了相同的过程,但是更改了 dropout 值。
显示图表的代码与之前的非常相似!如果一切顺利,结果应该如下。
指标的演变改变了差值—按作者分类的图片
在这种情况下,差异似乎更不明显。在最后一个时期中,具有 0.25 的下降的训练精度仅好于 0.007,并且还具有大约 0.037 的较低训练损失。当参考验证数据时,精度比 0.35 的压降低 0.003,验证损失大 0.025。这里没有明确的“赢家”,但是我们将假设 0.35 是一个更好的值,因为在验证数据中结果稍微好一些。
模型评估
正如我们所划分的,训练集构成了总数据集的 30%,其对应于 2216 幅图像。既然我们已经发现了最佳超参数,我们终于可以用它来检查我们模型的准确性了。
首先,我们创建一个模型,并根据训练数据对其进行训练。我使用了 32 和 15 个时期的批量大小。然后,我们让它根据我们的测试数据进行预测,并将值从一次性编码转换为十进制值——预测品种的 ID,以便更好地可视化正在发生的事情。
现在有一些有趣的方法来直观显示模型预测图像的好坏。其中之一是混淆矩阵,这是理解模型预测错误的情况的一种非常好的方式。我们可以用下面的代码展示这个矩阵的热图表示。
这段代码应该会向我们展示一个如下图所示的图形。
混淆矩阵-作者图片
有趣的是,这个模型在预测埃及猫品种时遇到了一些麻烦。它经常预测品种孟加拉而不是它。当看这些图片时,就有可能明白为什么会发生这种情况,因为它们彼此相当接近,尽管有时它们有不同的皮毛颜色。然而,在绝大多数情况下,分类器能够预测正确的品种。
孟加拉猫和埃及猫图片示例-来自数据集的图片
微调基本模型
正如我之前所说的,在许多事情中,有一件事我们可以做,以提高模型性能,我想在这里探讨一下,这就是解冻预训练的 InceptionV3 模型的最后一些层。
有了一个已经根据我们的数据训练好的模型,我们现在可以解冻基础模型的一些层。在这种情况下,我们选择解冻基础模型层的最后三分之一。然后,该模型使用 0.0001 的非常低的学习率再训练 10 个时期。程序如下。
现在我们可以像以前一样重复同样的技术来评估我们新改进的模型。如果我们给出混淆矩阵的一个表示,我们应该得到一个这样的表示。
混淆矩阵-作者图片
很明显,这一次模型在区分孟加拉猫和埃及猫时没有那么多麻烦。我们甚至成功地将精确度、召回率、f1 分数和准确度提高到了 92%。
结论
这当然不是一篇短文,但是我真诚地希望你在这个过程中学到了一些东西。我们仍然可以做很多事情来改进这个模型,使它更加稳健。我把它作为家庭作业留给你:)你也可以在 GitHub 上点击查看 Jupyter 笔记本上的所有代码(以及更多)!
非常感谢您的阅读!请留下您的任何反馈意见。
机器学习工程师——企业中扩展人工智能的合作伙伴
原文:https://towardsdatascience.com/machine-learning-engineers-partners-for-scaling-ai-in-enterprises-ea8d89593e14
(图片由作者提供)
许多行业的企业都在快速采用人工智能(AI)和机器学习(ML)。加速采用的原因有几个,包括需要从多渠道客户交互产生的大量数据中实现价值,以及来自企业运营各个方面的数据存储不断增加。这种增长提出了一个问题,需要哪些知识和技能来帮助组织在企业中扩展 AI 和 ML?
要回答这个问题,重要的是要了解企业为了更好地利用数据正在经历哪些类型的转型。
成长 AI/ML 成熟度
许多大型组织已经超越了单个团队内的试点或样本 AI/ML 用例,开始考虑如何巩固他们的数据科学项目,并将其扩展到其他业务领域。随着数据的变化或更新,组织需要不断优化 ML 模型的结果。
数据科学主流化
数据科学已经成为许多组织的主流。在产品、营销和供应链等各种业务线团队中工作的人都渴望应用预测分析。随着这种增长,分散的数据科学团队正在单个企业中涌现。对于许多希望应用预测技术的人来说,他们在数据科学方面的培训或生产规模 AI/ML 基础设施基础知识方面的培训有限。此外,企业面临着特别技术、工具和流程的激增。
数据日益复杂
已经取得了一些早期的胜利,通常是在结构化或表格数据用例中,组织渴望从大量的非结构化数据中获取价值,包括语言、视觉、自然语言等。组织越来越多地寻求帮助来应对这些挑战的一个角色是机器学习工程师。
什么是机器学习工程师?
我观察到,随着组织在人工智能/人工智能实践中的成熟,他们会超越数据科学家,雇佣具有人工智能工程技能的人。对数百个机器学习工程师职位发布的定性审查揭示了为什么这个角色是满足企业变革需求的一种方式。例如,检查某些术语在招聘信息的自由文本中的出现频率,会发现几个主题;
软件工程
ML 工程师与软件工程功能紧密相关。雇用 ML 工程师的组织已经在他们最初的 AI/ML 试点中取得了一些胜利,他们正在从实施 ML 用例到在他们的组织中扩展、操作和优化 ML 的 ML 采用曲线上移动。许多招聘信息强调 ML 的软件工程方面,而不是纯粹的数据科学技能。ML 工程师需要应用软件工程实践并编写高性能的产品质量代码。
数据
企业正在寻找有能力为 ML 工作流程的各个方面创建管道或可重用流程的人。这既包括与数据工程师(另一个受欢迎的角色)合作,也包括在整个端到端的 ML 过程中为健壮的数据实践创建基础设施。换句话说,ML 工程师创建流程和合作关系,以帮助清理、标记和处理来自整个企业的大规模数据。
生产
许多雇主寻找有端到端 ML 流程经验的 ML 工程师,尤其是将 ML 模型投入生产的工程师。ML 工程师与数据科学家合作,为大规模部署准备好数据和模型;为模型的持续培训、自动验证和版本控制建立管道。
系统
许多人工智能工程师被雇佣来帮助组织将架构、系统和最佳实践落实到位,以将人工智能/人工智能模型投入生产。ML 工程师在云环境或内部基础设施上将 ML 模型部署到生产中。对系统和最佳实践的强调有助于推动一致性,因为数据科学或基础架构基础知识有限的人学会了从预测分析中获取价值。这种对系统化人工智能/人工智能的关注也是开发人工智能/人工智能治理策略的重要前提。
(图片由作者提供)
ML 工程师做什么?
- 大数据
- 复杂的问题
- 驱动洞察力
- 实现商业价值(如客户获取和增长)
- 规模—影响数百万用户(B2B 和消费者)的项目
- 建立最佳实践
(图片由作者提供)
ML 工程师如何工作?
- 跨多个角色的跨职能协作
- 交叉业务
- 软件开发实践
- 敏捷
- 利用最佳实践
ML 工程师居住在组织的什么地方?
在企业内部,ML 工程师存在于各种团队中,包括数据科学、软件工程、研发、产品组、流程/运营和其他业务部门。
有哪些行业在寻找这项技能?
虽然对 ML 工程师的需求空前高涨,但有几个行业正处于雇佣这些角色的前沿。对 ML 工程师需求最高的行业包括;计算机和软件、金融和银行业以及专业服务。
这个角色有什么好学的?
随着 AI 和 ML 实践在企业中不断发展和成熟,机器学习工程师在帮助扩大 AI/ML 的使用和成果方面发挥着关键作用。ML 工程师使数据科学家能够专注于他们最擅长的领域——通过建立基础设施、流程和最佳实践,在生产中实现 AI/ML 模型的商业价值。随着数据量和用例复杂性的增加,情况尤其如此。
**结束语:**这份关于人工智能工程职位的总结并不是基于对某个特定职位发布的评估,甚至也不是针对我所在企业的评估,它反映了对人工智能/人工智能工程师(企业衡量人工智能/人工智能的一个关键角色)的公开职位发布的总体主题的定性评估。
关于作者:詹妮弗·奥蒂格贝在人、技术和设计的交叉点上写作和进行研究。她目前在谷歌领导着一个专门的 UX 团队,致力于开发软件工具,使企业开发人员能够利用人工智能和人工智能进行创作。
用于预测的机器学习:规模很重要
原文:https://towardsdatascience.com/machine-learning-for-forecasting-size-matters-b5271ec784dc
经典预测方法与机器学习的比较
米卡·鲍梅斯特在 Unsplash 上的照片
机器学习已经越来越多地应用于解决预测问题。ARIMA 或指数平滑等经典预测方法正在被 XGBoost、高斯过程或深度学习等机器学习回归算法所取代。然而,尽管受到越来越多的关注,但对机器学习方法的预测性能仍然存在疑虑。
Makridakis 是预测文献中最著名的名字之一,他最近提出的证据表明,对于单变量时间序列预测,经典方法系统地优于机器学习方法[1]。这包括 LSTM、多层感知器或高斯过程等算法。令人惊讶的是(或者不是,正如我们将看到的),其中大多数甚至无法胜过简单的季节性朴素模型— 使用同一季节的最后观察值来预测未来观察值。后来,Makridakis 发表了另一篇文章,对需求预测进行了类似的比较[2]。在这项新的研究中,他们得出结论,机器学习方法提供了更好的需求预测性能。
第一项研究的结论是,需要找到机器学习预测模型相对于经典预测模型表现不佳的原因。
这是不是只是具体应用的问题,也就是机器学习对于需求预测表现很好但不全面?
尺寸很重要
照片由安德鲁·詹金斯在 Unsplash 上拍摄
我对此的看法是,第一项研究偏向于极小的时间序列。他们对一组超过 1000 个时间序列进行了实验,但是每个单独的时间序列都非常小。平均观察次数只有 118 次。在机器学习社区中,众所周知,当使用较大的训练集时,具有大量参数的模型往往比具有较少参数的模型表现得更好。这就是为什么深度学习只是在大型数据集变得可用之后才成为一件事情的原因之一——神经网络可以有许多参数,并且众所周知非常渴望数据。因此,很明显,对于机器学习标准来说,平均 118 个数据点可能不足以创建一个性能良好的模型。
在他们的第二项研究中,大部分系列包括了 280 多项观察。虽然对于机器学习标准来说,这仍然被认为是小规模的,但这比之前的研究中使用的要多得多。不幸的是,他们没有测试训练样本大小对结果的影响。
测试我的假设
我做了一些实验来测试在应用机器学习进行预测时训练样本大小很重要的假设。我尝试了五种经典方法(ARIMA、ETS、TBATS、Theta 和 Naive)和五种机器学习方法(高斯过程、M5、LASSO、随机森林和 MARS)来预测时间序列的下一个值,并通过增加训练样本量来检查它们的性能。
结果如下图 1 所示,图 1 显示了随着训练集的增长,每个模型的误差。x 轴表示训练样本大小,即有多少数据用于拟合预测模型。y 轴代表所有时间序列中每个模型的平均误差,使用交叉验证进行计算。以粗体显示的是两条平滑线,代表根据局部加权回归的每种方法的平滑误差。最后,点 118 处的垂直黑线代表 Makridakis 在实验中使用的平均样本大小。
图 1:随着训练样本量的增加,多个预测模型(分为经典和机器学习方法)的误差。图片作者。
结果显示了一个明显的趋势:当只有少数观测值可用时,经典方法表现出更好的性能,从而证实了 Makridakis 的发现。然而,随着样本规模的增长,机器学习方法优于经典方法。
你可以在这里查看我的文章。你可以用我在 Github 账户上分享的代码重现这些结果:
- https://github.com/vcerqueira/MLforForecasting
混合解决方案
自然,机器学习模型无法从小数据集进行归纳的证据可以被视为相对于传统方法的限制。特别是因为预测在宏观或微观经济领域中非常重要,在这些领域中,观测数据通常很少。在这种情况下,更简单的模型(参数更少)更可取。
尽管如此,机器学习模型可以在更大的时间序列中产生重要影响,例如传感器收集的时间序列。
也许更好的解决方案在于机器学习与经典模型的结合。这是广受欢迎的 M4 预测竞赛获胜者采用的方法。竞赛的获胜者 Slawek Smyl 使用指数平滑法对进行去季节化并对序列进行归一化,同时深度神经网络(LSTM)完成外推工作。
外卖
- 机器学习方法在预测方面是有竞争力的,前提是它们有足够大的训练数据集;
- 当只有少量的观测值时,ARIMA 或指数平滑等经典方法是更可取的;
- 将指数平滑等经典方法与机器学习相结合,可以提高预测的准确性。
参考文献
[1] Makridakis,Spyros,Evangelos Spiliotis,和 Vassilios Assimakopoulos。"统计和机器学习预测方法:关注点和前进方向."PloS one13.3(2018):e 0194889。
[2] Spiliotis,Evangelos 等人,“每日 SKU 需求预测的统计和机器学习方法的比较”运筹学 (2020)
[3]塞尔奎拉、熊伟、路易斯·托戈和卡洛斯·苏亚雷斯。"时间序列预测的机器学习与统计方法:大小问题." arXiv 预印本 arXiv:1909.13316 (2019)。
用于预测的机器学习:多变量时间序列的监督学习
原文:https://towardsdatascience.com/machine-learning-for-forecasting-supervised-learning-with-multivariate-time-series-b5b5044fe068
使用 Python 对自回归分布滞后模型的介绍。
图 1:关于每月葡萄酒销售的多元时间序列。此处为数据源的链接。图片作者。
在这里,您将学习如何使用多元时间序列创建预测模型。
这是我在之前的帖子的后续。在那里,我描述了单变量时间序列监督学习的主要步骤。
介绍
如果一个时间序列包含一个以上的变量,那么它就是多元的。
参见图 1 中的示例。它显示了不同类型的葡萄酒销售的每月多元时间序列。每种葡萄酒都是时间序列中的一个变量。
假设你想预测其中一个变量。说一下,气泡酒的销量(个人最喜欢:-)。你如何建立一个模型来做到这一点?
一种常见的方法是将该变量视为一个单变量时间序列。有很多方法可以用来模拟这些序列。例子包括 ARIMA,指数平滑,或脸书的先知。自动回归机器学习方法越来越多地被使用。
然而,其他变量可能包含关于起泡酒未来销售的重要线索。看看下面的相关矩阵。
图 2:不同类型葡萄酒之间的相关矩阵。图片作者。
起泡酒的销售(第二行)显示出与其他酒的销售有相当的相关性。
因此,尝试将这些变量包含在模型中可能是个好主意。
我们可以用一种叫做自回归分布滞后(ARDL) 的方法来实现。
自回归分布滞后
单变量时间序列的自回归
顾名思义,ARDL 模型依赖于自回归。
自回归是大多数单变量时间序列模型的支柱。它分两步工作。
首先,我们将(单变量)时间序列从值序列转换为矩阵。我们用延时嵌入的方法做到这一点。尽管名字很花哨,但这种方法非常简单。这个想法是根据每个值之前的最近值来为每个值建模。查看我之前的帖子,了解详细的解释和实现。
然后,我们建立一个回归模型。未来值代表目标变量。解释变量是过去的最近值。
多元案例
对于多元时间序列来说,这个想法是相似的。但是,您也将其他变量的过去值添加到解释变量中。这导致了被称为**自回归分布滞后的方法。**分布式滞后名称是指使用额外变量的滞后。
把这些放在一起。时间序列中一个变量的未来值取决于它自己的滞后和其他变量的滞后。
让我们把这个方法编码清楚。
把手放在某物或者某人身上
多元时间序列通常指许多相关产品的销售数据。我们将以葡萄酒销售时间序列为例。可以从这里或者这里得到。然而,ARDL 的方法也适用于零售以外的其他领域。
转变时间序列
我们首先使用下面的脚本转换时间序列。
我们将函数time _ delay _ embedding应用于时间序列中的每个变量(第 18–22 行)。第 23 行的结果被连接成一个 pandas 数据帧。
解释变量( X )是每个时间步长上每个变量的最后 12 个已知值(第 29 行)。以下是滞后 t-1 的情况(为简明起见,省略了其他滞后):
滞后 t-1 时的解释变量样本。图片作者。
第 30 行定义了目标变量。这些指的是起泡酒销售的未来 6 个价值:
目标变量的样本。图片作者。
建立模型
准备好数据后,您就可以构建模型了。下面,我使用一个随机森林应用一个简单的训练和测试周期。
拟合模型后(第 11 行),我们得到测试集中的预测(第 14 行)。该模型的平均绝对误差为 288.13。
选择滞后的数量
米凯尔·克里斯滕森在 Unsplash 拍摄的照片
我们使用每个变量的 12 个滞后作为解释变量。这在函数 time_delay_embedding 的参数 n_lags 中定义。
应该如何设置这个参数的值?
很难说应该包括多少个值。这取决于输入数据和具体变量。
一个简单的方法是使用特征选择。首先,从大量的值开始。然后根据重要性分数或预测性能减少这个数字。
这是这个过程的一个简化版本。根据随机森林的重要性分数选择前 10 个特征。然后,重复训练和测试循环。
前 10 个特征显示出比所有原始预测器更好的预测性能。以下是这些功能的重要性:
前 10 个特征的重要性分数。作者图片
正如所料,目标变量(闪光)的滞后是最重要的。但是,其他变量的一些滞后也是相关的。
ARDL 的延伸
多目标变量
我们旨在预测一个单一变量(起泡酒)。如果我们有兴趣预测几个呢?
这将导致一种称为向量自回归(VAR)的方法。
像在 ARDL 一样,每个变量都是基于其滞后和其他变量的滞后来建模的。当你想预测许多变量,而不仅仅是一个变量时,使用 VAR。
与全球预测模型的关系
值得注意的是,ARDL 并不等同于全球预测模型。
在 ARDL 的情况下,每个变量的信息被添加到解释变量中。变量的数量通常很少,并且大小相同。
全球预测模型汇集了许多时间序列的历史观测数据。一个模型符合这些观察结果。因此,每个新的系列都作为新的观察值添加进来。此外,全球预测模型通常涉及多达数千个时间序列。在的上一篇文章中,我描述了全球预测模型是如何运作的。这些是越来越多地用于预测的方法。
外卖
- 多元时间序列包含两个或更多的变量;
- ARDL 方法可用于多变量时间序列的监督学习;
- 使用特征选择策略优化滞后的数量。
- 如果要预测多个变量,请使用 VAR 方法。
感谢阅读,下一个故事再见!
参考
[1] Rob Hyndman 和 Yangzhuoran Yang (2018)。时间序列数据库。https://pkg.yangzhuoranyang.com/tsdl/的
用于预测的机器学习:转换和特征提取
原文:https://towardsdatascience.com/machine-learning-for-forecasting-transformations-and-feature-extraction-bbbea9de0ac2
时间序列的监督学习。如何使用 Python 创建单变量预测模型
亚当·米格尔斯基在 Unsplash 上的照片
在本文中,您将学习使用 Python 对时间序列应用监督学习。
这包括两件事:
- 将时间序列从序列转换成表格格式;
- 基于汇总统计信息添加新功能。
介绍
预测是数据科学中研究最多的问题之一。目标是预测时间序列的未来值。
准确的预测对决策者来说是非常宝贵的。它们减少了未来的不确定性,从而改进了行动计划。
传统的预测方法包括 ARIMA 或指数平滑法。但是,机器学习回归方法越来越多地被用来解决这个问题。
机器学习方法将任务构建为监督学习。目标是基于历史数据创建一个模型。然而,还不清楚如何使用一系列值作为输入来训练模型。
事实证明,有一个巧妙的转变让我们可以做到这一点。
延时嵌入
模型被训练以导出观察和那些观察的结果之间的模式。
我们如何处理时间序列呢?
时间序列的值可以被认为是它之前的最近值的结果。该值作为目标变量。过去最近的值被用作解释变量。
这种过程将序列从一系列值转变为表格格式。这个变换叫做延时嵌入,是自回归的关键。
这里有一个 Python 函数来完成:
以下是将该函数应用于从 1 到 9 的序列时的数据集:
作者图片
以第一行为例。目标是预测数字 4(列系列(t+1))。解释变量是它之前的过去 3 个值:3、2 和 1。
延时嵌入有很强的理论基础。详情可查阅参考文献[1]。嵌入定理假定时间序列在变换前后是等价的。
完整示例
让我们编写一个完整的例子。我们将使用太阳黑子时间序列。该数据集可在 pmdarima 库中获得。它看起来是这样的:
太阳黑子时间序列。作者图片
下面是如何转换时间序列和训练自回归模型的示例。
在每一步中查看更多的上下文注释。
滞后次数和预测范围
转换时间序列需要指定滞后的数量。也就是我们要用多少最近的过去值来预测下一个点。
对此有一些处方。例如,检查部分自相关并确定它在哪里是显著的。您还可以使用交叉验证来优化该参数。
另一个参数是预测范围。这是您想要预测的未来步数。如果该值为 1,则该问题被称为单步提前预测。否则,多步提前预测。
值得一提的是,转换并没有取代其他预处理步骤。
您仍然需要考虑趋势和季节因素,并在需要时删除它们。
特征抽出
由 Pratiksha Mohanty 在 Unsplash 上拍摄的照片
自回归方法背后有一个基本假设。过去的滞后包含了足够多的关于系列将如何发展的信息。
然而,你可以从中获得更多的信息。
在机器学习中,特征工程是建立精确模型的关键部分。这也可以用于预测问题。
我们可以用统计数据总结最近的数值。这些统计数据丰富了时间序列的表示。希望这能转化为更好的预测性能。
例如,过去几个值的平均值可能很有用。它给出了级数在每个点的平滑指标。
这里有一个例子。我重复了上面的代码,但是添加了一个特性工程步骤(第 20–22 行)。
在第 21 行和第 22 行,我添加了滞后的平均值作为解释变量。这使得预测性能略有提高。
下面是功能重要性的样子:
作者图片
均值特征是最重要的特征。
在这个例子中,我添加了一个特性。但是,你只受到你的想象力的限制。
您可以测试其他几个汇总统计数据,并检查它们是否改进了模型。
外卖食品
- 使用时间延迟嵌入将时间序列转换为矩阵
- 时间序列中的值是根据它们过去的滞后时间建模的(自动回归)
- 用部分自相关分析或交叉验证选择滞后数
- 使用汇总统计从过去的滞后中提取更多特征
感谢阅读,下一个故事再见!
你可能想读的以前的故事
</12-things-you-should-know-about-time-series-975a185f4eb2>
参考
[1]塔肯斯,芙罗莉丝。"探测湍流中的奇怪吸引子."动力系统和湍流,沃里克 1980 。施普林格,柏林,海德堡,1981。366–381.
[2] Bontempi、Gianluca、Souhaib Ben Taieb 和 Yann-al Le bor gne。"时间序列预测的机器学习策略."欧洲商业智能暑期学校。施普林格,柏林,海德堡,2012。
区块链上的机器学习
原文:https://towardsdatascience.com/machine-learning-haiku-generator-on-blockchain-f6e5a801ca25
我如何建立一个运行在区块链上的俳句生成器
在这篇文章中,我将描述我创建的 Bitku,一个完全在区块链上运行的俳句生成器。我开始的目标是创造真正区块链本土的艺术——在链条上产生和储存的艺术。我将描述生成艺术 NFTs 的现有景观以及它的不足之处。然后,我将介绍培训和部署第一个在区块链上运行的生产机器学习模型的过程。
Bitku #40
现有的创成式 NFT
生成艺术 NFTs 的趋势越来越明显——艺术是由一种算法创造的,并被“铸造”以在区块链上创造出该艺术的独特表现,从而可以证明所有权。有几种生成 NFT 的现有方法:
Art Blocks 是最大的生成性 NFT 平台之一。艺术家们创建一个脚本(通常使用 p5js )可以将一个数字转化为一件艺术品。每个数字将产生不同的输出。该脚本作为文本保存在以太坊区块链上。当你在艺术块上铸造一个 NFT 时,交易会在你的以太坊地址存储一个随机种子。为了观看艺术,你的计算机从区块链中取出脚本,取出你的种子,然后你的计算机在那个种子上运行脚本。所有的计算都发生在观看艺术品的电脑上;区块链只是一个记录剧本和谁拥有哪些种子的账本。
另一种方法是按需生成艺术,但是通过调用区块链之外的 API。这就是所谓的神谕。Oracles 通常用于访问外部数据源,比如金融应用程序的汇率。但是它们也可以用于调用一个使用 GPT-3 生成文本的 API,或者一个代表你的区块链碳排放的像素艺术树。在这种情况下,艺术是由一个黑盒生成的,你不知道你是如何得到你所得到的结果的。结果也是离线存储的,所以区块链不能保证不变性。
还有一个就是提前创造艺术。例如,使用处理生成 100 个随机的 jpg 图像,然后在 OpenSea 上将这些图像制作成 NFT。这些图像将被存储在区块链之外,使用类似 IPFS 的东西,区块链上的 NFT 将链接到该图像。
有真正了不起的艺术家创作出令人惊叹和新奇的艺术作品。我并不批评艺术的质量。但从技术的角度来看,我发现这些方法并不令人满意,因为它们都有一个共同点:它们将生成艺术的计算复杂性从区块链转移到其他一些计算机上,无论是你的计算机、云中的 API 还是艺术家的计算机。NFT 仅仅是作为所有权的证明,并不是艺术品本身的一部分。
没有做的是在区块链本身上运行生成代码——原因很好,我稍后会解释。
鉴于这是一个多么糟糕的主意,这正是我要做的。
Bitku #240
这个想法
我打算做的是创造完全生活在区块链上的生成艺术,没有任何外部依赖性。只要区块链存在,任何人都应该能够确切地知道这件艺术品是什么以及它是如何产生的。
为了满足第一个要求,我们需要将艺术本身存储在区块链上,为了满足第二个要求,我们需要在区块链上运行生成过程。
为什么这是个坏主意
区块链,本质上,有时间和空间的限制。区块链通过在许多计算机上重复计算和存储来工作。
以以太坊为例,全世界有几千个全节点,每个全节点都会执行事务,再加上几十万个矿工,其中一部分会执行事务。试图用大量的计算来运行一个复杂的过程是不可行的,也是不环保的。
这一成本反映在执行事务的 gas 需求中。以太坊黄皮书概述了不同操作的成本,例如计算、存储或加载字节。储存 1MB 的天然气需要花费 6.4 亿美元,按照今天的天然气价格和汇率,这大约相当于 64 ETH 或 17 万美元。这只是存储,并没有计算成本的因素。
考虑到这些限制,在区块链存放视觉艺术显然是不可行的。俳句,我之前已经和 T1 一起试验过了,看起来非常适合:它们是文本——而且是很短的文本。
有了目标,下一个问题是如何在区块链创作俳句。我上面链接的项目使用了一个递归深度学习模型,它在磁盘上有 140MB,运行起来计算量很大。
Bitku #29
马尔可夫链模型
进入马尔可夫链:它们远不如最先进的深度学习模型强大——它们生成的文本更不真实。但是它们很小,很容易理解。
马尔可夫链对一系列事件进行建模,其中下一个事件的概率取决于当前状态。在我们的例子中,这可能意味着为了生成句子中的下一个单词,我们将考虑当前的单词。
下图显示了一条马尔可夫链,训练在 的第一句话上,鼠标、鸟和香肠 。
"很久很久以前,一只老鼠、一只鸟和一根香肠合伙建立了一所房子。"|作者用 PlantUML 创建的图像
每个单词都通过前一个单词后面的那个单词的概率与其他单词相联系。要从中生成文本,如果我们从“once”开始,100%的时间我们会选择单词“once”,从那里我们总是选择“a”。在这一点上,我们有几个单词可以跟随。每当我们生成一个包含“a”的句子时,我们可能会在后面出现一个不同的单词:“一只鸟”或“一根香肠”
这些概率来自训练过程。训练是通过处理一组文本(称为语料库)并记录单词之间的每个过渡来完成的。
在这种情况下,“once”总是跟在“once”后面,所以在我们的模型中它有 100%的概率跟在它后面。而“a”后面有四个单词,每个单词出现的次数相等,所以它们都有 25%的机会。
随着语料库的增长,这变得更加复杂,但想法是相同的:词对在语料库中出现的频率将为模型提供生成该词对的概率。
比特库#189
打造区块链俳句生成器
此时,我已经确定了我的目标和需求:构建一个可以在区块链上生成和存储俳句的马尔可夫模型。是时候建造它了!
流量区块链
我的第一步是选择一个区块链一起工作。由于生态系统的流行和成熟,以太坊是显而易见的选择,但由于上面提到的限制,我显然需要寻找其他地方。我将搜索重点放在区块链的利益证明(PoS)上,以最小化我需要的计算对环境的影响。
在考虑了几个选项后,我登陆了流。Flow 是一个相对较新的应用,最出名的是《T2》和《NBA 顶级射手》,但它的应用生态系统正在不断发展。Flow 使用一种 PoS 一致性算法,该算法被划分到几个不同类型的节点。结果是,与大多数区块链相比,执行发生在相对较少的计算机上。很难估计在区块链上运行复杂交易的计算/能源/碳影响,因为交易目前都被收取相当于不到百分之一便士的统一费用,但可以肯定的是,这比使用以太坊等区块链低几个数量级。
相比其他 PoS 区块链,将我推向 Flow 的是它的编程语言、 Cadence 和开发者生态系统。Cadence 为智能合约编程提供了一个简单易懂的界面,尤其是对于初次接触智能合约编程的人,比如我自己。有很好的教程和一个在线平台,可以帮助你入门和了解 API 参考文档。Flow 和 Cadence 仍在积极开发中,因此我确实遇到了一些文档过时的地方,开发人员生态系统的一些部分仍处于早期阶段——如 JavaScript 库——但总体而言,我在 Flow 上有很好的开发体验。
构建生成俳句的马尔可夫模型
在上面的例子中,马尔可夫模型的状态只是当前的字。当当前单词是“在”时,下一个单词将是“a”。从“a”有四个相等的选择。该模型的一个可能输出是:
从前,香肠和老鼠合伙,老鼠和老鼠…
很容易看出我们如何产生无意义的结果。选择一个好的下一个单词需要更多关于句子的上下文。但是状态不一定只是当前的词。为了这个项目,我训练了一个马尔可夫模型,其中状态由当前单词、前一个单词和行号组成。这远非完美,但比仅使用当前单词的情况有了很大的改进,同时足够简单以适应约束条件。
训练语料来自几个来源。没有很多俳句训练数据,所以这个模型的大部分是在其他诗歌或散文句子上训练的,这些句子被重组为具有类似俳句的外观。总之,我有几百兆字节的训练数据。
我处理了每个“俳句”来分类从状态(行号、当前单词和前一个单词)到下一个单词的转换。在这一步之后,我有大约 650 万次转换。
以下示例显示了从当前单词(The)到“the”后面的每个单词的这些转换,以及这些转换发生了多少次。
the, world, 644
the, night, 474
…
the, sudden, 2
the, change, 1
有了这个,你可以计算从一个状态到一个单词的概率。如果这四个词是我们语料库中“the”之后仅有的四个词,“世界”发生的时间为 57.4%,“夜晚”发生的时间为 42.3%,“变化”发生的时间不到 0.1%。这意味着当我使用这个模型生成俳句时,57.4%的情况下当前单词是“the”,下一个生成的单词将是“world”,而“change”的情况很少。
创建马尔可夫模型就像计算训练数据中每个转换的概率一样简单。我从这组转换中创建了三个不同的马尔可夫模型:
- 第一个模型使用行号、当前单词和前一个单词来生成下一个单词。
- 第二个模型使用行号和当前单词来生成下一个单词。当第一个模型中不存在当前状态时,此模型是一个后备。例如,当我们在俳句的开头时,我们还没有两个单词,或者如果生成了一个不常用的词对,如“the change”,就是这种情况
- 最后一个模型专门模拟了俳句的结尾。我以前在创作俳句时遇到的一个问题是,因为俳句的篇幅有限(只有有限的行数和音节数),所以你在诗的结尾经常会留下不完整的想法。我通过在训练数据中建立俳句末尾的状态模型来解决这个问题。如果在生成一首诗的结尾附近遇到了那个状态*,我们将选择那个选项并结束这首诗,而不是试图继续生成。*
处理训练数据和建立模型的完整代码可以在这里找到。
移植到流
训练好模型后,我必须让模型在区块链上运行。这需要将我的 Python 代码移植到 Cadence 智能合约中。Cadence 是一种“面向资源”的编程语言,它使得安全可靠地完成所有区块链部分变得非常容易——铸造 NFT,将它们存储在正确的帐户中,以及交换资金。但是尽管 Cadence 已经完全成熟,但它比 Python 这样的语言更受限制,这使得移植俳句生成代码更具挑战性。
缩小我的模型
但最大的挑战是找到一个由 Cadence 支持的模型表示,它足够小,可以存储在区块链上。我对这三种模式的实际限制是 1-2MB,来自 Flow 的 4MB 事务大小限制。
为了将数百 MB 的训练数据和 650 万个状态转换减少到只有几 MB,我做了以下工作:
- 将字典中的单词删减到 1000 个最常用的单词。由于新单词和其他单词之间的无数相互联系,随着字典中单词数量的增加,最终模型的大小呈指数增长。我发现 1000 是模型大小和生成的俳句种类之间的一个很好的平衡。
- 删除剩余单词之间不常见的过渡。这也是以减少产出多样性为代价的。以上面的例子为例:尽管“the”和“change”包含在 1000 字的字典中,但“the change”并不作为状态转换包含在内。这不仅节省了空间,而且有助于防止语法错误的转换。当“change”是一个名词时,“change”可能是正确的,如“给我零钱”,通常“change”是一个动词。该模型不知道一个单词被用作哪个词性,因此“change”通常会被解释为动词,后跟“my”和“your”等单词。
- 把单词压缩成更小的表示法。我的压缩方案是用唯一的字母数字代码替换单词,给最常见的单词赋予最短的代码。
当我创建模型并在 Flow 上测试它们时,获得不频繁转换的确切字数和阈值是一个迭代过程。Flow/Cadence 还处于早期阶段,Cadence 运行时的新版本迫使我几次调整模型的大小。
下面是字典中用于反转压缩的一个片段:
{
"a":"END",
"b":"START",
"c":"\n",
"d":"the",
"e":"I",
"f":"to",
"g":"and",
"h":"a",
"i":"of",
…,
"pg":"walking",
"ph":"lonely",
"pi":"thousand"
}
“开始”和“结束”是我用来标记俳句开始和结束的符号。之后,“\n”(换行符)是最常见的,其次是“the”和“I”。“千”碰巧是字典中 1000 个单词中出现频率最低的,正如您所看到的,可以压缩为两个字符(“pi”)。
这是模型的样子:
{
"d":{
0:{"c":8,"aw":9,…,"i6":34,"d7":35},
1:{"c":7,"bB":8,…,"gm":46,"aj":47},
2:{"cP":1,"bG":2,…,"k9":51,"mc":52}
}
}
这是单词“d”后面的单词模型,翻译过来就是“the”里面有三本字典,每一本分别对应三行俳句。对于第一行,跟在“the”后面最常见的词是“c”(“\ n”),其次是“aw”(“only”),最后是“D7”(“Lord”)。最大数量是 35,所以要在俳句的第一行“The”后面生成一个单词,请在 1 到 35 之间随机选择一个数字,并选择其值小于或等于该数字的第一个单词。5 会翻译成“\n”,而 9 会翻译成“only”转换成百分比,对于这个特定的情况,这意味着大约 23%的时间换行符跟在“the”后面,而其他每个单词有 3%的机会。
将生成代码移植到 Cadence
模型整理出来并与 Cadence 兼容后,下一步是用它们生成俳句。
基本思想是从模型中随机抽取一个词,然后用这个词随机挑选下一个词。在生成过程中,如果第一行有 5 个或更多音节,或者第二行有 7 个或更多音节,则插入一个换行符作为下一个“单词”在最后一行,如果有 4 个或更多的音节,那么我们试着从上面描述的特定于结尾的模型中提取。
虽然我一直在谈论选择“随机”单词,但我们实际上想要一个伪随机单词——一些看起来随机但行为可重复的单词。在区块链建造这座桥的好处之一是它是俳句的明确来源。我们不仅可以像了解其他 NFT 一样了解所有权历史,还可以确切地了解它是如何生成的。假设我们有种子,我们总是可以回去,追溯代码,并了解发生了什么产生任何给定的俳句。
在这种情况下,我用块的 ID 和俳句的 ID(每个俳句都用一个唯一的序列 ID 标记)作为伪随机数发生器的种子。因为块 ID 包含在种子中,所以你永远不知道在你创作之前俳句会是什么。
*如果您足够快,可能会提前知道您的事务将包含在哪个块中,但实际上这是不可预测的。
智能合约的完整代码,包括模型和生成代码,可以在这里找到。
结果
接下来是有趣的部分——结果。我已经在本文中介绍了其中的一些。以下是我更喜欢的几个:
Bitku #0
比特库#5
比特库#221
是的,最后一个就是两个字。正如我之前提到的,使用区块链的一个好处是,给定随机数种子,你可以准确地计算出发生了什么。(我将把它作为一个练习留给读者;这里的是产生俳句#221 的事务。)
你可以阅读更多关于这个项目的内容,也可以在下面找到所有的源代码:https://github.com/docmarionum1/bitku
如果你想阅读更多关于生成俳句的内容,可以阅读关于使用 深度学习 ,以及关于从 生成俳句的工作描述 。
杰里米·内曼在他的网站上做着同样毫无意义的事情:http://jeremyneiman.com/
感谢 Abigail Pope-Brooks 的编辑和反馈。
本文中的所有图片均由作者创作,除非另有说明。
[1]附录:区块链中机器学习的状态
据我所知,Bitku 是第一个在生产区块链上运行的机器学习实现。
我只能找到在智能合约中实现机器学习的另一个源代码实例:微软研究院的区块链上的分散&协作 AI(也称为在区块链上共享可更新模型(SUM))。
这是一个有趣的项目,其主要焦点是确定如何利用区块链来民主化训练模型,并访问它们进行预测。要点是,它们提供了一种激励机制来为模型提供数据,实现了易于更新的最大似然模型(如最近质心分类器),从而降低了交易成本,然后允许免费访问预测,因为从区块链读取是免费的。
该项目在 Ethereum 上实现,并显示使用本地仿真器工作。我找不到任何迹象表明它被部署到 mainnet 上,而且现在使用它成本太高。但是我可以看到这种方法在 PoS 区块链上工作。
除此之外,我在区块链上找不到任何 ML 的实现。
- 有一篇论文提出了一个利用区块链来激励创造有效模型的方案(想想 Kaggle,但在区块链),但据我所知,这只是理论。
- 有专门针对区块链的研究,以促进机器学习,如拟议的学习链,但那是一个完全不同的野兽——它使用区块链来协调和保护分布式机器学习——它不在智能合同中运行机器学习。
5 分钟机器学习
原文:https://towardsdatascience.com/machine-learning-in-5-minutes-2fad75721850
引起媒体热议的新兴技术
凯文·Ku 在 Unsplash 上的照片
机器学习。它向你展示你的抖音推荐,让谷歌助手回复你的查询,这是一项可怕的技术,有一天将接管世界。但是真正的内幕是什么呢?让我们一起去看看🧐吧
基础知识
任何人工智能算法实际上都只是一个巨大的数学问题。你得到一堆输入,对这些输入执行数百个等式,然后得到想要的输出。
信用:塔拉普罗尔
这些圆圈中的每一个🔵代表一个数学方程式。两个圆圈之间的每条线代表一个变量,或者说权重,这是在一个叫做**训练的过程中“学会”的。权重允许确定这些圆或神经元、**之间关系的不同强度,这可以帮助神经网络找出最终的分类或预测。连接越强,权重值越高。
为了学习赋予机器学习算法其唯一的基于连接的值的权重值,神经网络需要**数据。**神经网络中常用的数据集的一个例子是 MNIST -存储在图像文件中的大量手写数字,每个数字都由其中包含的数字来标记。
MNIST 数据集
机器学习算法获取这个大规模数据集,并学习它可以用来确定如何获得该输出的模式。训练数据允许计算机查看成千上万的例子来完善它的技术。测试数据允许程序员测试他们的算法对训练集中没有的数据执行得有多好。
以下是在 MNIST 数据集的训练过程中会发生的情况:
- 所有手写数字图像都被输入到模型中。
- 每个图像中的像素序列乘以一组权重。然后产品被送入下一层。
- 这个过程一层一层地继续,直到到达输出层。
- 还有另一个函数,在这种情况下, softmax 函数 **,**应用于该输出层中的值集…
- 计算预测值(数字分类的标签),并将其与基础事实标签进行比较,基础事实标签是包含在原始数据集中的标签,用于说明图像实际属于哪一类。
很简单,对吧?等等,我们还没完呢。真正神奇的🪄of 神经网络发生在之后预测被计算出来,与你可能相信的相反。训练过程以所谓的反向传播达到高潮,它允许程序回过头来调整它认为将有助于神经网络在未来表现更好的任何数字。
它通过计算一个损失值来实现这一点,损失值是模型对数据表现好坏的指标。根据使用的数据类型,有许多不同的损失函数用于计算该值。
一个优化器获取损失值,用它来计算模型的错误有多严重,并相应地调整模型的权重。一旦模型被调整,训练过程再次开始。该过程的每次运行被称为单个时期。
这是你的总结:
- 向模型中输入进料
- 乘以一组权重,重复!
- 将 softmax 功能应用于输出
- soft max 的输出用于预测类别标签
- 使用损失值和优化器返回并修复导致问题的权重(反向传播)
一旦运行了期望数量的时期以获得性能良好的模型,训练过程就完成了。然后,在指定的测试数据上测试该模型的性能,并为在现实世界中的应用做好准备😎
神经网络的基本类型及其用途
本节包含不同种类的 ML 模型以及它们通常的用途。我还会链接到我的其他文章,这样你可以了解更多!
CNN-卷积神经网络。主要用于对图像数据的操作,如对不同种类的路标或水果进行分类。当你看谷歌镜头或那些告诉你一张图片中有哪些种类的植物的应用程序时,那些就是 CNN。
多层感知器。用于股票分析、垃圾邮件检测、路线规划和选举投票预测等任务。当您查看电子邮件收件箱时,注意到一些电子邮件被转到了垃圾邮件区,这很可能是受过识别网络钓鱼和诈骗训练的 MLP 所为。
RNNs- 递归神经网络。用于项目顺序很重要的任务,如视频分类或语音标记。当你上传一个视频到 YouTube 上时,它会被扫描是否可能侵犯版权。这可能是 RNN 在起作用,因为必须考虑视频帧和音频文件的顺序才能正确处理它。
强化学习- 当你想教计算机一项技能时使用。使用一个分数系统来强化和重复想要的行为,同时以同样的方式阻止不想要的行为。例如,智能人工视频游戏敌人可以使用强化学习来学习如何正确地玩游戏。
摘要
- 简单地说,人工智能算法是一个非常大的数学问题。
- 图像或数据集等输入乘以一组权重,在训练过程中对这些权重进行优化调整,以最好地完成手头的任务。
- 经过这些“层”的权重相乘后,到达输出层。
- 将一个函数应用于输出图层,以获得最终的预测或分类,该预测或分类可用于做出真实世界的决策。
- 有许多不同种类的神经网络,每一种都用于它们自己独特类型的任务(图像识别、基于数据的预测、学习新技能等)。
我真不敢相信这篇文章已经结束了!我希望你学到了一些关于人工智能世界的新知识。如果您有任何问题,请随时通过LinkedIn与我联系,并通过 订阅我的简讯 定期了解我的最新动态。
数据仓库中的机器学习
原文:https://towardsdatascience.com/machine-learning-in-a-data-warehouse-ab0701bbf631
使用 Snowpark Python 开始机器学习
贾斯珀盖伊在 Unsplash 上的照片
如果您是一名商业环境中的数据科学家,您将从数据仓库或数据湖中提取大量数据用于机器学习。大多数数据科学项目需要大规模的计算能力和对所需 python 库的访问,以处理海量数据和训练机器学习模型。
通常,数据被移动到另一个位置,例如云存储桶,在那里它可以被标准数据科学工具包使用。
这种情况导致了机器学习工作流的碎片化,这是许多云提供商正在寻求解决的问题。我之前写过关于 BigQueryML ,这是一个谷歌云平台(GCP)工具,它允许机器学习模型在 GCP 的数据仓库平台内纯粹使用 SQL 来开发和训练。
雪花,一个基于云的数据和分析平台,最近提供了另一个工具,试图以类似的方式简化数据科学的工作流程。
Snowpark Python API 允许使用数据科学家熟悉的工具在 Snowflake 中开发、训练和部署机器学习模型。与 BigQueryML 一样,可以开发模型,而无需数据离开存储位置,然而,使用 Snowpark 而不是用 SQL 编写代码,数据科学家现在可以使用 Python。
在这篇文章中,我写了一个使用 Snowpark 开始机器学习的简要指南。这并不意味着对机器学习本身的详尽指导,所以我将跳过通常工作流程中的一些步骤,如探索性数据分析和特征工程。它旨在给出 API 的一个应用的基本介绍。
本教程中的大部分代码都受到了雪花团队提供的示例的极大启发,这些示例可以在本 Github repo 中找到。
首先,一些有用的定义
雪花与您可能使用过的其他数据仓库工具有一点不同,因此首先理解雪花上下文中的一些常用术语可能会有所帮助。
**数据库:**一个雪花数据库由优化的数据存储组成。通常,一个数据库包含许多模式,每个模式包含一组表。在本教程中,我们将利用数据库来存储训练数据和预测。
仓库: Snowflake 为大规模查询处理提供独立的计算集群。每个计算集群被称为一个虚拟仓库。这些仓库的规模可以根据您的加工需求而定。我们将使用仓库计算来执行模型训练和推理。
**Stage:**Stage 是一个存储位置,用于存储数据和构件,以便在雪花中使用。在本教程中,我们将使用一个 stage 来存储我们训练好的模型。
**存储过程:**存储过程是存储在雪花中供以后重用的函数和操作的集合。Snowpark 存储过程提供了一种机制,可以通过 Snowpark 或其他 python 库(如 sci-kit-learn)使用雪花计算来运行 Python 代码。我们将使用存储过程来执行模型训练。
**用户定义函数(UDF):**UDF 是在雪花中运行 Python 函数的另一种方法。这些过程在许多方面不同于存储过程。这篇文章详细描述了两者的区别。在本教程中,我们将使用 UDF 来使用训练好的模型生成推理。
建立雪地公园
如果你还没有雪花的权限,你可以在这里注册免费试用。注册后,您需要配置一个数据库、仓库和模式。雪花有一个很棒的 20 分钟指南在这里。
对于本教程的剩余部分,我将假设您已经配置了雪花资源。
本文的设置部分介绍了 Snowpark 的本地安装。该设置基于 macOS。
Snowpark 使用 Snowflake 中的 Anaconda 环境来管理 Python 包。因此,虽然也可以使用 virtualenv,但是最好在本地复制这个环境。
要安装 Anaconda,请运行以下命令。
$ brew install --cask anaconda
接下来,将以下内容添加到您的。zshrc 文件。
export PATH="/usr/local/anaconda3/bin:$PATH"
现在,您可以通过运行以下命令来创建 conda 环境。SnowPark 目前只支持 Python 3.8。
$ conda create --name py38_env -c https://repo.anaconda.com/pkgs/snowflake python=3.8 numpy pandas sklearn
运行以下程序初始化 conda。您只需要运行一次。
$ conda init zsh
要激活环境,请运行。
$ conda activate my_env
进入环境后,安装 snowpark 和 Jupyter 笔记本。
$ conda install snowflake-snowpark-python$ conda install notebook
要运行 Jupyter 笔记本,请键入以下内容。
$ jupyter notebook
Jupyter 笔记本将推出 Python 3 内核。我的内核如下图所示。
带雪地公园的 Jupyter 笔记本。作者图片
雪地公园会议
一旦你设置了 Snowpark 的本地安装,你就可以开始运行代码了。使用 Snowpark API 执行的所有操作都在雪花仓库计算上运行。
要开始使用这台计算机,您需要创建一个 Snowpark 会话,该会话允许您进行身份验证并创建一个与您的数据库和仓库的连接。
要启动雪花会话,我们键入并执行以下命令:
数据准备
在本教程的剩余部分,我们将使用 Snowpark 来准备数据集,训练机器学习模型,然后使用该模型来生成预测。
我们将使用来自openml.org的‘credit-g’数据集。这包括银行客户的许多特征,带有一个标签,表明他们是好的还是坏的信用风险。可以使用如下所示的 sklearn API 下载数据集。
接下来,我们希望将数据帧写回 Snowflake,以用于训练模型。
以下代码将 pandas 数据帧转换为 Snowpark 数据帧,然后将其作为一个表写入 Snowflake。我们还保留了一个小数据集,用于稍后使用训练好的模型进行测试推断。这也保存为雪花中的一个表。
训练模型
对于模型定型,我们将创建一个存储过程。在此之前,我们需要将我们打算使用的包添加到会话中,如下所示。
我们现在可以创建存储过程了。这里的代码将数据分成测试集和训练集,并使用 scikit-learn 管道来执行预处理和训练。模型被训练,保存到指定阶段,并返回分类报告。
批量预测
既然我们已经训练了模型,我们想要测试在我们先前保留的推理数据集上生成预测。
在此之前,我们需要将模型位置添加到会话中。
接下来,我们创建一个 UDF 来使用该模型生成预测。
以下代码读入我们为推断保留的数据,并使用 UDF 生成预测。
让我们在雪花中查看结果表。
预测保存到雪花。作者图片
机器学习领域仍然非常分散。典型组织中的机器学习工作流由许多不同的工具组成,这些工具在该过程中扮演不同的角色。数据是这种碎片化可能严重影响机器学习模型的质量和成功的领域之一。
将数据从一个工具移动到另一个工具的需求可能会导致存储相同数据的多个副本,带来数据质量问题和潜在的安全漏洞。Snowpark 和 BigQueryML 等工具寻求最小化这种数据移动,并从本质上将数据科学引入数据仓库。
本教程简单介绍了机器学习工作流,包括 Snowpark Python API 的训练和部署。该工具远不止我在此分享的内容,还有许多实现数据科学工作流的潜在选项。为了获得更全面的指导,我参考了本文开头链接的 Snowpark 示例。
感谢阅读!
机器学习在乳腺癌研究中的应用
原文:https://towardsdatascience.com/machine-learning-in-breast-cancer-research-d1dd99f7819a
可解释的机器学习如何应用于乳腺癌研究中的模式识别
照片由来自 Pexels 的 Tara Winstead 拍摄
在过去的几年里,围绕机器学习将如何改变许多行业有很多炒作。经常提到的一个行业是医疗保健。在下面的博客文章中,我们将探索一种可以产生的影响。我们将通过检查 2021 年发表在《自然》杂志上的一篇文章来做到这一点。这篇文章的名字是通过可解释的机器学习进行形态学和分子乳腺癌剖析。
这篇文章将有三个部分:研究的目的、研究的方法 /结果以及为什么这对肿瘤学和卫生保健很重要。
目的
从历史上看,肿瘤学研究中的各种分析技术之间缺乏整合。这使得癌症特性的分子数据和形态学数据有些脱节。这项研究中的研究人员着手通过机器学习建立一种联系,在这两种类型的分析技术之间架起一座桥梁。如果成功,这种方法可以帮助产生细胞类型和分子特性之间联系的假设。
方法/结果
机器学习算法
这项研究使用的机器学习方法基于一种称为分层相关性传播(LRP)的技术。逐层相关性传播是一种可解释的机器学习算法。该方法可以突出用于给定输出的最重要的输入特征,而不是只给出特定输入的输出。对于使用图像的情况,突出显示的特征是像素。结果是输入图像上的热图,显示哪些像素对输出的波动影响最大。在给定分类的情况下,具有高颜色强度的像素与像素的高相关性分数相关。这个过程的基本等式可以表示为:
j 和 k 代表神经网络中连续两层的神经元。Zjk 表示神经元 j 对使神经元 k 相关有多少贡献。 R 是 j 和k的关联分数
LRP 计算的属性解释了输入要素的总贡献,而不是您在注意力热图中获得的对输入变化的敏感度。对于逐层相关性传播如何工作的更多技术解释,请参考以下论文:逐层相关性传播概述。
算法的应用
既然我们讨论了逐层相关性传播如何工作的概述,我们将探索研究团队如何能够将其用于癌症形态学和分子乳腺癌数据。
该团队首先创建了一个图像数据库(柏林癌症图像库,B-CIB ),其中包含显微镜图像数据的注释补丁。使用 LRP,研究小组能够区分癌细胞和肿瘤浸润淋巴细胞(til)。til 是淋巴细胞,可以渗透到肿瘤组织中,识别并杀死癌细胞。它们存在的密度可以用作预测患者存活的特征。LRP 允许他们的密度的视觉表现。
然后,研究人员使用形态学图像数据作为算法的输入来预测分子特征。本质上,试图通过扫描图像来获得对患者分子特性的洞察。用于训练的数据由组合的图像和分子谱数据组成。考虑到这部分研究的目的以及通过数据集获得的用于训练的信息,不需要手动空间注记。他们可以让算法在训练期间通过输入分子数据和形态学数据来识别图像中的模式。为了减少分类任务的维度,他们对不同的分子特征使用了高低分类方法。该算法基于对分子特征的表达是高还是低的预测给出分类,给定形态学图像作为输入。表达水平得分较高的两个基因是 CDH1 和 TP53。
CD h1 和 TP53 基因的重要性
基于它们对乳腺癌的影响的先验知识,为这些分子特征产生高表达分数的算法是有意义的。
TP53 基因被认为是一种肿瘤抑制基因。它调节细胞有丝分裂的水平。当这种基因突变时,就会发生过度的细胞分裂,形成肿瘤。
CDH1 突变通过增加增殖(即细胞数量增加)和转移(即癌症生长远离原发地)与癌症进展相关。CDH1 产生的蛋白质是 E-钙粘蛋白,它对细胞粘附至关重要,使细胞保持在一起,人们认为这种基因的遗传变化可以导致癌细胞更容易从肿瘤的原发部位分离,从而导致转移。
该团队还尝试预测分子特征的空间定位。该预测显示了与某些分子特征表达统计相关的空间区域。这些信息可用于创建关于肿瘤微环境的特定成分与分子肿瘤概况特征的相关性的假设,从而推动发现图像中显示的肿瘤组织学特征和可能表达的分子特征之间可能存在的潜在联系。这些链接可以导致候选人名单可用于分子特征。这些列表将包括与乳腺癌相关的分子特征。
最终,机器学习方法揭示了与各种分子特征的表达统计相关的空间和形态学特征。这些信息通过热图显示,热图显示了在大多数情况下,分子特征如何与测试的形态组(癌细胞、til 和间质(支持细胞))有特定的关联。
为了测试结果的有效性,该团队使用了一种称为免疫组织化学(IHC)染色的技术,将结果与机器学习算法生成的热图进行比较。他们利用样方测试来显示两者之间的联系。样方检验可用于测量点格局的空间随机性。象限检验通过卡方检验来测量空间随机性。总的来说,IHC 模式反映了从验证机器学习方法的机器学习算法预测的模式。
为什么
我们今天看到的研究是机器学习如何用于癌症研究目的的一个很好的例子。它不一定会取代已经存在的工具,但它可以作为这些工具的补充。肿瘤学研究的一些用例涉及通过基于机器学习的方法可能出现的新模式生成假设。从本文探索的研究中可能产生的假设的例子大多与在非空间分子特征和空间信息之间发现的新关系有关。这些关系有助于更精确的肿瘤分级和潜在靶向治疗的新候选列表。
在模式识别至关重要的行业,机器学习可以充当进一步推动发现的指路明灯。
分层相关性传播:概述 ResearchGate 上的科学数字。可从:https://www . researchgate . net/figure/Illustration-of-the-LRP-procedure-Each-neuron-re distributes-to-the-lower-layer-as-much _ fig 2 _ 335708351【2022 年 8 月 17 日获取】
Binder,a .,Bockmayr,m .,Hä gele,m .等,《通过可解释的机器学习进行形态学和分子乳腺癌剖析》。自然机器智能 3,355–366(2021)。【https://doi.org/10.1038/s42256-021-00303-4 号
MEDLINE plus[互联网]。贝塞斯达(MD):美国国家医学图书馆(US);[更新于 2020 年 6 月 24 日]。CDH1 基因;【更新于 2017 年 8 月 1 日;已审核 2018 年 7 月 1 日;引用 2022 年 8 月 17 日];供货地:【https://medlineplus.gov/genetics/gene/cdh1/#conditions
数字营销中的机器学习:4 个用例及未来展望
原文:https://towardsdatascience.com/machine-learning-in-digital-marketing-4-use-cases-and-future-outlook-654a49c131ad
照片:pix abay:https://www.pexels.com/ru-ru/photo/ipad-35550/
虽然适用于公司运营的机器学习解决方案并不缺乏,但营销已准备好从 ML 中获得最大收益。今天的公司收集了无数关于客户行为的数据点,从一个品牌的社交媒体页面上的赞数到客户点击“订单”所需的时间,一切都可以被记录和分析。
在这篇文章中,我们讨论了组织如何在数字营销中使用机器学习来寻找新客户,简化消费者细分,并使客户体验更加个性化。
寻找新客户
人们每天在网上产生的大量数据是每个营销人员的金矿。人们发布、喜欢、分享、搜索和互动的所有类型的内容可以帮助营销人员识别最有可能在不久的将来购买特定类型产品或服务的消费者。
同时,与计算机视觉解决方案相结合的自然语言处理引擎可以自动检测特定客户最感兴趣的主题。
事实上,这正是谷歌应用活动的运作方式。一个专有的机器学习模型分析了用户在线执行的无数操作,并向最有可能购买的用户显示品牌广告。更重要的是,谷歌的 ML 模型还负责应用广告的设计和格式。公司只需要提供相关的文本、视频、图像和 HTML5 资产,ML 就会计算出广告的最佳组合。谷歌通过自动测试 Google Play、Google Search、Discover 和 YouTube 上的不同广告设计来揭示哪些特定广告设计表现最佳。
改善客户群细分
客户细分是最古老和最有效的实践之一,它使营销人员能够降低每次采购成本(CPA)并提高投资回报率。传统上,营销人员主要根据年龄、地点、收入、生活方式选择、兴趣和其他直觉上合理的特征对客户进行分组。但 ML 可以帮助营销人员将客户的属性分组,这些属性乍一看没有意义。
这是在无监督 ML 算法的帮助下完成的,该算法可以在未标记的数据中找到模式。用非常粗略的术语来说,你可以将一个巨大的数据集扔给一个无监督的 ML 算法,它会自动找到不同客户之间的相似之处,并将他们聚集在细分市场中。首先,该解决方案可以显著减少客户细分所需的时间。其次,它允许揭示看似不相关的客户和客户群之间的隐藏关系。
增强个性化
在过去的十年里,个性化一直是营销领域的热门话题。营销人员通过基于规则的个性化方法取得了不同程度的成功,然而,随着目标受众随着产品供应范围的扩大,个性化变得越来越难以实现。最重要的是,客户的需求和希望是不断波动的,这使得手动个性化方法从长远来看是无效的。
ML 部分消除了人工配置个性化策略的需要,并允许公司实现大规模个性化。借助协作式和基于内容的过滤,组织可以节省大量时间来确定在下一封电子邮件简讯中或在客户购买了特定类别的产品后提供什么产品。从内容创建到推荐引擎,ML 让营销人员能够提高品牌忠诚度,培养长期客户参与度。
预测客户流失和需求
使用 ML,公司可以预测需求并确定客户流失的原因。虽然需求预测通常与财务管理相关联,但这种洞察力已被证明有利于改进营销活动。了解什么产品在特定时期会引起高度兴趣可以帮助营销人员推动更多的转化。
ML 模型还可以检测客户行为模式,并帮助企业了解流失背后的原因。这样,营销人员可以使用 ML 来了解哪些客户最有可能取消订阅时事通讯或取消订阅,并为他们提供特殊优惠和促销。
此外,对于大多数公司来说,获得一个新客户的成本要比留住一个现有客户高得多。ML 能够预测客户的终身价值,这使得营销人员能够确保最有价值的客户在长期内流失的可能性最小。
结论
上面提到的数字营销中 ML 应用的例子只是触及了该技术真正潜力的表面。简而言之,ML 允许营销人员不断分析其过去、现在和未来的消费者不断产生的巨大数据流。
在这个时间点上,忽视基于 ML 的营销必然会导致未来的竞争劣势。当一个顾客意识到他或她需要购买一个特定的产品时,使用 ML 的营销人员可以比不使用 ML 的营销人员更快地发现这个潜在客户。从那里开始,不同的品牌将根据广告的时机和设计相互竞争,这也迅速成为大联盟的专长之一。这样,随着技术的不断进步,营销竞争优势变得更加依赖于公司驾驭 ML 的能力。
实际上,ML 与营销运作的这种深度整合还需要数年时间,因为技术仍有改进的空间,法规也有跟进的空间。然而,当这种技术在营销环境中变得无处不在时,那些已经在探索 ML 驱动策略的人将拥有明显的优势。
材料科学中的机器学习
原文:https://towardsdatascience.com/machine-learning-in-materials-science-8c6c0db5ce7a
探索机器学习在材料科学中的应用
图片改编自 Adobe Stock
随着人工智能和机器学习领域的爆炸式增长,它们的普适性越来越明显。机器学习正在大量的子领域中发挥作用,材料科学和聚合物科学也不例外。本文将作为系列文章的第一篇,我将简要讨论与机器学习和材料科学相关的研究论文,其目标只是探索机器学习在该领域中的不同应用方式。
在这次迭代中,我们将关注聚合物。由于聚合物科学是一个非常特殊的领域,如果你正在阅读这篇文章,我假设你已经熟悉了它的基础知识。然而,如果你和我一样,只是天生好奇,我们会用一些基础知识作为论文评论的序言。我必须声明一下我的背景:虽然我在一个实验室工作过,在那里我经常进行聚合物特性测试,但我绝不是一个专家——我只是熟悉这个空间和一些使用的实验室技术。当我试图触及事情的科学方面时,请记住这一点。:)
聚合物基础
在深入了解什么是分子水平的聚合物之前,让我们看看一些熟悉的材料,它们是很好的例子。聚合物的一些例子包括:塑料、尼龙、橡胶、木材、蛋白质和 DNA。在这种情况下,我们将主要关注塑料和尼龙等合成聚合物。
图片改编自 Adobe Stock
在分子水平上,聚合物由长链重复分子组成。在这个链中重复的分子被称为单体(或亚单位)。单体就像链条中的一环。下图显示了多个单体连接在一起形成的聚合物,其中重复序列(用括号表示)可以重复 n 次。
作者图片
有大量的潜在分子可以用作单体:我们将要看的论文提到,已知的化合物大约在 10⁸,而据信最多在 10⁶⁰也可能存在!让我们来看看一些经典的单体例子。
作者图片
显然,单体有各种形状和大小!这一点很重要,因为单体是材料的基本组成部分,所以不同的分子结构会导致截然不同的材料特性。分子设计(设计和发现用作单体的新分子)是聚合物科学研究的一个关键领域,我们将看到一些专注于此的论文(在这篇文章中,以及在未来的迭代中)。
聚合物链并不总是只由一种重复单元组成。均聚物是重复亚单元相同的聚合物,而共聚物将包含两种类型的亚单元。甚至还有三元共聚物(3)和四元共聚物(4)!当聚合物有一种以上的亚单元时,它们不一定要交替,如下图所示。
作者图片
亚基的不同组成和构型可以产生不同的性质。在一级结构(链的线性序列)之上,聚合物还可以包含支链、交联或网络结构。
作者图片
作者图片
作者图片
更高级的结构取向和形态也会影响性能。进一步的细节超出了这篇文章的范围,但是这里要说明的一点是,有很多复杂性需要考虑,除此之外,在结构和组成上有无限的可能性(所有这些都会改变材料的属性和行为)。尽管如此,很明显,考虑到时间、材料和劳动力成本等因素,在实验室中探索所有可能性基本上是不可能的。这就是计算和机器学习的用武之地!
在聚合物信息学中,机器学习可以应用于聚合物的加速表征、建模和分析、性能预测和分子设计。将 ML 应用于研究工作流可以节省大量的时间和金钱,因为大多数试错过程都通过计算和预测来减少,从而大大缩小了实验室中实际工作的搜索空间。这听起来像是推进研究的一个不可思议的解决方案,但必须考虑到一些关键挑战:
- 与聚合物相关的数据有限
- 通常需要专家参与的混合方法
- 验证需要实验室分析
随着这一领域的工作越来越多,这些挑战的影响应该会大大减少。也就是说,这篇评论选择的论文专注于分子设计,并且有一个非常有趣的方法来克服这些关键挑战之一。我们去看看吧!
论文综述:使用分子设计算法通过机器学习辅助发现高导热聚合物
吴,近藤,杨,木本,马。等【使用分子设计算法的高导热聚合物的机器学习辅助发现】(https://rdcu.be/cNgzd)。npj 计算机材料 5,66 (2019)。https://doi.org/10.1038/s41524-019-0203-2
具有机器学习的分子设计非常有前途,并且有许多工作正在探索这个空间。在本文中,目标是设计具有高热导率的新型聚合物材料(侧重于重复亚单元的化学结构),与最先进的热塑性塑料相当。
图片改编自 Adobe Stock
这篇论文的有趣之处在于,该模型提出的分子结构是在实验室合成和验证的。引用作者的话:
"在聚合物的特殊情况下,设计的聚合物被合成和实验证实是前所未有的."
这项工作的另一个有趣的方面是使用领域知识来克服只有 28 个热导率数据训练实例的数据集。作者使用了替代数据,而是将玻璃化转变温度和熔点作为“代理属性”,因为他们知道玻璃化转变温度和熔点较高的聚合物往往也具有较高的导热性。
使用概率语言模型的贝叶斯分子设计
通常,当涉及到机器学习的分子设计时,预测有两个方向。“向前”方向被称为定量结构-性质关系,或 QSPR 。基本上在这个方向上,物理和化学性质是用给定的化学结构输入来预测的。后向方向被称为逆 QSPR ,正如它听起来的那样:给定一组目标物理或化学性质,一个分子结构被预测。
旁注:定量结构-活性关系(QSAR)类似于 QSPR,但它模拟的是分子结构和生物活性之间的关系,而不是物理/化学性质。这种类型的分子设计对于药物发现是必不可少的。
在本文中,使用了被称为贝叶斯分子设计的机器学习算法。如前所述,目标是在给定一组目标物理性质(iQSPR)的情况下设计单体单元的分子结构。化学结构使用简化的分子输入行输入系统(SMILES)符号表示,该符号实质上将化学结构压缩成一个字符串:
SMILES 算法的可视化。图片来源: 百科
正向模型定义为p(Y∈U|S)
,反向模型定义为p(S|Y∈U)
:
给定其物理性质 Y 的化学结构 S 的概率位于区域 U 中,其中 Y 是热导率, U 是所需的值范围。
先验分布
先验分布 p(S) 用于最小化模型预测的无效化学结构的出现。为了定义 p(S) ,在 PolyInfo 数据库中的 14423 个均聚物上训练n = 10
阶的 n-gram 模型,以学习真实且有利的结构分子模式。
这个可视化中没有有意义的数据,只有分布:)来源:作者
正向预测步骤
使用正向预测模型训练 1000 个神经网络,旨在预测玻璃化转变温度。和熔化温度。以单体化学结构作为输入。从这里开始,使用在预测热导率的原始目标方面表现最好的模型,并使用迁移学习和 MXNet 对少量热导率数据进行微调。
向后预测步骤和验证
最后,利用先验分布和正向模型,形成反向模型,并使用顺序蒙特卡罗方法用于产生 1000 个化学结构。从这些结构中,使用 SA 分数基于它们的理论可合成性和加工的容易程度选择了 3 个。
合成了 3 种选定的聚合物(称为 4、13 和 19 ),并在实验室测试了它们的热物理性质。4、13 和 19 的热导率绝对预测误差分别为 0.015、0.001 和 0.017 W/mK,表明预测值和实验值之间高度一致。最终,这些值的范围从0.18-0.41 W/mK,与最先进的热塑性塑料相当,并且与研究人员的目标范围一致。
这些结果确实令人兴奋,尤其是考虑到这个领域公开可用的数据仍然缺乏。如果科学界能够贡献并显著扩展可用的聚合物数据,我认为一些真正有前途的研究可以利用机器学习来完成。如果你对本文所用方法的细节感兴趣,我鼓励你读一读!
结论
看到机器学习应用到不同的领域真的很迷人,我认为在材料科学和工程领域可以取得一些非常酷和重要的成果,特别是在优化可持续发展和改善环境友好性方面。:)
希望长期延续这个系列,欢迎任何批评或改进建议!感谢阅读!
参考文献
- https://link.springer.com/article/10.1007/s10822-016-0008-z
- https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7050509/
- https://onlinelibrary.wiley.com/doi/10.1002/inf2.12167
生产中的机器学习
原文:https://towardsdatascience.com/machine-learning-in-production-mlops-80ab59628d97
(istockphoto.com)
介绍
机器学习模型很棒,但除非你知道如何将它们投入生产,否则很难让它们创造最大的可能价值。机器学习项目的工作流程是系统地规划出机器学习项目的生命周期。当构建机器学习系统时,思考机器学习项目的生命周期是计划出所有需要努力的步骤的有效方法。
例如,我们正在使用计算机视觉来检查从生产线上下来的手机,看看它们是否有缺陷。在下图中,左边的手机没有任何划痕。但如果有一段裂缝或其他什么,计算机视觉算法将有望找到这种类型的裂缝或缺陷。也许在它周围放上边框作为质量控制的一部分。如果你得到一组有划痕的手机的数据,你可以训练一个可能在你的网络中的计算机视觉算法来检测这些类型的缺陷。但是,为了将其投入生产部署,您现在需要做些什么呢?
图 1:示例计算机视觉项目,用于检查从生产线上下来的手机,看它们是否有缺陷。
这是一个如何部署这样一个系统的例子。您可能有一个边缘设备,即位于制造这些智能手机的工厂内的设备。边缘设备会有一个检查软件,它的工作是拍摄手机照片,看看是否有拉伸,然后决定这款手机是否可以接受。这实际上通常在工厂中完成,并被称为自动化视觉缺陷检查。检测软件所做的是,它将控制一台相机,当智能手机从生产线上下线时,该相机将拍摄智能手机的照片。然后,它必须进行 API 调用,以将该图片传递给预测服务器。预测服务器的工作是接受这些 API 调用,接收图像,决定该手机是否有缺陷,并返回该预测。最后,检测软件可以做出适当的控制决定,是否让它继续在生产线上运行。
ML 基础设施要求
在过去的十年里,机器学习模型受到了很多关注。然而,部署机器学习模型和产品需要的不仅仅是机器学习代码。所以你的神经网络或者另一种算法,学习从一些输入到一些输出的函数映射。这是机器学习模型的惊人进步。
但事实证明,如果你看一个生产中的机器学习系统,如果这个橙色的小矩形代表机器学习代码,机器学习模型代码[1]。然后这些就是你整个机器学习项目需要的所有代码。很多机器学习项目,可能只有 5–10%,甚至更少的机器学习代码。
图 2:只有一小部分真实世界的 ML 系统由 ML 代码组成,如中间的橙色小框所示。所需的周边基础设施庞大而复杂[1]。
这也是为什么当你在 Jupyter 笔记本上使用概念验证模型时。从最初概念验证到生产部署,仍然有许多工作要做。所以有时人们会提到 POC。或者对生产差距的概念证明。这种差距很大程度上是因为除了最初的机器学习模型代码之外,还要编写所有这些代码。
除了机器学习代码,还有许多组件,尤其是用于管理数据的组件,如数据收集、数据验证、特征提取。在你服务它之后,如何监控系统将监控回来的数据,帮助你分析它。但是通常还需要构建许多其他组件来支持工作生产部署。
机器学习项目生命周期
机器学习项目的工作流程是系统地规划出机器学习项目的生命周期。这些是机器学习项目的主要步骤。首先是范围界定,你必须定义项目或者决定做什么。你到底想对什么应用机器学习,什么是 X,什么是 y,在选择了项目之后,你就必须收集数据或者获取你的算法需要的数据。这包括定义数据和建立基线,然后标记和组织数据。有了数据之后,就必须训练模型。在建模阶段,您必须选择和训练模型,还要执行错误分析。
图 3: MLOps(机器学习操作)是一个新兴的学科,由一套工具和原则组成,以支持 ML 项目生命周期的进展[2]。
你可能知道机器学习通常是一项高度迭代的任务。在错误分析过程中,您可能会返回并更新模型,或者您也可能会返回到早期阶段并决定还需要收集更多数据。在部署系统之前,作为错误分析的一部分,您通常还会执行最终检查,可能是最终审计,以确保系统的性能足够好,并且对于应用程序来说足够可靠。当你第一次部署一个系统时,你可能已经完成了一半的工作,因为通常只有在你开启实时流量后,你才能学到使系统运行良好所需的重要课程的后半部分。
部署
任何机器学习项目中最令人兴奋的时刻之一是当你开始部署你的模型时,但是是什么使得部署变得困难呢?部署机器学习模型有两大类挑战。首先,是机器学习或统计问题,其次,是软件引擎问题[4]。
概念漂移和数据漂移
许多部署面临的挑战之一是概念漂移和数据漂移。不严格地说,这意味着如果您的数据在您的系统已经部署之后发生了变化,该怎么办呢?在我们之前的制造示例中,您可能已经训练了一个学习算法来在一组照明条件下检测智能手机上的划痕,然后工厂中的照明可能会发生变化。这是数据变化分布的一个例子。重要的是,你要认识到数据是如何变化的,以及你是否因此需要更新你的学习算法。当数据发生变化时,有时是一种渐进的变化,例如英语确实在变化,但变化非常缓慢,新词汇的引入速度相对较慢。有时,当系统突然受到冲击时,数据会突然改变。
新冠肺炎和信用卡诈骗系统
当疫情袭击新冠肺炎时,许多信用卡欺诈系统开始失效,因为个人的购买模式突然改变了。许多很少进行网上购物的人突然开始更多地使用网上购物。所以人们使用信用卡的方式突然改变了,这实际上触发了很多反欺诈系统。这种向数据分布的突然转变意味着,许多机器学习团队在 COVID 的开始阶段有点混乱,以收集新数据并重新训练系统,以便使它们适应这种非常新的数据分布。
有时,描述这些数据变化的术语并不完全一致,但有时,术语数据漂移用于描述输入分布 x 是否发生变化,例如,如果一位新的政治家或名人突然变得众所周知,他被提及的次数比以前多得多。术语概念漂移指的是从 x 到 y 的期望映射是否改变,例如在新冠肺炎之前的 if。也许对于一个给定的用户来说,许多令人惊讶的网上购物应该已经标志着该帐户的欺诈。在新冠肺炎开始后,就萎靡不振而言,也许同样的购买不会真的引起任何恐慌。那张信用卡可能被偷了。
软件工程问题
除了管理这些数据的变更之外,第二组问题是软件工程问题,您必须设法成功地部署一个系统。您正在实现一个预测服务,它的工作是接受查询 x 并输出预测 y,对于如何实现这个软件,您有很多设计选择。这里有一个问题清单,可以帮助你做出管理软件工程问题的正确决定。对于您的应用程序,您必须做出的一个决定是,您需要实时预测还是批量预测?例如,如果您正在构建一个语音识别系统,其中用户说话,您需要在半秒钟内得到响应,那么显然您需要实时预测。相比之下,为医院建立系统,记录病人的病历。拿走电子健康记录,运行一个通宵批处理程序,看看是否有与病人相关的东西,我们可以发现。因此,在这种类型的系统中,如果我们只是每晚在一批患者记录中运行一次,就没问题了。无论你是否需要编写实时软件,它们可以在数百毫秒内做出响应,或者你是否可以编写一夜之间完成大量计算的软件,这将影响你如何实现你的软件。
部署模式
当你训练学习算法时,部署它的最佳方式通常不是打开它并抱最好的希望,因为,嗯,如果出了问题怎么办?首次部署与维护和更新部署之间的一些差异。让我们把它充实得更详细一点。一种类型的部署是,如果您提供以前没有提供过的新产品或新功能。例如,如果您正在提供以前没有提供过的语音识别服务,在这种情况下,常见的设计模式是启动少量流量,然后逐渐增加流量。第二个常见的部署用例是,如果有人已经在做一些事情,但我们现在希望使用一种学习算法来自动完成或帮助完成这项任务。例如,如果你让工厂里的人检查智能手机的划痕,但现在你想使用一种学习算法来帮助或自动完成这项任务。人们以前这样做的事实为您如何部署提供了更多的选择。
阴影模式部署
让我们从视觉检查的一个例子开始,也许你已经让人工检查员检查智能手机的划痕缺陷。你现在想用一个学习算法来自动完成一些工作。当您最初让人们执行任务时,一种常见的部署模式是使用影子模式部署。
图 4: ML 系统遮蔽了人,并行运行。在此阶段,ML 系统的输出不用于任何决策。样本输出并验证 ML 系统的预测
这意味着,你将首先让一个机器学习算法跟随人类检查员,并与人类检查员并行运行。在这个初始阶段,学习算法输出不用于工厂的任何决策。因此,无论学习算法说什么,我们现在都要遵循人类的判断。假设这个智能手机,人们说它很好,没有缺陷。学习算法说没问题。也许对于这个中间大拉伸的例子,这个人说这不好,学习算法也同意。也许对于这个拉伸较小的例子,也许这个人说这不好,但学习算法犯了一个错误,实际上认为这是好的。影子模式部署的目的是让您收集数据,了解学习算法的执行情况以及与人类判断的对比情况。使用影子模式部署是一种非常有效的方法,可以让您在让学习算法做出任何真正的决定之前验证它们的性能。
金丝雀部署
当您准备好让学习算法开始做出真正的决策时,常见的部署模式是使用金丝雀部署。在金丝雀部署中,您可能会部署一小部分流量,可能是 5%,可能最初甚至更少,并开始让算法做出真正的决策。
图 5:最初推出一小部分流量(比如说 5%)。监控系统并逐渐增加流量。
但是,如果只在一小部分流量上运行这种算法,如果算法出现任何错误,它将只影响一小部分流量。这为您提供了更多的机会来监控系统,并且只有在您对此性能更有信心的情况下,才会逐渐提高流量百分比。Canary 部署允许您在问题可能对工厂或您部署学习算法的其他环境产生过大后果之前,及早发现问题。
蓝绿色展开
有时使用的另一种部署模式是蓝绿色部署。假设我们有一个系统,相机软件,用于在您的工厂收集手机图片(图 6)。这些手机图像被发送到一个软件,该软件获取这些图像并将其发送到一些视觉检测系统。在蓝绿色部署的术语中,您的软件的旧版本被称为蓝色版本,而新版本,即您刚刚实现的学习算法被称为绿色版本。在蓝绿色部署中,您要做的是让路由器向旧版本或蓝色版本发送图像,并让它们做出决策。
图 6:启用回滚的简单方法。
然后,当您想要切换到新版本时,您需要做的是让路由器停止向旧版本发送图像,并突然切换到新版本。因此,蓝绿部署的实现方式是,您将拥有一个旧的预测服务,它可能运行在某种类型的服务上。然后你会启动一个新的预测服务,绿色版本,你会让路由器突然将流量从旧的切换到新的**。**蓝绿色部署的优势在于可以轻松实现回滚。在蓝绿部署的典型实施中,人们会想到同时 100%地切换流量。
自动化程度
MLOps 工具可以帮助实现这些部署模式,或者您可以自己实现它们。考虑如何部署系统的最有用的框架之一是考虑部署,而不是像 0,1 那样要么部署要么不部署,而是设计一个系统,考虑什么是适当的自动化程度。例如,在智能手机的视觉检测中,一个极端是如果没有自动化,那么只有人类的系统。如果您的系统运行的是影子模式,那就是稍微自动化的模式。所以你的学习算法是把预测,但它实际上并没有用在工厂里。这就是影子模式。自动化程度稍高一点的将是人工智能辅助,给定一张像这样的智能手机照片,你可能会让人类检查员做出决定。
图 7:设计一个系统,考虑什么是适当的自动化程度。您可以选择在实现完全自动化之前停止。
但也许人工智能系统可以影响用户界面,突出显示有划痕的区域,以帮助吸引人们的注意力,让他们看起来最有用。用户界面或 UI 设计对于人工协助至关重要。但这可能是一种在保持人在循环中的同时获得稍微更高程度的自动化的方法。更大程度的自动化可能是部分自动化,如果给我们一部智能手机,如果学习算法确定它没问题,那就是它的决定。它肯定是有缺陷的,然后我们就去算法的决定。但是如果学习算法不确定,换句话说,如果学习算法预测不太有把握,0 或 1,也许只有这样我们才会把这个发给人类。所以这是部分自动化。对于学习算法的性能对于完全自动化来说不够好的应用程序,部分自动化有时是一个非常好的设计点。
当然,除了部分自动化,还有完全自动化,我们可以让学习算法做出每一个决定。因此,在左边有一个只使用人类决策的范围,一直到在右边只使用人工智能系统的决策。而且很多部署应用会从左边开始,逐渐向右移动。我发现,消费者的互联网应用程序,如如果你运行一个网络搜索引擎,写一个在线语音识别系统。许多消费软件互联网企业不得不使用完全自动化,因为每次有人进行网络搜索或产品搜索时,让后端的人做一些工作是不可行的。但是在消费软件互联网之外,例如,检查东西和工厂。它们实际上是许多应用程序,其中最佳设计点可能是人在回路中的部署,而不是完全自动化的部署。
监控
你如何监控一个机器学习系统,以确保它满足你的性能预期?监控机器学习系统最常见的方式是使用仪表板来跟踪它如何超时工作。根据您的应用程序,您的仪表板可能会监控不同的指标。例如,您可能有一个仪表板来监控服务器负载,或者有一个不同的仪表板来监控非空输出的衍射。当你试图决定监控什么时,我建议你和你的团队坐下来,头脑风暴所有可能出错的事情。然后你想知道是否有什么事情出错了。对于所有可能出错的事情,头脑风暴一些统计数据或一些度量标准来检测问题。例如,如果您担心用户流量激增,导致服务过载,那么服务器负载可能是一个指标,您可以跟踪其他示例。
当我第一次设计我的监控仪表板时,我认为可以从许多不同的指标开始,监控一个相对较大的集合,然后逐渐删除那些随着时间的推移你发现不是特别有用的指标。这里有一些我们的观点或者我见过的其他人在各种项目中使用的度量标准的例子。首先是软件指标,如内存、计算、延迟、吞吐量、服务器负载等,这些指标有助于您监控预测服务的软件实现或围绕您的学习算法的其他软件的健康状况。除了软件指标,我经常会选择其他指标来帮助监控统计健康或学习算法的性能。
当然,对于 web 搜索,您也可以使用非常粗略的指标,如点击率或 CTR,只是为了确保整个系统是健康的。这些输出指标可以帮助您找出您的学习算法,输出 y 是否以某种方式发生了变化,或者在您的学习算法输出之后是否发生了一些事情,例如用户切换到打字,是否以某种方式发生了重大变化。因为输入和输出指标是特定于应用程序的,所以大多数 MLOps 工具都需要专门配置来跟踪应用程序的输入和输出指标。
正如 ML 建模是迭代的一样,部署也是迭代的
你可能已经知道机器学习建模是一个高度迭代的过程,部署也是如此。以建模为例,你会提出一个机器学习模型和一些数据,训练模型,这是一个实验。然后进行误差分析,并使用误差分析来找出如何改进模型或数据,通过多次迭代这个循环,你就有希望得到一个好的模型。
图 8:选择正确的度量集进行监控的迭代过程。
强烈建议将部署视为一个迭代过程。当您启动并运行您的第一批部署,并设置一组监控仪表板时。这只是这个迭代过程的开始。一个运行的系统让你获得真实的用户数据或者真实的流量。通过查看您的学习算法在真实流量的真实数据上的表现,您可以进行性能分析,这反过来有助于您更新部署并持续监控您的系统。在您选择了一组要监控的指标之后,通常的做法是设置警报的阈值。您可以根据这个设置来决定,如果服务器负载超过 0.91,可能会触发警报或通知,让您或团队知道是否有问题,并可能启动更多的服务器。或者,如果不是这样,一小部分缺失值会高于或低于某组阈值,这可能会触发警报,如果您随时间推移调整指标和阈值,以确保它们向您标记最相关的问题案例,也没关系。如果这是与学习算法的准确性相关的性能问题,那么您可能需要更新您的模型。或者,如果这是一个与学习算法的准确性相关的问题,那么您可能需要回去修复它。这就是为什么随着时间的推移,许多机器学习模型需要一点维护或再培训。
管道监控
许多人工智能系统不仅仅是运行预测服务的单一机器学习模型,而是涉及多个步骤的流水线。那么什么是机器学习管道,你如何为此建立监控系统呢?让我们看一个涉及用户配置文件的例子。也许我使用了诸如点击流数据之类的数据来显示用户正在点击什么。并且这可以用来建立试图捕捉用户的关键属性或关键特征的用户简档。例如,我曾经建立了用户档案,试图预测用户的许多属性,包括用户是否拥有汽车。
图 9:点击流数据,显示用户点击什么来建立用户简档,试图捕捉用户简档的关键属性或关键特征。
因为这将帮助我们决定是否值得尝试向用户提供汽车保险办公室。建立用户档案的典型方法是使用学习算法来预测汽车用户。这种类型的用户配置文件可以有一个很长的预测属性列表,然后可以提供给推荐系统。然后,另一种学习算法利用这种对用户的理解,尝试生成产品推荐。现在,如果点击流数据发生变化,也许这种输入分布发生变化,那么随着时间的推移,如果我们失去了判断用户是否拥有汽车的能力,那么未知标签的比例可能会上升。并且因为用户简档输出改变,推荐系统的输入现在改变,并且这可能影响产品推荐的质量。当你有一个机器学习管道时,管道中的这些级联效应可能很复杂,难以跟踪。但是,如果未知标签的百分比确实上升了,这可能是您想要提醒的事情,以便您可以在需要时更新推荐系统,以确保您继续生成高质量的产品推荐。
摘要
随着机器学习(ML)社区继续积累多年的实时系统经验,一种普遍而令人不安的趋势已经出现:开发和部署 ML 系统相对快速而便宜,但随着时间的推移维护它们是困难和昂贵的。除了机器学习代码,还有许多组件,尤其是用于管理数据的组件,如数据收集、数据验证、特征提取。在你服务它之后,如何监控系统将监控回来的数据,帮助你分析它。但是通常还需要构建许多其他组件来支持工作生产部署。因此,我们在所有这些其他软件中所学到的东西对于有价值的生产部署是必需的。但是,比起查看所有这些复杂的部分,这是最有用的组织框架之一。机器学习项目的工作流程是系统地规划出机器学习项目的生命周期。我们深入研究了机器学习项目的整个生命周期。希望这个框架对你未来计划部署的所有机器学习项目都非常有用。
👋感谢阅读。如果你喜欢我的作品,别忘了喜欢,在 medium 上关注我。这将激励我为媒体社区提供更多的内容!😊
参考资料:
[1]斯卡利博士、霍尔特博士、戈洛文博士、达维多夫博士和菲利普博士(未标明)。机器学习系统中隐藏的技术债务。2021 年 4 月 28 日从 Nips 检索。https://papers . nips . cc/paper/2015/file/86 df 7 dcfd 896 fcaf 2674 f 757 a 2463 EBA-paper . pdf
【2】深度学习。人工智能
[3] AndKonstantinos,Katsiapis,Karmarkar,a .,Altay,a .,Zaks,a .,Polyzotis,n .,…李,Z. (2020)。迈向 ML 工程:张量流扩展简史(TFX)。http://arxiv.org/abs/2010.02013
[4]帕里耶斯,a .,乌尔马,R.-G .,&劳伦斯,未标明日期(2020 年)。部署机器学习的挑战:案例研究调查。【http://arxiv.org/abs/2011.09926
Web 3.0 中的机器学习
原文:https://towardsdatascience.com/machine-learning-in-web-3-0-8a8819a48aca
了解 Web3.0 如何使机器学习能够在不侵犯隐私和保持信任的情况下工作。
保护隐私的区块链 ML(来源: Unsplash
机器学习就是发现数据中的潜在关系。然后,这些关系可用于获得任何业务的可行见解。
然而,近年来,政府(如欧洲的 GDPR)和一般用户对数据所有权的认识日益提高。如今,人们无法接受自己的私人数据被公司随心所欲地用于分析。
这导致了以下困境:
- 公司需要对用户数据进行分析,以更好地了解和改进他们的服务。他们获得的数据越多,他们的洞察力就越强。这就产生了从其他用户或第三方公司获取数据的强烈动机。将数据作为资产出售给其他公司也有强大的金钱激励。
- 用户意识到他们的个人数据可能会被滥用或泄露给其他公司。这阻碍了他们与公司分享数据。
如何才能解决这一困境?
Web3.0 带来了零知识证明的思想来解决这类问题。
零知识证明
零知识证明在数据隐私至上的应用中非常有用。zkp 使应用程序能够证明来自数据的洞察力是正确的,而不会暴露底层数据。
ZKP 可以理解为两个参与者(证明者和验证者)之间的交互。
- 证明者: 证明者执行一个计算,并想向任何第三方证明该计算是有效的。
- **验证者 😗* 验证者的角色是验证其他人所做的计算是否有效。这是通过接受来自用户的见证来完成的,该见证具有带输入和预期相应输出的样本测试数据点。这被用户用来验证证明者的真实性。
证明者和验证者都部署在区块链网络上,以保持透明。
让我们以数独游戏为例来演示 ZKP 的工作原理。在这里,鲍勃试图解决一个数独难题,爱丽丝说她知道答案。Bob 现在不确定 Alice 是否真的知道答案。为了给 Bob 信心,Alice 必须透露解决方案。但是这会破坏鲍勃的兴致!有没有其他方法可以让 Bob 相信 Alice 知道解决方案,而不需要透露出来?这正是 ZKP 所能做到的。
展示证明者-验证者互动的实例。作者图片
Alice 可以部署一个 ZKP(证明者-验证者),它将难题的一部分作为输入,并给出该部分的解决方案。这样,Bob 可以将一个已解决的部分连同预期结果一起作为“见证”发送给 ZKP 的验证者。验证者可以验证 Alice 的证明者是否也给出了与 Bob 的预期解相同的解。这将使 Bob 相信 Alice 确实知道解决方案。然后,他可以直接与证明者交互,以获得谜题特定部分的提示。
那么 ZKPs 到底是如何实现隐私保护机器学习的呢?
ZKPs 可以在两种不同的情况下实现隐私保护的机器学习:
1.私有数据分析。
2.数据和最大似然算法市场。
私人数据分析
这里的数据是私人的,但是的模型必须是公开的,以给人们对所提取的洞察力的本质的信心,并让它到达更多的受众。
我们将以信用评分应用程序为例。这里可能需要用户的私人信息,如年龄、性别、工资、过去的债务偿还记录、每月开支等。这些然后被用于计算用户的信用分数。如今,没有办法确定计算信用评分的公司是否尊重用户隐私。他们完全可以将这些数据用于其他应用程序,或者在未经用户同意的情况下将其出售给其他方。将整个应用程序放在诸如以太坊这样的公共区块链上,可以实现透明性并保护用户同意。
将整个信用评分应用程序放在以太坊上有一个主要问题。计算信用评分的 ML 模型可能是公司的专有技术。公司没有动力在以太坊这样的公共区块链上托管。这就是 zkp 发挥作用的地方。ZKPs 将核心 ML 算法逻辑加密到一个证明者中。人们可以把证明者想象成一个在黑盒子中执行计算而不向任何人透露盒子内容的实体。zk-SNARK 和 zk-STARKs 是构造这种黑盒(证明者)的流行加密方法。你可以在这里阅读更多关于这些的内容。
我们将继续信用评分的例子,以了解这样的事情是如何实现的。让我们假设 Alice 经营一家公司,该公司根据用户的私人数据计算信用评分。该应用程序部署在 ZKP 框架内,以确保用户的数据隐私。任何用户,比如 Bob,都可以通过将一个由测试值(年龄、收入、支出)和预期结果(信用评分范围)组成的见证传递给验证者来验证这个 ZKP 应用程序的正确性。验证者使用这些值来验证证明者的输出是否与预期的见证输出相匹配。
用户(Bob)使用见证来验证信用计算的正确性。作者图片
一旦用户对信用分数计算的正确性有信心,他就可以直接与证明者交互,证明者将某些输入保密(年龄、收入),而将其他输入公开(费用)。证明者运行计算并返回信用分数作为输出。这样,我们让 Alice 对 Bob 的数据进行计算(分析),而不会影响隐私。
用户(Bob)每天直接与证明者交互以获得信用分数。作者图片
数据和 ML 算法市场
数据市场
ML 模型随着数据变得更好。这为公司之间买卖数据创造了强大的动力。如今,如果不实际共享数据集,就无法验证数据集的质量。这就是 zkp 再次出手相救的地方。
ZKPs 可用于构建一个证明器,该证明器使用某些计算来检查数据是否满足某些约束或属性,作为健全性检查。证明者将保持数据的私密性,并且它在公共区块链上的事实将确保在验证过程中没有舞弊。这确保了购买者在购买数据集之前对数据质量有信心。
如果 Alice 要向 Bob 出售私有数据集,她必须确保数据满足 Bob 指定的某些约束。根据 Bob 的约束构建 ZKP 应用程序,并将其部署到公共区块链上。然后,Alice 可以将她的私有数据集作为输入发送给证明者,并显示她的数据满足所有必要的约束。
Bob 在向 Alice 购买之前验证数据。作者图片
ML 算法市场
有时,手头的问题可能是销售 ML 算法和相关的预训练模型。在这里,我们也面临着在卖给卖家之前得到卖家确认的问题。ZKPs 在这里的使用方式与上面讨论的数据市场问题非常相似。
我们可以通过 ZKPs 将预先训练好的模型部署到证明者中。然后,购买者可以发送测试数据集来验证该模型在测试集上的表现是否令人满意。这确保了购买者在购买之前对 ML 算法有信心。
下图显示了 Alice 如何将 ML 模型训练和部署到 ZKP 框架中。这使得 bob 能够在从 Alice 那里购买 ML 算法之前,在定制的测试数据集上测试模型的性能。
Bob 在购买前验证了 Alice 的 ML 模型。作者图片
参考
[1] Horace Pan,Francis Ho,和 Henri Palacci, ZK 机器学习,0xPARC,2022 年 8 月访问
[2]尼克·乌尔文,以太坊数据市场,于 2022 年 8 月到访
MLOps:机器学习生命周期
原文:https://towardsdatascience.com/machine-learning-lifecycle-in-mlops-era-5b45284c0e34
苏珊·d·威廉姆斯在 Unsplash 上的照片
MLOps
面向 MLOps 时代的机器学习生命周期将模型和软件开发结合在一起,以构建 ML 辅助产品
构建机器学习产品或 ML 辅助产品功能涉及两个不同的学科:
- **模型开发:**数据科学家——在统计学、线性代数和微积分方面技术高超——训练、评估和选择表现最佳的统计或神经网络模型。
- **模型部署:**开发人员——在软件设计和工程方面非常熟练——构建一个健壮的软件系统,将其部署在云上,并对其进行扩展以服务于大量并发的模型推理请求。
当然,这是一个严重的过度简化。构建有用且成功的人工智能辅助产品还需要其他一些重要的专业知识:
- **数据工程:**构建数据管道,从不同的来源收集数据,对其进行筛选和转换,并将其转化为同质的、干净的数据,这些数据可以安全地用于训练模型。
- **产品设计:**了解业务需求,确定有影响力的目标和相关业务矩阵;为这些目标定义产品特性或用户故事,认识到 ML 更适合解决的潜在问题;设计用户体验,不仅将 ML 模型预测与其余产品功能无缝结合,而且收集用户(再)行为作为模型结果的隐式评估,并使用它来改进模型。
- **安全分析:**通过结合模型结果和其他可公开获得的信息或数据,确保软件系统、数据和模型是安全的,没有个人身份信息(PII)被泄露。
- **人工智能伦理:**确保遵守所有适用的法律,并添加措施以防止任何类型的偏见(例如,限制模型的范围,添加人工监督等。)
随着越来越多的型号被部署到生产中,MLOps 的重要性自然也就越来越大。人们越来越关注 ML 模型在整个产品中的无缝设计和功能。鉴于模型开发可能对产品和业务产生的后果,它不能在筒仓中完成。
我们需要一个适应 ML 辅助产品和 MLOps 现实的 ML 生命周期。它应该促进所有利益相关者的可见性,而不会对数据科学家和工程师的现有工作流造成太多的改变。
在本文的其余部分,我首先给出了典型的模型开发和软件开发工作流的概述,然后介绍了如何将这两者结合起来,以适应在 MLOps 时代构建 ML 辅助产品的需求。
模型开发生命周期
让我们暂时把在线部署模型放到生产中。十多年来,数据科学家一直在构建统计和神经网络模型。通常,这些模型被离线(即手动执行)用于预测分析。
模型开发包括两组活动:数据准备和模型训练。它从制定一个 ML 问题开始,以模型评估结束。
模型开发生命周期。图片由作者提供。
规划
数据科学家将业务目标转化为机器学习问题。您可能需要考虑几个因素:
- **业务目标:**缩小到可以服务于业务目标的一小组 ML 问题。
- **错误的代价:**没有一个 ML 模型可以做到 100%准确。假阳性和假阴性的代价是什么?例如,如果一个图像分类模型错误地预测一个健康人患乳腺癌,进一步的测试将纠正它。但是,如果该模型未能诊断出患者的癌症,那么由于检测过晚,它可能是致命的。
- **数据可用性:**这可能令人惊讶,但您可能从没有数据开始,然后启动您的数据收集。随着数据变得越来越丰富,它可能会使更多类型的模型变得可行。例如,如果您要在没有标记数据的情况下进行异常检测,您可以从各种非监督聚类算法开始,并将不在任何聚类中的点标记为异常。但是当你收集用户对你的模型的反应时,你会有一个带标签的数据集。然后,您可能想要尝试监督分类模型是否会执行得更好。
- **评估指标:**根据问题的表述,您还应该指定一个模型性能指标进行优化,该指标应该与您的业务目标的业务指标保持一致。
收集
从内部应用程序和外部来源收集必要的数据。可能是通过废弃 web、从您的移动应用程序或 web 服务中捕获事件流、从运营(OLTP)数据库、应用程序日志中捕获变更数据(CDC)流等。您可以将所有需要的数据摄取到您的数据管道中,该管道由数据工程师设计和维护。
副牧师
收集的数据几乎从来都不是原始的。您需要清理它,删除重复项,填充缺失值,并将其存储在数据仓库或数据湖中。如果是为了训练一个有监督的 ML 模型,那么你也必须标记它。此外,您必须对其进行分类,以便能够轻松发现和正确理解。尽可能多地尝试自动化,但是有些部分需要手动完成(特别是贴标签)。
改变
一旦数据被清理,您可以转换它以适应分析和 ML 建模。它可能需要改变结构、与其他表连接、沿重要维度聚合或汇总、计算附加特征等。数据工程师应该将数据管道中的所有数据自动化。
使生效
实施质量检查,维护一段时间内的统计分布日志,并创建触发器,以便在任何检查失败或分布波动超出预期限制时发出警报。数据工程师与数据科学家一起在数据管道中实施这些验证。
探索
数据科学家执行探索性数据分析(EDA ),以了解各种要素与他们希望模型预测的目标值之间的关系。他们也做特性工程,这很可能导致增加更多的转换和验证检查(前两个阶段)。
火车
数据科学家训练多种模式,运行实验,比较模型性能,调整超参数,并选择几个性能最佳的模型。
评价
根据业务目标和度量标准评估模型特征。一些反馈可能导致甚至不同地调整和表述 ML 问题,并再次重复整个过程。
这个 Data-ML 无限循环不是线性的。在每一个阶段,你并不总是前进到下一个阶段。一旦发现问题,你就回到相关的前一阶段去适应它们。所以从每一个阶段到前一个阶段都有隐含的边。
它类似于开发人员遵循的 DevOps 循环。不是每个进入测试阶段的代码都会进展到发布阶段。如果测试失败,它将返回到代码(有时甚至是计划)阶段,以便纠正问题。
软件开发生命周期
DevOps 无限循环是软件开发生命周期的事实上的标准,用于在云上快速构建和部署软件应用程序和服务。
它由两组活动组成:设计和开发软件系统,部署和监控软件服务和应用程序。
软件开发生命周期。图片由作者提供。
计划
这是任何产品或产品特性的第一阶段。您讨论业务目标和关键业务指标,以及哪些产品功能有助于实现它们。你深入到终端用户的问题,并讨论用户旅程以解决这些问题,并收集所需的数据来评估 ML 模型在现实世界中的表现。
密码
设计和开发软件,端到端的产品或应用程序,而不仅仅是 ML 模型。建立应用程序代码用来调用模型推理和使用其结果的契约和 API,以及将收集哪些用户反应和反馈。
让开发人员、数据工程师和数据科学家达成共识非常重要。这将减少以后令人讨厌的意外。
建设
这一阶段促进了各种部件的持续集成,因为它们会演化并打包成一种将被发布的形式。它可以是库或 SDK、docker 映像或应用程序二进制文件(例如 Android 应用程序的 apk)。
试验
单元测试、集成测试、覆盖测试、性能测试、负载测试、隐私测试、安全测试和偏差测试。想出这里适用的各种软件和 ML 模式测试,并尽可能地自动化它们。
测试是在一个与目标生产环境相似的试运行环境中进行的,但不是为相似的规模而设计的。它可能有虚拟的、人为的或匿名的数据来端到端地测试软件系统。
释放;排放;发布
一旦所有的自动化测试通过,并且在某些情况下,测试结果被手工检查,软件代码或模型被批准发布。就像代码一样,模型也应该被版本化,必要的元数据应该被自动捕获。正如 docker 映像在 docker repo 中被版本化一样,模型也应该在模型 repo 中被持久化。
如果模型与服务于模型的微服务的代码一起打包,那么 docker 映像也有模型映像。这是持续集成结束和持续部署接管的地方。
部署
从 docker repo 或模型存储中挑选已发布的工件,并将其部署到生产基础设施上。根据您的需求,您可以选择基础设施即服务(IaaS)、容器即服务(CaaS)或平台即服务(PaaS) 。
您还可以使用 TensorFlow Serve、PyTorch Serve 或类似 SageMaker 和 Vertex AI 的服务来部署您的模型服务。
操作
一旦部署了服务,您可能会决定先发送一小部分流量。金丝雀部署是分阶段更新的常用策略(例如 2%、5%、10%、25%、75%、100%)。如果出现问题、意外行为或指标下降,您可以回滚部署。
一旦门户对 100%的流量开放,您的部署基础架构应该会正常关闭旧服务。它还应该随着负载的峰值和下降而扩展。Kubernetes 和 KubeFlow 是实现这一目的的常用工具。
班长
在这个最后阶段,您不断地监控服务的健康状况、错误、延迟、模型预测、异常值和输入模型特性的分布等。如果出现问题,根据问题的严重性和诊断结果,您可以将系统回滚到旧版本,发布修补程序,触发模型重新培训,或者执行任何其他需要的操作。
MLOps 生命周期
目前,数据科学家开发一个模型,然后"把它扔过墙"给开发人员和 ML 工程师,与系统的其余部分集成,并在生产中部署它,这是很常见的。
ML 和 Dev 筒仓和分散的所有权是许多 ML 项目失败的最常见原因之一。统一模型和软件开发生命周期为所有涉众提供了急需的可见性。
模型开发和软件开发需要结合在一起,以构建 ML 辅助的产品。图片由作者提供。
计划步骤是起点
产品规划高于一切。定义业务目标和设计用户体验不仅应该包括产品功能,还应该包括如何将模型结果和捕捉用户反应融入产品设计。
与传统软件不同,当随着时间的推移收集更多的数据时,产品的 ML 方面的用户体验可能需要更新才能从中受益,即使没有“新功能”
首先构建没有 ML 的产品
我通常首先用基于规则的试探法或虚拟模型构建一个端到端的应用程序,完全切断 Data-ML 循环。这是一个基线模型,有助于收集数据。它还通过展示模型将如何在产品中使用来为数据科学家提供背景。
模型和软件开发的不同节奏
开发一个 ML 模型和开发软件是完全不同的。软件系统可以增量开发(某些部分不工作)。不像软件块,ML 模型不能被分解成细粒度。
单一生命周期并不排除数据、ML、Dev 和 Ops 车轮以不同的速度旋转。事实上,这已经发生在 DevOps。在一些团队中,并不是每个开发冲刺都会带来新版本的部署。另一方面,一些团队每小时部署一次新版本,也就是在一次冲刺中部署数百次。让每个轮子以自己的最佳速度旋转。
整合所有权,早期集成,经常迭代
这是我对提高开发和部署 ML 辅助产品的成功率的三点看法:
- **巩固所有权:**负责端到端项目的跨职能团队。
- **早期集成:**实现一个简单的(基于规则的或者虚拟的)模型,首先开发一个端到端的产品特性。
- **经常迭代:**构建更好的模型,替换简单的模型,监控,重复。
摘要
MLOps 时代的机器学习生命周期将模型开发和软件开发结合成一个永恒的结。在构建 ML 辅助的产品和功能时,它促进了所有利益相关者的可见性。
MLOps 生命周期 MLOps 时代的机器学习生命周期将数据、ML、Dev 和 Ops 集成在一个循环中。图片由作者提供。
如果你喜欢这个,请:
原载于https://www.ml4devs.com/articles/mlops-machine-learning-life-cycle/。
机器学习魔法三角
原文:https://towardsdatascience.com/machine-learning-magic-triangle-b1ee85c7a3fd
思考机器学习过程的新方法
迈克尔·泽兹奇在 Unsplash 上的照片
下面显示的机器学习流程看起来熟悉吗?这是所有数据科学家和机器学习工程师都习惯在工作中看到和使用的流程。
标准机器学习流程(图片由作者提供)
虽然这是一个完美的机器学习流程,但它有以下低效之处
- 它专注于数据科学的发展,而忽略了开发问题的业务假设的重要部分
- 这是一个开放的流程。然而,高效的机器学习需要在开始和结束之间形成闭环
在这篇博客文章中,我想介绍一种思考机器学习过程的新方法。我把它命名为机器学习魔术三角。
机器学习魔三角(图片由作者提供)
它试图克服标准机器学习流程的低效率。
- 它侧重于开发问题的业务假设。这非常重要,因为它将数据探索步骤转化为业务语言中的假设
- 通过确保模型解释与初始假设相匹配,这是一个闭环流程。
我举个例子说明一下过程。
让我举一个电信公司的例子。该公司收集与客户人口统计数据、客户所拥有的服务以及客户是否有过不愉快经历相关的数据。样本数据如下所示。
电信客户流失数据集(图片由作者提供)
使用这些数据,我们可以开发一个机器学习模型,用于预测可能流失的客户。目标变量是客户流失,这是我们需要预测的。其他列称为分类(非数字)特征和数字特征。
现在让我们来看看开发预测模型所需的步骤。使用这些数据,我们可以开发一个机器学习模型,用于预测可能流失的客户。
我们将经历机器学习神奇三角的以下步骤。
数据探索和假设:在这一步中,我们将探索数据,并提出一个假设,解释客户为什么会流失。
机器学习:我们将训练一个机器学习模型,它将能够预测客户是否会流失。
模型解释:我们将通过预测已知的流失案例并解释结果来理解机器学习模型。这个解释可以用最初的假设来检验。
数据探索和假设
数据探索有助于理解客户为什么会流失。一种有用的数据探索技术是分析分类(非数字)与目标变量(变动)。
下面是性别与流失/不流失以及技术支持与流失/不流失的例子。就性别而言,我们看到,女性与男性的比例相当于搅拌者和非搅拌者。这意味着性别对搅拌没有影响。
但是,对于技术支持,我们发现没有技术支持的客户流失率更高。因此,技术支持是决定客户流失的一个重要因素。
分类(非数字)与目标变量(变动)-按作者分类的图像
下图是对所有分类变量的分析。你会发现不同的服务(除了电话服务)有助于区分搅拌者和非搅拌者
所有分类(非数字)与目标变量(变动)-按作者分类的图像
我们可以使用箱线图可视化对数字变量进行同样的分析。下面是任期与目标变量的一个例子。我们可以观察到,任期较短的客户很可能会流失。
数字变量与目标(图片由作者提供)
下面显示的是对所有数值变量的分析。你会发现高月费和低总费用也是流失的原因。高总费用也意味着高任期。
所有数字变量与目标(图片由作者提供)
基于以上数据探索,我们可以做出以下假设:
月费高的客户和月费**低的客户(新客户)**更容易流失。此外,这些相对较新的客户支付较高的月费,并没有将所有服务都包括在较高的月费中。因此,尽管每月收费很高,但他们可能并没有获得价值和服务,所以他们可能会陷入困境
开发这样的文本解释有助于用商业语言描述问题。这也可以帮助业务线领导理解机器学习工作的目标。
机器学习模型
现在让我们训练一个机器学习模型。机器学习算法有很多种,这里用的是 XGBoost。
使用因变量(X)作为所有人口统计字段、服务类型和计费字段来训练该模型。目标变量(Y)是客户流失。机器学习模型将尝试在 X 变量和 y 之间建立关系。
数据分为训练(70%的数据)和测试(30%的数据)。机器学习模型是基于训练数据开发的。然后,该模型用于预测测试数据的流失。然后将预测的流失和实际的流失与测试数据进行比较。结果可以被可视化为如下所示的混淆矩阵。
训练和测试数据准确性都很好。所以我们可以保留基于 XGBoost 的机器学习模型。
机器学习模型的输出可以被可视化为如下所示的混淆矩阵。
混淆矩阵-作者图片
预测流失和解释预测
现在是使用机器学习模型进行预测的时候了。预测是在名为 score dataset 的数据集上进行的,score dataset 是一些不是模型训练的记录。
现在让我们来解释一些预测。例如,预测客户 6894-LFHLY 会流失。SHAP 解释器将显示如下所示的可视化。
正柱表示该因素有助于客户流失。负柱表示该因素不会导致客户流失。
我们看到,导致客户流失的主要因素是合同和总费用。合同是月合同,总费用(75)相对较低,表明任期较短。这个解释也符合最初的假设。
对流失客户的 SHAP 分析
现在让我们以客户 9767-FFLEM 为例,预计他不会流失。预测的解释如下所示。我们发现月费和租期是阻止客户流失的主要因素。任期(38)是高的,因此客户没有搅动。这个解释也符合最初的假设
针对非频繁交易客户的 SHAP 分析(图片来自作者)
你会发现我们闭合了假设和模型解释之间的回路。这使机器学习流程有了目的,并有助于确保结果与问题的商业解释相匹配
这就是机器学习神奇三角的力量!
机器学习魔三角(图片由作者提供)
结论
标准的机器学习流程侧重于数据科学的发展,而忽略了对问题的商业假设的发展。
机器学习三角有助于克服标准流程的低效率,方法是专注于开发问题的业务假设,以及关闭预测和假设之间的循环
数据集引用
电信数据集可在此获得。T5T7允许商业和非商业使用。
https://community.ibm.com/community/user/businessanalytics/blogs/steven-macko/2019/07/11/telco-customer-churn-1113
请订阅每当我发布一个新的故事时,请保持通知。
https://pranay-dave9.medium.com/subscribe
你也可以通过我的推荐链接加入 Medium
https://pranay-dave9.medium.com/membership
额外资源
网站(全球资讯网的主机站)
你可以访问我的网站进行零编码分析。https://experiencedatascience.com
Youtube 频道
这是我在 https://www.youtube.com/c/DataScienceDemonstrated 的 YouTube 频道
T5 的链接
机器学习在城市空中机动性和电动垂直起降中的应用
原文:https://towardsdatascience.com/machine-learning-ml-applications-in-urban-air-mobility-and-evtol-a6baec5ba0ba
人工智能(AI)已经改变了几乎每个行业的格局。它现在正在创造超越地球表面的印记。
Lilium Jet 是由 Lilium GmbH 设计的德国电动垂直起降电动飞机原型。计划推出七座量产版。鸣谢:lilium.com 媒体工具包
在过去的十年里,人工智能(AI)已经全面革新了各个行业。它现在留下的足迹已经延伸到了陆地之外。航空业就是这样一个行业。人工智能已经与自动驾驶汽车行业相交,它正在推动这场革命。自动驾驶汽车只是通往自动驾驶汽车数字化转型的漫长道路上的第一步。这股浪潮继续进入城市空中交通(UAM)行业,人工智能有可能实现自动飞行汽车的梦想。
城市空中交通的现状(UAM)
UAM 引进了一种新的运输方式——空运。航空是一种在城市之间连接和运送人员的交通方式。随着城市的无序扩张,在地面基础设施日益拥堵的情况下,快速穿越密集的人口变得更加困难。UAM 拥有从根本上改变景观和人们以安全、高效和可持续的方式出行的潜力。直升机、直升机停机坪和空中交通管制(ATC)服务构成了 UAM 的初始生态系统。基于这一生态系统,已经开发或正在开发许多其他用例。无人驾驶客机就是这样一个充满希望的市场,因为它们大大减少了旅行时间。考虑一架大到足以运送一名人类乘客的无人机!无人或自主飞行器是一个优于传统飞行器(UAV)的概念。现在可以驾驶一架完全自主的电动多用途直升机,使用电力来减少碳足迹。这种类型的飞机被称为电动垂直起降——电动垂直起降。
电动垂直起降飞机是一种起飞时不需要跑道的飞机。它通常可以搭载五名乘客(这取决于制造商),这使得它比传统直升机明显更节能。它由电力驱动,因此减少了排放,电动汽车是缓解交通拥堵的有效方式。
第一台电动垂直起降是由德国 Volocopter 公司开发的 Volocopter VC200。第一艘商业电动垂直起降于 2016 年 12 月在迪拜起飞。
高级空中机动性
AAM 的目标是在目前地面运输或现有航空模式不能或不容易服务的地区之间运输人员和货物,这是一种利用电动垂直起降(电动垂直起降)飞机的空中运输新概念。高级空中机动(AAM)有时被称为城市空中机动(UAM),但它的潜在用途超出了人口稠密的城市地区。从单人飞机到大型飞机,这些飞机将为城市、缺医少药的社区和地理上孤立的地区带来便利。
佛罗里达州诺娜湖的百合喷气机和 ABB 充电桩。鸣谢:Lilium.com 媒体工具包
人工智能满足城市空气流动性
当城市空气流动性与人工智能相结合时,航空业受益。人工智能(AI)和机器学习(ML)在航空领域的应用数不胜数,它们有潜力解决当今行业的主要挑战。例如,不可预测的天气变化会影响飞机的行程。我们可以使用机器学习来预测天气状况和关键路线的天气模式,以确保安全可靠的旅程。机器学习可以用来优化一切,从 UAM 电池技术到预测电动汽车的需求。
在 eVTOLs 的背景下,机器学习有很大的潜力。
潜在的使用案例
以下是机器学习在电动垂直起降市场的一些可能应用。我将深入研究三个潜在的用例;
预测交通拥堵和交通量需求
使用机器学习,我们可以实时预测给定城市的交通拥堵情况。这可用于预测一天中不同时间和不同地理区域的 eVTOLs 需求。机器学习模型评估各种交通参数,以便预测交通拥堵。随着时间的推移,该模型通过分析交通数据来学习识别给定小时、周或月的交通模式。这对于预测一个城市对电动汽车的季节性需求非常有用。当交通拥堵严重时,对电动汽车的需求就会增加。
提前规划资源分配有助于旅行者拥有愉快的旅程。预测电动汽车的需求对于制造商和城市规划者来说至关重要。他们必须配备必要的工具来进行航空作业。通过分析各个地区对 evt ol 的需求,我们可以确定 vertiport 部署的最佳位置(处理 evt ol 着陆和起飞操作的站点)。
在这种情况下,机器学习可以作为地理信息系统(GIS)中空间分析的关键组成部分。它可以用来确定最佳着陆位置,使人们能够快速乘飞机旅行。机器学习可以用来开发一个能够为飞机进行智能、实时调度和路由决策的系统。它有可能减少客户乘车时间和成本。此外,我们可以通过优化航班运营来提高需求满足率。
已经进行了大量研究来评估各种机器学习架构,以预测空中出租车和城市空中交通(UAM)服务的需求。一项研究[1]开发了四种机器学习模型来映射预测因素和需求水平之间的关系:多项式逻辑回归、人工神经网络(ann)、随机森林和梯度推进。梯度提升(GB)算法在分类性能方面一直优于其他架构。它采用集成方法,将众多弱学习模型结合起来,形成一个稳健的预测模型。梯度推进似乎是映射低需求水平和高需求水平预测值之间关系的有效方法。据观察,GB 算法能够准确地估计需求水平。此外,它们在训练期间需要最少的计算时间。
自主驾驶
尽管我们非常接近完全自主的陆地运输,但向自主飞行的过渡是一个漫长而渐进的过程。话虽如此,机器学习是工具箱中的一个工具,可以帮助将自主飞行的梦想变成现实。
与自动驾驶汽车一样,机器学习正被用于调查视觉技术执行关键飞行任务的能力。其中包括探测地面和空中的危险、定位安全的紧急着陆点、在没有全球导航卫星系统的环境中定位和导航,以及在预先标记和未标记的地点安全而精确地着陆。
视觉交通检测是机器学习的一个应用,它允许对空中的危险和交通进行连续扫描。对人类来说,持续跟踪天空是一项困难且容易出错的任务。通过自动流量检测,它变得更加专注、可靠和永无止境。在安全关键的航空电子设备中,已经讨论了神经网络。深度卷积神经网络能够扫描整个天空,以确定是否存在危险。因此,它们可以集成到复杂的系统中,用于检测空中交通、空中危险以及无人机和鸟类等不合作交通。有趣的是, Daedalean ,一家瑞士初创公司,已经开发了一种基于神经网络的飞行员辅助技术[2],它可以精确地跟踪超过 95%的入侵者。
机器学习可以完成的另一项任务是精确着陆。精确着陆利用基于视觉的位置和地图资源,使无人机能够以大约 10 英寸的精度着陆在目标上。无人机可以通过计算机视觉输出视野中适合紧急和正常着陆的区域。该系统能够实时监控地面上的障碍物,并识别出有障碍物的跑道。这些信息可以帮助系统自主选择安全着陆的最佳着陆点。神经元网络可用于构建自主精确着陆系统。在飞机下面,可以安装航空电子级摄像机,为神经网络提供实时输入。在不使用 GPS 的情况下,系统可以通过观察图像随时间的变化来确定其位置。这些参数可以被用作算法的输入,以提供一直到地面的精确着陆引导。
机器学习有可能最终实现不需要人类飞行员的飞行。然而,自主飞行提出了独特的监管挑战。对于城市空中交通工具来说,安全性是首要考虑的因素。我怀疑很多人会放心地把自己的安全托付给一架无人驾驶飞机。天气变化影响小型无人机,其持续的位置跟踪需要稳定的连接。因为机器学习模型的结果高度依赖于它遇到的情况的性质和它已经看到的数据,所以不是所有的情况都可以预测。
然而,通过额外的测试,我们可以随着时间的推移完善和提高自主飞机的性能和可靠性。
空中交通管制
空中交通管制技术的进步对于为无人机提供强大可靠的运输基础设施至关重要。机器学习可以应用于空中交通管制,以克服当前的技术限制,为自动化铺平道路。
垂直机场是一个集合术语,指的是专门为空空导弹飞机起飞和着陆而设计的区域,就像直升机场是直升机的指定区域一样。鸣谢:Lilium.com 媒体工具包
机器学习可以提供关键的见解,以帮助空中交通管制系统的决策过程。
ATC 系统必须确保飞机保持最小间隔距离,以避免间隔损失。要考虑的其他因素包括飞机在不同区域之间的有序转移、高效的燃油消耗和优化的着陆顺序。机器学习可以帮助优化这些元素中的每一个。它能够了解每次飞行的潜在未来,并预测潜在的冲突。
存在许多不确定性,包括天气条件、飞行员行为、飞机质量和偏好。机器学习可以解决所有这些不确定性,同时优化一个目标,即安全飞行。开发 ATC 系统的一个有前途的方法是强化学习,机器学习的一个子领域[3]。在这种情况下,人工智能代理将考虑有关 ATC 环境的可用信息。它将监控潜在的问题,如交通、天气和拥堵,然后向各种无人驾驶飞行器发出命令。当人工智能代理接收到有关 ATC 环境新状态的信息时,它会评估以前的命令。如果命令导致情况的改善,他们将获得“奖励”
然而,如果命令导致飞机之间的额外冲突,他们将受到处罚。这将使系统能够根据实时反馈不断更新自身。强化学习方法教导 ATC 系统确定完成给定目标的最有效方式。这将提供有见地的数据,并为自动化铺平道路。
总之
人工智能(AI)在全球范围内重塑了企业。城市空中交通的现状(UAM) UAM 增加了一种新的交通方式——空中旅行。当城市空中交通与人工智能相结合时,航空业将受益。人工智能(AI)和机器学习(ML)在航空领域有多种用途。机器学习可以用于优化各种各样的流程,从 UAM 电池技术优化到估计 eVTOLs 的需求。
参考
[1]拉金德兰、斯里尼瓦斯和格里姆肖(2021 年)。使用机器学习算法预测空中出租车城市航空服务的需求。ArXiv,abs/2103.14604。
[2]https://daedalean . ai/blog/tpost/mou 9 nieca 1-neural-networks-design-assurance-with-ea
[3]https://www . turing . AC . uk/research/impact-stories/put-ai-air-traffic-control
https://medium.com/data-driven-fiction/how-to-submit-5e0808dce313
使用 Flask 在 Heroku 上部署机器学习模型
原文:https://towardsdatascience.com/machine-learning-model-deployment-on-heroku-using-flask-467acb4a34da
机器学习平台即服务
凯文·Ku 在 Unsplash 上的照片
几周前,我写了一篇关于使用 Spark 部署机器学习模型的帖子,如果你想批量而非实时地进行预测,这是一种部署 ML 模型的好方法。
但是如果你想要实时预测,那么这个解决方案并不理想,因为…
- 做出预测需要几秒钟,而且不是实时的。
- 手动部署会有一些停机时间。
- 没有 UI 来测试解决方案。
将机器学习模型部署为服务可以解决大多数问题,预测将是实时的。但是会有一些问题,比如可伸缩性、监控和服务中断。尽管有许多云提供商来解决这些问题并提供 24*7 支持。尽管如此,如果你是一家小公司或者刚刚开始从事 AI/ML,并且不想花更多时间来处理云部署或 DevOps 任务,并且想要一个快速部署选项,那么使用 Flask 在 Heroku 上部署你的机器学习模型将解决你的所有问题。
Heroku 使用烧瓶
Heroku 是 Salesforce 的平台即服务工具。Heroku 由 AWS 支持,所有 Heroku 应用程序/服务都托管在 AWS 上。AWS 提供基础设施并处理所有负载平衡、资源利用、联网、日志记录、监控,Heroku 充当中间人,提供具有所有云功能的可扩展、自动化的快速部署平台。使用 Flask 将提供 UI 来测试,并且它可以与企业级应用程序集成。
作者图片
使用 Flask 在 Heroku 上部署的步骤
使用 Flask 在 Heroku 上部署从创建机器学习模型到部署有 7 个步骤。这些步骤对于所有机器学习模型都是一样的,你可以使用这些步骤在 Heroku 上部署任何 ML 模型。
- 创建 ML 模型并保存(pickle)它
- 为 UI 和 python 主文件(app.py)创建 Flask 文件,这些文件可以从步骤 1 中取消选取机器学习模型并进行预测
- 创建 requirements.txt 来设置 Flask web app 和所有 python 依赖项
- 创建 Procfile 以启动 Flask app 命令
- 在 Github repo 中提交步骤 1、2、3 和 4 中的文件
- 在 Heroku 上创建账户/登录,创建一个 app,连接 Github repo,选择 branch
- 在 Heroku 上选择手动部署(或启用自动部署)
大功告成!!
您可以在 Heroku 仪表板中检查日志或使用 Heroku CLI。在日志的结尾,它会给出访问 UI 上部署的应用程序的 URL。该网址将有语法像-【https://app-name.herokuapp.com/】
深潜
现在让我们来看看上面 7 个步骤的实际代码实现(用 python)。
在步骤 1** 中,我们创建一个 ML 模型并将其保存在 pickle 文件中。**
这里第 3 行中的 gbc 是为收入预测而训练的梯度提升分类器模型。如果你想知道我是如何创建这个梯度推进预测模型的,请参考 GitHub 链接。
在第二步中,我们将创建 flask 文件——index.html 和 app.py
****index.html,是一个烧瓶 UI 文件,用于向模型提供输入(或特征)。
app.py ,是一个 python 主文件,它从步骤 1 中解开梯度增强模型,呈现 flask UI 页面 index.html,并基于来自 UI 的输入进行预测。
在步骤 3** 中,我们将创建 requirements.txt 来添加 flask 应用程序的所有依赖项。**
在步骤 4** 中,我们将创建 Procfile 来指定 Heroku app 在启动时执行的命令。**
在步骤 5** 中,我们将把步骤 1–4 中的所有文件添加到 Github repo 中。收益预测的 Github 回购可以在这里找到。**
在步骤 6** 中,我们将登录 Heroku 并创建一个新的 app,这里是-收入-预测-api-heroku。**
作者图片
接下来,我们将在步骤 5 中创建的 GitHub repo 连接到 Heroku 应用程序,并选择一个分支。
作者图片
最后,在步骤 7** 中,选择手动(或自动)部署,您可以看到构建日志滚动。一旦应用程序被部署,您将在日志中获得应用程序的 URL 并显示成功消息。**
作者图片
点击“查看”,将带您进入第 2 步中的 Flask UI 页面。
作者图片
一旦你填写了所有的字段并点击预测,它将给出最终的预测结果。
作者图片
Heroku 日志
您可以在 heroku-cli 中查看 Heroku 应用程序部署日志。下载 cli 后,打开命令提示符并登录 Heroku。
作者图片
登录后,您可以使用命令查看部署日志
heroku logs -app
作者图片
您可以在日志中看到所有加载的构建库和应用程序部署步骤。如果在部署过程中出现任何错误/失败,这些日志对于调试更有用。
就这样!使用这些简单的步骤,任何机器学习模型都可以部署在 Heroku 上,以获得实时预测。
局限性
- 成本- Heroku 每小时的定价比 AWS/Azure 贵很多。所以如果你在 Heroku 上使用多个大规模的应用,那肯定会是一件成本更高的事情。
- 与 AWS/Azure 相比,Heroku 支持的地理区域有限。
- 手动扩展- Heroku 不提供 web 应用的自动扩展,但允许手动水平和垂直扩展。
- 部署与 Git 链接- Heroku 部署与 Git 链接,如果您使用其他版本控制工具,这是一个限制。
结论
使用 Heroku 生产 ML 模型是一个很好的选择,如果你正在部署一个中小规模的应用程序,不要担心自动伸缩,只想专注于构建 ML 应用程序而不是基础设施。
虽然使用 Heroku 部署会花费更多;同样的成本可以通过更少的资源来构建/监控本地/云基础架构和执行开发运维任务来节省。Heroku 是初学者友好的,你可以在几秒钟内创建一个服务器并部署应用程序。在 AWS 的支持下,Heroku 提供了所有的云平台功能,如扩展、日志、监控、配置和健康检查。此外,您还可以获得 24x7 全天候运营支持,并且不会停机/停机时间最短。
要使用 Heroku 访问实时预测的完整代码,请参考 GitHub 链接。
如果您想了解更多模型部署策略,请参考-
**https://medium.com/swlh/productionizing-machine-learning-models-bb7f018f8122
谢谢你的阅读。如果你喜欢这个故事,请喜欢,分享和关注更多这样的内容。如往常一样,请联系我们以获得任何问题/评论/反馈。
Github:https://github.com/charumakhijani 领英:https://www.linkedin.com/in/charumakhijani/**
机器学习模型漂移
原文:https://towardsdatascience.com/machine-learning-model-drift-9cc43ad530d6
类型、原因、检测、缓解和工具
罗宾·皮耶尔在 Unsplash 拍摄的照片
在机器学习中,模型漂移意味着由于输入特征、目标变量或变量之间关系的统计属性的变化,机器学习模型变得越来越不准确。训练机器学习模型的数据称为训练数据或源数据。模型试图预测的数据称为服务数据或目标数据。训练/源数据分布可能不同于服务/目标数据分布。在本文中,我们将介绍模型漂移的类型、原因、如何检测模型漂移、如何减轻模型漂移,以及我们可以用来监控模型漂移的工具。
环境
给定一组输入特征 X,我们预测目标变量 y。例如,在房价预测模型中,X 可以是一组房屋特征(例如,卧室数量、大小、位置),y 可以是房价。一对(X,y)表示数据集中的一条房屋记录。p(X)和 p(y)分别是观察到房屋特征 X 和房价 y 的概率,也称为边际概率或先验概率。p(y|X)是给定房屋特征的房价的条件分布。
模型漂移的类型
模型漂移包含两个关键类别:概念漂移和数据漂移。数据漂移还包括协变量/特征漂移和标签漂移。
协变量/特征漂移
当 p(X)改变但 p(y|x)保持不变时,会发生协变量漂移或特征漂移。输入房屋特征的边际分布发生变化,但给定房屋特征的房价的条件分布保持不变。
让我们来看看房子的一个特征——大小。想象一下,你的模型在 Covid 之前就已经被训练好了,而当时市场上有更多的大户型房子。在 Covid 期间,人们都想搬到更大的房子,市场上更大尺寸的房子越来越少。
图一。协变量漂移(图片由作者制作)
标签漂移
当 p(y)改变但 p(x|y)保持不变时,就会发生标签漂移。
在房价预测示例中,房价分布 p(y)可能会在之前训练模型后发生变化。例如,在疫情期间,房价大幅上涨,导致房价分布向更高值移动。
图二。标签漂移(图片由作者制作)
概念漂移
当 p(y|X)改变但 p(X)保持不变时,就会发生概念漂移。
在房价预测示例中,给定房屋特征 p(y|X)的房价的条件概率可以改变。让我们重新考虑一下前面的例子。假设房子大小的分布没有变化。因为现在人们喜欢更大的房子,更大的房子变得更贵。给定房屋尺寸的房价的条件概率可能发生变化,尤其是对于较大的房屋。
图 3。概念漂移(作者制作的图像)
模型漂移的原因
ML 模型漂移的原因有很多。以下是一些例子:
数据漂移的主要原因之一是采样不匹配。我们经常使用一些采样策略来寻找源数据来训练我们的模型。抽样策略可能有偏差,不能代表全部人口。例如,如果我们想要开发一个模型来预测一般波士顿地区的房价,我们就不能使用仅包含 Back Bay 地区周围列出的房屋的训练数据。
有时候,人们将他们训练有素的模型应用到一个新产品/市场/环境中,发现他们的模型并不好用。例如,疫情改变了世界的宏观经济。与《疫情时报》之前相比,我们处于一个非常不同的市场和环境中。此时,要素、标注的分布以及要素和标注的联合分布都可能发生变化。最好的解决办法是重新训练模型。
异常可能出现在训练数据中,也可能出现在目标数据中。异常会改变我们数据的分布。有时我们的模型会自动处理异常情况。其他时候,我们需要采用一些异常检测技术并过滤掉(例如,winsorize)异常。
季节性影响也会导致模型漂移。多个变量之间的关系,例如,房子大小和房价,可能会根据月份或季节而变化。通常建议在模型中考虑季节影响,或者在某些情况下,在不同的模型中模拟不同的场景。
数据质量问题的发生可能比你想象的要多。存在各种数据质量问题,例如不正确的输入数据、不正确的数据处理步骤和重复。有时,数据或数据处理管道中的变化不是质量问题,而是预期的和业务需求。如果不密切监控,这种数据变化也会导致模型漂移。设置护栏以验证数据并确保数据质量和一致性是 ML 模型的先决条件。
检测漂移
监控模型性能
检测漂移最直接的方法是通过监控模型性能指标。最常见的模型性能指标包括混淆矩阵、准确性、召回率、F1 评分和 ROC-AUC。根据您的模型使用情况,可能还有其他重要的模型行为度量标准。
同样,一些研究集中于误差率,并使用基于误差率的漂移检测方法。例如,漂移检测方法(DDM)算法可用于检测任何误差率的显著增加。
监控描述性统计
我们可以用最小值、最大值、中值、平均值、唯一性、相关性等度量来统计描述数据集。我们可以将所有这些描述性统计数据包含在我们的模型监控仪表板中,以可视化这些统计数据如何随时间变化。
监控分布变化
当您看到描述性统计数据中的变化时,您如何知道分布中有实际的变化,以及您如何知道这些变化是显著的和有意义的?人们使用几种统计测试或假设测试来检测统计上的分布变化,如群体稳定性指数、Kullback-Leibler (KL)散度、Jensen-Shannon、Kolmogorov-Smirnov 测试、Cramér-von Mises、Fisher 精确测试、最大平均差异(MMD)和最小二乘密度差。
例如,人口稳定指数 ( PSI )衡量“随着时间的推移,或者在一个单一数字中的两个不同人口样本之间,人口发生了多大的变化”。 Kolmogorov-Smirnov 检验(或 KS 检验)“量化样本的经验分布函数与参考分布的累积分布函数之间,或两个样本的经验分布函数之间的距离”。
此外,还有被设计用于检测模型漂移的监督和非监督 ML 模型。
缓解措施
在实践中,我们经常建立一个模型监测系统来检测模型漂移。其他时候,当我们的模型有问题时,我们通过模型分析发现数据漂移。在任一情况下,当模型漂移发生时,我们首先需要找到漂移的原因。模型漂移的原因是什么?不同的根本原因应该有不同的缓解策略。详情请参考“模型漂移的原因”。
假设我们的数据是正确和有效的,减轻模型漂移的一个常用方法是用新数据重新训练模型。你可能会想,为什么我们不一直重新训练模型,这样我们就不需要关心模型漂移了?这是一个很好的问题。事实上,很多公司都在尽可能地模拟再培训。然而,有时模型的再训练和部署并不那么简单。例如,您的新数据可能没有标签。你新训练的模型可能不会更好,特别是当你还没有做漂移分析,并且不确定它在哪里出了错的时候。
工具
有很多 ML 监控工具和模型漂移监控工具。以下是一些流行的工具:
whylogs 是一个“用于记录任何类型数据的开源库”。借助 whylogs,用户能够生成数据集的摘要(称为 whylogs 配置文件),用于跟踪数据集中的变化,创建数据约束以了解数据是否符合预期,并快速可视化数据集的关键摘要统计信息。”
显然是一个“在生产中评估、测试和监控 ML 模型的开源框架”
Alibi Detect 是一个“开源 Python 库,专注于异常、敌对和漂移检测。”
如果您使用这些平台,所有主要的云提供商和 MLOps 平台也都有自己的模型性能和模型漂移监控工具。例如,Amazon sage maker Model Monitor“通过将真实世界的数据与基线数据集(如训练数据集或评估数据集)进行比较来检测数据偏差。”
结论
在你的机器学习之旅中,设计一个机器学习模型只是一个开始。一旦你制作了完美的机器学习模型并将其部署到生产中,真正的挑战就来了。适用于当前数据的模型可能不适用于未来。持续监控的一个重要组成部分是模型漂移。监控和减轻模型漂移有助于保持您的机器学习模型愉快和成功!
参考资料:
- 概念漂移适应研究综述。 ACM 计算调查(CSUR)
- Quionero-Candela,Joaquin 等机器学习中的数据集转移。麻省理工学院出版社,2009 年。
- 设计机器学习系统。奇普·胡恩。2022.
- https://www . fiddler . ai/blog/drift-in-machine-learning-how-to-identify-issues-before-you-have-a-problem
- 【https://mwburke.github.io/data 科学/2018/04/29/人口-稳定-指数. html
. . .
作者索菲亚杨 2022 年 8 月 23 日。
Sophia Yang 是 Anaconda 的高级数据科学家。在 LinkedIn 、 Twitter 和 YouTube 上与我联系,并加入 ds/ml❤️读书俱乐部
大规模的机器学习
原文:https://towardsdatascience.com/machine-learning-on-a-large-scale-2eef3bb749ee
PySpark 中二项式和多项式逻辑回归的演示
戴维·尤斯科在 Unsplash 上的照片
随着本文本地部署的 Spark 3.2.1 的发布,PySpark 提供了一个流畅的 API,类似于 scikit-learn 的表达能力,但还提供了分布式计算的好处。本文演示了如何使用 pyspark.ml 模块在 spark 数据帧上构建 ml 管道(而不是使用旧的 pyspark.mllib 模块构建 rdd)。使用二项式和多项逻辑回归来举例说明该功能,不可否认,这不是最先进的机器学习算法。尽管如此,它们的简单性使它们成为演示 PySpark 机器学习 API 的理想选择。对于不熟悉 PySpark 机器学习的读者,以及更熟悉早期版本的 Spark,尤其是 pyspark.mllib 模块的读者,本教程可能会感兴趣。
目录
设定场景
二项逻辑回归
∘ 准备工作
∘ 首次建模尝试
∘ 评估模型质量
∘ 交叉验证和超参数调优
∘ 模型解释
多项逻辑回归
结论
设定场景
我们首先通过分配 8gb 内存和四个内核来创建一个 spark 会话
上面的代码还包含整篇文章所需的所有 pyspark 导入。当涉及到其他软件包时,我们将根据需要使用更多的进口产品。
我们将使用通过df = sns.load_dataset('iris')
从 seaborn 获得的虹膜数据集。这是一个著名的数据集,包含四个连续的特征,即属于三个不同物种的 150 朵鸢尾花的萼片和花瓣的长度和宽度:鸢尾、杂色鸢尾和海滨鸢尾。数据集没有空值,并且所有要素的比例都相当合理,但我们稍后将回到这一点。
显然,这是一个非常小的数据集,绝不需要分布式计算。然而,鉴于本文的目的是说明 PySpark 机器学习 API,选择一个小数据集进行实验是理想的,特别是在使用交叉验证进行超参数调整时,正如我们在本文中所做的那样。使用一个基本的机器学习算法和一个小的、相当干净的数据集并没有打破数据科学的新前沿,但这些选择是有意的。
为了了解分类的效果,我们使用sns.pairplot(df, hue='species')
在数据集中绘制成对关系,给出
图 1:数据集中要素的成对关系
粗略地看一下,我们可以看到鸢尾可能被正确分类,但我们预计在区分杂色鸢尾和海滨鸢尾时会有一些困难。
二项逻辑回归
为简单起见,我们将对二项式和多项式逻辑回归使用相同的数据集。对于二元分类,我们试图预测该物种是海滨鸢尾还是海滨鸢尾
从这一点开始,所有操作都将在 PySpark 中进行,将 pandas 数据帧转换成 PySpark 数据帧
自动转换自动生成了预期的模式。
准备工作
PySpark 使用转换器和估算器将数据转换为机器学习功能:
- 转换器是一种可以将一个数据帧转换成另一个数据帧的算法
- 估计器是一种算法,它可以适用于数据帧以产生变换
以上意味着变压器不依赖于数据。机器学习模型是一个转换器,它获取具有特征的数据帧,并通过其.transform()
方法产生包含预测的数据帧。另一方面,估计器有一个.fit()
方法,它接受数据帧并产生一个转换器。PySpark 中的流水线链接了 ML 工作流中的多个转换器和估算器。scikit-learn 的用户一定会有宾至如归的感觉!
回到我们的数据集,我们构造第一个转换器,将四个特征打包成一个向量
features 列看起来像一个数组,但它是一个向量。方便的是,向量组装器还会填充模式中 features 列的元数据属性
还可以检索原始特征的列名,尽管使用feature_assembler.getInputCols()
可能会更方便。
尽管这些特征或多或少是成比例的,但是如果我们确保所有特征的范围从 0 到 1,那么拟合的逻辑回归系数的解释将变得容易。这可以使用最小-最大缩放器估计器来实现
在上面的代码中,minMax_scaler_model
是一个通过将minMax_scaler
估计器与数据相匹配而产生的转换器。使用矢量可以方便地一次性缩放所有连续要素。顺便提一下,pyspark.ml.feature
模块包含了vector_to_array()
和array_to_vector()
函数来转换向量和数组,所以像minMax_scaler
这样的估计器也可以用于机器学习之外的数据转换。
原则上,features_scaled 和 species 列现在可用于拟合逻辑回归模型。然而,在此之前,我们将引入另一个概念,ML 管道,它可用于编排 ML 工作流
结果和以前一样,但是代码更简洁。管道在技术上是一个估计器,并有一个返回转换器的.fit()
函数。在幕后,装配一个管道调用用于变压器的.transform()
函数和用于估计器的.fit()
函数,按照它们在管道阶段被引入的顺序。在实践中,我们可以用不同的变压器和估算器构建多条管道,并通过构建模型来试验我们的选择的效果。
首次建模尝试
在调整模型和评估其准确性之前,先做一个粗略的尝试,看看最终得到一个合理模型的可能性有多大,这是很有用的。为此,我们不使用交叉验证,我们只指定必需的模型参数,其余的都保留默认值。
首先要做的是增加一个管道阶段,即使用一个StringIndexerModel
将物种列从字符串转换为数值
因为这是以后需要的。或者,我们可以使用StringIndexer
估计器通过使用数据创建模型,并根据物种名称的频率分配指数。我们选择不这样做,因为我们想确保 Iris virginica 映射到 1.0,而不是 Iris virginica 映射到 0.0。然后,我们将数据集分成训练集和测试集
我们将种子指定为一种良好的实践,尽管在 Spark 世界中,由于数据的底层分区,这并不能确保确定性行为。如果你对这个令人难以置信的话题感到好奇,下面的实验可以说明这个问题
您可以在这篇文章中阅读更多内容,包括如何避免这个问题的想法,例如,通过缓存整个数据集而不仅仅是训练集(如下所述)。存储训练集和测试集并再次读取它们以构建模型是确保确定性行为的另一种方式。请注意,训练数据集在任何情况下都应该被缓存,因为在拟合模型时会重复使用它。这可能是 Spark 中最典型的缓存用例。
剩下的唯一事情就是拟合模型并评估其准确性。为了完整起见,提供了整个代码,这样更容易理解
为了方便起见,我们使用pyspark.ml.feature.IndexToString()
将预测的数值转换回标签。请注意,我们只使用了萼片宽度和花瓣宽度作为自变量。此外,我们只使用了 20%的数据集进行训练。这些奇怪选择的原因是,这个机器学习问题实际上非常容易解决,因此我们几乎总是不费吹灰之力就能获得一个好的模型。通过丢弃一些特征和使用一个小的训练集,我们从一开始就计算出不完美的度量。
我们现在有了对测试集的预测,并且可以一瞥我们做得有多好。
评估模型质量
也许获得关于二项式分类模型性能的概念的最常见方法是计算混淆矩阵。在 PySpark 中,这可以很容易地用
使用 seaborn 中带注释的热图,混淆矩阵也可以很容易地可视化
产生了
图 2:混淆矩阵
如果您尝试跟随,由于训练集和测试集的不同划分,您可能会获得稍微不同的结果。
有许多可以计算的指标,但我列出了与稍后的 ROC 曲线计算相关的最重要的指标:
- **召回率、灵敏度或真阳性率:**反映模型识别阳性的能力,定义为 TP/(TP+FN)
- 精度或阳性预测值:显示预测阳性为真阳性的频率,定义为 TP/(TP+FP)
- 特异性或真阴性率:反映模型识别阴性的能力,定义为 TN/(TN+FP)
- 假阳性率:反映假阳性的概率,定义为 FP/(TN+FP)= 1-特异性
在计算出混淆矩阵后,这些度量很容易手动计算。或者,为了方便起见,也可以使用 PySpark API
测试集的受试者操作者特征(ROC)曲线可从metrics.roc
中检索,但我们也将使用原始概率手动计算。我们使用 seaborn 来可视化这两种方法的结果
产生了
图 3: ROC 曲线。曲线从(0。, 0.)对应于等于 1 的阈值。这意味着每个预测都是负面的。曲线在(1)处结束。, 1.)对应于等于 0 的阈值。,这意味着每个预测都是正面的。
我不建议手动计算 ROC 曲线,原因有几个,包括对性能的担忧。然而,考虑到基于数据帧的 PySpark 机器学习 API 是新的,文档还不完善,所以要小心谨慎。建议检查结果是否有意义,以确保正确使用 API。PySpark API 还可以返回精确召回(PR)曲线,这在类非常不平衡时很有用。ROC 曲线的目的是选择阈值,以实现所需的灵敏度和特异性。阈值可视为模型的参数,与所有参数一样,它不应在测试集上计算,而应在训练集上计算,或者更好的是,在验证集上计算(见下一节)。PySpark API 通过lr_model.summary.roc
和lr_model.summary.pr
为训练集曲线提供 ROC 和 PR 曲线。
ROC 曲线也用于计算 ROC 曲线指标下的面积。完美模型的 ROC 曲线会接近左上角,而随机模型会接近对角线(真阳性率=假阳性率)。ROC 曲线下的面积介于 0。并且可以通过一个BinaryClassificationEvaluator
对象来计算
结果令人印象深刻,尽管试图阻碍模型质量。训练集的 ROC 曲线下的面积可从模型摘要中获得lr_model.summary.areaUnderROC.``BinaryClassificationEvaluator
对象也可用于计算 PR 曲线下的面积。
交叉验证和超参数调整
与大多数其他模型一样,逻辑回归模型具有可以微调的参数,以优化模型的准确性和稳健性。上一节描述了第一次建模的尝试,这种尝试走了很多弯路。我们对逻辑回归模型的所有参数使用默认值,并通过将原始数据集分成两部分来简化模型开发,一部分用于训练,一部分用于测试。这是一个很好的开始方式,以便获得可以实现的第一个想法。一旦我们确信我们有可能产生一个可行的模型,我们就可以使用交叉验证来优化模型的参数。这是一个昂贵的手术。首先,对于一组给定的模型参数,我们对模型进行多次拟合和评估(这就是所谓的折叠)。其次,我们尝试许多不同的模型参数集。本节给出了使用 4 重交叉验证的二项式逻辑回归的完整代码,并作为 PySpark 中其他机器学习模型如何训练和优化的示例。
图 4:将原始数据集分成训练集、验证集和测试集(图片由作者提供)
交叉验证需要三个构件:
- 估计器(模型),通常封装在 ML 管道中
- 我们试图调整的超参数网格
- 一个评估度量,本质上是超参数调整的目标函数
在我们的例子中,我们使用早期设置的管道。我们将微调逻辑回归模型的两个参数,即
- 弹性净混合参数α(α= 0 表示 L2 惩罚,α-1 表示 L1 惩罚),以及
- 正则化参数
这听起来可能很奇怪,但是我们也将通过调整传递给向量汇编器的特性来使用超参数调优来选择特性。对于这个例子来说,这有点牵强,但是我选择这样做是为了表明所有管道阶段的所有参数原则上都可以包含在微调中。参数组合的数量迅速增长,实际上运行时间的限制抑制了热情。
使用的评估器是在不同折叠的平均验证集上计算(正确)的 ROC 曲线下的面积。
网格搜索检查了 165 个参数组合,每个组合都适合并评估了四个模型。尽管 iris 数据集与 Spark 通常处理的数据集相比非常小,但这花费了大约 350 秒。每个参数组合的 ROC 曲线下的平均面积可以用cv_model.avgMetrics
来检索,这表明几个组合达到了近乎完美的度量(ROC 曲线下的面积~=1。).可以用cv_model.bestModel
从中检索最佳模型(不确定当两组参数表现相同时 Spark 如何选择最佳模型,但这在现实世界用例中不太可能发生)。
所有拟合的模型被存储,并可通过cv_model.subModels[k][i]
检索,其中 k 是折叠,I 是参数网格中参数集的索引。在我们的例子中,对于 i=4 获得了最好的结果,并且对应于四个折叠的 4 个模型可以通过[cv_model.subModels[k][4] for k in range(4)]
获得。我们应该检查不同褶皱的系数分布,这是模型稳定性的指示,甚至使用最佳超参数再次拟合整个训练集。这超出了本文的范围。我们将使用与cv_model.bestModel
一起返回的最佳模型,无需进一步调查。
模型解释
也许令人惊讶的是,通过仅使用萼片和花瓣宽度而不进行正则化,我们获得了一个非常好的模型。线性模型的系数和截距可以很容易地恢复
并储存在熊猫系列中。方便的是,vector assembler 将特征名称存储为可用于设置系列索引的模式元数据。这些特征已经过最小-最大缩放,有助于解释。花瓣宽度的影响大约是萼片宽度的 4 倍。
这是一个特殊的情况:超参数调整碰巧只保留了两个特征,而没有必要进行正则化。这允许可视化决策边界,唯一的复杂性是特征缩放的处理
产生了
图 5:最佳模型的决策边界
图 5 显示了训练集(意味着除测试集之外的所有数据点),我们可以看到有一个假阴性和两个假阳性(一个几乎在决策边界上),这与训练集的混淆矩阵一致。一切看起来都很好。
多项逻辑回归
PySpark 还支持多项式逻辑回归(softmax),因此可以一次性预测虹膜数据集的所有类别。我们不会涵盖所有细节,因为这篇文章已经很长了。下面是首次尝试拟合多项式逻辑回归模型的完整代码
我们再次获得了一个良好的模型与第一次尝试。
结论
机器学习是一个广阔的领域。解决问题需要设计功能、选择模型、决定使用哪个指标进行超参数调整,并设计一个策略通过改变参数来最大化该指标。本文不提供以上问题的答案,而是重点介绍如何使用 PySpark 最新的机器学习 API。构建模型从来都不是一个线性的过程,熟悉pyspark.ml
模块提供的功能是加速实验的良好起点。大规模构建模型从未如此简单!
基于图形数据的机器学习
原文:https://towardsdatascience.com/machine-learning-on-graph-data-eef60a8533f1
如何在 ML 模型中利用图形信息
美国宇航局在 Unsplash 拍摄的照片
介绍
许多真实世界的数据集可以自然地被框成图表。比如在社交网络等线上平台上,用户可以表示为节点,关注或喜欢可以表示为边。
然而,当在来自这些领域的数据上构建模型时,人们经常通过忽略底层的图结构来简化问题。在这样做的时候,机器学习实践者忽略了有用的信息,这些信息将有助于将实体(例如,用户)置于他们所属的更广泛的网络的上下文中。
在这篇文章中,我将介绍各种方法,从非常简单到复杂,将图形信息合并到机器学习模型中。我将主要关注节点级别的任务,比如节点分类,而不是整个图级别的任务,因为它们在行业中更常见。
什么是图?
图形是对实体对之间的关系进行编码的数据结构。图中的实体称为节点,关系称为边。边缘可能是也可能不是定向的,这是指边缘是否双向应用。在有向图中,从 A → B 的边的存在并不意味着从 B →A 的边的存在,而在无向图中,它是存在的。
例如,在社交网络上,一个图可以根据用户关注的人将用户与其他用户联系起来。在这种情况下,节点将是用户,边将编码谁跟随谁。该图中的边是有向的,因为用户不必跟随跟随它们的每个人。
这个图中的节点也有特征,比如账户的年龄,或者用户来自哪个国家。这些属性将节点相互区分开来,并提供额外的上下文。
图为 Kier 在的视线中在 Unsplash 上
在这个社交网络图中,我们可以观察到许多我们知道的社交网络现象,例如:
- 一些用户,如名人,会有更多的优势,因为名人有数百万粉丝,但很少跟随每个人回来。
- 一些用户,如机器人,会有相反的情况,可能会有更多的优势,因为机器人往往会跟随随机的人,但真正的用户不会跟随机器人。
- 用户通常会形成由倾向于相互认识的用户组成的紧密连接的群组的集群。同一兴趣小组中的朋友或人往往会追随许多相同的人。
机器学习方法
现在我们将开始讲述如何在您的模型中利用图表信息。这些方法从简单的统计到完全可训练的图形神经网络,每种方法都有自己的优点和缺点。
图表统计
利用图表信息的最简单方法是计算基本计数和比率,并将它们用作数字特征。
一些图表统计包括:
- 度,它计算给定节点有多少条边。
- 中心性,抽象地衡量一个给定的图对整个图的连通性有多重要。有各种类型的中心性度量,例如中间中心性和接近中心性,但是在高级别上,对于位于有效地将许多节点相互连接的路径中的节点,它们更高。
- 聚类系数,它有效地测量了图中节点局部的密度。具有全部相互连接的邻居的节点将具有较高的聚类系数。
这些图表统计是一个很好的起点,并且具有易于理解的标准化含义。
然而,它们所能传达的信息相对有限:
- 他们根本无法利用节点功能。对于这些汇总统计信息,具有相同值的所有节点是无法区分的。
- 在这些特征的产生中没有可学习的成分。我们不能适应一个定制的目标,也不能用一个下游任务来训练他们。
节点嵌入
利用图信息的下一种方法是学习图中每个节点的节点嵌入,并将这些嵌入用作下游模型中的特征。节点嵌入是我们可以映射到图中每个节点的可学习的数字向量,允许我们学习每个节点的唯一表示。
了解这些嵌入的最常见方法(来自 DeepWalk 和 Node2vec 的论文)是强制彼此靠近的节点具有相似的表示。他们使用随机行走的概念,从一个给定的节点开始,随机穿过边,产生彼此相邻的节点对。通过最大化在随机行走中同时出现的节点之间的余弦相似性来训练嵌入。这个训练目标利用了同向性假设,该假设指出相互连接的节点倾向于彼此相似。
这些节点嵌入方法允许我们学习每个节点的独立于任务的表示,并且可能比前面部分的方法具有更大的表示能力,因为它们可以学习每个节点的独立表示。
然而,虽然这是朝着正确方向迈出的一步,但这些方法仍有不足之处:
- 它们根本不使用节点功能。他们假设附近的节点是相似的,而没有实际使用节点特征来证实这一假设。
- 它们依赖于从节点到嵌入的固定映射(即这是一种直推式方法)。这意味着对于可能添加新节点和边的动态图,必须从头开始重新运行算法,并且所有节点嵌入都需要重新计算。在现实世界的问题中,这是一个相当大的问题,因为大多数在线平台每天都有新用户注册,并且新的边缘不断被创建。
图形卷积网络
我将介绍的最后一种方法是图形卷积网络(GCN)。这是一种执行图形表示学习的更高级的方法,克服了以前的节点嵌入方法的许多缺点。
图卷积网络通过学习聚集节点邻域(连接到原始节点的节点集)的函数,使用图结构和节点特征来学习节点的表示。这些表示是节点邻域的函数,并且不是每个节点硬编码的(即,这是一种归纳方法),因此图结构的变化不需要重新训练模型。
从概念上讲,GCN 的单个图层可以简单地认为是对原始节点邻域中的节点要素进行加权平均,其中权重是通过训练网络来学习的。然后,我们可以堆叠这些 GCN 层,以产生使用更多图表的聚合。对于我们添加的每个 GCN 层,我们将用于产生节点嵌入的子图的跨度扩展 1 跳。
GCNs 可以通过监督和非监督的方式进行训练。对于有监督的训练,我们简单地根据下游任务的标签进行训练。对于无监督训练,有几个选项,但最常见的方法类似于 Node2vec/DeepWalk 方法,其中我们强制附近的节点具有相似的表示。
gcn 克服了先前方法的许多问题,因为 1)它们是端到端可训练的,2)它们充分利用节点特征,以及 3)它们不依赖于从节点到嵌入的固定映射。
由于这些优势,这种类型的图形神经网络已经在许多现实世界的工业系统中使用,例如推荐系统(例如 PinSage )和欺诈检测系统。
结论
在这篇博文中,我们介绍了在机器学习模型中利用图形信息的三种不同方法,从简单的图形统计到端到端的可训练图形神经网络。
这些方法允许机器学习模型利用许多问题中存在的丰富的图形上下文。读完这篇文章后,希望你能更清楚如何在你的模型中利用图表信息。
图上的机器学习,第 4 部分
原文:https://towardsdatascience.com/machine-learning-on-graphs-part-4-44b690ec2ba3
由 Unsplash 上的 Clarisse Croset 拍摄的照片
学习离散节点嵌入
在这篇文章中,我想提出离散图节点嵌入,作为我关于图的机器学习系列的一部分(第 1 部分、第 2 部分、第 3 部分)。特别是,它还讨论了我上一篇关于离散节点嵌入的研究论文,这篇论文最近被接受在 AAAI 第 23 届人工智能大会上发表。
在之前的一篇文章中,我介绍了学习节点嵌入算法背后的主要思想。默认情况下,当我们谈论自然语言处理中的单词嵌入或图形学习中的节点嵌入时,我们隐含的意思是连续嵌入。也就是说,像图形节点这样的离散对象由固定大小的连续向量表示。比如一个五维向量 like (0.3,0.67,-0.9,0.06,-0.55)。
众所周知,连续嵌入是一种强大而通用的数据表示技术。然而,尽管连续嵌入可能很强大,但对于人类用户来说,它们并不是很直观。
例如,考虑一个社交网络图,其中节点代表用户。每个用户由不同的属性描述,例如性别、位置、专业经验、个人兴趣等。连续的节点嵌入将利用这个信息。例如,伦敦的软件工程师很可能最终拥有类似的节点嵌入,因为他们在伦敦地区共享许多连接,例如与招聘人员或软件工程师同事。然而,连续的嵌入对于人类读者来说是没有意义的。我们大多数人都无法计算大脑中 50 维向量之间的余弦相似度或欧几里德距离。
另一方面,我们擅长通过滚动大量文本来获取信息。离散节点嵌入背后的思想是嵌入向量可以由人类可读的离散特征组成。所以,代替由 k 实值,每个节点由 k 离散特征表示。这些特征可以是节点属性或简单的其他图形节点。那么一个离散的嵌入向量看起来像
(软件工程师,伦敦,数据科学家,剑桥,化学家)
每个坐标都有一个定义明确的值。从上面我们可以推断,用户是拥有 STEM 学位的英国专业人士的专业团体的一部分。
我们需要解决以下问题:
- 是什么使得离散节点嵌入在下游机器学习任务中有用?
- 如何生成离散嵌入?
- 如何用离散节点嵌入训练机器学习模型?
有用的离散嵌入
如前所述,离散节点嵌入很容易被人阅读。但是为了在可解释的机器学习模型中使用它们,我们需要有效地比较两个嵌入。第一个想法是随机收集一组“足够接近”的节点,和/或描述它们的属性,然后使用一组比较运算符,如 Jaccard similarity 。如果对应的集合彼此相似,则两个节点嵌入是相似的。这种方法可以工作,但用集合代替向量将限制我们可以使用的机器学习算法的选择。此外,计算集合交集比连续嵌入中使用的内积计算要慢。(即使两种情况下的渐近复杂度都是线性的,集员运算也会引入一些计算开销。)取而代之,我们设置以下要求。
也就是说,我们要求每个坐标对都是节点间相似性的无偏估计量。然后通过它们的汉明距离比较两个嵌入向量,即它们不同的坐标数,所谓的重叠或汉明核。关于上述定义中两个节点之间的相似性度量,在(属性化的)图节点之间有大量的度量。事实上,这是我在上一篇文章中讨论图形内核的主题。在高层次上,我们希望被具有相似特征的节点“包围”的节点具有相似的嵌入。例如,一个简单的度量是由汉明核近似的节点邻域之间的 Jaccard 相似性。
如何生成离散嵌入?
第一个问题是哪些节点应该被认为是“足够接近”的采样点。我们因此定义一个节点的 k- 跳邻域为通过遍历最多 k 条边可以到达的所有图节点。节点 u 的 0 跳邻域因此是 u 本身,1 跳邻域是通过边连接到 u 的节点,2 跳邻域包括邻居的所有邻居,等等。
定义了要从中对节点或节点属性进行采样的节点集之后,下一个问题是如何生成向量。例如,在每个森林开始随机行走并遍历 k 条边将是一个选项。对于一个 n 维嵌入向量,我们可以在每个节点开始 n 次随机行走。但是很容易看出,这种嵌入不太可能有大的重叠。因此,对于有用的相似性度量,不满足上面定义的形式要求。因此,我们将利用一种叫做协调采样【3】的技术。
协同采样是一种强大的集合间相似性估计技术*。非正式的,*它是基于这样的想法,如果 x 是从集合 A 中采样的,并且如果 x 是另一个集合 B 的成员,那么 x 更有可能也是从 B 中采样的。这样,A 和 B 的共同点越多,它们越有可能以相同的样本结束,从而反映 A 和 B 之间的相似性。有许多不同的协调采样技术,例如参见本演示文稿。让我们考虑一个简单的技术,使上述非正式的描述更加直观。
使用 minwise 哈希的协同采样。
考虑图 1 中简单的玩具图形示例。两个红色节点有两个橙色节点作为共同邻居。因此,1 跳邻域的 Jaccard 相似性是 2/16,因为所有图节点都连接到红色节点。2 跳邻域之间的 Jaccard 相似性是 8/16,因为 8 个蓝色节点在 2 跳中不能被 2 跳中的一个红色节点到达。我们想要应用协调采样,以便我们采样一个邻域节点作为每个节点的嵌入向量中的坐标。
图一。原始图表。图片作者。
我们将应用 minwise 独立哈希。我们将随机排列节点,并将邻域集中排列的第一个节点作为样本。我们展示了如何以一种非常有效的方式实现该方法。
第一步,我们在每个节点生成一个介于 0 和 1 之间的随机数,见图 2。以这种方式可以生成的随机数的数量是巨大的(232 或 264),因此随机数给出的顺序给出了节点上的随机排列。对于每个节点,我们希望从它的 k 跳邻域中找到具有最小随机数的节点。这将是节点的样本。
图二。给节点分配随机权重。图片由作者提供。
对于 k=0,我们什么都不做。节点的 0 跳邻域是节点本身。
图 3。将最小权重传播到邻居节点。图片由作者提供。
每个节点的 1 跳邻域是该节点的直接邻居的集合,即只有通过边连接的节点。在上面的例子中,蓝色节点只有一个邻居。
在图 3 中,我们看到了用一个邻居的最小权重更新每个节点后的结果。例如,右上绿色节点的邻居是权重为 0.02 的绿色节点和权重为 0.28 的红色节点。两个权重都小于其初始权重 0.84,我们更新红色节点,使其现在的权重为 0.02。类似地,右边的红色节点获得新的权重 0.12,因为这是权重最小的邻居。请注意,如果没有比当前节点权重更小的邻居节点,我们不会更新该节点的权重。
图 4。最小节点权重传播的第二次迭代。图片由作者提供。
在图 4 中,我们看到了该方法再一次迭代后的结果。最小权重 0.02 已经到达其 2 跳邻域内的所有节点。具体来说,两个红色节点现在具有相同的样本,即最初被赋予权重 0.02 的绿色节点。请注意,如果将最小的权重分配给其中一个蓝色节点,那么两个红色节点会以不同的样本结束。因为正好 50%的节点是蓝色的,所以很容易看出每个坐标是两个红色节点的 2 跳邻域之间的 Jaccard 相似性的无偏估计量。同样的推理适用于所有节点对。此外,该算法是高效的。每次迭代花费的时间与图的边数成线性关系。因此,对于在 m 边上的图,我们需要时间 O(mkd) 来通过从每个节点的 k 跳邻域中采样来计算所有节点的 d 维嵌入。
如何在机器学习模型中使用离散节点嵌入?
节点嵌入的两个典型用例是链路预测和节点分类。连续和离散节点嵌入可以以本质上相同的方式用于链路预测。预测具有由它们的嵌入给出的最高相似性分数的节点对作为新链接。然而,在节点分类中,我们已经标记了节点,并且我们想要使用嵌入向量来训练模型。因此,我们需要一个能够处理离散节点嵌入的分类模型。显然,像逻辑回归这样的标准选择并不真正适用。
*内核机器。*节点分类的一个直接选择是使用带有预先计算的核矩阵的核机器[2]。在选择节点的子集作为训练数据集 S 之后,我们计算 S 的 Gram 矩阵,即,对于来自 S 的每对节点,我们计算它们之间的重叠。显然,这种方法可能效率很低,因为它需要 O(m)的内存来训练 s 中的 m 个示例。训练具有预先计算的内核的内核机器可能导致 O(m)的时间复杂度。
*显性特征地图。*设 U 是离散嵌入可以取值的论域。例如,如果节点属性是短文本描述,那么 U 就是所有字典单词的集合。通过枚举 U 、 d 中的所有元素,维度嵌入可以由维度的稀疏向量 d *| U |来描述。即使这些向量非常稀疏,只有 d 个非零条目,在机器学习模型中使用它们也可能导致密集的决策向量,并且该方法将变得不可行。这是将嵌入投影到低维向量的想法,因此我在论文中指出,我们可以将离散嵌入投影到近似保持汉明核的 O(d)维向量。你可以在我的主题为的帖子中读到更多关于显性特征地图的内容。显式地图的主要优势在于,我们用仅使用线性时间和空间的高效线性 SVM 模型取代了核心 SVM 模型。
可解释的机器学习。
监督学习。在介绍可解释的机器学习时,决策树是教科书上的例子。离散嵌入自然适用于这种设置。例如,我们选择训练向量的第 12 个坐标作为分割点,并在该坐标处区分两个子集的值。最终我们得到的结果是:“我们预测用户会下载这个 app,因为第 12 个坐标是‘会计’,第 8 个是‘生化科学家’,第 17 个是‘数据分析师’”。总体而言,这表明用户与其他具有技术背景的用户有联系,因此他们很可能会使用该应用程序。
*无监督学习。*我们可以使用适当的距离函数对向量进行聚类。然后,我们可以计算每个集群中的特征分布。在图 5 中,我们展示了对来自 PubMed 数据集的研究论文进行聚类后的结果。相应图中的节点是论文,如果一篇论文引用另一篇论文,那么两篇论文通过一条边连接。每个节点都有一组出现在论文摘要中的关键字作为属性。因此,嵌入由出现在邻域节点中的关键字组成。注意,在下面的例子中,我们使用了显式特征图,距离是欧几里德距离。请注意,该分布以对数标度显示,并显示三个群集的关键字分布非常不同。因此,我们确实发现了相互引用和具有不同主题的研究论文的分割。
图 5。来自(source [1])的群集。图片由作者提供。
连续与离散节点嵌入。
让我们简单比较一下离散节点嵌入和连续节点嵌入。离散嵌入的优点是:
- *计算的简易性和可扩展性。*离散嵌入相对容易计算。我们所需要的是在每个节点上从局部邻域进行协调采样的过程。特别是,我们避免了训练可能很慢的类似 word2vec 的模型的需要。
- 可解释性。如果节点包含可解释的属性,那么由此产生的嵌入为可解释的机器学习打开了大门。
另一方面,离散嵌入的缺点是:
- 离散嵌入并不是真正通用的。我们可以使用连续嵌入作为每个可以想到的机器学习模型的输入,而离散嵌入的选择相当有限。使用上面描述的显式映射方法可以解决这个问题。但是,如果可能的话,可解释模型的设计变得更具挑战性。
- 连续嵌入可以捕获更多关于数据底层结构的信息。像emb(king)~*emb(queen)+emb(man)—emb(woman)*这样臭名昭著的单词嵌入例子显示了连续嵌入捕捉更复杂关系的能力。不太可能使用离散嵌入从原始图结构中提取如此复杂的关系。
更多关于我的 AAAI 论文和提供的代码。
最后,让我简要总结一下我的 AAAI 论文的要点:
- 基于 minwise 散列的方法相当有限。它只考虑图中两个节点之间是否存在路径,而不考虑有多少条路径。本文提出了局部邻域采样算法,该算法根据两个节点之间的路径数对邻域节点进行采样。
- 一个主要的贡献是提出的算法有严格理解的性质。特别是,我提供了定理和不同采样方法保存的节点之间的相似性度量的解释。
一些实用的建议如果你使用实现:
- 一个参考 Python 实现可以在https://github.com/konstantinkutzkov/lone_sampler找到,虽然这个实现对于较小的图来说已经足够好了,但是对于大规模的图来说还需要进一步优化。特别地,不同坐标的嵌入生成可以容易地并行化,因此可以利用现代计算平台。
- 为了了解预期的结果,请尝试高度可伸缩的 minwise 哈希方法和 NodeSketch [2]方法。对 minwise 哈希使用较小的邻域深度,1 或 2。
- 小心二分图。如果在二分图上应用这些方法,建议使用偶数跳深。
- 如本文的实验评估所示,最好的结果是通过称为 L1 和 L2 采样的更先进的方法获得的。这些方法相当先进,可以在面向普通读者的博客文章中描述。但是如果您决定使用它们,请注意草图大小实际上是一个需要调整的超参数。对于实验评估,我使用了理论分析建议的相当大的草图尺寸值。但是草图越大,算法的时间和空间消耗就越大。建议先小数值开始,然后再加大,看看有没有实际优势。
参考
[1]康斯坦丁·库茨科夫。LoNeSampler:学习离散节点嵌入的局部邻域采样。AAAI 2023,出现。
[2]杨、、保罗·罗索、和菲利普·库德雷-毛鲁。NodeSketch:通过递归草图实现高效的图形嵌入。KDD 2019
[3]伊迪丝·科恩(Edith Cohen),海姆·卡普兰(Haim Kaplan):你能用协调样本做什么。大约随机 2013 年
如何在雪花上建立机器学习模型
原文:https://towardsdatascience.com/machine-learning-on-snowflake-ea6c559af2d
雪花是领先的数据平台之一。在本文中,我们将探索它的 snowpark Python 库的功能
照片由 Aaron Burden 在 Unsplash
在本文中,您将学习如何使用,
- 用于原始数据预处理的 snowpark-python 功能
- 在雪花中训练和部署机器学习模型
- 以 pythonic 方式定义 UDF,并在雪花中部署它们
本文涵盖的主题|作者图片
如果你愿意跟随教程,你应该有一个支持 Anaconda 集成的雪花账户。否则,你必须注册一个免费的雪花试用账户,并按照这里的描述进行配置。
乍一看,snowpark 是一个机器学习和数据科学框架,它在 Python 的灵活性中提供了 SQL 的强大功能。有时,这类似于 Apache spark 框架。然而,这为我们的机器学习和数据科学项目提供了一个普遍的框架。在尝试本文中的任何内容之前,您应该在 python 和雪花之间建立一个连接。代码示例可以参考我的代码报告。让我们创建一个数据库连接。
from snowflake.snowpark.session import Session
accountname = "********" # your accountname
username = "**********" #your snowflake username
password = "*************" #snowflake password
connection_parameters = {
"account": accountname,
"user": username,
"password": password,
"role": "ACCOUNTADMIN"
}
def snowflake_connector():
try:
session = Session.builder.configs(connection_parameters).create()
print("connection successful!")
except:
raise ValueError("error while connecting with db")
return session
#define a session
session = snowflake_connector()
现在我们可以开始主要的数据预处理部分。我将在 snowpark 端做这件事,而不是用 Pandas DataFrame 进行预处理。这里我将使用新冠肺炎数据集,它可以在 CC0: Public Domain 下的 Kaggle 中找到。我已经将该数据集作为雪花表加载。由于这不是本文的主要目标,我将跳过这一部分。您可以按照本文的 GitHub repo 中的描述加载数据集。让我们读一下表格。
snowpark_df = session.table("COVID19_RECORDS")
print(type(snowpark_df) # snowflake.snowpark.table.Table
print(f"Size of the table object: {(sys.getsizeof(snowpark_df)/1e6)} MB")
#'Size of the table object: 4.8e-05 MB'
上面的snowpark_df
是一个惰性评估表;因此,它不会像熊猫数据帧那样消耗太多内存。但是我们可以应用任何转换聚合,就像我们对熊猫所做的那样。
snowpark_df.schema.fields
# [StructField('USMER', LongType(), nullable=True),
# StructField('MEDICAL_UNIT', LongType(), nullable=True),
# StructField('SEX', LongType(), nullable=True),
# StructField('PATIENT_TYPE', LongType(), nullable=True),
# StructField('DATE_DIED', StringType(), nullable=True),
# StructField('INTUBED', LongType(), nullable=True),
# StructField('PNEUMONIA', LongType(), nullable=True),
# StructField('AGE', LongType(), nullable=True),
# StructField('PREGNANT', LongType(), nullable=True),
# StructField('DIABETES', LongType(), nullable=True),
# StructField('COPD', LongType(), nullable=True),
# StructField('ASTHMA', LongType(), nullable=True),
# StructField('INMSUPR', LongType(), nullable=True),
# StructField('HIPERTENSION', LongType(), nullable=True),
# StructField('OTHER_DISEASE', LongType(), nullable=True),
# StructField('CARDIOVASCULAR', LongType(), nullable=True),
# StructField('OBESITY', LongType(), nullable=True),
# StructField('RENAL_CHRONIC', LongType(), nullable=True),
# StructField('TOBACCO', LongType(), nullable=True),
# StructField('CLASIFFICATION_FINAL', LongType(), nullable=True),
# StructField('ICU', LongType(), nullable=True)]
数据集中有 1,048,575 条唯一记录和 21 列。我们来做一些基本面分析。首先,让我们如下定义目标变量。根据数据集的描述,CLASSIFICATION_FINAL
列中的 1、2、3 值代表正例,其余代表负例。让我们应用上面的逻辑定义一个名为TARGET
的新列。等效的 SQL 逻辑将是,
SELECT
"USMER",
"MEDICAL_UNIT",
"SEX",
"PATIENT_TYPE",
"DATE_DIED",
"INTUBED",
"PNEUMONIA",
"AGE",
"PREGNANT",
"DIABETES",
"COPD",
"ASTHMA",
"INMSUPR",
"HIPERTENSION",
"OTHER_DISEASE",
"CARDIOVASCULAR",
"OBESITY",
"RENAL_CHRONIC",
"TOBACCO",
"CLASIFFICATION_FINAL",
"ICU",
CASE
WHEN ("CLASIFFICATION_FINAL" < 4 :: INT) THEN 1 :: INT
ELSE 0 :: INT
END AS "TARGET"
FROM
COVID19_RECORDS
因为我们正在使用 snowpark API,所以让我们用 snowpark 来创建它。
import snowflake.snowpark.functions as F
snowpark_df.with_column('TARGET', F.when(F.col('CLASIFFICATION_FINAL')
< 4, 1).otherwise(0))
让我们看看我们的目标分布。
snowpark_df\
.group_by("TARGET").count().to_pandas().set_index("TARGET")\
.plot.bar()
plt.title("Target distribution",fontweight='semibold')
plt.show()
目标分布|作者图片
让我们再创造一个情节。
snowpark_df\
.select('AGE').to_pandas()\
.plot.hist(bins=100,alpha=0.5)
plt.title("Age distribution",fontweight='semibold')
plt.show()
年龄分布|作者图片
让我们找出年龄变量和目标变量之间的关系。
snowpark_df = snowpark_df.with_column(
"AGE_BKT",
F.when(F.col("AGE") < 21, "YOUNG").otherwise(
F.when(F.col("AGE") < 49, "ADULT").otherwise("OLD ADULT")
),
)
age_bkt_df = snowpark_df.select(
F.col("AGE_BKT"),
F.when((F.col("AGE_BKT")=='YOUNG') & (F.col("TARGET")==1),1).otherwise(0).as_("YOUNG_"),
F.when((F.col("AGE_BKT")=='ADULT') & (F.col("TARGET")==1),1).otherwise(0).as_("ADULT_"),
F.when((F.col("AGE_BKT")=='OLD ADULT') & (F.col("TARGET")==1),1).otherwise(0).as_("OLD_ADULT_")
)
age_bkt_df.group_by(F.col("AGE_BKT")).count().show()
# -----------------------
# |"AGE_BKT" |"COUNT" |
# -----------------------
# |OLD ADULT |342413 |
# |ADULT |628554 |
# |YOUNG |77608 |
# -----------------------
age_bkt_df.select(
((F.sum("YOUNG_") * 100 ) / F.count("YOUNG_")).as_("YOUNG % OF CASES"),
((F.sum("ADULT_") * 100) / F.count("ADULT_")).as_("ADULT % OF CASES"),
((F.sum("OLD_ADULT_") * 100) / F.count("OLD_ADULT_")).as_("OLD_ADULT % OF CASES")
).show()
# --------------------------------------------------------------------
# |"YOUNG % OF CASES" |"ADULT % OF CASES" |"OLD_ADULT % OF CASES" |
# --------------------------------------------------------------------
# |1.534463 |20.877858 |14.969745 |
# --------------------------------------------------------------------
完成分析后,我们可以使用以下方法将转换后的数据集保存为新的雪花表。
snowpark_df.write.save_as_table(
table_name='COVID19_RECORDS_PROCESSED',
mode='overwrite'
)
好了,现在我们有了预处理过的数据集。让我们开始模型训练阶段。
# read the table
train_data = session.table("COVID19_RECORDS_PROCESSED")
#create the stage for storing the ML models
session.sql('CREATE OR REPLACE STAGE ML_MODELS').show()
我们可以使用两种不同的方法在雪花中训练和部署模型。
- 我们可以在本地训练模型,将其上传到一个阶段,并在调用 UDF 时从该阶段加载它。
- 我们可以定义 SPROC,它可以训练模型,并在调用 SPROC 时将训练好的模型保存到雪花阶段。这里我们需要一个独立的 UDF 用于推理部分。
在本文中,我们将探讨上述两种方法。
在本地训练模型,将其上载到舞台,并从舞台加载它
作者图片
首先,我们必须定义局部训练模型的函数。
def train_model_locally(train:snowflake.snowpark.table.Table):
from sklearn.tree import DecisionTreeClassifier
#convert into pd dataframes
train = train.to_pandas()
xtrain,ytrain = train.drop('TARGET',axis=1),train['TARGET']
model = DecisionTreeClassifier()
model.fit(xtrain,ytrain)
return model
#let's train the DT model
model = train_model_locally(train_data_sf)
#save the model
import joblib
joblib.dump(model, 'predict_risk_score.joblib')
#upload into the ML_MODELS SNowfla
session.file.put(
"predict_risk_score.joblib", "@ML_MODELS", auto_compress=False, overwrite=True
)
与其他机器学习管道类似,我们需要定义库依赖关系。
session.clear_imports()
session.clear_packages()
#Register above uploded model as import of UDF
session.add_import("@ML_MODELS/predict_risk_score.joblib")
#map packege dependancies
session.add_packages("joblib==1.1.0", "scikit-learn==1.1.1", "pandas==1.3.2")
让我们来定义 UDF。在 UDF 内部,它应该从舞台加载模型,然后使用它进行推理。
from snowflake.snowpark.types import PandasSeries, PandasDataFrame
def read_file(filename):
import joblib
import sys
import os
#where all imports located at
import_dir = sys._xoptions.get("snowflake_import_directory")
if import_dir:
with open(os.path.join(import_dir, filename), 'rb') as file:
m = joblib.load(file)
return m
#register UDF
@F.udf(name = 'predict_risk_score', is_permanent = True, replace = True, stage_location = '@ML_MODELS')
def predict_risk_score(ds: PandasSeries[dict]) -> PandasSeries[float]:
# later we will input train data as JSON object
# hance, we have to convert JSON object as pandas DF
df = pd.io.json.json_normalize(ds)[feature_cols]
pipeline = read_file('predict_risk_score.joblib')
return pipeline.predict_proba(df)[:,1]
现在我们已经成功地在雪花注册了我们的 UDF。您可以使用以下方式验证它。
session.sql("DESC FUNCTION PREDICT_RISK_SCORE()").show()
# ------------------------------------------------------------------------
# |"property" |"value" |
# ------------------------------------------------------------------------
# |signature |() |
# |returns |FLOAT |
# |language |PYTHON |
# |null handling |CALLED ON NULL INPUT |
# |volatility |VOLATILE |
# |body | |
# | |import pickle |
# | | |
# | |func = pickle.loads(bytes.fromhex('800595050400... |
# | |# The following comment contains the UDF source... |
# | |# import pandas as pd |
# | |# def read_file(filename): |
# | |# import joblib |
# | |# import sys |
# | |# import os |
# | |# |
# | |# import_dir = sys._xoptions.get("snowflake... |
# | |# if import_dir: |
# | |# with open(os.path.join(import_dir, fi... |
# | |# m = joblib.load(file) |
# | |# return m |
# | |# @F.udf(name = 'predict_risk_score', is_perman... |
# | |# def predict_risk_score(*args) -> PandasSeries... |
# | |# df = pd.DataFrame([args]) |
# | |# pipeline = read_file('predict_risk_score.... |
# | |# return pipeline.predict_proba(df)[:,1] |
# | |# |
# | |# func = predict_risk_score |
# | |# |
# | | *********RESULTS TRUNCATED************** |
# ------------------------------------------------------------------------
让我们用 UDF 进行推理。
# `test_data_sf` is a fraction of `train_data`
test_data_sf.with_column(
'PREDICTION',
predict_risk_score(F.object_construct('*')))\
.select("TARGET","PREDICTION").show(20)
# ---------------------------------
# |"TARGET" |"PREDICTION" |
# ---------------------------------
# |1 |0.8333333333333334 |
# |1 |0.0 |
# |1 |1.0 |
# |1 |1.0 |
# |1 |0.3333333333333333 |
# |0 |0.0 |
# |1 |0.4 |
# |0 |0.5 |
# |1 |0.421875 |
# ---------------------------------
#similary, you can use below SQL as well.
select
target,
predict_risk_score(object_construct(*)) as predictions
from
COVID19_RECORDS_PROCESSED
limit
100;
定义培训和推理过程/自定义项
此方法将创建用于定型模型的存储过程和用于推理模型的 UDF。你可以参考下面的图表获得更多的见解。
作者图片
让我们定义存储过程。首先,我们将实现 Python 函数,我们可以在后面的步骤中将其转换为雪花存储过程。
def train_dt_procedure(
session: Session,
training_table: str,
feature_cols: list,
target_col: str,
model_name: str,
) -> T.Variant:
"""
This will be our training procedure. Later we will register this as snowflake procedure.
training_table: snowflake table name to be used for training task
feature_cols: list of columns to be used in training
target_col: target column to be used
model_name: model name to used for model saving purpose
"""
#convert as pandas DF, rest of the steps similar to the local model training and saving.
local_training_data = session.table(training_table).to_pandas()
from sklearn.tree import DecisionTreeClassifier
X = local_training_data[feature_cols]
y = local_training_data[target_col]
model = DecisionTreeClassifier()
model.fit(X, y)
#do what ever you want to do with model, even the hyperparameter tuning..
# here I'll get feature importance
feat_importance = pd.DataFrame(
model.feature_importances_, feature_cols, columns=["FeatImportance"]
).to_dict()
from joblib import dump
dump(model, "/tmp/" + model_name)
session.file.put(
"/tmp/" + model_name, "@ML_MODELS", auto_compress=False, overwrite=True
)
return feat_importance
让我们将上面的 Python 函数注册为一个存储过程。
sproc_train_dt_model = session.sproc.register(
func=train_dt_procedure,
name='sproc_train_dt_model',
is_permanent=True,
replace=True,
stage_location='@ML_MODELS',
packages=[
'snowflake-snowpark-python',
'scikit-learn',
'joblib']
)
现在我们可以如下使用程序SPROC_TRAIN_DT_MODEL()
。
train_data = session.table("COVID19_RECORDS_PROCESSED")
#create train and test dataframes
train_data_pd,test_data_pd = train_test_split(
train_data_pd,
stratify=train_data_pd['TARGET'],
test_size=0.1
)
# writing as tempoary tables for mode training and inferencing part
session.write_pandas(
train_data_pd,
table_name="TRAIN_DATA_TMP",
auto_create_table=True,
table_type="temporary",
)
session.write_pandas(
test_data_pd,
table_name="TEST_DATA_TMP",
auto_create_table=True,
table_type="temporary",
)
train_data_pd = train_data.to_pandas()
feature_cols = train_data.columns
feature_cols.remove('TARGET')
target_col = 'TARGET'
model_name = 'decisiontree.model' # How model should be saved in stage
model_response = sproc_train_dt_model('TRAIN_DATA_TMP',
feature_cols,
target_col,
model_name,
session=session
)
print(model_response)
# {
# "FeatImportance": {
# "AGE": 0.4543249401305732,
# "ASTHMA": 0.029003830541684678,
# "CARDIOVASCULAR": 0.025649097586968667,
# "COPD": 0.019300936592021863,
# "DIABETES": 0.059273293874405074,
# "HIPERTENSION": 0.05885196748765571,
# "INMSUPR": 0.0232534703448427,
# "INTUBED": 0.026365011429648998,
# "MEDICAL_UNIT": 0.08804779552309593,
# "OBESITY": 0.02991724846285235,
# "OTHER_DISEASE": 0.026840169399286344,
# "PATIENT_TYPE": 0,
# "PNEUMONIA": 0.04225497414608237,
# "PREGNANT": 0.012929499812685114,
# "RENAL_CHRONIC": 0.015894267526361774,
# "SEX": 0,
# "TOBACCO": 0.028563364646896985,
# "USMER": 0.059530132494938236
# }
# }
#plot feature importance
feature_coefficients = pd.DataFrame(eval(model_response))
feature_coefficients\
.sort_values(by='FeatImportance',ascending=False)\
.plot\
.bar(y='FeatImportance', figsize=(12,5))
plt.show()
功能重要性|按作者分类的图片
我们可以把 UDF 定义如下。这个函数类似于上一个函数。
def udf_predict_risk_score(*args) -> float:
import os
import sys
from joblib import load
IMPORT_DIRECTORY_NAME = "snowflake_import_directory"
import_dir = sys._xoptions[IMPORT_DIRECTORY_NAME]
model_name = 'decisiontree.model'
model = load(import_dir+model_name)
#unlike previous JSON object, this will be a array, hence no need to
# decode the input
scored_data = model.predict(pd.DataFrame([args]))[0]
return scored_data
最后,注册 UDF。
udf_risk_score_model = session.udf.register(
func=udf_predict_risk_score,
name="udf_risk_score_model",
stage_location='@ML_MODELS',
input_types=[T.FloatType()]*len(feature_cols),
return_type = T.FloatType(),
replace=True,
is_permanent=True,
imports=['@ML_MODELS/decisiontree.model'],
packages=['scikit-learn==1.1.1','pandas','joblib'],
session=session
)
好了,是时候为我们的验证数据集进行预测了。在这里,我用雪花编辑器做它。
SELECT
"TARGET",
udf_risk_score_model(
"USMER",
"MEDICAL_UNIT",
"SEX",
"PATIENT_TYPE",
"INTUBED",
"PNEUMONIA",
"AGE",
"PREGNANT",
"DIABETES",
"COPD",
"ASTHMA",
"INMSUPR",
"HIPERTENSION",
"OTHER_DISEASE",
"CARDIOVASCULAR",
"OBESITY",
"RENAL_CHRONIC",
"TOBACCO"
) AS "PREDICTION"
FROM
COVID19_RECORDS_PROCESSED limit 100;
结论
虽然 snowpark 为我们的机器学习任务提供了一个全面的平台,但在撰写本文时,它还有一些问题。作为一个例子,PyTorch 仍然需要一个雪地公园的支持。此外,康达仅提供精选套餐;如果我们想使用其他包,比如 catboost,我们必须手动将它们导入到我们的环境中,如这里的所述。
感谢阅读!在 LinkedIn 上与我联系。
我如何将数据处理时间从 5 天缩短到 5 小时
原文:https://towardsdatascience.com/machine-learning-on-supercomputers-data-processing-aa403e51516b
关于高性能计算的人工智能的文章第 1 篇:并行数据处理
图片提供: CINECA 。
在这一系列文章中,我将概述在开发更快更精确的机器学习(ML)项目中利用高性能计算(HPC)的各种方法。
作为 CINECA高性能计算部门的一名数据科学家,我的任务是 EU-资助的 CYBELE 项目的提前一天冰雹预测问题。该项目的目的是开发一个集成在 Cybele Demonstrator 3 中的冰雹预测模型,该模型是 CINECA、 GMV 和 CACV 之间合作的结果,这是一个位于瓦伦西亚的农民联盟,提供用户要求和历史观测数据。这些帖子中介绍的工作是与我在 CINECA 的同事一起完成的,他们是 Giorgio Pedrazzi、 Roberta Turra 、 Gian Franco Marras 和 Michele Bottazzi 。
红色表示预测区域,黑色表示历史冰雹事件。图片提供:作者。
冰雹是一种只有在特定的大气条件下才有可能发生的现象。这种不确定性使得冰雹事件很少发生,也很难预测。我们的方法旨在通过从过去发生的真实事件中学习,在给定的天气条件下区分冰雹事件和非冰雹事件。因此,我们的冰雹预测模型是基于自动学习技术建立的,使用天气预报数据和导出的气候不稳定指数作为输入,并根据现场收集的冰雹事件数据执行
验证。
问题是
输入数据由 ECMWF 提供,时间分辨率为 1 小时,空间分辨率为 5 公里,覆盖了西班牙大约 20 万平方公里的土地。数据由 73 个字段组成,描述了一天中每个小时每个网格点(由经度和纬度描述)的天气情况,其中一些字段是在地面以上 65 个不同高度计算的。因此,产生的变量数量约为 1000。
原始数据集中的 GRIB 文件概述。图片提供:作者。
输入数据需要缩小到 2.2 公里的分辨率,以便与西班牙目标区域的农业地块和农作物的范围相匹配。205 x 333 ~ 68000 个网格点的更精细网格是通过小规模建模模拟获得的,适用于感兴趣的区域,这需要 HPC 机器上的 640 个核心(20 个节点),每次预测消耗 128 个核心小时。在 ECMWF 提供的 2015-2019 年的历史数据中,每天运行两次预测(提前 48 小时和 24 小时),总计约 2100 天。这项任务由我在 CINECA 的同事负责,他们是吉安·佛朗哥·马拉什和米歇尔·博塔齐。由此产生的输入数据量约为 6 TB,即 5 年内每天 3 GB 的数据量。
解决方案
第一项任务是为 ML 准备好这些数据。原始数据被写成每小时一次的 GRIB 文件,这是一种在气象学中广泛使用的二进制格式。人们可以将其视为张量(或 N 维数组),其中任何给定的网格点都有自己的一组值,在我们的情况下,最终的 68000 个网格点中的每一个都有对应于 1000 个气象特征的值。
数据格式
考虑到为了将数据制成表格,我必须编写 2100 x 24 ~ 50000 个文件(5 年的数据,每天每小时一个),68000 行(当时的数据样本)和 1000 列(当时的特征),我决定使用相对于 csv 更紧凑的数据格式。我选择了 羽毛 (或者拼花),这是一种**高度轻便的样式。**如果安装了正确的 Apache Arrow 绑定,或者简单地说就是“ PyArrow ”包,Python 可以轻松处理 Feather 文件。此处提供了对相同内容的极好概述。
数据准备
现在的任务是读取二进制 GRIB 文件,并将其重写为羽毛文件的形式。所有 50000 个文件都必须这样做。虽然这一步不是必需的,但它很方便,因为可以用熊猫来读取羽毛文件,但对 GRIB 文件来说就不一样了。IO 操作的伪代码可以定义如下:
def grib_to_feather(grib_file):
grib_data = pygrib.read(grib_file)
tabulated_file = tabulate(grib_data)
pyarrow.write(tabulated_file)for grib_file in GRIB_FILES:
grib_to_feather(grib_file)
函数 grib_to_feather 每个文件花费 10 秒(平均),因此处理所有的 50000 个文件我们大概需要 5 天!
并行数据准备
我决定在“令人尴尬的并行”范例下并行 IO 操作。之所以这么叫,是因为并行处理手头的任务需要很少的努力,尤其是因为在产生的单个并行任务之间没有通信。在我们的例子中,我们可以利用这种思想,因为原始数据是作为单独的文件写入的(每小时一个文件),我们只需要以不同的形式(和格式)读入和写出它。换句话说,我们可以异步地读入原始数据并写出处理过的文件。
使用 8 个 CPU 异步并行执行 IO 操作。图片提供:作者。
在 CINECA,我们拥有欧洲最大的超级计算设施之一。因此,我决定在一台名为“马可尼 100 ”的机器上只使用一个节点(32 核)。我用 python 中的多重处理模块修改了代码。伪代码如下:
from multiprocessing import Pooln_workers = 30
p = Pool(n_workers)
p.map(grib_to_feather,GRIB_FILES)
就这么简单!由于文件是相互独立的,并且对每个文件的处理都是独立的,所以这种方法非常适用。 map 调用可以将一个具有给定输入的函数‘映射’到一系列可行的输入。换句话说,它创建了 30 个并行任务,其中一个任务被定义为对从存储在列表 GRIB 文件中的文件名列表中提取的文件名运行函数 grib_to_feather 。当一个任务在处理器上完成时(即,一个原始 GRIB 文件被读入、处理并写出为羽毛文件),下一个可用的文件名被传递给处理器。调用图在幕后负责这个文件处理器关联。
因此,使用并行版本的 IO 脚本,我获得了与使用的内核数量成比例的速度提升,即 4 小时而不是 5 天!
在下一篇文章中,我们将看到如何利用数据并行性在超级计算机上加速神经网络。
注:文章中的所有图片均由作者创作,并归作者所有。
使用 Ploomber、PyCaret 和 MLFlow 的机器学习管道
原文:https://towardsdatascience.com/machine-learning-pipeline-with-ploomber-pycaret-and-mlflow-db6e76ee8a10
用于训练和推理的端到端机器学习管道
照片由迈克·本纳在 Unsplash 上拍摄
介绍
机器学习流水线由自动化机器学习工作流的一系列步骤组成。机器学习管道中的常见步骤包括数据收集、数据清洗、特征工程、模型训练、模型评估。在本文中,我们将研究如何使用 Ploomber、Pycaret 和 MLFlow 创建机器学习管道,用于模型训练和批量推理。让我们来看看这些工具各自的功能。
目录
- 简介
- 机器学习管道
- 设置环境
- MLFlow 服务器
- 训练管道
- 运行训练管道
- 服务管道
- 批量推理
- 结论
砰然一声
Ploomber[1]是一个开源框架,用于使用 python 脚本、函数或 Jupyter 笔记本的集合来构建模块化的数据管道。假设我们有多台 Jupyter 笔记本,每台都有不同的用途,例如数据清理、特征工程、模型训练和模型评估。Ploomber 帮助根据用户定义的pipeline.yaml
文件将所有这些笔记本连接成一系列步骤。下面显示了一个 Ploomber 管道的例子。visualize
和train
任务依赖于clean
任务。
管道插图。作者图片
这就是对应的pipeline.yaml
。product
是任务的输出,可以作为后续任务的输入(upstream
)。例如,visualize
任务依赖于clean
任务的产品,因此clean
任务是visualize
任务的upsteam
。
# pipeline.yamltasks:
- source: tasks/clean_data.ipynb
name: clean
product:
nb: products/clean_train_data.ipynb
data: products/cleaned_train_data.csv
... - source: tasks/visualize_data.ipynb
name: visualize
upstream: ['clean']
product:
nb: products/visualize_data.ipynb
... - source: tasks/train_model.ipynb
name: train
upstream: ['clean']
product:
nb: products/train_model.ipynb
...
MLFlow
MLFlow[2]是一个管理 ML 生命周期的开源平台,包括实验、可复制性、部署和中央模型注册。MLFlow 提供 4 种不同的组件:
- MLFlow Tracking: 记录和查询实验:代码、数据、配置和结果
- **MLFlow 项目:**将数据科学代码打包成可在任何平台上运行的格式
- **MLFlow 模型:**在不同的服务环境中部署机器学习模型
- **模型注册:**在中央存储库中存储、注释、发现和管理模型
我们将使用 MLFlow 跟踪功能来记录机器学习实验的参数、结果和工件。它允许在本地或远程跟踪服务器上记录实验,用户可以从图形用户界面查看实验。
MLFlow UI。图片作者。
PyCaret
PyCaret[3]是一个用 python 编写的开源、低代码自动机器学习(AutoML)库。PyCaret 通过自动化数据预处理、超参数优化、叠加、混合和模型评估等步骤,帮助简化模型训练过程。PyCaret 与 MLFlow 集成,并自动将运行的参数、指标和工件记录到 MLFlow 服务器上。
机器学习管道
现在我们已经了解了每个工具的作用,如果你还没有猜到,我们将使用 Pandas 清理数据,使用 PyCaret 训练模型,使用 MLFlow 记录我们的实验,整个管道将由 Ploomber 协调。
让我们来看一个例子。我们将使用美国国家糖尿病、消化和肾脏疾病研究所的 Pima 印度糖尿病数据集[4]。数据集的目的是基于数据集中包含的某些诊断测量结果,诊断性地预测患者是否患有糖尿病。从一个较大的数据库中选择这些实例有几个限制。特别是,这里的所有患者都是至少 21 岁的皮马印第安血统的女性。数据集由几个医学预测变量和一个二元目标变量Outcome
组成。预测变量包括患者的怀孕次数、身体质量指数、胰岛素水平、年龄等。我们将数据分成两组,分别命名为diabetes_train.csv
和diabetes_test.csv
,用于开发我们的训练管道和测试服务管道。
设置环境
此示例需要一个 IDE(例如,VSCode、Jupyter Notebook 等)和命令行界面。我使用的是 Windows 11、VSCode 和 VSCode 中的终端,但是你可以自由使用任何你选择的操作系统、IDE 和命令行界面。
康达环境
确保您的机器上安装了 miniconda3。从命令行界面创建 python 3.7 conda 环境。环境名是任意的,我将其命名为general
。
#command line
conda create -n general python=3.7
激活康达环境。我们将在这种环境下进行所有的开发工作。
#command line
conda activate general
安装必要的软件包
#command line
pip install ploomber
pip install pycaret
pip install mlflow
pip install pandas
项目目录结构
让我们设置项目目录结构。Ploomber 提供命令行来创建一个准系统项目目录结构。
# command line
ploomber scaffold sample-ml --empty
Ploomber 用一个空的pipeline.yaml
文件创建了一个名为sample-ml
的新文件夹。让我们修改项目目录结构,以更好地满足我们的需求。
# command line
cd sample-ml
mkdir tasks # add folder
mkdir mlflow # add folder
mkdir input_data # add folder
rmdir /s exploratory # remove folder created by ploomber
这是修改后目录的样子。
作者图片
tasks
:包含要执行的 Jupyter 笔记本products
:存储已执行的 Jupyter 笔记本、中间和最终输出mlflow
:存储使用 MLFlow 记录的参数、结果和工件input_data
:存储原始数据
MLFlow 服务器
让我们在本地主机上启动一个 MLFlow 跟踪服务器来记录实验。
#command line
cd sample-ml/mlflow
mlflow server --backend-store-uri sqlite:///expt.db --default-artifact-root "file:\\My Drive\\Data-Science\\Projects\\ploomber\\sample-ml\\mlflow\\ml-artifacts"
作者图片
backend-store-uri
:将实验和运行数据保存到的 URI。为此,我们使用了 SQLite 数据库。default-artifact-root
:为任何新创建的实验存储工件的目录
MLFlow 服务器可以通过 http://127.0.0.1:5000 访问。
培训渠道
定义 pipeline.yaml
Ploomber 管道是一个包含任务和任务间关系的.yaml
文件。让我们在pipeline.yaml
文件中定义我们的培训管道。
# pipeline.yaml
meta:
extract_upstream: Falsetasks:
- source: tasks/clean_data.ipynb
name: clean
product:
nb: products/clean_train_data.ipynb
data: products/cleaned_train_data.csv
params:
input_path: '{{root}}\\input_data\\diabetes_train.csv' - source: tasks/visualize_data.ipynb
name: visualize
upstream: ['clean']
product:
nb: products/visualize_data.ipynb - source: tasks/train_model.ipynb
name: train
upstream: ['clean']
product:
nb: products/train_model.ipynb
config: products/config.pkl
params:
mlflow_tracking_uri: <http://127.0.0.1:5000>
experiment_name: 'diabetes-expt'
train_size: 0.8
fix_imbalance: True
numeric_imputation: median
include_models: ['rf', 'lightgbm', 'lr']
我们在管道中定义了 3 个任务,分别名为clean
、visualize
和train
。对于每项任务,我们定义了以下内容:
source
:我们打算运行的 Jupyter 笔记本(.ipynb
)或 python 脚本(.py
)的路径name
:任务名称upstream
:上游任务名称。这就告诉了我们笔记本之间的依赖关系。例如,visualize
任务将clean
任务作为其上游,表示visualize
任务需要来自clean
任务的输出。product
:任务生成的输出(如果有)。每台笔记本可以有多种产品。例如,在clean_data.ipynb
中我们有两个产品:(1)nb
:被执行的clean_data.ipynb
的副本的路径。该笔记本副本将包含 Jupyter 笔记本单元的输出。(2)data
:由clean_data.ipynb
输出的中间数据的路径params
是执行时传递给 Jupyter 笔记本的参数
{{root}}
占位符是指pipeline.yaml
文件所在的目录路径。
创建笔记本
现在我们已经定义了管道,让我们为 3 个任务创建 Jupyter 笔记本。
# command line
ploomber scaffold
我们再次使用了ploomber scaffold
,但是 Ploomber 没有使用空的pipeline.yaml
文件创建项目目录结构,而是读取现有的pipeline.yaml
文件并创建缺失的 Jupyter 笔记本。现在在tasks
文件夹中有 3 个新的 Jupyter 笔记本。
作者图片
下图显示了 Ploomber 创建的clean_data.ipynb
笔记本。upstream
和product
变量当前设置为None
。
作者图片
我们之前已经在pipeline.yaml
中定义了upstream
和product
信息。让我们使用细胞注射将信息转移到 Jupyter 笔记本中。
# command line
ploomber nb --inject
Ploomber 基于带有单元注入的pipeline.yaml
文件,帮助填充每个笔记本的输入(即上游)和输出(即产品)路径。一个新的“参数”单元格被注入到笔记本中,其中包含我们之前定义的product
和upstream
信息。前 3 个单元格没有用,我们可以删除它们。
作者图片
现在我们已经设置了参数,让我们进入每个笔记本的实际代码。
**clean_data.ipynb**
本笔记本包含基本的清洁步骤。为了简化示例,我们在本笔记本中只有一个数据清理步骤。我们在选定的列中找到“0”值,并用“NaN”替换它。缺少的值将由 PyCaret 中的数据转换管道处理。
import pandas as pd
import numpy as npdf = pd.read_csv(input_path, sep = ',')# replace zeros in the following columns with NaN
df[['Glucose','BloodPressure','SkinThickness','Insulin','BMI']] = df[['Glucose','BloodPressure','SkinThickness','Insulin','BMI']].replace(0,np.NaN)df.to_csv(product['data'], index = False)
这是笔记本现在的样子。
作者图片
我们使用注入参数单元中的input_path
和product
来分别确定原始输入数据和干净训练数据的路径。
**train.ipynb**
此笔记本使用 PyCaret 训练模型,并将参数、结果和工件记录到 MLFlow 服务器。这是笔记本注射细胞后的样子。
作者图片
我们在后续单元格中执行以下步骤:
- 导入包
- 设置 MLFlow 跟踪 URI
- 读取
clean_data.ipynb
输出的清除数据 - 为培训设置 PyCaret
- 保存 PyCaret 安装配置
- 培训、调整和最终确定模型
from pycaret.classification import *
import pandas as pd
import mlflow# set the mlflow tracking uri
mlflow.set_tracking_uri(mlflow_tracking_uri)# read the cleaned data
df = pd.read_csv(upstream['clean']['data'])# setup pycaretnumeric_features = ['Pregnancies', 'Glucose', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI', 'DiabetesPedigreeFunction', 'Age']clf = setup(data=df,
target='Outcome',
train_size = train_size,
fold_shuffle = fold_shuffle,
fold = fold,
html = False,
silent = True,
numeric_features = numeric_features,
experiment_name = experiment_name,
log_experiment = True,
fix_imbalance = fix_imbalance,
numeric_imputation = numeric_imputation,
session_id=1)# save setup configuration
save_config(product['config'])
mlflow.log_artifact(product['config'])
下面是 PyCaret setup
中使用的参数的简要说明:
data
:训练数据框target
:目标变量的列名train_size
定义了用于训练的训练数据帧的比率。其余的将用作维持集。shuffle_fold
:设置为真,进行交叉验证时混洗数据html
:设置为假,防止监视器运行时显示silent
:设置为真,跳过输入数据类型的确认experiment_name
: MLFlow 实验名称log_experiment
:设置为 True,将度量、参数和工件记录到 MLFlow 服务器fix_imbalance
:设置为 True,平衡目标类的分布。默认情况下,应用 SMOTE 为少数类创建合成数据点numeric_imputation
:数值的插补方法,如平均值、中值或零session_id
:控制实验的随机性。用于生成可重复的实验
PyCaret 的compare_models
函数使用交叉验证来训练和评估模型库中所有可用估计器的性能,并输出一个带有平均交叉验证分数的分数网格。这些估计器在它们的默认超参数上被训练,在这个阶段没有超参数调整。
作者图片
参数:
include_models
:训练和评估模型的选择列表。请注意,我们已经使用细胞注射将此参数从pipeline.yaml
传递到笔记本中。sort
:根据选择的指标对输出结果进行排序n_select
:选择要返回的前 n 个型号
我们将n_select
定义为 1,因此只选择基于 AUC 的最佳模型。
作者图片
接下来,我们使用tune_model
对逻辑回归模型进行超参数调整,选择基于 AUC 的最佳表现模型,并最终确定模型。Pycaret 的finalize_model
函数在包括维持集在内的整个数据集上训练给定的估计器。
final_model = finalize_model(tune_model(best, choose_better = True, optimize = 'AUC'))
**visualize.ipynb**
在本笔记本中,我们使用 pandas-profiling[5]对清理后的数据进行基本的探索性描述性分析。
作者图片
在执行 Ploomber 管道之后,我们可以在由 product[' nb ']【T16]给出的文件路径中查看已执行的笔记本,其中填充了 pandas 分析结果。
作者图片
可视化管道
让我们想象一下管道的样子。
# command line
ploomber plot
此功能对于检查复杂的管线非常有用。
作者图片
运行培训管道
让我们执行培训管道。
# command line
ploomber build#OR
ploomber build -e pipeline.yaml
默认情况下,Ploomber 寻找pipeline.yaml
文件作为入口点,因此显式定义入口点是可选的。这是成功运行后的终端输出。
PyCaret 自动将结果记录到 MLFlow 中,并可在http://127.0.0.1:5000
通过 MLFlow UI 查看。
作者图片
请注意,本文的目的是演示 Ploomber、Pycaret 和 MLFlow 的使用,因此我们不会花费额外的精力来获得更好的模型结果。让我们选择使用 PyCaret 的finalize_model
训练的模型。记下model.pkl
文件的路径,我们稍后将需要这个文件用于服务管道。
作者图片
服务管道
服务管道用于对新的未知数据进行预测。为了避免训练-服务-偏斜,看不见的数据应该经过与训练数据相同的数据处理步骤。我们可以通过修改upstream
和product
来重复使用clean_data.ipynb
笔记本。我们还需要另一个笔记本serve_model.ipynb
来对看不见的数据进行预测。
为服务管道创建pipeline.serve.yaml
。
# pipeline.serve.yamlmeta:
extract_upstream: Falsetasks:
- source: tasks/clean_data.ipynb
name: clean
product:
nb: products/clean_serve_data.ipynb
data: products/cleaned_serve_data.csv
params:
input_path: '{{root}}\\input_data\\diabetes_test.csv' - source: tasks/serve_model.ipynb
name: serve
upstream: ['clean']
product:
nb: products/serve_model.ipynb
data: products/predict_data.csv
params:
model_path: '{{root}}\\mlflow\\ml-artifacts\\1\\fb5528b6883748e9a200c43465126517\\artifacts\\model\\model'
类似于我们在培训管道中创建笔记本的方式,从我们使用的pipeline.serve.yaml
中创建笔记本
# command line
ploomber scaffold -e pipeline.serve.yaml
记得说明入口点,否则将使用默认的pipeline.yaml
入口点。接下来,我们使用以下方法将参数注入笔记本
ploomber nb --inject -e pipeline.serve.yaml
除了上述细胞注射命令引起的参数变化外,clean_data.ipynb
不需要进一步的代码变化。
**serve_model.ipynb**
笔记本电脑执行以下操作:
- 加载已清理的不可见数据
- 从 MLFlow 加载模型
- 对已清理的不可见数据运行预测
- 将预测保存到 csv 文件中
单元注入创建了一个包含所需参数的附加单元
作者图片
下面是主要的服务代码
# import packages
import pandas as pd
import mlflow
from pycaret.classification import *# load data
df = pd.read_csv(upstream['clean']['data'])# load model
loaded_model = load_model(model_path)# predict
df_result = predict_model(loaded_model, data = df, raw_score=True)# save results
df_result.to_csv(product['data'], index = False)
批量推断
使用服务管道对新数据集运行批量推断
# command line
ploomber build -e pipeline.serve.yaml
在每个ploomber build
期间,Ploomber 会复制一份已执行的笔记本,并将其存储为产品。我们可以在products/serve_model.ipynb
中查看笔记本及其电池输出。我们来看看df_result
DataFrame。Label
、Score_0
和Score_1
列是 PyCaret 附加到输入数据帧的预测结果。
Label
:预测类,0 或 1Score_0
:类 0 的概率Score_1
:第一类的概率
作者图片
结论
在本文中,我们研究了如何集成三个开源包——Ploomber、Pycaret 和 MLFlow,以构建机器学习训练和批量推理管道。Ploomber 使我们能够将管道构建为代码,这省去了直接在笔记本中编辑参数的麻烦,并允许使用 Git 对管道进行版本控制。PyCaret 通过简化预处理、训练和评估过程实现了快速实验,而 MLFlow 跟踪则确保了我们机器学习实验的高重现性。这些工具还有很多,请在下面的参考资料部分查看它们各自的文档。
- 加入灵媒阅读更多这样的故事
- 关注我获取更多类似的帖子
参考
[1] 爆炸头
[2] MLFlow
[3] PyCaret
[4] 皮马印第安人糖尿病数据集,特许 CC0 公共领域
[5] 熊猫简介
机器学习的经验法则
原文:https://towardsdatascience.com/machine-learning-rules-of-thumb-b50232b4b2f8
给你的模型一个好的起点的一些可靠的指导方针
可悲的是,机器学习项目的成功并不能得到保证。因此,您从一个快速而肮脏的实现开始。首先出现的问题之一是“我需要多少数据”?问任何一个机器学习从业者,他们都会告诉你:“看情况”。他们是对的!谈到机器学习,没有放之四海而皆准的真理。它既是一门科学,也是一门艺术。然而,我确实发现,对于机器学习的大多数方面来说,有一些经验法则往往是很好的起点。在这里,我试着收集了一些常见的规则。
多少数据?
图 1:模型性能与收集的样本的数量和质量、特征的数量和效用以及模型的容量相关联。图片作者。
样本数量( m )、特征( n )、模型参数( d )构成了机器学习的神圣三位一体。大多数经验法则在很大程度上可以回到这个三元组(图 1)。让我们仔细看看。
多少样品?
性能通常与 log *m,*成比例,其中 m 是样本数(图 1),通常由标签中的噪声从上方限制。因此,当训练数据被人类标记时,该界限通常对应于人类水平的表现。因此,关注数据质量而非数量可能是有用的,正如以数据为中心的人工智能运动所建议的那样。
一般来说,回归比分类需要更多的样本[1]。具体来说,给定 n 个输入特征和 C 个类别进行分类,建议至少有[1]
m≥10nC
样品。用于回归
m ≥ 50 n,
建议提供样品。
多少参数?
回想一下你的线性代数课,要解一个有 d 个自由度的线性系统,你需要 d 个约束。对于线性回归,每个样本都是一个约束。因此,要确定 d 的参数,你需要至少一样多的样本——否则你的系统被称为欠定。更一般地,通过将模型的参数解释为自由度,一个常见的启发是十倍超定系统
d ≤ m/ 10,
尽管也建议了神经网络的更保守的界限,例如d≤m/50【2】。反过来,确定了参数的数量后, *d,*可以帮助您决定是否需要减少特征的数量, n,。
然而,需要谨慎,因为对于许多模型,例如概率图形模型,约束条件的数量可以是 O ( n )并且与样本大小 m 无关。
哪个型号?
结构数据
图 2:表示为概率图形模型的朴素贝叶斯。节点表示分布,箭头表示条件依赖。特征周围的标牌指示该关系被复制(在本例中为 n 次)并由 i 索引。图片作者。
- 小数据集(20 ≤ m ≤ 10 ): 朴素贝叶斯,弹性网
当样本不足时,使用具有较大归纳偏差的可解释模型。具体来说,当校准是一个问题时,进行逻辑回归。否则,它的生成对应朴素贝叶斯[3,4]是一个很好的候选(图 2)。(关于它们等价性的讨论,见参考文献。)。[3]).在小数据范围内,后者优于前者[5]。这里的直觉是,强加额外的严格模型假设会减少模型系数的方差。弹性网络(或者,对权重同时使用₁和₂惩罚的线性回归)以类似的方式工作,事实上编码了对稀疏和收缩系数的偏好。
- 中间数据集(10 ≤ m ≤ 10⁴): 梯度增强树
Kaggle 竞赛让我们了解到,梯度增强树的用途非常广泛,在许多真实生活、嘈杂的数据集上都能很好地工作。对于较大的数据集,相对于较简单的模型,更灵活的梯度增强树可以从数据中榨取更多的汁液,但代价是可检查性。幸运的是,可解释人工智能的进步(例如, SHAP )使得这些以前的黑箱模型变得相当容易理解和询问。
- 大型数据集(10⁴ ≤ m ):神经网络
正如人工智能的大多数进展所证明的那样,大型训练语料库与神经网络的结合是天作之合。训练与数据集的大小成线性比例(即,对于给定的一组权重,∧m),使得神经网络特别适合于大型数据集。此外,TensorFlow 和 PyTorch 已经使神经网络变得惊人地快速和容易地跨多台机器扩展。
非结构化数据
当数据集由图像、文本或音频等非结构化数据组成时,建议您利用现有的预训练模型。为了微调一个图像分类器,你可能只需要每类 m = 10 个样本。你可以浏览模型动物园或 TensorFlow Hub 寻找符合你需求的预训练模型。或者,也有无代码云服务,如亚马逊的 SageMaker 或谷歌的 Vertex AI 来帮你做所有繁重的工作。
规范化程度如何?
帮助你的模型在训练集之外一般化的一个方法是对你的模型的重量大小进行惩罚。这就是所谓的正规化。两个受欢迎的惩罚是曼哈顿规范(或₁规范),以城市命名,因为距离类似于沿着矩形街道网格行进,以及“标准”欧几里得规范(或₂规范)。
进行正则化时,验证所有特征是否通过标准化等手段缩放至一阶(无量纲)。这确保了惩罚同等地影响所有权重。球园估计可以通过研究两个线性回归案例得到,这两个案例可以以封闭形式求解,即拉索(即 L ₁正则化λ₁|w|₁权重 w )和岭型回归( L ₂正则化 λ ₂| 当特征集中且不相关时,它们的解可以用非专利解[a,b]来表示。套索剪切(或截断)所有低于 λ ₁ [a]的未设定系数。因此,合理的起点可以是 λ ₁ = 0.1。另一方面,Ridge 只会将它们的大小缩小到零[b]。要选择λ₂,你可能要考虑你的系统在多大程度上是超定的。一个警告:scikit-learn 为他们的目标使用了稍微不同的约定(参见脊与套索中的 1/(2 m )因子)。
多少个哈希桶?
图 3:散列技巧的图示,其中通过计算散列将项目分配给桶。图片由 Jorge Stolfi 拍摄,从维基百科的散列表文章中获得,版权归 CC BY-SA 3.0 所有。
哈希技巧通过基于项目的哈希生成新的索引来对项目进行分组(图 3)。例如,英语包含n= 60 万个单词[6],我们可能希望在训练之前将其缩减到固定的大小n↦n’。此外,当我们说话的时候,新的词汇正在被发明出来。).这给上菜带来了问题,因为这些新单词不是训练集的一部分。特征散列以保真度为代价解决了这两个问题。保真度由哈希冲突的数量控制,而哈希冲突的数量又由哈希桶 n 的数量决定。为了解决词汇外的项目,建议采取[1]
n ' = n /5,
哈希桶。当目标是减少词汇量时,我还没有找到一个好的启发式方法。然而,选择 n 散列桶使得模型的大小 d ≤ m/10 似乎是一个合理的猜测。
我嵌入的大小?
在神经网络中,嵌入层将稀疏向量——主要包含零——压缩成一个小的密集表示。考虑将一个独热编码的单词——除了单词的索引之外,所有条目都是零——转换成单词嵌入。但是我应该将我的高基数输入数据压缩到什么程度呢?给定一个要嵌入的类别列表,我遇到了以下两种选择嵌入大小的启发法。即平方根标度[1]
d ₑ = 1.6 √ q,
或取第四个根[7]
d ₑ = ∜ q 。
讨论
没有免费的午餐定理告诉我们,机器学习没有灵丹妙药。因此,这些经验法则必然是错多于对。然而,它们可能是针对特定数据集进行估计的良好起点。超参数调整和交叉验证可以帮助您找到数据集的最佳点。
正如您已经注意到的,这篇文档远非详尽无遗。例如,我们还没有触及到训练神经网络的所有超参数。为此,我推荐杰夫·马卡卢索的优秀深度学习清单。
我很想知道你的启发。请在评论中留下你的建议来扩充这个收藏。
承认
我要感谢 Rik Huijzer 发起这篇文章的讨论和校对。
脚注
[a]:假设特征集中且不相关。设 w* 为无正则化线性回归的最大似然估计。拉索回归(线性回归用 L ₁正则化 λ ₁| w |₁为权数 w )解原来是[3]
w= SGN(w*)relu(w-λ*₁)
其中 sgn( x )是 x 的符号,ReLU( x )是整流后的线性单位。
[b]:像在[a]中一样,假设特征是集中的和不相关的,并且 w 是非正则解。对于脊回归(线性回归用 L ₂正则化λ*₂|w|₂的权重 w )权重缩小它们的大小【3】**
w=w/(1+λ₂),*
走向零。
参考
[1]:瓦利亚帕·拉克什马南、萨拉·罗宾逊和迈克尔·穆恩。机器学习设计模式。奥莱利媒体,2020。
[2]: Ahmad Alwosheel、Sander van Cranenburgh 和 Caspar G. Chorus。“你的数据集够大吗?使用人工神经网络进行离散选择分析时的样本大小要求。”选择建模杂志28(2018):167–182。
[3]:凯文·p·墨菲,概率机器学习:导论。麻省理工学院出版社,2022 年。
[4]:达芙妮·柯勒和尼尔·弗里德曼。概率图形模型:原理与技术。麻省理工学院出版社,2009 年。
[5]:吴恩达和迈克尔·乔丹。"区别性与生成性量词的比较:逻辑回归和朴素贝叶斯."神经信息处理系统进展 14 (2001)。
[6]:https://en . Wikipedia . org/wiki/List _ of _ dictionary _ by _ number _ of _ words
[7]:https://developers . Google blog . com/2017/11/introducing-tensor flow-feature-columns . html
机器学习传感器:真正以数据为中心的人工智能
原文:https://towardsdatascience.com/machine-learning-sensors-truly-data-centric-ai-8f6b9904633a
在边缘设备上嵌入机器学习智能的新方法
“矛盾的是,数据是人工智能最被低估和去美化的方面”——谷歌研究作者“ 数据在高风险人工智能 ”
“数据是人工智能的食物”——吴恩达,加州大学伯克利分校教授,以数据为中心的人工智能哲学的先驱。
在 ML 传感器中,ML 模型与物理传感器紧密耦合。
近年来,机器学习已经出现了向更小和更大模型的分叉。具有数千亿个参数的大规模语言模型正在定期发布,并且没有性能饱和的迹象,我们可以期待看到这种趋势继续下去。
另一方面,微型机器学习 (TinyML)领域——在资源受限的微控制器上部署机器学习模型——也开始占据一席之地。TinyML 的商业应用已经存在,从智能手机中的关键字识别(例如,“嘿 Siri”和“OK Google”)到控制智能照明、HVAC 和安全系统的人员检测。
这种分歧体现了以计算为中心和以数据为中心的人工智能的竞争哲学。虽然内置在云端的大规模模型体现了以计算为中心的人工智能的极端,但以数据为中心的人工智能迄今为止一直受到传统系统架构的限制。将这一理念发挥到极致,我们需要将计算能力直接放在生成数据的地方。进入机器学习传感器。
ML 传感器
ML 传感器范式的灵感来自 Andrej Karpathy 的“软件 2.0 ”概念——我们目前熟悉的定义明确、基于规则的软件(被描述为软件 1.0)向更“智能”的软件的转变,这些软件以更难解释的方式编码,例如在神经网络的权重中。
同样,我们可以想象现有的嵌入式 ML 硬件实例体现了“传感器 1.0”或“嵌入式 ML 1.0”范式,将传感器数据传递给 CPU 进行模型处理。相比之下,ML 传感器范例体现了“传感器 2.0”或“嵌入式 ML 2.0”,其中处理发生在传感器本身,只有更高级别的和已经处理的信息被发送到 CPU。
在此基础上,ML 传感器可以定义为:
“ML 传感器是一个独立的系统,
通过观察物理世界中一些复杂的
现象,利用设备上的机器学习来提取有用的信息,并通过一个简单的接口将其报告
给更广泛的系统。”
根据上述定义,这与物联网设备的不同之处在于,设备不需要连接到互联网,而是使用 ML on-device 来代替云处理。这也不同于与 TinyML 一起配置的标准传感器,因为处理直接发生在传感器上,如下所示。
比较(上图)更传统的传感器 1.0 模式和(下图)ML 传感器(传感器 2.0)模式。ML 模型与物理传感器紧密耦合,与 ML 传感器中的应用处理器分离。
这种区别似乎微不足道,不重要,但是这种方法有几个实际的好处。
**无摩擦界面。**ML 传感器的首要目标是从最终用户那里提取 ML 细节。从硬件的角度来看,人们不需要成为 ML 方面的专家来构建包含它的设备。通过提高抽象级别,也减少了非专家将 AI 纳入现有硬件系统的摩擦。如果需要任何定制,例如定制面部识别算法以仅识别您的面部而不识别其他人的面部,它应该不需要用户加载 Python 环境并执行模型训练或转移学习。
**自成一体,模块化。**ML 传感器中的硬件和软件必须是独立的和模块化的。想象一下,去商店买一个“人体探测器”模块,就像你可能会买一个红外线或温度传感器一样。这种模块化使它们可以轻松集成到电子设备中,而不需要任何 ML 领域的专业知识。
**隐私和安全。**与更传统的工作流程相比,ML 传感器可提供更强大的隐私和安全保障因为敏感信息无需离开设备。虽然这最明显地应用于视频和音频数据,但是原则上,这可以应用于任何传感器数据。
**可组合性。**通常,当工程师设计和构建一个系统时,他们需要以特定的方式组合传感器来解决更具体的问题,例如控制车辆的运动。ML 传感器的模块化特性允许类似地实现这一点。更重要的是,ML 传感器可以与传统的非 ML 传感器以及其他 ML 传感器相结合,以创建增强的应用解决方案。
作为可组合性的一个例子,想象一个简单的场景,我们想要创建一个智能门系统,只为房子的主人打开前门。该系统需要(1)系统知道何时有人在门口的方法,(2)系统知道门口的人是否是主人的方法,以及(3)解除门锁的方法。
第一个挑战可以通过使用人体探测器“芯片”来解决这种输出可以与第二个“芯片”结合,进行面部识别。对于像这样的常见组合应用,这些模型可以折叠到单个 ML 传感器中,构建人检测模型,并从开源训练集(如视觉唤醒词数据集)闪存到设备中。类似地,面部识别模型可以使用通过少数镜头学习定制的模型来训练。该传感器可以集成到标准电子锁系统中,m1 传感器的输出与锁机构相互作用。
上述方法的好处是(1)架构师不需要知道任何关于机器学习的知识来构建系统,因为 ML 方面是从用户那里抽象出来的,(2)我们使用传感器的可组合性来创建一个更具体的问题的解决方案,而不仅仅是“人的检测”,以及(3)我们的数据不需要去任何地方,这意味着系统是隐私保护的,并带来最小的安全风险。此外,从可扩展性的角度来看,如果数以千计的智能门配置了 ML 传感器,那么配置它们要比手动为每个门设置计算环境容易得多。
数据表
传统传感器都有数据表,比如这个。ML 传感器应提供类似的数据表。为什么?当有人购买传感器时,他们主要想知道它是否适用于他们的特定应用——如果你知道温度传感器会在水的沸点以下融化,你不会为你的水壶购买温度传感器,对吗?
这对于 ML 传感器来说没有什么不同。对于人员探测器,我们希望确保传感器能够在部署它的物理环境中工作。这可能需要考虑影响模型的特征,例如照明环境或人与传感器的距离。然而,它可能还需要考虑硬件特性,如工作温度、功耗和每秒推断次数。
借鉴传感器界的智慧,ML 传感器包含包含这些硬件和模型特性的数据手册是有意义的。通过这种方式,潜在的最终用户可以审查这对于他们的应用是否有意义,并且还可以获得关于设备性能的一些保证。由于增加了透明度,这还可以提高对基础模型的信任,特别是如果数据是由第三方实体验证的,并且有其他认证或认可。
下面是一个人体探测器模块的 ML 传感器数据表示例。请注意,这包括在传统传感器数据手册中看到的部分,以及概述 ML 模型性能、端到端性能分析和明确阐述隐私、道德和环境考虑因素的附加部分。
ML 传感器数据手册的示例。顶部是当前标准数据手册中的内容:描述、特性、使用案例、原理图和外形尺寸、硬件特性、通信规格和引脚排列。在底部,我们有需要包含在 ML 传感器数据手册中的新项目:ML 模型特征、数据集营养标签、环境影响分析和端到端性能分析。虽然我们将数据手册压缩为一页的说明性示例,综合了各种来源的特性和数据,但在实际的数据手册中,我们假设每一部分都会更长,并包含额外的说明文字,以增加器件对最终用户的透明度。
生态系统
人们可以将 ML 传感器生态系统想象为闭源、开源或两者的混合。这些方法各有利弊。类似于 ML 和其他软件的开源开发如何从根本上改变了软件生态系统,围绕 ML 传感器的开源生态系统可能会加快 ML 传感器的步伐和创新。
与 TensorFlow 和 Keras 等大型开源 ML 项目类似,人们可以想象一个类似的生态系统,为最普遍的应用(如人员检测和关键字识别)构建强大的 ML 传感器。这种方法将允许数据的众包,并可以利用社区的专业知识,同时还确保最终的模型和设备是公开可用的,从而拓宽了 ML 资源的整体可访问性。
然而,真正开放的方法可能会将 ML 传感器的效用限制在最普遍的应用中,因为公司提供自己的解决方案的经济激励很少。闭源解决方案也不理想,因为它会通过付费墙限制可访问性,并且由于有限的专业知识和对数据的访问,解决方案可能不是最理想的。
也许最突出的方法是半透明的。这样,公司可以通过其 ML 传感器数据表提供保证,并向用户提供足够的信息,如验证数据集,以探测和验证模型声明,而无需发布 ML 传感器的全部知识产权,如模型架构和完整的训练数据集。这种方法将在激励最普遍应用的开源解决方案、为公司提供自己的解决方案提供经济激励以及促进公司和最终用户之间的信任之间实现平衡。
摘要
与任何方法一样,这种模式也不是没有问题。传统的 ML 问题仍然存在,例如模型偏差,敌对攻击的可能性,以及我们设备功能的可解释性和可解释性的降低。ML 传感器也有可能被恶意利用,例如用于武器或压制言论自由。也就是说,增加 ML 在硬件应用中的可用性所带来的净影响有望是积极的,特别是如果个人在设计和开发 ML 传感器时考虑了适当的道德因素。
正如电气工程从电磁学发展而来,化学工程从化学发展而来,ML 传感器标志着 ML 从研究领域到与电气工程紧密交织的成熟(原型)工程学科的旅程中的天然垫脚石。虽然还不清楚该领域将如何继续发展,但我预计它将以某种方式融入 ML 传感器的理念。
截至撰写本文时,还没有人构建出一个功能性的 ML 传感器,但考虑到这种设计方法的实用性,这很可能只是一个时间问题。你可以在我们发布在 ArXiv 上的“ 机器学习传感器 ”论文中更深入地了解这个概念,并在 MLSensors.org了解更多关于我们的最新更新。
https://github.com/harvard-edge/ML-Sensors http://mlsensors.org
本文包含的所有图片均由作者提供。
使用 AWS Lambda、Docker、ECR、S3 和 API Gateway 的无服务器机器学习
原文:https://towardsdatascience.com/machine-learning-serverlessly-1a532685fa7c
如何在 AWS 上无服务器地构建复杂的机器学习 web 应用
作者 GIF
大量的工作和努力投入到生产基于机器学习的产品中。我们最近完成了一个网络应用的开发,用来预测奥运会跳水运动员的表现。给定一个奥运跳水运动员的视频,该网络应用程序预测人类裁判会给出什么分数,并输出分数。ML 模型已经在内部进行了训练,我们必须将它们部署并生产成服务。我们选择了无服务器架构,这样我们就不必供应、运行和维护服务器,而是专注于开发用户友好的应用程序。下面是实现无服务器架构的 React web 应用程序。
作者图片
在这篇文章中,我们将介绍我们在 AWS 上托管的一个简单但“复杂”的机器学习 web 应用程序的部署设计和实现。这是一个非常简单的项目,只有一个功能,从一个用户上传的视频潜水动作的预测得分。但是我们确实认为这个项目“复杂”,原因如下:
- web 应用程序需要接收用户上传的视频作为输入,该视频相对较大(高达 200MB)。
- 预测过程中涉及的机器学习模型相对较大(564MB)。
- web 应用程序需要等待预测过程,这需要相当长的时间(可能超过几分钟)才能完成。
对于我们预测过程的机器学习部分背后的理论,如果你感兴趣,可以在本文中找到更多。这里我们将只介绍我们的部署设计,并详细讨论我们使用的工具。下面是我们的云架构设计图,我们将在下面详细讨论架构的每个部分。
作者图片
简而言之,我们的前端 React 服务托管在 AWS Amplify 中,它与连接到 AWS Lambda、AWS 简单存储服务(S3)和 AWS DynamoDB 的 AWS API 网关端点进行交互。
我们如何处理(大量)用户上传的数据?
用户通过我们的前端 React 服务上传一个视频文件作为输入。由于视频文件很大,在前端处理它并不容易。在我们的例子中,用户可以一次上传一个视频,每个文件必须小于 200 MB。红色箭头(#1 — #8)描述了对来自客户端的输入文件的处理,这就是我们将在下面描述的内容。
作者图片
当用户上传一个大文件时,在前端处理它的最好方法是不要上传。我们遵循了 AWS 关于如何将视频直接发送到 S3 的文档,它会在检测到新上传的视频时通知我们的后端。视频文件源自用户的计算机,我们的前端代码将文件上传到 S3 桶中(箭头 1 到 7);S3 桶上有一个触发器(箭头 8),这样一旦上传完成,包含模型的 lambda 就会在新的视频输入上被调用(箭头 9)。
简而言之,我们的前端做这两件事:
- 当接收到一个视频时,前端向我们的 HTTP API(箭头 1)和它后面的 lambda 函数发送一个 GET 请求,以获取存储所有用户上传视频的 S3 桶的预签名 URL(箭头 2 和 3)。
- 在获得预签名的 URL(箭头 4、5 和 6)之后,前端向该 URL 发送 PUT 请求,其中视频以二进制数据的形式包含在正文中,该请求被存放到 S3 桶中(箭头 7)。一旦数据被上传到桶中,一个触发器被触发,警告模型 lambda 新数据准备好了(箭头 8)。
上面的教程足够详细,可以让你理解它的基础。但是有时根据我们自己的特定需求调整教程仍然是一个挑战。因此,我们将在这里链接我们的代码库。
这是一个 lambda 函数,它从 S3 生成一个预签名的 URL。
https://github . com/yumyumqing/AQA _ S3 _ pre signed _ URL/blob/main/getSignedURL/app . py
这是我们为这个 lambda 函数和它前面的 HTTP API 准备的 SAM 模板。
https://github . com/yumyumqing/AQA _ S3 _ pre signed _ URL/blob/main/template . YAML # L6-L70
您可以看到,我们为前端应用程序实现了一个 AWS API 网关,以获得一个预签名的 URL 这里是。当您使用SAM deploy-guided部署堆栈时,按照 AWS 的文档,您可能会遇到 AWS SAM CLI 错误:
"不满足安全约束。"当您对这个问题回答“否:HelloWorldFunction 可能没有定义授权,这样可以吗?[是/否]。您可以在此找到修复问题的详细信息。
设置所有这些似乎有点复杂(这意味着我们需要先学习所有这些工具!)我们可以将视频从前端直接发送到运行推理代码的主机。然而,在这种情况下,我们将负责更多的操作细节:大视频的多部分传输,等待大文件的到达,编码,检查,这些数据的临时和永久存储处理,以及许多我们甚至没有想到的事情。
如何无服务器部署大型机器学习模型
接下来,用户数据在 AWS lambda 中得到处理。AWS Lambda 是“一种无服务器的事件驱动计算服务,让您无需配置或管理服务器即可运行代码。”它几乎是一个抽象的后端,你可以像实现一个函数一样实现它。AWS Lambda 代表客户管理扩展,并支持高可用性和容错。Lambda 部署包是一个 zip 文件,其中包含代码、库和依赖项。然而,lambda 的包大小限制妨碍了我们,因为我们要部署的文件和 ML 模型的总大小至少是 564MB,但是 Lambda 施加了以下大小限制:
压缩文件的部署包大小限制:50MB
2020 年 12 月,AWS 开始支持将 Docker 容器映像部署为 lambda 函数的功能。你可以在这里查看 Docker 的更多信息(https://www.docker.com/)。这意味着 docker 容器映像可以部署到 lambda,而不是 zip 文件。我们只需要确保容器映像是基于 linux 的。对我们来说,对 Lambda 使用 docker 映像的好处是它提供了更多的空间。我们的模型甚至没有接近 Lambda 允许的 50 MB,但确实符合当您将 Docker 映像部署到 Lambda 时得到的限制。这些限制处理起来有点复杂,但是仍然为你的模型提供了更多的空间。
至于模型的无服务器部署,没有太多可用的选项,lambda 容器是我们能够包含 5g 模型的最佳选择。与 Sagemaker 不同,lambda docker 不需要管理端点和端点配置等多项内容。最重要的是,lambda 提供了一个真正的无服务器解决方案,无需选择特定的实例进行部署。Lambda 容器部署有以下限制,记住这些限制很重要:
lambda 容器映像的部署包大小限制:10GB
/tmp 上的临时文件系统存储:512 MB
以上信息对于确定 ML 模型的归属非常重要。我们考虑了两个选项:(1) S3,以及(2)用推理代码文件将模型附加到容器映像。对于选项#1,ML 模型需要从 S3 加载到容器映像中。从其他地方(比如 S3)加载的任何额外文件都被写入临时文件存储区/tmp,这是外部文件下载的唯一路径。可写入该路径的最大文件大小为 512 MB。然而,我们的模型的总大小是 564MB,这超过了临时文件系统存储的限制。因此,我们选择了选项#2,将我们的模型作为一个代码文件附加到容器映像中。
第一步。写一个 Dockerfile
在我们的示例中,我们从公共 aws 存储库中提取一个映像,并用 lambda python 3.7 设置基本映像。将基础映像设置为 lambda 基础映像非常重要。大多数 docker 文件以 FROM 指令开始,图像可以是任何有效的图像。然后,lambda 的推理代码包被复制到工作目录,以便该代码可用于后续指令。由于我们有了一个全新的映像,我们正在安装必要的库,并在运行后设置变量,这将执行 docker 映像中的命令。Requirements.txt 包含了我们希望安装在全新映像中的所有库。最后,CMD 执行一个命令行来调用 lambda 处理程序,并指定 lambda 处理程序所在文件的名称。
第二步。编写推理逻辑
- 模型位置
我们考虑将我们的 ML 模型放在 S3 并下载它们,但是这次我们将模型附加到 docker 容器中。这意味着当我们打包我们的容器时,我们将模型作为文件留给我们的推理逻辑来直接使用模型。这里,我们在部署中附加了模型及其权重文件。
- 处理程序文件
在 docker 文件中指定的处理函数名称下,您可以添加您的推理逻辑。对于处理程序,建议使用以下模板:
def handler_name(事件,上下文):
您可以选择返回值或不返回值。在我们的例子中,我们返回一个预测值,以便客户端应用程序可以显示该预测值。
第三步。添加云形成模板
https://github . com/yumyumqing/AQA _ S3 _ pre signed _ URL/blob/main/template . YAML # L75
您可以为云的形成配置您的 lambda 容器,以自动化部署。确保类型为“AWSServerlessFunction ”,包类型为“Image ”,以表明它是 lambda docker 图像容器。您可以从这里获得更多信息:https://docs . AWS . Amazon . com/lambda/latest/DG/configuration-images . html # configuration-images-cloud formation此外,您还可以轻松集成 lambda 函数,以便在 S3 发生变化时得到通知。正如 Events 部分所提到的,我们让 S3 在创建或删除对象时向 lambda 函数发送一个事件。
第四步。本地测试:验证功能
为了在本地测试其功能,我们将通过运行 sam build 进行测试。在本地测试 docker 之前,请确保下载并运行它。然后,确保 sample_data.json 中有正确的样本数据信息。在我们的例子中,我们使用 s3 中的视频文件,所以我们在 sample_data.json 中有我们的视频和 s3 信息。然后运行 sam 本地调用<函数名> -e <样本事件文件>
sam 本地调用" ProcessPredictFunction "-e getPredScore/sample _ event . JSON
还有一种方法可以在本地测试你的 lambda 函数,这种方法不涉及 aws sam,它是 AWS Lambda 运行时接口仿真器(RIE)。你可以在这里得到更多信息,但是我发现使用 SAM 来测试我的 lambda 函数要容易得多。
第五步。使用 sam 构建进行部署
然后,您可以再次使用 sam 部署应用程序。简单来说就是 sam 部署——制导 。部署结束后,您将看到 docker 映像被部署到 AWS 弹性容器注册中心(ECR ), lambda 函数引用 ECR 中的映像。
Lambda docker 容器映像为无服务器启动我们的 ML 模型提供了一个有趣的解决方案。将模型作为代码附加并部署在打包的容器中是很简单的。S3 与 lambda 的方便集成提供了一个更加独立和安全的解决方案。它是独立的,因为我们的前端应用只需将输入的视频数据放入 S3,而无需触及架构的其他部分,我们的 lambda 函数会自动触发,以处理输入视频并对其进行预测。它是安全的,因为前端应用程序在尝试直接调用 lambda 函数时不需要包含任何身份验证信息。
如何处理长的预测请求?
机器学习预测有时需要相当长的时间来处理。我们现在将描述我们如何处理来自机器学习模型的预测结果,该过程在图中用红色箭头表示。
作者图片
我们没有让我们的前端等待和监听缓慢的响应,而是加入了 DynamoDB 来存储从后端发送的结果,并让我们的 React 前端每五秒钟 ping 一次数据库,以检查结果是否就绪,并用最新状态刷新浏览器。这样,我们使用更多的 pings 来避免长时间的开放连接。
在我们进行预测的 lambda 函数中,每当分数准备好了,我们就将其发送到 DynamoDB 表中,您可以在这里看到代码。我们的前端一直 ping 一个与 lambda 函数相关的 HTTP Api,该函数与我们的 DynamoDB 表进行对话。这里是为预测结果可用性持续 pinging 的前端实现。
还可以查看一下背后的 HTTP Api 和 lambda 函数:
https://github . com/yumyumqing/AQA _ S3 _ pre signed _ URL/blob/e 5e 23 e 16 cab 7 dad 246 f 300 e 39 c 31d 28 b 097576 Fe/template . YAML # L100-L118
https://github . com/yumyumqing/AQA _ S3 _ pre signed _ URL/blob/e 5e 23 e 16 cab 7 dad 246 f 300 e 39 c 31d 28 b 097576 Fe/fetchScore/app . py
我们的前端部署(AWS Amplify)。
我们使用 AWS Amplify 来部署我们的前端代码。它有很好的持续部署功能,因为我们的代码库驻留在 AWS CodeCommit 上,每当我们推出新的更改,它都会默认重建和重新部署我们的应用程序,而不会使我们的网站宕机。AWS Amplify 还可以很好地检测 React 应用程序,以便直接获取我们的配置文件并开始自行工作。这里有更多关于如何在 AWS Amplify 上部署 React 应用程序的信息。
作者图片
结论
我们为我们的机器学习服务演示了一个无服务器架构设计。设计并不像我们服务的功能听起来那么简单。使用了多个 AWS 组件,包括 AWS Amplify、HTTP API Gateway、Lambda、简单存储服务(S3)、弹性容器注册中心(ECR)和 DynamoDB。
这是与 延庆戴 的协同工作
注意
延庆戴我开始了我们的自由职业生涯。我们接受专注于生产机器学习服务、数据工程和分布式系统的自由职业者的工作。
Kafka、Debezium 和 BentoML 的机器学习流
原文:https://towardsdatascience.com/machine-learning-streaming-with-kafka-debezium-and-bentoml-c5f3996afe8f
使用现代数据相关工具创建实时价格推荐系统
照片由EJ·斯特拉特在 Unsplash 上拍摄
介绍
最近,GitHub 宣布了预期的(也是有争议的)Copilot,这是一个能够生成和建议代码片段的人工智能,具有相当好的性能。
然而,Copilot 令人印象深刻的不仅是它的建议能力——这在科学论文中已经实现了——而且主要是因为它是一款优秀产品(我也是从用户的角度来说的),能够通过简单的文本编辑器扩展同时为数百万开发者提供实时预测。
随着机器学习技术的成熟,不仅要了解人工智能模型如何工作以及如何提高它们的性能,还要了解如何将它们投入生产并与其他系统集成的技术部分,这变得越来越重要。
为了练习“人工智能基础设施”的这一部分,在这篇文章中,我们将模拟一个真实的情况(或几乎真实的情况),在这种情况下,将有必要将机器学习模型与“生产”数据库相集成,以便随着新记录的增加而进行实时预测。
也许这篇文章有点长,所以卷起你的袖子和我一起做这个项目吧。
问题是
假设我们有一个卖车平台,用户可以在那里注册并公布他们的车辆。随着新车注册(在数据库中),应用程序应该建议(使用我们的机器学习模型)车辆的价格。当然,这个应用程序需要实时运行,这样用户才能快速收到适当的反馈。
提议的应用程序。图片作者。弗里皮克的图标。
为了模拟数据,我们将使用 Kaggle 的 福特二手车列表 数据集,该数据集包含超过 15k 辆汽车的销售价格及其各自的属性(燃料类型、里程、型号等)。
我之前在数据集上做了一些实验,发现了一个足够好的模型,(完整代码将在 GitHub 上提供),所以让我们跳过数据分析/数据科学部分,专注于我们的主要目标——让应用程序工作。
提议的架构
为了解决我们的问题,我们将需要以下东西:一种检测新条目何时添加到数据库的方法(更改数据捕获),一种读取这些条目并使用机器学习模型预测价格的应用程序,以及一种将这些条目写回原始数据库(带有价格)的方法,所有这些都是实时的。
幸运的是,我们不必重新发明轮子。下面几节中介绍的工具对我们帮助很大,只需要很少(或者根本不需要)代码。
CDC 与 Debezium & Kafka
变更数据捕获,或简称为 CDC,是监视和跟踪数据库中的变更的行为。您可以将 CDC 视为数据八卦,每次数据库内部发生一些事情时,CDC 工具都会监听并与它的“朋友”共享消息。
例如,如果将条目(joo,21)添加到表 *neighbors,*中,该工具将发出类似这样的声音:{ ' added ':{ ' name ':' joo ',' age':21,' id':214}}。
这对于许多应用程序非常有用,因为捕获的更改可以用于许多任务,如数据库同步、数据处理和机器学习,这就是我们的情况。
Debezium 是一个专注于 CDC 的开源工具。它的工作原理是读取数据库(在本例中称为源)日志,并将检测到的更改转换为标准化的结构化消息,格式为 AVRO 或 JSON,因此另一个应用程序可以使用它,而不用担心谁是源。
来源 CDC 与 Debezium。图片作者。弗里皮克的图标。
它也可以用另一种方式来完成,通过接收描述变更的标准化消息,并将其反映到数据库中(在这种情况下称为 sink)。
用 Debezium 吸收 CDC。图片作者。弗里皮克的图标。
Debezium 建立在 Apache Kafka 的基础上,Apache Kafka 是一个著名的开源分布式事件流工具,被许多大公司使用,如优步和网飞,每天移动千兆字节的数据。由于在数据移动方面的巨大可扩展性, Kafka 拥有巨大的潜力来帮助生产中的机器学习模型。
对于这个项目,我们不需要了解很多关于卡夫卡的知识,只需要了解它的基本概念。在 Kafka 中,我们有一个主题结构,包含由生产者编写并由消费者阅读的信息(字面上只是一串字节)。后两者可以是任何能够与 Kafka 连接的应用程序。
它已被证明是一个大规模应用程序的优秀工具,这肯定不是我们这个简单项目的情况,但它在使用中的简单性支付了任何额外的开销(在这个项目中)。
这就是我们的数据是如何移动的:当 Debezium 被配置为观察我们数据库中的某个表时,它会将检测到的变化转换为标准化的消息,将它们序列化为字节,然后将它们发送到 Kafka 主题。
然后,另一个应用程序可以连接到该主题,并根据需要使用数据。
数据移动。图片作者。弗里皮克的图标。
BentoML
BentoML 是一个服务于 ML 模型的开源框架。它允许我们用一个简单的 python 库对我们的机器学习模型进行版本控制和部署。
这是一个非常好的工具,尤其是如果您来自数据科学领域,并且从未从 Jupyter 笔记本的“快乐领域”脱下模型进入“生产”世界。
著名的用于机器学习的 python 库要么没有为模型服务的方法,因为他们认为这超出了范围,要么当他们有它时,它不那么容易使用。正因为如此,许多项目依赖于通过用 FastAPI 或 Flask 构建的 API 来交付他们的模型,这很好,但不是最佳的。
在我看来,BentoML 非常好地缩小了模型训练和部署之间的差距。
我们将在接下来的章节中了解更多。
将一切连接在一起
现在我们知道了,至少是表面上知道了,所使用的工具,你可能已经知道我们将如何解决这个问题。
提议的架构。图片作者。弗里皮克的图标。
我们将有一个 Debezium 实例监视我们的数据库,将检测到的每一个变化传输到 Kafka 主题。另一方面,python 应用程序使用消息并将它们重定向到 BentoML 服务,后者返回预测价格。然后,python 应用程序将记录与它们的预测价格结合起来,并将它们写回到另一个 Kafka 主题。最后,Debezium 实例也在关注这个主题,它读取消息并将它们保存回数据库。
好吧,这是很多步骤,但不要害怕,我保证做这一切的代码非常简单。
为了便于理解,让我们在上面的图像上做一个 x 光,看看我们的生物(建筑)的一些内部器官(组件)。
建议的建筑 X 射线。图片作者。弗里皮克的图标。
我们需要做的就是创建数据库,配置 Debezium 连接器(源和宿)并使用 Python 部署我们的机器学习模型。
实施
我会尽量简短,完整的详细代码将在 GitHub 上。
环境
首先要做的是配置环境,您所需要的是:
- 包含以下包的 Python 环境:
numpy
pandas
scikit-learn==1.1.2
xgboost==1.6.1
bentoml
pydantic
用于训练和部署机器学习模型。
2.Docker 和 docker-compose。
所有的基础设施都是使用容器构建的。此外,我们将使用 Postgres 作为我们的数据库。
仅此而已👍
配置 Postgres
Postgres 的配置非常简单,我们只需要创建一个表来存储汽车数据,并设置配置 wal_level=logical。
在 Postgres 中创建表的 SQL 脚本。
因此,Postgres Dockerfile 文件是这样的:
wal_level=logical 是 Postgres 正确使用 Debezium 所需的配置。
配置 Debezium 和 Kafka 连接器
开始使用 Kafka 和 Debezium(使用 Docker)很简单,只需要正确配置图像和连接器。这个项目中使用的 docker-compose 和 Dockerfile 是基于官方库中 Debezium 的一个例子。
注意:我隐藏了一些行以使这个代码块更短,请查看 GitHub 上的完整代码。
Debezium Dockerfile 配置了 Kafka Connect 和 Postgres 的驱动程序。
有了这个 docker-compose 文件,我们就可以配置 Debezium 了。要启动容器,请在终端中键入:
docker-compose up --build
在一些初始配置(和大量日志)之后,容器应该可以正确启动。现在你可以在 localhost:8083 上打开你的浏览器。
本地主机:8083。图片作者。
这是 Debezium 的 API 的基本端点,所有的配置都发生在这里。例如,如果我们移动到localhost:8083/connector-plugins/,就可以看到所有可用于创建连接器的插件。
localhost:8083/连接器插件。图片作者。
为了创建一个新的数据库连接器,我们需要向端点**/连接器**发送一个 POST 请求,其中包含连接器的配置。如前所述,有两种类型的连接器,源连接器从数据库中检索更改并将其传输到 Kafka,接收器连接器从 Kafka 读取消息并将其反映到数据库。
让我们为 Postgres 数据库创建源连接器,看看它是如何工作的。
我们只需要传递数据库地址和凭证、连接器类(端点 /connector-plugins/ 中可用的一个)以及我们想要捕获的表。
你可以在这篇文章中了解更多关于这些连接器和配置的信息。
现在,Debezium 将创建一个名为car _ database . public . car _ data的 Kafka 主题,并开始对其进行流式修改。
流向卡夫卡的记录示例。图片作者。
在上面的图片中,在左边,我们可以看到我添加到数据库中的条目,在右边,是在 Kafka 上创建的消息。消息是用 AVRO 写的,可以理解为一个 JSON,分为“payload”和“schema”。
接收器连接器的配置遵循相同的逻辑,但是在这种情况下,我们还需要为我们的主题命名。Debezium 将使用主题的标题自动创建 sink 表(如果它不存在),根据发送的第一条消息推断列。
这就是为什么我们不需要在 Postgres 中创建第二个表:它将由 Debezium 自动生成。
用 BentoML 部署我们的模型
接下来要做的就是用 BentoML 部署我们的机器学习模型。这是通过三个步骤实现的:保存我们的模型,构建一个便当,并将其转换为 Docker 容器。
保存一个模型与保存任何文件没有太大的不同,您只需给它一个名称,并使用 save_model 函数将它保存在磁盘上。
BentoML 提供了许多功能来监控和版本化保存的模型,这些都是值得检查的。
有了保存的模型,我们可以构建一个服务来部署它。
训练好的模型作为跑步者被加载,这是 BentoML 用来表示模型的一种特殊类型的对象。runner 用于创建一个服务对象,并使用它定义 / 预测 端点,负责接收记录并返回其预测价格。
在部署我们的模型之前要做的最后一件事是定义一个 bentofile ,BentoML 使用这个特殊的配置文件来描述部署环境。
然后,通过运行下面的命令,我们用我们的名为Ford _ price _ predictor:1 . 0 . 0的服务创建一个 Docker 映像。
bentoml build --version 1.0.0
bentoml containerize ford_price_predictor:1.0.0
最后,我们可以启动服务的容器实例。
docker run -p 3000:3000 ford_price_predictor:1.0.0
并通过访问 localhost:3000 与之交互。
连接流和模型
现在,我们已经构建了管道的两个主要部分,剩下的就是连接它们,这将使用 python 脚本来实现。
一方面,Debezium 将数据流式传输到 Kafka 主题car _ database . public . car _ data,并等待 car_data_predicted 中的消息。另一方面,BentoML 服务被部署并等待端点上的预测**/预测**。
为了连接到 Kafka,我们将使用 confluent_kafka 包,并且为了连接到部署的模型,请求包。
接下来,我们定义 Kafka 主题和便当服务 URL。
URL 不是 localhost,因为这个脚本将在另一个容器中运行。
然后,为源主题创建一个 Kafka 消费者,为接收器主题创建一个生产者。
消费者从源主题中一次检索一条消息。然后,从每条消息中只提取进行预测所需的字段。
消息通过 POST 请求发送到机器学习模型,该请求返回预测价格。
最后,预测价格被添加到原始消息中,以便生产者可以将其发送到接收器主题。
Debezium 将读取这个新消息,并在数据库中创建相应的记录。
我们可以收工了!
看到它工作
终于到了看到我们的项目工作的时候了:)
不幸的是,我没有一个花哨的应用程序来测试我们的管道,所以我们将通过直接与数据库交互来完成。
添加示例记录。图片作者。
正如我们在 gif 中看到的,当在 car_data、中添加一条新记录时,在car _ data _ predicted表中会自动创建另一条记录,并给出建议价格。
如果我们继续在 car_data 表中添加越来越多的记录。
记录已添加。图片作者。
它们将与建议价格一起复制到 car_data_predicted 表中。
预测价格的记录。图片作者。
综上所述,奏效了!
结论
将机器学习项目投入使用不是一项简单的任务,就像任何其他软件产品一样,它需要许多不同种类的知识:基础设施、商业、数据科学等。
我必须承认,在很长一段时间里,我只是忽略了基础设施部分,让我的项目在 Jupiter 笔记本中平静地休息。但是当我开始学习它的时候,我意识到这是一个非常有趣的话题。
机器学习仍然是一个不断发展的领域,与其他 IT 相关领域(如 Web 开发)相比,社区仍然有很多东西需要学习。幸运的是,在过去的几年中,我们已经看到了许多新技术的出现,帮助我们构建一个 ML 应用程序,如 Mlflow,Apache Spark 的 Mlib 和 BentoML,在这篇文章中进行了探讨。
在这篇文章中,我们探索了一种机器学习架构,并利用其中的一些技术来构建一个实时价格推荐系统。为了将这一概念付诸实践,我们不仅需要与 ML 相关的工具(BentoML & Scikit-learn),还需要其他软件(Postgres、Debezium、Kafka)。
当然,这是一个简单的项目,甚至没有用户界面,但是这篇文章中探讨的概念可以很容易地扩展到许多情况和真实场景。
我希望这篇文章在某种程度上帮助了你,我不是所讨论的任何主题的专家,我强烈建议进一步阅读(见下面的一些参考资料)。
感谢您的阅读!;)
参考
所有的代码都可以在 这个 GitHub 资源库 中找到。
[1] Debezium 官方文档
【2】Jiri Pechanec(2017)将数据流式传输到下游数据库 — Debezium 博客
【3】Bentoml API I/O 描述符 — BentoML 文档
【4】Bentoml 概念 — BentoML 文档
【5】Kai Waehner(2020)流式机器学习与 Kafka-原生模型 — BentoML 博客
【7】Debezium 实例 — Debezium 官方知识库在 Github 上
【8】本·迪克森(2022)Github Copilot 是首批基于大型语言模型的真实产品 —技术会谈
【9】福特二手车上市、 CC0:公共领域 — Kaggle
机器学习系统与机器学习模型
原文:https://towardsdatascience.com/machine-learning-systems-versus-machine-learning-models-3955d038ea1f
一个机器学习系统的例子。图片由作者提供
一个正常运行的人工智能产品不仅仅包含一个机器学习模型。你需要在你的 ML 汤里加很多食材,才能让它给顾客带来价值。我们把所有这些成分合在一起称为机器学习系统。
但是,我们往往过于关注我们的 ML 模型,而忽略了更大画面的其他部分。我把这种现象称为以模型为中心的偏见。
以模型为中心的偏见会导致糟糕的结果,原因有很多:
首先,我们机器学习系统的大部分复杂性都存在于 ML 模型源代码之外。如果我们忽视这一事实,只专注于模型开发,我们就有积累太多技术、文化和研究债务的风险。
其次,除了编程代码,机器学习模型还有一个额外的组件——数据。你的模型只能和你的数据一样好。以模型为中心的偏见造成了忽视数据的倾向。
由于对数据的依赖性,机器学习系统往往比传统软件更脆弱。我们必须做出额外的系统设计决策来处理这种脆弱性,并为系统引入适应性。
最后,从精益的角度来看,只有系统作为一个整体才能给客户带来价值。
这就是为什么我们需要停止孤立地考虑我们的机器学习模型,而是将它们视为一个动态的、不断适应的系统的一部分——机器学习系统。
本文的其余部分将更详细地探讨这个概念。
模型中心偏差
我相信以模型为中心的偏见来源于我们在人工智能和人工智能社区中制定研究目标的方式。我们使用标准数据集创建了一个基准,并撰写了一些论文,试图在这方面击败当前的 SotA。同样的方法也出现在应用 ML 和 DS 社区中,以 Kaggle 和其他带有排行榜设置的竞赛的形式出现。
虽然这是迄今为止我们所知的符合波普尔的可证伪性原则并拥有衡量研究进展的共同标准的最佳方式,但它造成了只关注模型的偏见。当我们从学术环境转向构建人工智能产品时,我们带来了它。
为什么以模型为中心的观点有害?
机器学习系统的复杂性
机器学习系统的复杂性存在于机器学习模型源代码之外。
2015 年,谷歌发表了一篇论文“机器学习系统中隐藏的技术债务”,他们根据自己对机器学习产品的经验,对这一现象进行了探索。下面是那篇论文的图片,展示了大部分复杂性集中的领域,以及系统倾向于积累技术债务的领域。
机器学习系统的复杂性地图。D.Sculley 等人《机器学习系统中隐藏的技术债务》
开发和部署机器学习模型相对容易,但很难让整个机器学习系统投入生产。ML 系统是复杂的,并且倾向于积累技术、研究和文化债务。随着时间的推移维护它们是非常困难和昂贵的。
机器学习模型反映了它被训练的数据。
机器学习模型是编程代码和数据的融合。我们没有明确地写出程序的算法,而是从给出的例子中推断出来。
这意味着数据在最终学习算法的质量中起着至关重要的作用。此外,虽然模型的代码可以被显著标准化并提取到可重用的库和工具中,但是数据代表了产品的唯一性。您生成的模型只能和您的数据一样好:
垃圾进,垃圾出。—GIGO 原则。
这意味着我们需要更多地关注数据:测试数据,迭代改进数据,统一和修正数据集的标注方案。
Andrew NG 在举办的精彩网络研讨会更详细地探讨了这个想法。
脆弱和适应。
模型生命周期不会随着初始部署而结束。这对于传统的非最大似然算法是有效的,对于高度依赖数据分布稳定性和缺乏可解释性的机器学习模型尤其如此。这些系统非常脆弱。
补救措施来自反馈循环的系统理论概念:通过引入快速的反向信息流和对该信息的反应,我们使系统具有适应性和持久性。
这就要求我们在系统层面做出设计决策。只专注于模型而不考虑其背景将导致脆弱和低质量的人工智能产品。
机器学习模型本身并不能带来价值。
最后,我们为客户提供的价值才是最重要的。价值——是客户想要并愿意支付的东西。这是精益的核心理念,也是 DevOps 和 MLOps 实践的核心。一个经过训练的机器学习模型本身并不能带来任何价值。它只是一个二进制文件,在文件系统的某个地方积灰。是系统作为一个整体产生并带来价值。
那么,什么是机器学习系统呢?
什么是机器学习系统?
让我们看看机器学习项目的一般价值流。
这是一个迭代过程,通常由以下阶段组成:
- 数据收集—我们创建数据集并保持其最新状态。
- 实验—我们探索我们的数据,制定和验证关于数据和模型的不同假设,并构建训练和预测管道。
- 部署——我们将结果管道集成到工作产品中。
- 运营—我们监控运行模型,使其与不断变化的环境保持同步。
机器学习系统的通用价值流图。图片由作者提供。
虽然这是一个通用的模板,但是许多参数决定了您的 ML 项目的独特性。其中一些是:
- 问题域
- 项目时间和其他资源限制
- 数据质量
- 标签质量和可用性
- 数据集大小
- Data domain 可变性
- 应用基础设施和架构
- 贵公司的结构
根据您在这个属性空间中的位置,您将拥有一组独特的活动、工具和流程。
我喜欢将术语机器学习系统定义为所有活动及其工件的具体实现,这些活动及其工件使用机器学习方法向客户交付价值。
机器学习系统是我们使用机器学习方法向客户提供价值的一种方式。
机器学习系统指定:
- 我们如何收集、标记和测试数据,
- 我们如何迭代我们的模型和特性的增量改进,
- 我们如何部署模型并对其进行变更,
- 我们如何将模型与应用程序的其余部分集成在一起,
- 我们如何监控它并对环境的变化做出反应。
所有这些构成了复杂的动态适应系统。
让我们看一些机器学习系统的例子。
早期 PoC 项目。
在早期项目中,我们的目标通常是验证主要的业务假设和 ML 方法解决问题的适用性。我们花时间确定项目范围,收集初始数据,并计算出目标度量和评估协议。
通常,目标是快速提出一些基线解决方案,然后迭代地改进。因为项目在那个阶段的计划通常不是向用户交付价值,而是验证有一些价值的假设,最终结果很可能不是现成的产品,而是用户应该验证的原型。这就是我们不关注部署和运营阶段的原因。相反,我们优化实验阶段。我们的目标是通过自动化指标和交互式仪表板创建更短的反馈循环。
在计算资源调度和实验可重复性方面,没有成熟的培训基础设施。通常,数据科学家在 JupyterHub 这样的交互式编码环境中完成大部分工作。
部署是手动的,通常是将模型从数据科学家移交给 ML 或软件工程师。
分离代码库区域是实现实验灵活性和技术成熟度积累之间的折衷的典型模式。实验区没有在代码上强加工程质量标准——它是一个短命的、不可重用的实验功能区。我们不断地审查、重构和提取稳定的、可重用的功能到一个稳定的代码库区域,并将其部署到一个包中。
在这样的项目中,我们通常不太关心数据的可观察性或血统。数据科学家仅从交互式数据科学环境中创建新数据集。
早期 PoC 项目的机器学习系统示例。图片由作者提供。
无标签场景。
想象一个不同的场景,当我们对一个新启动的项目没有任何合适的质量标签。
注释过程可能是我们价值流的瓶颈,一个合适的注释工具将在项目的成功中起到至关重要的作用。
此外,我们可能会利用主动学习方法来提高贴标流程的效率,并减少必要的贴标工作。
如果我们的 ML 基础设施足够成熟,我们可以通过作业调度程序和提交界面来自动化培训过程。
最终的机器学习系统可能看起来像这样:
一个利用主动学习方法的机器学习系统的例子。图片由作者提供。
上图中的边代表了工件流。它们可以手动或自动执行。自动化程度越高,迭代和进步的速度就越快。
标签存在于推理时间数据中。
让我们想象一下相反的场景——我们有一个日常用户正在运行的产品。我们可以从用户那里得到标签,在推理时纠正模型的错误行为。
我们实施持续培训模式,端到端地自动化整个周期。数据被自动收集并转换成训练和验证数据集。我们将培训过程自动化,并通过自动化的单元和数据测试以及模型评估将其放入 CI/CD 管道中。
我们可以选择持续部署或持续交付策略,并通过引入 canary 版本和自动回滚来进一步推进这一策略。
模型服务被持续监控模型性能,当性能下降时,系统触发重新训练/重新部署过程。
利用连续训练方法的机器学习系统的例子。图片由作者提供。
这些只是可能的机器学习系统的几个例子。还有很多,适合你的会不一样,体现你项目的独特性。
所以我们总结一下。
结论
虽然机器学习模型是人工智能产品的核心,但它通常不是复杂性集中的地方。
当我们计划我们的 ML 项目时,我们需要从一开始就将我们的焦点转移到机器学习系统上。
我们必须开始更多地思考和讨论这样的系统,并开发可重用设计模式和良好实践的词汇表。
跟我来
如果你觉得我与你分享的想法很有趣,请不要犹豫,在媒体、推特、 Instagram 或 LinkedIn 上联系我们。
机器学习应对气候变化
原文:https://towardsdatascience.com/machine-learning-to-tackle-climate-change-7911e004c3a2
人工智能如何帮助对抗全球变暖并从人类手中拯救世界
图片由作者使用 DALL-E 2 拍摄
去年夏天表明,变暖是一个我们不能再忽视的问题。全球气温上升正在导致越来越多的极端事件,未来可能会更糟。
机器学习和人工智能可以帮助对抗全球变暖。在本文中,我们将尝试回答这些问题:如何解决?目前人工智能已经在该领域的应用有哪些?
为什么现在这么急?
图片来源:【unsplash.com】的林丽安的
六月,孟加拉国和印度遭受了有史以来最严重的洪水袭击。不到三个月后,巴基斯坦遭受洪水袭击,导致该国三分之一的土地被水淹没。与此同时,西班牙和葡萄牙今年遭遇了 1000 年来最严重的干旱。今年夏天影响欧洲大陆的持续热浪期间,法国和其他欧洲国家经历了可怕的森林火灾。在过去的十年里,加利福尼亚正在经历破坏性野火数量的增加。
图片来自unsplash.comYODA·亚当
总之,我们知道这些破坏性事件和气候变化之间存在联系。全球气温上升越多,预计这些事件将会更加频繁和更具破坏性。所有的气候模型都同意,如果不减少二氧化碳,我们将会看到全球气温上升和极端事件。
“没有所有部门的立即和深入的减排,将全球变暖限制在 1.5 摄氏度是不可能的”——IPCC 新闻稿
如果这还不足以解释事情的紧迫性,那么欧盟已经发现其能源供应链是多么的脆弱,以及对俄罗斯天然气的依赖程度。因此,有人呼吁从化石燃料过渡到可再生能源。
“我们正处于十字路口。我们现在做出的决定可以确保一个可居住的未来。我们拥有限制变暖所需的工具和技术,”—hoe sung Lee,IPCC 新闻稿
在这篇文章中,我将讨论机器学习和人工智能如何在能源转型和二氧化碳减排中发挥关键作用。
机器学习如何拯救世界?
图片由作者使用 DALL-E 2 制作
自 2019 年气候变化人工智能(CCAI )是一个志愿者驱动的社区(由学术界和工业界的成员组成),其使命是催化气候变化和机器学习专业知识的交叉。
他们最近发布了一份报告,其中列出了一长串机器学习有助于应对气候变化的领域和应用。文章将可能的策略划分为:
- 高杠杆,非常适合 ML 工具的领域
- 长期,预计应用在 2040 年前不会产生主要影响的领域
- 不确定影响,难以解释应用策略是否会有帮助或导致不良影响的领域(由于技术不够成熟或效果不可预测)
一个表格总结显示了不同的气候变化解决方案领域,其中机器学习可能是有益的。表来自原文章:此处
作者认为电力系统是一个高杠杆领域。事实上,他们提出机器学习可以有助于电力系统技术的运行(帮助向低碳能源过渡、能源需求优化、电网管理等等)。
有趣的是,他们标记为高杠杆,但也标记为现有植入物减少排放的不确定影响。事实上,在向可再生能源过渡的同时,我们可以优化现有的植入物(例如,使用机器学习来避免天然气管道中的甲烷泄漏)。然而,优化的化石燃料植入可能被视为“更加绿色”,并可能推迟过渡(因此,影响不确定)。
该报告对许多人工智能技术的应用细节进行了投资。虽然更容易思考如何使用强化学习和自动驾驶汽车,但它们显示出人工智能的几乎所有子领域都可能是相关的(从自然语言处理到因果推理等等)。
“机器学习,像任何技术一样,并不总是让世界变得更好——但它可以”——气候变化人工智能报道
实施 ML 减缓气候变化的真实案例
图片由作者使用 DALL-E 2
该报告为未来的应用和战略提供了详细的建议。好消息是,已经有公司和研究人员致力于实现这些目标。
事实上,即使风力涡轮机的成本直线下降,风也不是恒定的,它是不可预测的。为此, DeepMind 应用了 ML 算法来预测风力发电。利用神经网络模型,他们优化了向电网输送所产生能量的承诺。谷歌和 DeepMind 在美国中部的一个风电场测试了该模型(700 MW 植入)。
最近,谷歌决定通过谷歌云向风电场出售这项技术。谷歌宣布,这种算法可以提前三十六小时预测风力输出。6 月,Engie(一家法国公司)宣布成为该项目的第一个客户。
图片由来自 unsplash.com的卡拉什尼科夫拍摄
另一个有趣的应用是气候追踪,这是一个利用计算机视觉追踪温室气体排放的大学联盟。他们利用卫星图像和遥感技术来识别温室气体来源,并对其进行监控以促进气候行动。此外,大量收集的数据可供社区自由用于新的潜在应用。您可以在此视频中了解更多信息:
卫星图像也被用来监测海平面上升或者预测对干旱敏感的地区,追踪森林砍伐等等。此外,Pano AI 和黄锦雯技术公司等初创公司正在实施计算机视觉,以识别火灾风险区域,检测野火,并预测如何蔓延。
由于太阳能和风能是可变的,也有其他项目对改善电池存储感兴趣。例如,卡内基梅隆大学与 Meta AI 合作,建立了开放催化剂项目。他们还发布了一个大型数据集来改进 catalyst 模拟,并组织了不同的相关公开挑战。
此外,农业占全球排放量的 10 %以上,化肥不仅破坏环境,还会产生强大的温室气体。这导致了一个新领域的出现:精准农业。事实上,不同的公司正在使用人工智能来优化资源消耗和肥料使用
图片由来自 unsplash.com 的罗曼·辛克维奇·🇺🇦拍摄
此外,建筑物产生的碳排放量接近总排放量的五分之一,因此对其进行优化极其重要。有些公司专注于如何改善建筑过程,有些初创公司专注于改善空调、寻找新材料等等。此外,最近 DeepMind 宣布使用人工智能将谷歌数据中心的冷却费用减少 40% 。
其他的应用就不那么简单了,然而,它们已经引起了几家公司的兴趣。计算一家公司的碳足迹也不容易,不同的初创公司现在都提议为大公司计算碳足迹。一旦一家公司意识到哪个流程的碳足迹更高,就可以考虑采取行动和进行优化。例如,分水岭评估其他公司的二氧化碳排放量,并提出减排方案。
"世界上第一个万亿美元将在气候变化中产生."—Chamath Palihapitiya的预测
所有这些项目都显示了公司如何投资使用人工智能来应对气候变化。如果这对环境有好处,对商业也有好处。
离别的思念
“我们强调,在每个应用中,ML 只是解决方案的一部分;它是一个跨领域启用其他工具的工具。”—气候变化人工智能报告
因此,人工智能在气候变化方面没有单一的“灵丹妙药”。相反,广泛的机器学习用例可以帮助我们在世界脱碳的竞赛中获胜。”——福布斯
年复一年,全球变暖被证明是一个日益紧迫的问题。极端事件的频率每年都在增加,导致大范围的破坏。预言说,如果我们不采取紧急和激烈的行动,我们将面临更糟糕和更频繁的事件。
机器学习和人工智能并不是可以单独解决全球变暖的神奇药水(更何况人工智能和大数据的使用还会产生二氧化碳)。然而,ML 和 AI 被认为是对抗全球变暖的关键手段:既用于能源转换又用于减排。该报告详细说明了哪些策略和应用将从 ML 和 AI 中受益最多。
学术界一直致力于研究和提出解决方案。好消息是,今天也有几家公司正在致力于开发这些策略和新的应用程序。此外,在能源转型(可再生能源、电动汽车等)方面的投资近年来大幅增长,并且出现了新的消费者意识和关注。另一方面,政府方面需要有全球性的更强有力的承诺。
如果你知道关于使用人工智能应对气候变化的其他倡议、其他公司和项目,请告诉我。
如果你觉得有趣:
你可以寻找我的其他文章,你也可以 订阅 在我发表文章时获得通知,你也可以在**LinkedIn**上连接或联系我。感谢您的支持!
这是我的 GitHub 知识库的链接,我计划在这里收集代码和许多与机器学习、人工智能等相关的资源。
**https://github.com/SalvatoreRa/tutorial
或者随意查看我在 Medium 上的其他文章:
https://pub.towardsai.net/a-new-bloom-in-ai-why-the-bloom-model-can-be-a-gamechanger-380a15b1fba7
附加资源
- 关于全球变暖:这里,这里,这里
- 关于二氧化碳和气候变化:这里,这里,这里,这里
- 关于可再生能源投资和能源转型:此处,此处
- 关于谷歌数据中心 DeepMind AI 解决方案:此处,此处,此处
- 关于风能预测的 DeepMind AI 解决方案:此处**
基于机器学习的视觉排序
原文:https://towardsdatascience.com/machine-learning-to-visually-sort-7349d3660e1
无监督学习方法
在 unsplash 上由 Luisa Brimble 拍摄的照片。
随着时间的推移,我们收集书籍,在某个时候,我们可能会决定整理它们。拼色书架是根据颜色相似度来排列书籍的一种趋势。整理书架的一种流行方式是创造彩虹效果。这创造了一个令人愉快的视觉安排。根据书籍的数量,这可能是一项容易或困难的任务。
可以自动排书吗?我想探索机器学习技术来分类书籍并比较它们的结果。在这篇文章中,我应用无监督的机器学习方法按颜色对书籍进行排序。每种算法都使用一个目标函数来对颜色进行排序。基于算法的类型,我们会得到不同的结果。我用流行的无监督算法对颜色进行了分类,包括:
- 主成分分析
- 核主成分分析
- 随机投影
- 最近邻
- 自组织地图
我将首先检查数据集准备,然后探索不同的无监督算法。
数据集和要素
我们的任务是根据图像来排列书籍。图像像素不是信息特征。由于大多数书籍的颜色很少,将书籍图像中的颜色分布减少到单一 RGB 值效果很好。书单涵盖了各种各样的颜色。我用占主导地位的 RGB 值创建了一个调色板数据集来表示我的图书列表中的每本书。最终数据集的维数为 99 × 3,用颜色特征的数量来表示书籍的数量。该数据集的随机排序颜色如下所示。
随机排序的颜色。
接下来,我探索了将数据从 RGB 空间映射到一维空间的不同方法。
降维
降维映射。
降维算法在降维的同时保留了高维数据中的信息。每种方法的独特之处在于它在低维中保存的信息类型。我们希望我们的颜色排列成相似的颜色相互靠近。我们将 RGB 数据映射到一维数组,并对其进行排序。不同的方法使用不同的目标函数来降维。
在降维方法中,我们首先学习一个维数较低的投影矩阵, d × m ,其中 m < d 。然后将我们的 N × d 维数据映射到投影矩阵上,得到低维数据 N × m 。我使用了 scikit-learn python 库来实现降维方法。
主成分分析
主成分分析(PCA)将数据映射到一个低维子空间上,使方差或数据分布最大化。低维子空间包含称为主分量的样本协方差矩阵的特征向量。由于 PCA 将 RGB 数据降低到一个维度,它可以用于对颜色进行排序。我将主成分分析应用于调色板数据集。为了可视化,我们将 RGB 数据映射到第一和第二主成分上。
PCA 变换后的可视化颜色。
相似的颜色群聚集在一起。颜色趋势是从浅到深(从左到右)。我们将特征映射到第一主成分上,并对投影进行排序。
PCA 变换后的颜色排序。
存在从较亮到较暗颜色的明显颜色亮度趋势,但是没有颜色内聚类排列。
核主成分分析
高维数据可以有一个非线性的数据结构,不能用线性 PCA 学习。PCA 的非线性扩展是核版本,其中我们在提取主成分之前使用核函数来映射数据。核函数计算数据点之间的相似性。不同的核函数捕捉不同类型的相似性。基于我们的数据,我们必须试验不同的内核函数。我使用余弦内核,因为它提供了一个很好的排序结果。
核 PCA 变换后的颜色可视化。
在上面的二维图像中,我们看到了漂亮的内部颜色簇。我们也看到不同颜色的阴影向中心聚集。在将我们的数据映射到主要主成分并排序后,我们看到颜色趋势从暖色变为浅灰色并趋向冷色。
核 PCA 变换后的排序颜色。
随机投影
在 PCA 和核 PCA 中,我们学习数据相关投影。当数据维数非常高时,我们会遇到特征值分解或协方差矩阵构造的内存问题。随机预测是与数据无关的预测。当我们在这些投影上绘制数据时,成对距离会保留一定的系数。我们通过从具有零均值和方差的高斯分布中采样向量来构建具有较小维度的投影矩阵,其中方差由维度的数量 m 来缩放。我们的最终结果每次都会改变,因为抽样是随机的。
高斯随机投影变换后的颜色可视化。
随机预测出奇地有效。色带从冷蓝色调过渡到中间的浅粉色,并向暖色过渡。
高斯随机投影变换后的排序颜色。
虽然核 PCA 和随机投影学习分离暖色和冷色的映射,但有趣的是看到这两种方法的过渡如何不同。
接下来,我将探索如何通过将颜色排序重新定义为成本最小化问题来使用优化方法对颜色进行排序。与降维方法相比,优化方法使用 RGB 数据而不降维。
旅行推销员问题
最近邻旅行商问题的一种解法。黑线显示连接每种颜色的最短路径。
旅行推销员问题的目标是为给定的城市列表找到最短距离的路线,使得所有的城市只被访问一次。我们可以将颜色排序重构为一个旅行推销员优化问题。在我们的颜色排序问题中,我们可以用颜色代替城市。上图展示了给定起始节点,基于欧几里德距离的五个 RGB 值之间的最短路径。
最近邻
解决旅行推销员问题的一个启发式方法是使用最近邻法。给定一个二维距离相似矩阵,我们从一个随机颜色开始,迭代地找到最近的颜色聚类。根据使用的距离度量,结果会有所不同。这种方法会产生彩虹般的效果。我使用欧几里得度量来创建距离相似性矩阵。
用最近邻法对颜色进行排序。
自组织映射
自组织映射(SOM)是一种人工神经网络,它通过竞争学习来学习数据的二维映射。SOM 有助于数据可视化和聚类。我们学习了 N × d 维数据的二维图,其中数据基于相似性进行聚类。相似的颜色比不相似的颜色更接近。
用自组织图排序颜色。
与降维方法相比,SOM 创建了更好的颜色内聚类。我使用了 MiniSom python 库来实现 Som。
结论
综上所述,以上所有方法对颜色的分组都是不同的。在 PCA 中,只有亮度信息被认为是重要的。与 PCA 相比,其他降维方法保持颜色内分组,而 SOM 和最近邻方法创建彩虹般的颜色排列。
参考文献
[1] Jura Koncius,按颜色整理书籍:你属于哪个阵营?
[2] Cameron Davidson-Pilon,利用 PCA 对高维对象进行分类
[3] Alan Zucconi,令人难以置信的具有挑战性的颜色分类任务
所有图片,除非特别注明,均为作者所有。
基于模糊逻辑的机器学习
原文:https://towardsdatascience.com/machine-learning-with-fuzzy-logic-52c85b46bfe4
如何使用模糊逻辑从数据中提取控制规则
Photo by 愚木混株 cdd20 on Unsplash
介绍
模糊逻辑的基础是真理可以表述为连续体的思想;换句话说,一个事件不是绝对的真或假,而是部分真或部分假[1]。在模糊逻辑中,系统变量的值可以用语言术语来表示,例如“高”、“低”和“中”,它们指定了数据集中感兴趣的区域。这些语言表达式被称为模糊集(或区域)。然后,模糊化提供了一种方法,通过这种方法,每个观察值被分配一个属于每个模糊集的程度。这允许我们开发一个数字数据集的语言摘要,并获得对潜在模式的直观理解。随后,生成的文本模型可以被部署来创建用于做出未来预测的控制规则。这就是模糊逻辑在控制系统中大量应用的原因。
本文的目的是提出一种从数字数据中生成模糊规则的直接而实用的方法。这将通过一组包含对相对湿度、温度和炎热指数的观测数据来说明。最后,一个数字观察,如
将被转换成如下文本信息
要获得规则语句:
**IF** relative humidity is medium and temperature is medium, **THEN** heat index is high
通过这种输入到输出的逻辑映射,即,
模糊逻辑为我们提供了一个强大的建模工具——一个可以应用于预测建模的 IF-THEN 规则。
从数据生成模糊规则的步骤
步骤 1 :对数据进行预处理后,输入和输出空间的论域(或模糊逻辑中常用的论域)被确定。
变量域:相对湿度、温度和热指数是由记录的最小和最大观察值定义的区间。
步骤 2: 使用如下所示的三角函数将区间分成 3 个重叠区域。这现在创建了表示为低、中和高的模糊区域。
数值测量的模糊化,x .作者的图像
单个测量值将属于该区域的每一个,等级范围为 0-1。该值越高,属于该特定区域的程度越大。度量的隶属度之和将等于 1。上图中位于标尺长度区间内的尺寸 X 将被模糊化,如下所示。
因此,X 属于等级为 0 的集合“低”,等级为 0.4 的集合“中”,等级为 0.6 的集合“高”
步骤 3:将每个观察值分配到其成员级别最高的区域。上面的测量值 X 将被分配给“高”区域,并记录其程度。
第四步:此时,数据中的每一条记录都已经转化为文本,形成了一条规则。计算规则度,它是构成规则一部分的模糊集的隶属度的乘积。例如,下面显示的转换后的观察给出了一个规则,如前所述。
那么,标准度数就是相对湿度为“中等”,温度为“中等”,热指数为“高”的等级的乘积。
步骤 5:在最后阶段,通过从一组具有相同前提的规则中获取一个规则来构建规则摘要。所选择的规则是具有最高规则等级的规则;因此,其结果得以保留。规则的前件是输入变量(温度和相对湿度)的指定成员,而后件是输出变量(热量指数)的成员。
Python 实现
为了设置开发 Python 代码的阶段,需要导入相关的模块和预处理数据。该数据由 13801 个训练集观察值组成。为了简单起见,我分别用湿度和热量来表示相对湿度和热量指数。
万隆理工学院 AP-Channel 研究小组的数据
接下来,构造模糊化器类——它接受一个数据序列并给观察值分配一个属于模糊集的等级。sickit-fuzzy 库用于开发代码。fuzz.trimf()方法创建三角形隶属函数,而 fuzz.interp_membership()方法为观测值分配模糊区域的隶属等级。
以下代码将模糊化应用于数据中的所有变量。
相对湿度:
温度:
热量指数:
合并成员资格列时,将获得以下转换后的数据集:
最后,创建一个总结函数来提取最终的规则集。
上面的数据框描述了数据中最强的关系。这可以很容易地转换成如下控制规则:
Rule 1: **IF** relative humidity is high and the temperature is low, **THEN** heat index is lowRule 2: **IF** relative humidity is high and temperature is medium, **THEN** heat index is medium
.
.
.
Rule 6: **IF** relative humidity is medium and temperature is medium, **THEN** heat index is medium
结论
已经开发了一种基于模糊逻辑的机器学习方法来从数据集中提取被建模为规则的关系。在这个项目中获得的六个规则总结了变量之间最强的联系,并可用于预测在相同条件下收集的一条看不见的数据的热指数——使用温度和相对湿度——具有显著的准确性,同时提供数据集中固有模式的语言快照。这使得模糊逻辑在需要非清晰输入的控制系统中成为一个重要的建模工具。
参考资料:
[1] Sickit 图像团队(2016),模糊控制初级读本,sick it-模糊文档
数据来源: 图中使用的数据集是由印尼万隆理工学院的 AP 频道 研究小组为 Acep Purqon 博士赞助的一个精准农业项目生成的。它包括生长青菜的生长室的温度、相对湿度和热指数的 20 天测量。
用 Mathematica 进行机器学习
原文:https://towardsdatascience.com/machine-learning-with-mathematica-4c0da30b3073
主要概念的介绍
法比安·伯奇利在 Unsplash 上的照片
Wolfram Mathematica(或简称 Mathematica)是科学计算和多任务处理的最佳软件之一。有了 Mathematica,人们基本上可以执行所有的科学计算,包括机器学习(ML)。
Mathematica 最强的一点是符号计算,在这方面,没有软件可以与 Mathematica 竞争。另一方面,关于 ML 领域,有几种软件可以执行出色的计算,如 Python、R、Java、Matlab 和 Maple。
Mathematica 的早期版本有效执行 ML 的能力非常有限,但是随着时间的推移,它已经集成了许多不同的 ML 功能,这些功能易于使用短代码行。最后两个版本,即 Mathematica 12 和 13 有许多内置的 ML 函数,可以执行几乎所有需要的 ML 计算。
在本文中,我想展示如何使用 Mathematica 执行 ML,并了解它的主要优点和缺点。我假设读者至少知道如何使用 Mathematica,如果你不知道如何使用它,我建议学习它,因为在我看来,由于许多内置的函数和方法,它非常容易学习。在我的脚本中,我将使用 Mathematica 13 。
回归
要使用 Mathematica 执行 ML,首先需要一个数据集,使用 Mathematica 可以创建或导入它,或者加载任何现有的默认数据集。
Mathematica 有许多著名的默认数据集,用于许多经典的 ML 示例中。其中之一就是 W 不等式 dataset[1,2]。这个数据集是众所周知的,人们可以在 ICS ML 数据集上找到它,并直接下载为 CSV 文件。
第一件事是以包含行和列的表格的形式查看数据集。这将允许我们看到什么是特性和它们的价值。一旦在计算机上下载了 CSV 文件,就可以运行下面的 Mathematica 代码:
**Code 1:****[In]**: PATH = "path of your CSV file on your computer"
**[In]**: dataset = **SemanticImport**[PATH]
**[Out]**:
图一。对于红酒类型,输出包含 1596 条记录的数据库表。
代码 1 中的第一个输入命令是您计算机上的 CSV 文件路径。第二个输入命令是使用 SemanticImport[] 函数将 CVS 文件转换成 Mathematica 数据集,并以行列表格的形式显示数据集。每列的功能名称也清晰可见。
Mathematica 的一个重要特性是,在大多数情况下,不需要像 R 和 Python 那样导入任何模块或库。Mathematica 有许多内置函数,你只需要知道它们是做什么的,然后直接调用它们。
葡萄酒质量数据集包括不同葡萄酒的几种化学特性,如 PH 值、酒精含量、葡萄酒质量等。对于这种类型的数据集,人们常常试图使用回归或分类来从其他特征中预测葡萄酒的质量。在这种情况下,可以将葡萄酒质量视为输出(目标)变量( y =葡萄酒质量),特征矩阵 X 由剩余的特征组成。葡萄酒质量通常在区间[1,10]内评分,其中 10 是一个葡萄酒品牌的最大质量值。
Mathematica 对于 ML 最重要的功能之一是预测[…] 功能。该函数可用于不同的数据类型,如数字、分类、文本、图像、声音等。正如这个函数的名字所说,Predict[…]的目标是在给定一组不同数据的情况下,用 ML 进行预测。Mathematica 在输入数据类型和预测方法方面非常灵活。详情可以在 Mathematica 网页上找到。
Mathematica Predict[…] 功能灵活性的重要方面是不同的选项,用户可以从中受益。这些选项包括:
- 法 。此选项指定要使用的回归算法。可能的回归算法有线性回归、决策树、随机森林、最近邻等。
- 绩效目标 *。*该选项允许用户使用不同类型的 ML 性能,如算法计算的“质量”和“速度”。“质量”优化最终结果的质量,而“速度”优化获得最终结果的速度。
- ValidationSet。 该选项允许用户在训练过程中选择验证数据集。可以直接指定验证集或使用自动默认选项。
- **。该选项设置计算期间内部使用的随机伪生成器的种子。
现在是时候给出一个具体的例子,说明回归实际上是如何与 Mathematica 一起工作的。作为数据集,我使用白葡萄酒类型的葡萄酒质量数据集。这个数据集实际上默认包含在 ExampleData[…] 函数中。为了提取数据集,我运行以下代码:
***Code 2:****[In]**: trainset = ExampleData[{"MachineLearning", "WineQuality"}, "Data"];*
代码 2 默认提取白葡萄酒*数据集,并且不显示输出,因为我在它的末尾添加了分号。在代码 2 中,“trainset”只是一个 Mathematica 变量。ExampleData 函数的一般语法是 **ExampleData[" type "," name "}," Properties"],*其中" type "指定特定类别的默认数据集类型, name 是从" type "类别中选择的数据集名称。
另一方面,“属性”是一个给出所选数据集属性的选项。例如,以下代码给出了白葡萄酒数据集的一些属性:
***Code 3:****[In]**: ExampleData[{"MachineLearning", "WineQuality"}, "Properties"]**[Out]**:{Data, Description, Data, Dimensions, LearningTask, LongDescription, MissingData, Name, Source, TestData, TrainingData, VariableDescriptions, VariableTypes}**[In]**: ExampleData[{"MachineLearning", "WineQuality"}, "Dimensions"]**[Out]**: <|NumberFeatures -> 11, NumberExamples -> 4898|>**[In]:** ExampleData[{"MachineLearning", "WineQuality"}, "VariableDescriptions"]
**[Out]:** {fixed acidity, volatile acidity, citric acid, residual sugar, chlorides, free sulfur dioxide,
total sulfur dioxide, density, pH, sulphates, alcohol} → wine quality (score between 1-10)*
在代码 3 中,在第一部分可以看到 WineQuality 数据集的一些属性,如 TrainingData、TestData 等。在第二部分,我们可以看到数据集的“维度”,它有 11 个特征和 4898 个例子。在第三部分中,可以看到变量描述选项,而输出显示数据集的特征名称和目标变量(葡萄酒质量)。
现在让我更详细地介绍一下白葡萄酒数据集的回归问题,并做一些真正的预测。我运行以下代码:
***Code 4:****[In]:** trainset = ExampleData[{"MachineLearning", "WineQuality"}, "TrainingData"];
**[In]:** predictor = Predict[trainset, Method -> "LinearRegression", PerformanceGoal -> "Quality"]
**[Out]:***
在代码 4 中,第一个输入从 ML 类别的数据集提取 WineQuality 数据集,“TrainingData”属性自动选择 WineQuality 数据集的一部分作为训练数据。在我定义了一个新的变量 predictor 之后,这个变量在训练过程中被赋予了学习到的函数。对于这个特殊的例子,我选择了方法选项“线性回归”。人们也可以自由选择其他方法。另一个选项是我上面描述的“绩效目标”。最后,作为输出,Mathematica 给出了“预测函数[…]”,这是在训练集中学习到的函数。
要获得关于学习的预测函数的更多信息,需要运行以下代码:
***Code 5:****[In]**: Information[predictor]
**[Out]:***
既然我已经展示了如何使用 Mathematica 进行培训,那么让我向您展示如何在验证数据集上评估我们的模型的性能。在这种情况下,需要运行以下代码:
***Code 6:****[In]:** validationset = ExampleData[{"MachineLearning", "WineQuality"}, "TestData"];
**[In]:** PredictorMeasurements[predictor, validationset, {"StandardDeviation", "RSquared"}]**[Out]:** {0.726808, 0.177385}*
现在让我解释一下代码 6 中的步骤。第一个输入从 MachineLearning Mathematica 存储库中提取 WineQuality 数据集,它自动为验证集(或测试集)选择一部分数据。第二个输入使用 PredictorMeasurements 函数来估计验证集残差的标准偏差。它使用在代码 4 中通过选项“标准偏差”和“RSquared”在验证数据集上学习的预测函数作为参数。Mathematica 的前一个选项对应于残差的 RMS。作为最终输出,残差的 RMS 约为 0.73,而确定系数为 R = 0.17。
此外,Mathematica 使得绘制与训练和测试性能相关的结果变得非常容易。例如,如果要创建残差图,运行以下代码就足够了:
***Code 7:****[In]:** PredictorMeasurements[predictor, validationset, "ResidualPlot"]
**[Out]:***
图二。用代码 7 获得的残差图。
分类
现在我向你展示如何使用 Mathematica 解决分类问题。类似于回归问题的预测[…]函数,分类问题的相应函数是分类[…]。 It 可用于多种类型的数据,包括数字、文本、声音和图像,以及这些数据的组合。
分类[…] 与上述预测[…]功能共享多个选项和方法。要看 Classify[…]是如何工作的,最好举一个具体的例子。假设我们有以下特征{1,2,3.5,4,5}和以下对应的类{“A”,“A”,“A”,“B”,“B”}。现在我用下面的代码训练分类器:
***Code 8:****[In]:** class = Classify[{1, 2, 3.5, 4, 5} -> {"A", "A", "A", "B", "B"}, Method -> "LogisticRegression"]
**[Out]:***
在代码 8 中,我使用 Classify[...]来训练具有上述特征和类的模型,结果是 classifier function[...],这是在训练步骤中学习的分类函数。我用“逻辑回归”作为训练方法。类是代码 8 中的一个变量。
要获得有关训练步骤的更多信息,只需运行以下代码:
***Code 9:****[In]:** Information[class]
**[Out]:***
为了在一个验证数据集{ 1.4-->“A”,5.3-->“B”,1.2-->“B”,4.6-->“B”,-3-->“A”}上用 Mathematica 符号测试已学习的分类函数,我运行以下代码:
***Code 10:****[In]:** validationset = {1.4 -> “A”, 5.3 -> “B”, 1.2 -> “B”, 4.6 -> “B”, -3 -> “A”}
**[In]:** CM = ClassifierMeasurements[class, validationset]
**[Out]:***
从代码 10 的输出可以看出,应用于验证数据集的分类器给出了 0.8 的准确度,这是非常好的。输出还提供了额外的测量信息。
如果想了解模型性能的更多信息,可以运行下面的代码并获得混淆矩阵:
***Code 11:****[In]:** CM["ConfusionMatrixPlot"]
**[Out]:***
图 3。分类混淆矩阵。
结论
在本文中,我向您展示了如何使用 Mathematica 来执行 ML。尽管我展示了如何使用 Mathematica 进行回归和分类,但它也可以用于与深度学习和神经网络相关的其他目的。
在我看来,使 Mathematica 成为 ML 最佳软件之一的一个关键特性是它的简洁和最少的代码编写。的确,你可以看到在 Mathematica 中,大多数情况下不需要导入任何库。这是因为 Mathematica 有许多内置函数,简化了用户与软件的交互,减少了代码编写的时间。
Mathematica 的另一个重要方面是,很容易用很少的线条绘制 EDA 并构建流程管道。我在这篇文章中没有展示这一点,你可以通过 Mathematica 实验来验证这一点。
参考资料:
[1] P .科尔特斯、a .塞尔代拉、f .阿尔梅达、t .马托斯和 j .赖斯。通过从理化特性中进行数据挖掘来建立葡萄酒偏好模型。在决策支持系统中,爱思唯尔,47(4):547–553,2009。
[2]Dua d .和 Graff c .(2019 年)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。
如果你喜欢我的文章,请与你可能对这个话题感兴趣的朋友分享,并在你的研究中引用/参考我的文章。不要忘记订阅将来会发布的其他相关主题。
基于现代数据堆栈的机器学习:案例研究
原文:https://towardsdatascience.com/machine-learning-with-the-modern-data-stack-a-case-study-50f0c2e1fbb
在 Unsplash 上由 Hitesh Choudhary 拍摄的照片
关于现代数据栈 (MDS)已经说了很多,但情况明显更分散在机器学习这一边:一旦数据被适当地转换,它如何在下游被消费以产生商业价值?
这篇文章是为那些想在处理数据和使用机器学习实际交付商业价值之间架起一座桥梁的人而写的。
TL;博士
- 在采用现代数据堆栈(MDS)后,各组织仍处于成为“数据驱动型”的早期阶段,MDS 需要与 MLOps 和实际数据驱动型软件相结合才能取得成功;
- 您将了解在一个不断发展的组织中处理数据所面临的挑战以及如何克服这些挑战,包括端到端代码展示如何将 DataOps 和 MLOps 与最先进的开源工具相结合,如 Metaflow 和 dbt
- 一个关键点是操作的抽象级别:从数据到服务,整个管道不需要任何特殊的 DevOps 人员、基础设施工作或 yaml 文件。SQL 和 Python 是存储库中仅有的语言。
- 在后续的帖子中,我们将展示好的工具如何提供一种更好的方式来思考工作和生产力的划分,从而为经理和数据领导者提供一个组织模板。
从现代数据堆栈到 MLOps
现代数据堆栈(MDS)已被整合为围绕数据收集、存储和转换的一系列最佳实践。特别是,MDS 包括三大支柱:
- 可扩展的摄取机制,通过工具(例如 Fivetran、Airbyte)或基础设施;
- 将所有数据源存储在一起的数据仓库(例如雪花);
- 一种转换工具(如 dbt),确保使用 SQL 对原始数据进行版本化的 DAG 式操作。
关于这种 MDS 已经说了很多,但是栅栏另一边的情况更加“分散”:一旦数据被适当地转换,下游如何消费这些数据以产生商业价值?归根结底,吸收和转换数据(对大多数公司来说)本身并不是目的:虽然科技巨头们不久前就想出了如何“将模型投入生产”,但大多数公司仍在努力在不到 3 个月的时间内将模型投入生产。
我们采取的方法是接受这样一个事实,即不是每个公司都需要像 Googles 和 Metas 部署的那样复杂和无限可扩展的基础设施,而这完全没问题 : 以“合理的规模”进行 ML 比以往任何时候都更有价值和有效,这要归功于供应商和开源解决方案的伟大生态系统。在的配套知识库中,我们使用深度学习管道来训练一个用于顺序推荐的模型:如果你看到一个购物者正在与 k 产品进行交互,她接下来会做什么?我们…
- 有目的地避免玩具数据集和仅本地部署;
- 提供一个云就绪、“合理规模”的项目;
- 展示培训、测试和服务( MLOps 步骤)如何自然地嵌入到以数据和功能(数据操作步骤)开始的管道中。
这项工作的主干由 Metaflow 提供,这是我们的开源框架,它(除其他外)降低了数据科学家从原型到生产的机器学习的门槛,一般堆栈看起来是这样的,尽管 Metaflow 允许您切换任何其他组件:
作者图片
在这篇文章和后续文章中,我们讲述了我们在 Coveo 任职期间工具和文化如何一起改变的故事,并分享了在工作代码中体现我们数据协作原则的开源存储库:根据我们的经验,DataOps 和 MLOps 在相同的原则下做得更好,而不是将工件“移交”给围栏另一边的团队。特别是,我们的目标是双重的:
- 基于我们的经验,我们为刚起步的团队提供了一个技术模板,并想知道如何有效地加入 DataOps 和 MLOps
- 我们展示了好的工具如何提供一种更好的方式来思考工作和生产力的分工,从而为经理和数据领导者提供一个组织模板。我们将在后续文章中讨论这个问题。
总而言之,虽然我们对空间的增长感到兴奋,但我们也知道可供选择的数量可能会让和感到害怕,因为这个领域仍然是混乱的,并且在快速发展。我们在此提出的解决方案是一个实用的堆栈,它不仅为测试 MLOps waters 的团队提供了可靠的第一步,还详细展示了简单的部分如何在构建可扩展的 ML 系统方面发挥重要作用。
你可以在这里找到指导这篇文章的视频教程:
利用现代数据堆栈进行机器学习
这个堆栈如何将良好的数据文化转化为大规模的工作软件?隔离(和降低)复杂性的一个有用的方法是通过理解计算发生的地方。在我们的管道和我们在组织间看到的模式中,有几个计算步骤和多个提供商:
- 数据在雪花中存储和转换,雪花为 SQL 提供底层计算,包括由 dbt 这样的工具管理的数据转换;
- 训练在 AWS Batch 上进行,利用 Metaflow 提供的抽象;
- 服务在 SageMaker 上,利用 AWS 的 PaaS 产品;
- 调度基于 AWS 步骤函数,再次利用元流(未在回购中显示,但可以直接实现)。
设计中的一个关键点是我们选择操作的抽象层次:整个管道不需要任何特殊的 DevOps 人员、基础设施工作或 yaml 文件。SQL 和 Python 是存储库中仅有的语言:基础设施要么是不可见的(如 Snowflake 透明地运行我们的 dbt 查询),要么是声明性的(例如,您指定您需要的计算类型,如 GPU,Metaflow 使它发生)。
虽然您可能对 DataOps 部分比较熟悉,但还是有必要对 MLOps 部分做一个高层次的概述。请记住:转换和规范化数据本身很少是目的——只有当你从中获得一些东西,从可视化中获得洞察力或从机器学习模型中获得预测时,数据才有价值。
我们的堆栈利用由 dbt 构建的 SQL DAG ,为我们的推荐模型运行从原始购物事件到产品交互序列的必要转换,通过使用 SQL,我们实现了两个重要目标:
- 首先,ML 团队可以在一个统一的环境中直接在分析工程师下班后工作(可以说 SQL 是数据的通用语);
- 第二个,我们将所有(分布式)计算下推到雪花,使 Python 方面的事情变得相当轻量级,因为我们不会真正受到机器内存的限制来聚合和过滤数据行。
这段代码显示,由于数据已经被转换,所以只需要一个简单的查询就可以让数据集为下游建模做好准备
培训部分本身是一个典型的深度模型用于会议建议,没有任何细节依赖于它:同样的原则适用于更复杂的模型。令人兴奋的是,一个简单的 SQL 查询,易于阅读和维护,是连接云中 GPU 上的功能准备和深度学习训练所需要的一切。训练一个模型产生一个工件(也就是一个现成的模型!),它现在可以生成预测:由于在将模型部署到实际交通之前,在看不见的数据上测试模型是一个好的做法,我们在 test_model 中展示了一种先进技术的草案,行为测试。
最后,管道以部署结束,也就是说,将通过培训产生并通过测试验证的工件运送到像任何其他 API 一样可以到达的公共端点的过程;根据我们训练的模型,通过提供购物会话,端点将以最可能的延续进行响应。
对于数据科学家、经验丰富的 ML 工程师、刚刚开始使用 Python 的分析工程师,甚至监控项目的 PM 来说,这是一个舒适的堆栈:鼓励协作和有原则的分工。虽然我们提出了一个 RecSys 用例,但上述考虑因素大部分(如果不是全部的话)适用于许多其他行业的类似管道:毕竟,你不需要更大的船来做尖端的 ML。
这个堆栈还可以在越来越复杂的配置中运行,这取决于您想要包含多少工具/功能:即使在“完全复杂”的情况下,它也是一个非常简单的、无需干预的万亿字节级处理堆栈;此外,一切都是相当解耦的,所以如果你想用塞尔顿交换 SageMaker,或者用权重&偏差交换 Comet,你可以轻而易举地做到。
整个堆栈包含到实验跟踪平台的现成连接和一个定制 DAG 卡(的存根),根据雅格布团队的行为测试开源框架 RecList 显示推荐系统的表现。我们可以就此写一篇文章(实际上,我们确实写了,加上两篇学术论文):即使作为一个非常小的测试例程,我们也希望确保将它作为一个通用的提示,提醒人们在部署之前理解模型行为是多么重要,以及可读、可共享、低接触的文档对于生产中的 ML 管道是多么重要。
这个堆栈很简单,但是很有效,而且是模块化的:它可能不是旅程的终点,但是它肯定是一个很好的开始。
在后续的帖子中,我们将展示好的工具如何提供一种更好的方式来思考工作和生产力的划分,从而为经理和数据领导者提供一个组织模板。如果您对这些话题感兴趣,请在我们的社区 slack here 上与我们聊天。
注:
这篇博文是 Metaflow 的长期用户 Coveo 和 Outerbounds 的合作。在外部,我们已经与 Coveo 合作过几次,合作项目包括元流卡和最近关于合理规模机器学习的炉边谈话——你不是谷歌,这完全没问题。在这篇文章中,我们与来自 Coveo 的 Jacopo 坐在一起,思考他们如何使用 Metaflow 将 DataOps 与 MLOps 连接起来,以回答这个问题:一旦数据被适当地转换,它如何在下游被消费以产生商业价值?
致谢
特别鸣谢:dbt Labs 的 Sung Won Chung、Coveo 的 Patrick John Chia、Luca Bigon、Andrea Polonioli 和 Ciro Greco。如果没有 Coveo 去年发布的神奇数据集,这一切都是不可能的,该数据集包含了数百万真实世界的匿名购物事件。
如果您喜欢这篇文章,请花点时间支持我们的开源计划,为我们的 RecList 包、 this repo 和 Metaflow 添加一颗星。
原载于 2022 年 7 月 28 日 https://outerbounds.com**T21。
使用 Cometinho 进行机器翻译评估
原文:https://towardsdatascience.com/machine-translation-evaluation-with-cometinho-c89880731409
着眼于性能,减少模型大小、节省计算时间和金钱的实用建议
什洛莫·沙莱夫在 Unsplash 上拍摄的照片
欧洲机器翻译协会(EAMT)会议是机器翻译研究人员、用户和翻译人员聚集在一起讨论行业最新进展的场所。去那里看看欧洲大陆在机器翻译开发和采用方面的进展真的很有意思。在这篇文章中,我想分享今年最佳论文奖的一些想法。它的标题是“搜索 COMETINHO:可能的小度量”,来自葡萄牙里斯本 Unbabel 公司的研究实验室,该公司提供使用机器翻译和人工翻译的翻译服务。你可以在 ACL 选集里找到这篇论文的在线版本。
本文的目的是展示如何在实际场景中使用文献中的几种技术来加速大型模型的执行并节省计算量,同时降低翻译质量的损失。这篇文章的重点是简化机器翻译(MT)自动评估的模型 Comet,但是其中描述的许多点都很通用,足以让任何处理大型和缓慢模型的人感兴趣,因为金钱或时间的原因需要节省计算。
彗星是什么?
最初在这篇论文中描述的 Comet 是基于 XML-R 的机器翻译评估模型。大型语言模型用于分别对源、假设和参考句子进行编码,以便将它们中的每一个简化为单个向量。然后,这些向量被组合以在输出中产生代表假设质量分数的单个实数。
彗星拓扑,有一种比萨斜塔的感觉。图片作者。
在 WMT 的 metrics 共享任务中,Comet 因与人类判断的相关性而排名靠前。然而,它是一个具有 500M 参数的相当大的模型,并且运行缓慢。当我们需要经常运行它时,这尤其是一个问题,例如以最小的风险解码或训练。
代码加速
Cometinho 论文中获得的第一对加速是关于纯代码修改的。它们是,长度排序和缓存。
深度学习模型以小批量运行,以更好地利用 GPU 并行化能力。对于文本和其他可变长度的输入,小批量将具有其中最长序列的长度。其他序列用零填充,为填充位置执行的所有计算基本上都被浪费了。一种有效且众所周知的减少计算量的方法是在开始解码过程之前,根据句子的长度对句子进行排序。长度排序总是能提高速度,而且实现起来又快又容易。因此,当以批处理模式执行推理时,应该总是使用,并且所有最著名的深度学习工具都实现了它。
Comet 编码其三元组的方式使缓存成为可能:源、假设和引用被编码成三个独立的向量。这样,就有可能存储代表一个句子的向量,并在它再次出现时检索它。你是问多久发生一次吗?假设您正在使用 Comet 来比较两个不同的 MT 模型。两个模型通过完全相同的测试集运行,因此所有的源句子和参考句子至少出现**两次。**另一个例子是,当 Comet 用于最小贝叶斯风险解码时,为相同的源句子计算许多假设,并且再次节省了大量资源。这个想法是,如果你要为一个速度很重要的任务设计一个深度学习模型,在可能的情况下,尝试以一种可以利用缓存来节省重要资源的方式来设计它。
长度排序和缓存的结合在对一个系统评分时带来了高达 39%的速度提升,在对 8 个系统评分时带来了高达 65%的速度提升,因此缓存更加有效。
层修剪
接下来是实际缩小模型的部分,本文用两种方法进行了实验。第一种是从模型中物理移除整个层,第二种是减少一些子层的大小。
在第一种情况下,一个层被认为是一个完整的变换器层,包括自关注、前馈、剩余连接和层归一化。
在本文的情况下,很容易从编码器模型的顶部删除层,因为池层不直接使用最后一个编码器层的输出。相反,他们使用相同的方法来生成带有 Elmo 的嵌入向量,并对各层的输出进行加权平均。权重都是正的,并归一化为 1.0,在训练期间学习,之后保持不变。对权重的快速分析表明,最顶层的权重最低,因此这些层的贡献最少*。然后作者决定只移除它们,这在推断过程中不会造成伤害,因为编码器输出总是许多层之间的平均值。结果显示,删除最上面的 4 层时,性能略有提高,删除 5 层时,性能没有下降。考虑到该模型由 25 层组成,这是 20%的免费参数缩减(排除仍然代表参数计数的大部分但不影响推断那么多的嵌入权重)。
第二种方法是从每个自我关注层中去掉两个头(总共 16 个头)。此外,它们还将前馈大小从 4096 减小到 3072(原始大小的 75%)。由此产生的模型称为 Prune-Comet,其结果与 Comet 相当,但推理时间加快了 37%。
使用 [TextPruner](https://textpruner.readthedocs.io/en/ latest) 执行修剪,这是一个开源工具,为许多预训练的变压器实现层修剪。如果这是你的任务,就查一下。
*权重成比例地降低向量的大小,但这只是故事的一部分。Kobayashi 和他的同事的这篇论文表明,矢量的原始大小也必须被考虑。这也很直观,因为外部权重也可以重新平衡不平衡的向量。
知识蒸馏
虽然层修剪有助于以较小的退化减少参数计数,但是模型仍然太大而不能有效地使用。事实上,它的大尺寸极大地影响了计算能力和时间,特别是对于需要重复 MT 评估计算的应用,例如最小贝叶斯风险解码。为了实现显著的参数缩减,需要知识提炼。
通过知识提炼,使用原始模型的输出作为学习目标来训练一个新的更小的模型。对于这个特定的任务,作者使用 OPUS 语料库的 15 个语言对,用两个不同质量的机器翻译模型翻译其 2500 万个句子对,并使用 5 个 Comet 模型的集合来评估翻译。由于这些是没有经过人工评估的机器翻译训练数据,所以没有用来训练 Comet。
学生模型保持了与教师模型相同的深度学习架构,但它要小得多:12 层,12 个注意头,前馈大小为 1576,嵌入向量大小为 384。它的参数计数是 119M 参数,而不是原来的 582M。
最终结果显示,模型大小减少约 80%,质量下降 10%,速度提高 53%。确实需要新的训练,但是在实际的应用场景中,训练只做一次,而推理要做上千次甚至更多。
结论
Cometinho 的论文具有实际意义,因为它展示了如何应用模型简化技术,这些技术可以很容易地转移到不同的应用领域。
你应该带什么回家?首先,有许多方法可以在性能损失很小甚至没有损失的情况下节省计算,并且它们可以在不同的域之间重用。尽可能使用它们。
第二,从某种意义上来说,模型应该被设计成可缩减的。在本文的示例中,Elmo 类层平均值允许层修剪。
第三,顺便说一下,如果他们也显示从零开始训练基线模型到缩小尺寸的结果,而不是使用这里提出的技术,这篇论文会更完整。证明减少现有模型比训练更小的模型更好总是更好的。
如果你对这个话题感兴趣,你的下一步可以是研究量化作为进一步的技术,以减少模型的大小和提高推理时间。
机器遗忘:遗忘的职责
原文:https://towardsdatascience.com/machine-unlearning-the-duty-of-forgetting-3666e5b9f6e5
意见
从人工智能模型中删除数据点信息的重要性
图片由来自 unsplash.com的罗马法师拍摄
什么是被遗忘的权利?什么是机器去学习?对隐私的新关注导致了新的法规。机器学习应该能够消除信息,但这是一项具有挑战性的任务。为什么?我们能做什么?
电子象的记忆
图片由作者用OpenAIDALL-E 生成
2020 年互联网上的数据量是 64 zettabytes (其中一个 zettabyte 就是一万亿千兆字节)。此外,Instagram 上有超过 400 亿张图片,每天有 3.4 亿条推文,脸书上有无数帖子等等。我们分享了很多数据,但仅仅是浏览互联网,我们也会留下很多痕迹。
近年来,全球对隐私和数据保护的兴趣日益增长。用户已经意识到我们通过使用无数的应用程序或访问无数的网站分享了多少数据。另一方面,用户已经意识到这些数据被收集、使用和出售。像《剑桥分析》这样的丑闻增加了人们对我们在线共享数据价值的认识。
定向广告在社交网络和谷歌搜索中的显示方式也证明了数据分析的有效性。算法能够如此好地描述我们的事实让我们想知道这些数据被卖给了谁。事实上,利用数据中的信息可以让公司赚取数十亿美元。
无论我们如何改变,我们的思想如何改变,世界如何改变,但数据永远留在互联网上。
被遗忘权
图片来自廷杰伤害律师事务所上【Unsplash.com】T2
被遗忘权被定义为“在某些情况下,一个人的私人信息被从互联网搜索和其他目录中删除的权利”(维基百科)。然而,对于这一定义,或者是否考虑将这一定义列入人权清单,还没有达成一致意见。另一方面,一些机构和政府正着手讨论并提出监管建议(阿根廷、欧盟、菲律宾)。
这种被遗忘权的概念是基于个人以自主方式决定其生活发展的基本需要,而不会因为过去所做的具体行为而永久或周期性地蒙受耻辱,特别是当这些事件发生在许多年前并且与当代背景没有任何关系时——欧盟提案
事实上,过去的信息和事件甚至在多年后仍然会造成耻辱和后果。举个简单的例子,詹姆斯·古恩在他的攻击性推文再次出现后,被迪士尼从《银河护卫队 3》中解雇。他在 2018 年被解雇,因为他在 2008 年至 2011 年间写了推文。
“我将近十年前的话在当时是完全失败和不幸的挑衅努力。从那以后,我后悔了很多年——不仅仅是因为它们愚蠢、一点也不好笑、麻木不仁,当然也不像我希望的那样具有煽动性,还因为它们没有反映出今天的我或者一段时间以来的我。”—詹姆斯·古恩声明
当然,你可以删除你在脸书和 Instagram 上发的推文或帖子。但是,删除网上分享的东西就没那么容易了。例如,脸书推出了一款名为“脸书外活动”的工具,用户可以删除第三方应用和网站与脸书分享的数据。然而,事实证明脸书只是将数据与用户分离。
2014 年,西班牙法院裁定一名要求从谷歌搜索中删除某些信息的男子胜诉。这个男人在 1998 年不得不卖掉一处房产来偿还社会保险的债务。谷歌拒绝了,但法院和欧盟法院都裁定谷歌需要删除搜索结果。法院裁定,由于债务是在很久以前支付的,搜索结果“根据过去的时间来看,似乎是不充分、不相关或不再相关或过多的。”
谷歌的一个视频讲述了被遗忘的权利、规则以及如何请求删除内容
被遗忘的权利在许多情况下被视为一种必要,防止一个人在搜索引擎中被索引为复仇色情,过去犯下的轻微罪行,未偿还的债务等等。然而,批评这项权利的人说,这项立法被视为对批评权和言论自由的攻击。欧盟试图在隐私权与批评和言论自由之间寻求平衡。
机器学习被认为能够通过收集和分析所有这些数据(从电子邮件到医疗数据)来永久保存这些信息,从而加剧这个问题。此外,在保险、医疗和贷款申请模型中使用这些信息会导致明显的伤害和放大偏见。
如何教会机器遗忘
图片来自罗伯特·林德在Unsplash.com
机器遗忘是人工智能的一个新兴领域,目标是在不影响性能的情况下,从模型中删除选定数据点的所有痕迹(选择性遗忘)。机器遗忘有不同的应用:从授予被遗忘的权利到避免人工智能模型可能泄露敏感信息。此外,机器学习将有助于对抗数据中毒和敌对攻击。
公司花费数百万美元来训练和部署大型人工智能模型,他们不愿意重新训练或移除这些模型。然而,欧盟和美国监管机构警告称,根据敏感数据训练的模型可能会被强制删除。英国政府在一份专注于人工智能框架的报告中解释说,机器学习模型可能会在 GDPR 下遭到数据删除。例如,Paravision 不恰当地收集了数百万张面部照片,美国美国联邦贸易委员会迫使删除了数据和训练模型。
然而,正如该领域的一篇开创性论文所强调的那样,机器遗忘并不是一件容易的事情:
- 我们对数据点如何影响模型的了解有限。对于大型神经网络来说,这尤其困难,因为我们有许多不同的层,一个数据点可以影响许多参数。
- 训练中的随机性。在神经网络期间,我们使用随机选择的小批量数据,并且顺序随着时期而变化,因此在训练期间很难重建数据流。
- 培训是渐进式的。如果模型因训练数据点的存在而更新,则所有后续的模型更新都以某种隐含的方式依赖于该数据点。
- 学习中的随机性。将数据点与从中得出的假设相关联是一项挑战
最简单的方法是从训练数据中删除数据点,然后重新训练模型。然而,这显然是昂贵的。例如,OpenAI 估计花费了 1000 到 2000 万美元来训练 T2 GPT 3 号。因此,我们需要更好更便宜的替代品。
其中最著名的是由多伦多大学的研究人员在 2019 年提出的 SISA 方法。分片、隔离、切片和聚合(SISA)方法建议将数据分成多个部分进行处理。其思想是,如果有数据点要删除,只需重新处理一小部分输入数据。简而言之,数据集被分成不同的碎片,并在训练期间逐渐呈现给模型。在添加另一个碎片之前保存参数,从而允许刚好在要被忽略的点被使用之前开始重新训练。
SISA 方法:代替重新训练模型,你只需要重新处理部分输入。图片来自原始文件(此处)
然而,这种方法并非没有缺陷:它只能忘记一定数量的数据点,如果不是以特定的顺序询问的话。因此,2021 年发表了一篇文章,旨在解决这些问题。他们声称他们的方法可以删除更多的数据点。
另一个有前途的方法是差别隐私,公司只收集和分享关于用户习惯的汇总信息,维护个人隐私。微软、谷歌和苹果都在投资这项技术,但它仍未得到广泛应用。
虽然这个主题相对较新,但已经发表了几篇文章,将来还会有所增长。
离别的思念
被遗忘的权利就是拥有不完美过去的权利——苏珊·摩尔
2022 年,被遗忘的权利被几项裁决重申(义大利、阿根延、印度)。一般来说,GDPR 规定,如果被要求,公司必须删除用户数据。目前,现行判例法要求对每一项请求进行个案分析。但是,近年来,机构越来越关注隐私和人工智能。
未来几年很可能会通过更多的新法规。加州在 2020 年颁布了一项被遗忘权法律(加州消费者隐私法案),北卡罗来纳州也在朝着相同的方向,在联邦层面也有讨论。此外,欧盟正在讨论监管人工智能的其他方面,如上所述,人工智能模型可能会受到被遗忘权的影响。
另一方面,我们需要平衡隐私和表达权,防止被遗忘权被用作一种审查形式。此外,区块链等新技术也带来了有待解决的新挑战。
此外,如今人们对这个话题更加敏感。这种观点已经被许多公司所接受,其中许多公司正在采取措施增加用户隐私。例如,最近,谷歌宣布了一项针对美国公民的扩展政策,从搜索结果中删除个人数据(电子邮件地址和物理地址、手写签名、未经同意的明确或私密的个人图像等等)。
如前所述,我们需要找到一种方法,如果数据被消除,已经训练的人工智能模型就会被从这些数据点提取的信息清理掉。机器去学习并不是一个简单的挑战,但是一些方法已经被测试过,其他的正在开发中。总之,机器去学习虽然是一个相对较新的领域,但它是一个不断发展的领域,随着规则的增加,它将变得非常有用。
如果你从事机器学习或者对人工智能伦理感兴趣,我对你的观点感兴趣。
如果你觉得有趣:
你可以寻找我的其他文章,你也可以 订阅 在我发表文章时得到通知,你也可以在**LinkedIn**上连接或联系我。感谢您的支持!
这是我的 GitHub 知识库的链接,我计划在这里收集代码和许多与机器学习、人工智能等相关的资源。
**https://github.com/SalvatoreRa/tutorial
或者随意查看我在 Medium 上的其他文章:
附加资源
- 关于被遗忘权的附加信息:此处、此处、此处
- 一个 GitHub 知识库,提供了大量关于机器遗忘的文章:这里
- 一个关于机器遗忘的研讨会:这里
- 关于差分隐私:这里**
机器没有同理心,数据科学家应该有
原文:https://towardsdatascience.com/machines-do-not-have-empathy-data-scientists-should-69b38a37ee45
同理心是许多数据科学家忽视的解决问题的重要步骤。ML 民主化可能没有帮助!
之前我写了一篇关于UX 研究和数据科学如何拥有非常相似的工作流程并可以互补以帮助解决问题和做出明智的决策的文章。在这篇文章中,我想重点介绍 UX 研究过程中的一个重要步骤(也称为设计思维),这个步骤在数据科学解决问题的过程中被忽略了:同理心!
什么是移情?为什么重要?以及如何在数据科学设计过程中应用?
https://unsplash.com/photos/IMfgE5lyMrU
什么是共情?
移情是设计思维的第一步,它帮助研究者理解他人的感受、问题和处境。
移情被认为是设计思维的第一步,研究者可以通过感受用户的情绪来更好地理解问题。
为什么共情很重要?
缺乏同理心意味着你没有考虑到与你不相似的其他群体的感受。这导致了各种各样的偏差,包括选择偏差、刻板印象偏差、权威偏差、确认偏差、支持创新偏差、聚合偏差、评估偏差,以及其他各种各样的隐性和显性偏差。
作为一名数据科学家,您的偏见将影响您的 ML 模型设计,并导致有偏见的模型。一个非常著名的例子是选举结果预测,以及数据样本或模型有偏差的不同方式。例如,一些特朗普的支持者如何感到害羞,这误导了预测模型(见此处和此处)。
这些是数据科学家忽视同理心的一些症状:
- 你没有付出足够的努力来发现设计中的偏差
- 或者你知道模型或数据的偏差,但是忽略或隐藏它们
- 或者没有计划去纠正它们。
惊喜!惊喜!模特总是有偏见的!如果你想把同理心带到你的设计过程中,你应该努力理解这些偏见。
所有的模型,即使是大型 AI 公司建立的那些花哨的大模型,都是有偏差的。在我写这篇文章的时候,OpenAI 又一次以一种新的迷人的文本到图像生成模型出现在新闻中。E 2 。但是抛开炒作不谈,OpenAI 的科学家们也让人们意识到了它的偏见:
在一些地方,它过多地代表了几代女性(如提示:“空姐”),而在另一些地方,它过多地代表了几代男性(如提示:“建筑工人”)。(https://github . com/open ai/dalle-2-preview/blob/main/system-card . MD # bias-and-representation)
OpenAI/DALL 的 AI 生成图像截图。E 2 ( 环节)
错误的假设
现在你可能会说,我们希望建立模型来帮助企业发展,只要我们覆盖了大多数,我们就很好:错了!而且不得不说这种无视“长尾”的态度我见得多了。因此,我强烈建议数据科学家读一读卡罗琳·克里亚多·佩雷斯的《看不见的女人》这本书。这本书展示了仅仅是数据上的性别差距就让我们在全球 GDP 中损失了数万亿美元。
作者图片
S o,如果你使用只考虑多数人的指标,如果你不能确保少数人在你的数据中得到适当的反映,你就没有表现出同理心;更糟糕的是,如果你没有意识到你的模型的偏差,你正在花费企业一大笔钱,而你不知道问题在哪里。
在数据科学过程中,在哪里应用共情?
我坚信换位思考是解决问题的一个重要部分。如果您对收集的数据和 ML 模型有很好的了解,您的数据缺乏什么,您选择的模型在哪里表现最好,它在哪里有偏差,以及它对什么情况/用户不起作用,那么您在您的数据科学过程中表现出了同理心。
另一方面,你没有在你的 ML 设计中表现出同理心,如果:
1 你在完全理解问题之前就开始寻找解决方案。
2 您在没有对数据进行大量预先分析的情况下训练模型。
3 你只使用了错误的、非包容性的指标,比如纯粹的准确性。
4 您只通过微调模型的参数来关注改进度量分数,并决定完全基于分数来部署您的模型。
在将模型投入生产之前,你忽略了进行适当的误差分析。
现在,我们都同意在数据科学设计过程中具有同理心的重要性,让我提供几个实际的例子。
示例 1 — AutoML 和 ML 民主化
尽管这在很大程度上是设计师和产品经理的责任,但作为一名数据科学家,你也应该对模型的最终用户感同身受。比如你的模型对用户体验有什么影响?了解整个过程,而不仅仅是关注输入和输出。你的模型预测对用户的感受有多敏感?你的用户只是想从模型中得到一些指导,更愿意自己成为最终的决策者,还是希望模型为他们做决定?他们想要透明吗?他们需要实时结果吗?
相反,AutoML*和类似的工具最近引起了很多人的兴趣,也许它们还会存在一段时间。它们受欢迎的主要原因无非是让每个人的 ML 设计过程更加容易和快速(数据输入,预测输出)。AutoML 等有一个有效的观点:机器学习的民主化。然而,危险在于将人类排除在整个过程之外。
所以,如果你正在使用 AutoML(或者表现得像个 AutoML!)只是为了挑选最准确的模型而不考虑上述重要的用户关注点,你在你的 ML 模型设计过程中没有同理心!
示例 2 —搜索引擎
另一个非常明显的例子是改进搜索引擎,无论是搜索工作列表还是一长串待售商品。了解用户使用当前搜索引擎的原因。这将需要大量的数据挖掘,以了解各种用户行为,然后再开始构建或改进搜索引擎。你甚至可以请你的 UX 团队帮助你组织一些与最终用户的直接面谈。解决这个问题的非同理心方法是只关注使用不同的模型,并根据 CTR、MAP、nDCG 等决定使用哪个模型。
结论
同理心是解决问题的重要一步,但却被许多数据科学家完全或部分忽视了。我发现自己在我们的社区中一遍又一遍地倡导在这个过程中要有同理心,以减轻偏见。使用 AutoML 和其他自动建模,以及自动样本标记,应该谨慎。一个模型得分高不代表它没有偏见。
在 Google Sheets 和 Neo4j 中维护一个配套的植物知识图表
原文:https://towardsdatascience.com/maintain-a-companion-plant-knowledge-graph-in-google-sheets-and-neo4j-4142c0a5065b
将数据存储在表格中,并从图表中获得洞察力
Anna Juchnowicz 的三姐妹同伴种植技术在知识共享署名‐共享相似 4.0 许可下使用( CC BY‐ SA 4.0
尽管有足够 100 亿人吃的食物, 10%的世界人口仍然经常饿着肚子睡觉。气候变化、新冠肺炎和乌克兰战争加剧了粮食危机。虽然养活世界人口(2022 年为 80 亿)已经够困难了,但为未来人口(2050 年为 98 亿)生产和分配足够的食物将更具挑战性,特别是当表土、农业知识和土壤生物多样性的损失继续对我们不利的时候。
面对大规模的粮食危机和气候变化,我们需要改革我们目前不可持续的农业。在水培和水培这几种替代方案中,老友记种植确实值得我们关注。这是一种在附近种植不同植物的农业实践,这些植物通过提供养分、吸引有益昆虫或抑制害虫而相互支持。与单一种植相比,配套种植更高产,也更环保。一个众所周知的例子是三姐妹:西葫芦、玉米和豆类(标题图)。豆类用它们的共生细菌固定氮。南瓜用它宽大的叶子保护土壤免受恶劣天气的影响,用它的毛发驱赶害虫。玉米是豆子攀爬的天然棚架。它们一起可以保护土壤免受侵蚀并提高生产力。
人类已经实践了几千年的同伴种植,并积累了关于这个主题的广泛知识。这些知识被编成表格,比如维基百科上的这个。使用 Google Sheets 等工具可以很容易地修改和维护表格。但是对于像伴生植物数据这样的关系丰富的数据,表格并不是我进行可视化分析的首选。相比之下,网络图(图 1)可以很容易地显示植物之间错综复杂的联系。我们还可以执行图形查询和基于图形的算法来获得对数据的新见解。
图一。伙伴植物和动物在 Neo4j 中可视化为图形。图片作者。
但是像 Neo4j 这样的图形数据库中的数据编辑并不像在表格中那么简单。虽然 graph app**Neo4j Commander将数据编辑带入了 Neo4j 平台,但其用户体验与我们从 Google Sheets 中所能得到的还是相差甚远。我们能否通过一个真实的来源,既能轻松编辑数据,又能轻松绘制图表?**
图一。此项目中的数据流。原始数据首先被接收到 Google Sheets 中。经过处理后,数据由 Jupyter 笔记本转换成 Neo4j 图形。图片作者。
在这篇文章中,我想向你展示我的解决方案。我把维基百科上的伴生植物数据存储在谷歌工作表中。这是我唯一的真理来源。然后,我使用 Neo4j Desktop 创建一个本地知识图。Python Jupyter 笔记本作为桥梁。笔记本从 Google Sheets 下载并格式化数据。然后在 Neo4j 中删除旧数据并导入新数据(图 1)。然后我们将对 Cypher 和 Python 中的知识图进行一些数据挖掘。例如,我将使用 Neo4j 的图形数据科学(GDS)库中的度中心性算法来计算我们可以在马铃薯周围种植的最大的互助植物集。
本文的代码存放在我的 GitHub 存储库中。
**https://github.com/dgg32/companion_plants_2
这个项目中的数据来自维基百科,受知识共享署名共享许可保护。谷歌表单在这里。
https://docs.google.com/spreadsheets/d/1U4K93EeOU6V4SZ9AgI3wOeV4kgdW9TmKSY_pIypDA-A/edit#gid=0
0.准备
首先,在您的 Neo4j 桌面中创建一个名为“companion plants”的 Neo4j 项目。打开其import
文件夹("……"➡️"打开文件夹"➡️"导入)。复制看似随机的数据库字符串(dbms-xxxxxx),在 config.yaml 中设置为neo4j_project_id
的值,另外在config.yaml
中填写其他细节。
此外,您需要在Plugins
选项卡中启用 GDS 库。
1.将配套工厂数据集加载到 Google Sheets 中
我的表格是基于维基百科 上的 伴生植物列表。数据被分成五个表:蔬菜、水果、草药、鲜花和其他。然后,我使用IMPORTHTML
函数将数据提取到五个谷歌表单中,并将它们合并到一个主表中。
图二。IMPORTHTML 函数从 Wikipedia 中检索数据,并填充 Google Sheets 中的表。该图显示了五个原始数据表中的一个。图片作者。
之后,我手动整理数据。我首先纠正了几个错别字,清除了一些类似“几乎所有东西”的模糊术语,添加了许多数据条目,并对同义词进行了规范化(左图 3)。最后,我为分类法构建了一个表(图 3 右侧)。
图 3。Google Sheets 中两个手动管理的表格。左图:伴生植物。右:分类学。图片作者。
为了简单起见,我将 Google Sheets 设置为一般可访问,这样pandas
库就可以在我的脚本中轻松加载数据。而且因为数据在公共领域,所以对于这个项目来说是可以的。但是如果你的项目数据是保密的,你最好在 Google Sheets 中设置一个更严格的访问策略,在你的 Python 脚本中使用gsheets
库。
2.蟒蛇桥
中间件由三个文件组成:一个 Jupyter 笔记本、一个config.yaml
和一个包含所有 Neo4j 命令的文本文件。Config.yaml
包含本地 Neo4j 凭证和其他用户数据。Python Jupyter 笔记本连接了 Google Sheets 和我的 Neo4j 数据库。
来自 Google Sheets 的数据可以用pandas
中的read_csv
函数直接读取(清单 1)。
清单 1。通过 pandas read_csv 从 Google Sheets 加载数据。
然后,脚本遍历表中的单元格,并拆分逗号分隔的内容。它们大多是植物和动物的名字。该脚本将复数形式单数化,解析同义词,并将伞形分类群具体化(“豆科”变成“灌木豆”、“苜蓿”等等)(清单 2)。它还将节点和关系存储在变量中,如nodes_plant
和plant_plant_help
。
清单 2。名称规范化。
之后,脚本将这些变量写入 TSV 文件。例如,下面的代码为工厂节点创建文件。
清单 3。将节点和关系写入 TSV 文件。
然后,该脚本连接到本地 Neo4j 实例,并清除所有约束和旧内容。
清单 4。连接到 Neo4j 并删除数据库。
然后它将所有新生成的 TSV 文件复制到我的项目的import
文件夹中。
清单 5。文件传输到导入文件夹。
最后,脚本执行neo4j_command.txt
文件中的导入命令。这些命令将 TSV 文件导入知识图并创建相应的约束。
清单 6。Neo4j 命令的执行。
清单 7。Neo4j 命令。
知识图有如下简单的模式。它代表了各种植物和动物之间的支持和对抗关系。
图 4。知识图的模式。图片作者。
3.伴生植物知识图
一旦数据加载完毕,我们就可以开始探索知识图了。图中有 162 个植物节点、71 个动物节点和 2182 个关系。在这些关系中,我们观察到 1455 个HELPS
和 404 个AVOIDS
关系。
3.1 哪些植物是西红柿的好伴侣?
如果我们想种西红柿,让我们来看看哪些植物是好伙伴。
清单 8。获取番茄的伴生植物的 Cypher 查询。
图 5。番茄的伴生植物。图片作者。
事实证明,许多植物可以帮助我们种植西红柿。例如,罗勒、琉璃苣、大蒜和金盏花都可以驱除害虫,而细香葱、薄荷和欧芹可以让番茄更健康。但是我们应该避免卷心菜、茴香和核桃树。
3.2 最避免的植物
正如上面的例子所示,有一些对抗性的组合,我们最好避免一起生长。例如,核桃合成了一种叫做胡桃醌的化感化合物,可以抑制番茄的生长。在这里,让我们看看哪些植物是数据集中最避免的。
清单 9。创建最避免的植物列表的 Cypher 查询。
根据维基百科,第一名应该是亚麻。然而,该数据条目已被标记为“需要引用”,我无法在其他任何地方验证该信息。相反,亚麻可以促进西红柿和土豆,因为它可以击退科罗拉多马铃薯甲虫。为了安全起见,我清除了它的回避关系。然后茴香成为第一名。茴香的根分泌抑制甚至杀死许多花园植物的物质。事实上,莳萝是唯一能长在茴香旁边的东西。除此之外,自己种[ 1 ]。
3.3 计算马铃薯的最大无规避支持植物网络
让我们假设我们想在一大片地里种土豆。我们想在那里种植最具生物多样性的植物。但是有一个限制,在社区中不允许有“回避”关系。换句话说,该组中不应有任何对抗性植物对。请注意,可以有多个这样的最佳列表。
我谷歌了一下,但找不到任何解决办法。所以我自己用 Python 开发了两个算法。这两种算法都是从能够促进马铃薯生长的植物列表开始的。
清单 10。获取马铃薯伴生植物的代码。
这个最初的列表包含苜蓿、芸苔、大蒜、洛伐他汀等等。
对于每一株植物,我计算它与其他植物有多少避免关系(清单 11 中的第 12–19 行)。
在第一轮中,大蒜和名单上的其他 13 种植物长得不好。它也是回避关系最多的植物。因此,我将大蒜从支持植物列表中删除(清单 11 中的第 24–29 行)。然后,算法重复这个过程(清单 11 中的第 8–29 行),直到再也找不到避免关系。如果有多于一个的植物具有相同的避免量,代码会随机删除其中的一个(清单 11 中的第 25–27 行)。
清单 11。从支持植物列表中连续删除最避免的植物的代码。
在我的一次运行中,这个算法依次删除了大蒜、韭菜、细香葱、洋葱等等。最终的清单包含以下 22 种植物:紫花苜蓿、罗勒、卷心菜、胡萝卜、三叶草、玉米、皇冠草、死荨麻、亚麻、山葵、扁豆、川芎、羽扇豆、金盏花、薄荷、豌豆、花生、菜豆、大豆、龙蒿、野豌豆和蓍草。
第二个算法使用来自 GDS 的度中心性来决定在每次迭代中移除哪个植物。度中心性算法用于查找图中的流行节点。在我们的例子中,我用它来寻找最避免的植物。该算法测量来自每个节点的传入或传出(或两者)关系的数量,并将该度量存储在 pandas 数据帧的“score”列中。它的实现不同于“while”循环中的第一个。
清单 12。通过 GDS 的度中心性,从支持植物列表中连续删除最避免的植物的代码。
在每次迭代中,该算法首先将支持植物及其回避关系投影到一个图形对象(清单 12 中的第 14–17 行)。然后,它计算每个植物的度中心性分数(清单 12 中的第 19 行),获取最“流行”的一个(清单 12 中的第 20–26 行),并将其从支持植物列表中删除。当有多个植物具有相同的度中心性得分时,代码会随机删除其中一个。该算法不断重复,直到所有的度中心性分数都为 0(清单 12 中的第 34–35 行)。
每次运行该算法时,都会发现一组新的 22 株植物。但是一些植物,如蓍草和死荨麻,将总是在集合中,因为它们在子图中没有任何避免关系。两种算法都有效。你可以通过证明任何两种植物之间没有必然的联系来验证这些结果。
清单 13。验证两个伙伴工厂列表的 Cypher 查询。
但是度中心性方法用了 0.4 秒,而我的第一个算法用了 2.6 秒。
结论
本文向您展示了我如何用 Google Sheets 和 Neo4j 将复杂的植物关系编码成知识图。植物之间的共栖、共生和化感关系是复杂而令人敬畏的。我们可以在同伴种植中很好地利用这些知识。通过配套种植,我们不仅能减少化肥和农药的使用,还能让植物更有活力、味道更好。最后,它还可以帮助我们减少灌溉量。面对当前史无前例的全球干旱,后一点尤其重要。
表格和图表的结合具有两者的优点。本质上,本文中的工作流将关系数据库与图形数据库结合在一起。我们可以在表格中轻松地编辑数据,并在图表中轻松地分析它们。我在这个项目中使用了 Google Sheets。但是对于其他大型项目,我们可以使用 PostgreSQL 甚至雪花或者 BigQuery。一旦数据加载到 Neo4j 这样的图形平台上,我们就可以使用图形查询和算法来检查数据,并通过 Cypher 或 Python 获得有趣的结果。你可以阅读由托马兹·布拉坦尼克撰写的本和本来学习更多关于在 Python 中使用 GDS 的知识。Python 桥还允许我们控制哪个子集的数据流向图。当您的完整数据集太大而不适合小型 Neo4j 实例时,这尤其有价值,您可以只选择和导入相关的子集。或者当我们在一个团队中工作时,我们可以根据每个团队成员的请求或访问级别来切片然后交付数据。事实上,阿斯利康制药公司已经在他们的内部知识图应用程序中构建了一个类似的数据流。
通过将配套的植物表编码成知识图,我已经使这一宝贵的农业知识可供所有人搜索和扩展。但是也有改进的余地。你能提出一个更好的算法来计算土豆的最大无规避支持植物集吗?我鼓励你验证这个知识图表中的信息,甚至制作你自己的版本。
https://dgg32.medium.com/membership **
如何用 Python 制作树形图
原文:https://towardsdatascience.com/make-a-treemap-in-python-426cee6ee9b8
使用 Plotly 制作一个带有滑块的树形图来调整深度
完整的最终树形图—滚动到底部进行交互式可视化(图片由作者提供)
简介
树形图是一种用于显示分层数据的可视化方式。传统的树形图由嵌套的矩形组成,然而,也有由嵌套的圆形组成的圆形树形图。总的来说,任何适合嵌套的形状都可以使用。树形图最适合用来显示与其父节点大小成比例的节点。
数据集
正在使用的数据集是 flare.json,它包含有关 flare 类的分层数据。该数据包含每个节点的名称、子节点以及每个节点的值,这些值将用作树图中节点的大小。
在这里下载:https://github . com/kruthik 109/Data-Visualization/blob/main/Advanced-Visualizations/flare . JSON(博斯托克,2021)
系统要求
本文中的 treemap 将使用 Python 中的 Plotly 库构建,因为它易于使用,并且能够添加交互式功能,如控制深度的滑块和工具提示。需要的库是 json、anytree、ipywidgetsm、plotly 和 pandas 库。我使用 Google Colab 来实现我的可视化,因为它简化了库导入过程。
import json
!pip install anytree
from anytree import PostOrderIter
from anytree.importer import DictImporter
import ipywidgets as widgets
import plotly.graph_objs as go
import pandas as pd
第一步:导入数据
数据被格式化为 JSON 文件。为了便于使用,用 JSON 库导入文件。
with open(‘flare.json’) as f:
js_data = json.loads(f.read())
JSON 格式很难处理,所以使用 anytree 库中的 DictImporter 来简化这个过程。该库通过直接访问子节点、父节点和遍历命令,以与树相同的方式转换要访问的数据。
#imports dictionary in a tree form
importer = DictImporter()
root = importer.import_(js_data)
步骤 2:计算所有节点的大小
在 flare 数据中,只有叶节点附有值。要获得每个节点的大小,请将每个对应叶节点的值相加。下面的递归算法使用 DFS 的一种改变形式,它从叶节点开始,递归地将子节点值添加到父节点。
def format(node):
for i in node.children: #check if node as attribute value
if hasattr(i, ‘value’) == False:
format(i) #check if node parent as attribute value
if hasattr(i.parent, ‘value’):
i.parent.value += i.value #if node parent doesn’t have a value set to same val as child
elif hasattr(i.parent, ‘value’)== False:
i.parent.value = i.value #insert step 3 code hereformat(root)
第三步:树形图的格式参数
为了在 Plotly 中制作树形图,所使用的参数必须是列表或数据帧格式。在下面的代码中,创建了三个列表,一个用于大小、名称、父节点和级别,这将在步骤 5 中进一步讨论。这段代码将被插入到格式函数的末尾
size = []
name = []
parent = []
level = []#append parent to parent list
parent.append(i.parent.name)
#append node name to name list
name.append(i.name)
#append node size to size list
size.append(c)
#get the level of each node by taking the length of its ancestors
#used for step 5
level.append(len(i.ancestors))
在 format 函数中,根节点的数据不会被追加到列表中,因为它不是子节点,所以我们在 format 函数之外添加这些值。
#append attributes for root
level.append(0)
name.append(root.name)
parent.append(“”)
size.append(root.value)
第四步:创建树形图
使用上面的列表,创建树形图。Plotly 中的 treemap 函数有两个必需的参数,即标签和双亲。标签列表对应于为每个节点显示的文本。父列表用于确定当前节点将嵌套在哪个节点中。值列表是可选的,用于为每个节点指定大小。尺寸列表包含每个节点的尺寸,如果没有给出,尺寸将由 Plotly 自动确定。
#create plotly figure
fig = plotly.graph_objs.Figure()
fig.add_trace(plotly.graph_objs.Treemap(
labels = name,
parents = parent,
values = size
))#show figure
fig.show()
第五步:添加滑块(可选)
Plotly 中的树形图有许多级别,根据数据的深度和复杂性,树形图可能会变得难以解释。这个问题的解决方案是添加一个滑块,这将允许用户控制可视化中显示的深度以提高可读性。上面创建的代码,加上一些额外的东西,可以很容易地修改成包含一个滑块。
步骤 5a:创建深度参数
首先,如第四步所示,确定每个节点深度的 level 参数将用于控制滑块改变时显示哪些节点。anytree 库提供的 node.ancestor 功能简化了这个过程。如果使用 Python 中的传统节点类,可以进行遍历来计算每个节点的祖先的数量。
步骤 5b:用参数创建数据帧
第二步是创建一个数据框,将第三步中创建的列表作为列。这是下一步创建新的树形图所需要的。
#create DataFrame
df = pd.DataFrame()
df[‘parent’] = parent
df[‘name’] = name
df[‘value’]= size
df[‘level’] = level
步骤 5c:用滑块值创建树形图的函数
使用第四步中创建的 DataFrame,创建一个函数来创建树形图和滑块。除了使用数据框中的列而不是列表之外,树状图的制作方式与第四步中的类似。使用一个简单的 DataFrame filter 命令将滑块合并到树形图中。第一个文件服务器正在检查该级别中的哪些节点小于当前滑块值。第二个过滤器是相应列的名称。
#create figure and slider
def update(sliderVal):
fig = plotly.graph_objs.Figure()
fig.add_trace(plotly.graph_objs.Treemap(
labels = df[df[‘level’]<sliderVal][‘name’],
values = df[df[‘level’]<sliderVal][‘value’],
parents = df[df[‘level’]<sliderVal][‘parent’]))
将根部颜色更新为浅灰色,以提高清晰度,因为原始颜色是白色。然后调整节点大小,使其与带有 branchvalues 的父节点成比例。更改布局以适应所需的空间。我把高度和宽度调整为 900 乘 900。
fig.update_traces(root_color=”#f1f1f1", branchvalues =’total’, width = 900, height = 900)
显示调整后的图。
fig.show()
步骤 5d:创建 Widget 并连接更新功能
将微件连接到更新功能,并将滑块的范围从最小深度设置为最大深度。
widgets.interact(update, sliderVal = (0, 5))
最终输出
作者可视化
将鼠标悬停在上面可视化效果中的节点上,以查看它们的值。单击一个节点以放大并查看其子节点的更详细视图。使用滑块控制要在可视化中显示的节点深度。目前,滑块处于最大值,因此向下滑动将减少细节。
结论
用 Plotly 制作的树形图可以清晰地显示分层数据。查看者能够看到节点与其父节点成比例的大小,这对于比较非常有用。树形图只是可用于分层数据的一种可视化形式。请继续关注我的下一篇文章,我将讨论更多类型的分层数据可视化。
完整代码可以在这里找到:https://github . com/kruthik 109/Data-Visualization/blob/main/Advanced-Visualizations/enclosure _ diagram . ipynb
引文
博斯托克,M. (2021,10 月 27 日)。具有 Flare 类层次结构的 JSON 文件。https://gist . github . com/MBO stock/1044242 # file-readme-flare-imports-JSON。许可证:gpl-3.0
如何在 Python 中创建用户定义的可重复项
原文:https://towardsdatascience.com/make-class-iterable-python-4d9ec5db9b7a
在 Python 中创建迭代器和可迭代的用户定义类
亨利&公司在 Unsplash 上拍摄的照片
介绍
在我以前的一篇文章中,我讨论了 Iterable 和 Iterator 对象,以及它们是如何参与 Python 迭代协议的。
在今天的文章中,我们将探索如何创建用户定义的迭代器,这些迭代器可以在用户定义的类中使用,以使它们成为可迭代的。换句话说,我们将展示如何创建一个用户定义的类,并实现所有需要的方法,以满足迭代协议中描述的 Python Iterables 的特征。
Python 中的迭代器和可迭代对象
迭代器和可迭代对象是 Python 迭代协议的两个主要结构。简而言之,Python 中的 Iterable 是一个对象,您可以在其上迭代其元素,而 Iterator 是一个对象,它返回 Iterable 对象并用于在迭代过程中产生值。
换句话说,
- Iterable 对象实现
__iter__()
方法并返回一个 Iterator 对象 - Iterator 对象实现了
__next__()
方法,并在 iterable 对象的元素用尽时引发一个StopIteration
。此外,迭代器对象本身是一个 Iterable,因为它也必须实现__iter__()
方法并简单地返回自身。
有关 Python 中迭代协议、迭代器和可迭代对象的更多细节,请务必阅读引言中所附的文章,在该文章中我们将更详细地讨论这些概念。
创建用户定义的迭代器和可迭代对象
现在让我们假设我们想要创建一些对象来说明一所大学是如何运作的。显然,我将要使用的例子会过于简化,因为这里的最终目标是展示如何实现和使用可迭代对象和迭代器。
class Student: def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name def __str__(self):
return f'Student Name: {self.first_name} {self.last_name}'class Lecturer: def __init__(self, first_name, last_name, subject):
self.first_name = first_name
self.last_name = last_name
self.subject = subject def __str__(self):
return f'{self.subject} Lecturer: '
f'{self.first_name} {self.last_name}'class UniversityClass:
def __init__(self, lecturers=[], students=[]):
self.lecturers = lecturers
self.students = students def add_student(student):
raise NotImplementedError def remove_student(student):
raise NotImplementedError def add_lecturer(lecturer):
raise NotImplementedError def remove_lecturer(lecturer):
raise NotImplementedError
现在让我们也创建一些Student
和Lecturer
对象的实例,然后我们将使用它们来创建一个UniversityClass
对象的实例:
s1 = Student('Andrew', 'Brown')
s2 = Student('Helen', 'White')
s3 = Student('George', 'Johnson')l1 = Lecturer('Maria', 'Richardson', 'Algorithms')
l2 = Lecturer('Bob', 'Johanson', 'Programming')uni_cl = UniversityClass(lecturers=[l1 ,l2], students=[s1, s2, s3])
现在让我们假设我们想要迭代一个UniversityClass
对象的实例,以便访问每个成员,包括某个班级的讲师和学生。
for member in uni_cl:
print(member)
上面的命令将失败,并显示一个错误,提示我们UniversityClass
不是一个可迭代的对象。
Traceback (most recent call last):
File "iterable_example.py", line 45, in <module>
for member in uni_cl:
TypeError: 'UniversityClass' object is not iterable
为了能够迭代用户定义的UniversityClass
类,我们需要创建一个迭代器,我们将在使UniversityClass
成为可迭代对象时使用它。
如前所述,迭代器必须实现__next__()
方法,当 Iterable 对象的元素用尽时抛出一个StopIteration
,还必须实现返回自身实例的__iter__()
方法。
首先,让我们创建一个迭代器对象,我们将在其中实现__next__()
方法中的逻辑。注意,这里期望的行为是首先返回讲师,一旦这些大学类成员的集合用尽,我们将开始返回学生。
class UniversityClassIter: def __init__(self, university_class):
self._lect = university_class.lecturers
self._stud = university_class.students
self._class_size = len(self._lect) + len(self._stud)
self._current_index = 0 def __iter__(self):
return self def __next__(self):
if self._current_index < self._class_size:
if self._current_index < len(self._lect):
member = self._lect[self._current_index]
else:
member = self._stud[
self._current_index - len(self._lect)] self._current_index += 1
return member raise StopIteration
既然我们已经创建了适当的迭代器类,现在我们可以继续修改UniversityClass
对象,使其实现__iter__()
方法。
class UniversityClass:
def __init__(self, lecturers=[], students=[]):
self.lecturers = lecturers
self.students = students def add_student(student):
raise NotImplementedError
def remove_student(student):
raise NotImplementedError
def add_lecturer(lecturer):
raise NotImplementedError
def remove_lecturer(lecturer):
raise NotImplementedError def __iter__(self):
return UniversityClassIter(self)
瞧啊。我们现在已经创建了一个用户定义 Iterable。让我们测试一下——这一次,如果我们试图迭代一个UniversityClass
对象的实例,我们应该能够检索所创建的类的讲师和学生:
s1 = Student('Andrew', 'Brown')
s2 = Student('Helen', 'White')
s3 = Student('George', 'Johnson')l1 = Lecturer('Maria', 'Richardson', 'Algorithms')
l2 = Lecturer('Bob', 'Johanson', 'Programming')uni_cl = UniversityClass(lecturers=[l1 ,l2], students=[s1, s2, s3])for member in uni_cl:
print(member)
现在输出将是:
Algorithms Lecturer: Maria Richardson
Programming Lecturer: Bob Johanson
Student Name: Andrew Brown
Student Name: Helen White
Student Name: George Johnson
完整代码
今天教程的完整代码也作为 GitHub 要点分享如下——在 Python 中创建自己的用户定义类和 iterables 时,可以随意使用它作为参考!
Python 中用户定义的可重复项的示例类—来源:作者
最后的想法
在今天的教程中,我们讨论了 Python 中的可迭代对象和迭代器,以及它们是如何参与迭代协议的。此外,我们展示了如何通过实现所有必需的方法来创建用户定义的 Iterable。
成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。
https://gmyrianthous.medium.com/membership
你可能也会喜欢
在 Streamlit 中使数据框架具有交互性
原文:https://towardsdatascience.com/make-dataframes-interactive-in-streamlit-c3d0c4f84ccb
如何使用 Streamlit-Aggrid 在 Streamlit 中显示漂亮的交互式表格
图片由 Pixabay 提供(作者修改)
使用 Streamlit 构建数据应用程序时,有时您可能需要在应用程序中将数据框显示为表格。Streamlit 自带数据显示元素,可以使用st.write()
、st.dataframe()
或st.table()
方便地显示表格数据。
这些方法的一个缺点是应用程序中显示的表格看起来简单而静态。您可以上下或左右滚动来查看数据,甚至可以使用pandas.Styler
和st.dataframe()
来在某种程度上改变呈现的数据帧的样式,但就样式化表格并使其具有交互性而言,您能做的也就这么多了。
使用 st.write()显示的数据帧(图片由作者提供)
在这篇文章中,我想和你分享一个很棒的 Streamlit 定制组件streamlit-aggrid
,它是建立在 AG 网格 ( 不可知网格)之上的。您可以使用streamlit-aggrid
在 Streamlit 中将 pandas 数据帧显示为可编辑的交互式表格。您可以像在 Excel 中一样编辑、排序和过滤表格。您还可以选择或多选数据框中的行,并将所选数据传递给应用程序中的另一个组件,例如,图表、地图、另一个表格等。
streamlit-aggrid
有许多奇妙的特性,可以在数据帧上执行各种各样的交互活动。在这篇文章中,我将主要关注以下功能:
- 给你的桌子添加一个“主题”
- 给表格添加分页
- 对表格中的行进行排序、过滤和选择/多选
- 将所选数据传递给应用程序中的另一个组件
下面是使用streamlit-aggrid
创建的交互式表格的快速演示。不再拖延,让我们看看它是如何工作的!
作者的 YouTube 视频
在使用streamlit-aggrid
创建交互式表格之前,让我们安装streamlit-aggrid
并将我们的样本数据集导入到 pandas 数据框架中。样本数据集是从美国教育部的大学记分卡公开数据(许可:知识共享署名)中创建的。
pip install streamlit-aggrid
作者图片
现在我们已经准备好了样本数据框架,让我们首先使用streamlit-aggrid
创建一个基本的交互式表格。只需一行代码,您的表就已经比前面显示的普通 dataframe 好看多了!
AgGrid(df)
作者图片
接下来,让我们使用以下代码向该表添加更多的交互式功能:
第 1–5 行:我们配置了各种gridOptions
特性,包括分页、侧栏和行选择。
第 7–18 行:我们通过将数据帧名称、gridOptions
、theme
和其他参数传递给AgGrid()
来构建网格。第 10–11 行允许基于行选择更新网格数据,并将其发送回 streamlit。
第 20–22 行:如果从网格中选择了任何行,那么所选的数据将被传递到一个中间数据帧中,并且可以被其他组件重用。
下面的屏幕记录显示了与表交互的各种方式,例如排序、过滤、分页、选择行以及将选择的数据传递到中间数据帧。
作者 GIF
最后,我们可以通过将选择的数据传递到一个叶子地图来使这个练习更有趣。对于任何选定的大学,我们可以立即在地图上显示它们的位置,并在工具提示中显示一些关于这些机构的附加信息。这里你需要做的只是使用中间数据帧(df)作为你的叶子图的输入。
作者 GIF
我已经写了两篇详细的文章,关于如何创建一个带有自定义标记和工具提示的叶子地图,就像上面显示的那样,所以我不会在这篇文章中花时间解释这部分是如何完成的。你可以阅读我的文章在这里了解更多关于叶地图。
正如我前面提到的,streamlit-aggrid
有更多的特性可以探索。例如,您可以使单元格可编辑,根据最大/最小值突出显示单元格,使列可分组,等等。你可以查看 streamlit-aggrid 的 GitHub 页面来获得更多的例子。
请注意,AG-Grid Enterprise 是由 AG-Grid 技术人员提供支持的商业产品,由 EULA 经销。如果要使用企业功能,您需要从 Ag-Grid 购买许可证。价格信息可以在这里找到:https://www.ag-grid.com/license-pricing.php
感谢阅读,我希望你喜欢这篇文章。
参考资料和数据来源:
参考:
Github 中的 Streamlit-Aggrid 文档页面:https://github.com/PablocFonseca/streamlit-aggrid
数据源:
美国教育部大学记分卡开放数据平台(https://data . ed . gov/dataset/College-score card-all-data-files/resources)。
许可:知识共享署名。本作品根据知识共享署名许可协议进行许可。
访问级别:公共
访问网址:https://ed-public-download . app . cloud . gov/downloads/colleges corecard _ Raw _ Data _ 02072022 . zip
你可以通过这个推荐链接注册 Medium 会员(每月 5 美元)来获得我的作品和 Medium 的其他内容。通过这个链接注册,我将收到你的一部分会员费,不需要你额外付费。谢谢大家!
通过微调 Bert 充分利用您的小型 NER 数据集
原文:https://towardsdatascience.com/make-the-most-of-your-small-ner-data-set-by-fine-tuning-bert-98a2c8b544f7
利用大型语言模型来提高模型的性能
作者图片
在小数据集上训练 NER 模型可能是一件痛苦的事情。当只对几百个样本进行训练时,很难建立一个模型来很好地概括非平凡的 NER 任务。这类问题我们将在本帖中尝试解决。
首先,我们将定义任务。然后,我们将描述模型架构的不同变化,然后比较预训练模型与从头开始训练的模型之间的结果。
问题是
我们将解决一个应用于食谱数据集的 NER(命名实体识别)问题。这项任务的目标是从原始文本中提取标记实体形式的结构化信息。
原始文本
标记文本
我们的目标是给原始文本的每个实体分配正确的标签。
我们将使用的数据集很小,大约有 700 个句子。这是这项任务的主要挑战,也是预训练模型可以大放异彩的地方,因为对它们进行微调可以将预训练步骤中学习到的表示重新用于手头的任务。
模特们
建筑:
我们模型的基本架构是:
模型架构(图片由作者提供)
标记方案:
上面提到的生物标签是一种在从原始文本生成的标记序列上编码标签的方法,如下所述:
来源:https://x-wei.github.io/notes/xcs224n-lecture3.html
每个实体的起始标记被标记为 B-,同一实体的每个其他标记被标记为 I-。所有其他令牌都标记为 o。
实施:
我们将为伯特使用拥抱脸变形金刚库。我们可以像插入任何其他火炬层一样插入预训练模型,因为它是 nn.Module 的一个实例。
在模型的 init 中,我们可以这样做->
from transformers import BertConfig, BertModelclass BertNerModel(BaseModel):
def __init__(
self,
...
dropout=0.2,
bert_path=None,
...
):
super().__init__()
... self.bert = BertModel(config=CONFIG)
...
if bert_path:
state_dict = torch.load(bert_path)
self.bert.load_state_dict(state_dict)
self.do = nn.Dropout(p=dropout)
self.out_linear = nn.Linear(CONFIG.hidden_size, n_classes)
其中 CONFIG 是 BertConfig 的实例,从 JSON 文件初始化。
然后,在向前的方法中,我们做:
def forward(self, x):
mask = (x != self.pad_idx).int()
x = self.bert(
x, attention_mask=mask, encoder_attention_mask=mask
).last_hidden_state
# [batch, Seq_len, CONFIG.hidden_size]
x = self.do(x)
out = self.out_linear(x)
return out
我们首先为填充标记生成一个掩码,然后将输入提供给 BERT 模型。我们从 BERT 的输出中提取最后一个隐藏层,然后通过线性分类器层来产生每个类的分数。
作为比较,我们也将使用 nn.TransformerEncoder 从头开始训练一个模型。你可以在这里找到所有的。
结果呢
我们使用 Tensorboard 跟踪损失指标:
验证损失(橙色:BERT,蓝色:nn。变压器编码器)
令牌级精度(橙色:BERT,蓝色:nn。变压器编码器)
我们可以看到,基于微调 BERT 的模型比从零开始训练的模型概括得更好。以下 F1 分数进一步证实了这一点:
验证集的 F1 分数:
BERT 初始化: 92.9%
nn。变压器编码器: 76.3%
在 3080 RTX GPU 上的推理速度(批量大小为 1):
BERT 初始化:43 句/秒
nn。TransformerEncoder: 303 句/秒
结论
通过使用预先训练的模型,我们能够获得额外的 16% F1 积分。由于拥抱脸的变形金刚库和 PyTorch,这很容易做到,但代价是推理速度较慢。然而,我们仍然可以使用权重量化或模型提取来缓解推理延迟问题,同时保持 F1 的大部分性能。
您可以在这个库中找到重现所有这些结果的代码:https://github.com/CVxTz/ner_playground
数据集:https://github.com/taisti/TASTEset/tree/main/data(麻省理工学院许可)
感谢阅读!
用稳定的扩散动画让你的艺术动起来
原文:https://towardsdatascience.com/make-your-art-move-with-stable-diffusion-animations-80de62eec633
介绍 Giffusion,这是一个简单的 web UI,用于创建具有稳定扩散的动画 gif 和视频
作者图片
随着稳定的传播,互联网变得热闹起来——它正在慢慢成为创造美丽视觉效果的必备工具。如果你想让你的视觉效果更上一层楼,那么用稳定扩散制作的动画是个不错的选择。
您不再需要花费数小时来创建传统动画,通过稳定的扩散,您可以立即让您的视觉效果栩栩如生。所以,让我们打开这个神奇的工具,看看如何使用它来创建令人惊叹的动画!
在这篇文章中,我将介绍一些用于创建稳定扩散动画的技术。
一些背景信息
在事情开始之前,我们需要熟悉一些概念。
第一个是扩散模型如何工作。简单来说,这些模型把噪音变成图像。在训练过程中,这些模型学习反转一个去噪 过程,该过程通过一个调度函数以固定的步数逐渐将噪声添加到图像中,直到图像与随机噪声无法区分。
一旦经过训练,我们就可以通过传入图像整形噪声张量来使用该模型生成图像,并通过使其迭代通过该模型来将其去噪到图像中。
正向和反向扩散过程(来源)
上图中,密度函数 q 指的是正向去噪过程。这意味着,前向过程基于时间步长t-1处的样本和时间步长**处的噪声调度函数的值来估计时间步长t处的噪声样本**
密度函数重复该过程,直到创建图像。****
我们需要掌握的下一个概念是潜在扩散。这就是稳定扩散模型的动力。
在普通扩散模型中,正向和反向过程对与图像张量具有相同形状的噪声张量进行操作。潜在扩散使用 VAE 模型,在执行正向和反向过程之前,首先将图像编码成潜在代码。
这使得该过程更快,因为该模型在低维潜在代码上操作,而不是在完整的图像成形张量上操作。
关于稳定扩散的另一件事是,它是文本引导的。这意味着图像生成过程由剪辑编码器产生的文本嵌入来指导。来自文本嵌入的信息通过交叉注意被结合到扩散过程中。
现在我们已经讨论过了,让我们继续制作一些动画。
在两个图像之间过渡
作者图片
我们可以制作的最基本的动画是两幅图像之间的过渡。在上面的动画中,我们在提示a picture of a corgi
和a picture of a lion
之间创建了一个过渡。为了创建这个转换,我们需要定义几个值。
- 图像之间要生成的帧数。
num_frames
- 动画的帧速率,以每秒帧数定义。
fps
帧数将让我们知道我们期望在两帧之间生成多少个点,而帧速率将决定动画移动的速度。
我们已经介绍过潜在扩散是通过在图像的潜在代码上运行扩散过程来工作的。为了生成新的图像,我们创建一个与我们的潜在代码形状相同的噪声张量,并通过我们的模型将其与我们的剪辑文本一起嵌入。这将导致一个潜在的代码,我们可以传递到我们的解码器模型来创建图像。
这里有一些伪代码来演示这一点。
**seed = 42
latent = torch.randn(
(1, self.pipe.unet.in_channels, height // 8, width // 8),
device=self.pipe.device,
generator=generator.manual_seed(),
)
text_embedding = get_text_embedding(["a photo of a corgi"])
output_latent = diffuse(text_embedding, latent)
image = decode_latent(latent)**
文本嵌入和潜在都是张量。我们将为我们的开始和结束帧创建这些张量,并使用插值来填充帧之间的内容。插值是一种在两个已知数据点之间创建新数据点的方法。
为了在开始帧和结束帧之间创建指定数量的帧,我们将使用球形线性插值,因为考虑了潜在空间的形状。你可以把球面线性插值想象成沿着两个单位法向量之间的一个角度生成向量。
插值函数接受两个张量和一个插值权重,插值权重决定了结果张量与起始值和结束值的接近程度。插值权重为 0.1 将产生更类似于起始张量的插值张量,而插值权重为 0.9 将产生更类似于结束张量的张量。
**import numpy as np
def slerp(t, v0, v1, DOT_THRESHOLD=0.9995):
"""helper function to spherically interpolate two arrays v1 v2"""
# from https://gist.github.com/nateraw/c989468b74c616ebbc6474aa8cdd9e53
if not isinstance(v0, np.ndarray):
inputs_are_torch = True
input_device = v0.device
v0 = v0.cpu().numpy()
v1 = v1.cpu().numpy()
dot = np.sum(v0 * v1 / (np.linalg.norm(v0) * np.linalg.norm(v1)))
if np.abs(dot) > DOT_THRESHOLD:
v2 = (1 - t) * v0 + t * v1
else:
theta_0 = np.arccos(dot)
sin_theta_0 = np.sin(theta_0)
theta_t = theta_0 * t
sin_theta_t = np.sin(theta_t)
s0 = np.sin(theta_0 - theta_t) / sin_theta_0
s1 = sin_theta_t / sin_theta_0
v2 = s0 * v0 + s1 * v1
if inputs_are_torch:
v2 = torch.from_numpy(v2).to(input_device)
return v2
start_seed = 42
latent_start = torch.randn(
(1, 4, 64, 64),
generator=generator.manual_seed(start_seed),
)
end_seed = 43
latent_end = torch.randn(
(1, 4, 64, 64),
generator=generator.manual_seed(start_seed),
)
num_frames = 60
schedule = np.linspace(0, 1, num_frames)
in_between_latents = []
for t in range(schedule):
in_between_latents.append(slerp(float(t), start_latent, end_latent))**
我们也将同样的插值应用到文本嵌入中。
**start_text_embedding = get_text_embedding(["a picture of a corgi"])
end_text_embedding = get_text_embedding(["a picture of a lion"])
for t in range(schedule):
in_between_latents.append(slerp(float(t), start_text_embedding, end_text_embedding))**
这导致生成的图像之间的平滑过渡。
保留动画中的主题
你可能已经注意到,在早期的动画中,我们的柯基在动画过程中发生了巨大的变化。如果我们试图在我们的动画中创建一个焦点,这可能是非常不可取的。
为了避免这一点,我们可以使用可组合扩散。这种技术允许以保留单个组件的方式组合提示。
为了做到这一点,Composable Diffusion 根据分隔符分割输入提示,比如|
**'a red house | a house in a lightning storm'**
该提示将基于分隔符被分成两个提示,并被馈入具有相同潜在成形噪声张量的扩散模型。由该模型产生的结果潜在值将被平均为单个张量,并被发送到解码器模型。
作者图片
请注意,上面动画中的房子在整个动画过程中变化不大。这是因为提示符的各个组成部分是通过组合来组合的。
这种方法的伪代码:
**prompt = ['a red house | a house in a lightning storm']
prompts = prompt.split('|')
text_embeddings = [get_text_embedding(prompt) for prompt in prompts]
latent_noise_tensor = torch.randn(
(1, 4, 64, 64),
generator=generator.manual_seed(start_seed),
)
output_latents = [diffuse(text_embedding, latent_noise_tensor) for text_embedding in text_embeddings]
final_latent = mean(output_latents)
image = decode_latent(final_latent)**
音频反应动画
到目前为止,我们已经讨论了如何使用球形线性插值在图像之间进行过渡。这种类型的插值在各个帧上产生彼此等距的点。
作者图片
请注意,X 方向上的每一步如何导致 Y 方向上的等量移动。
当尝试创建音频反应动画时,我们希望当有特定类型的音频事件(如鼓声)时,在我们的视觉效果中产生戏剧性的变化,否则保持我们的变化相对恒定。
要产生这样的变化,我们需要创建一个插值权重表,当帧包含音频事件时,它会快速变化,而当帧不包含音频事件时,它会保持相对恒定。
使用像librosa
这样的音频分析库可以帮助我们检测这些音频事件。
**import librosa
audio_input = "my_audio_file.mp3"
audio_array, sr = librosa.load(audio_input)
onset_env = librosa.onset.onset_strength(audio_array, sr=sr)
onset_env = librosa.util.normalize(onset_env)**
这个片段加载到一个音频文件中,并提取一个开始强度包络,向您显示音频事件发生的位置。此外,我们对包络进行了归一化,以便将值限制在 0.0 和 1.0 之间
作者图片
图表中的峰值对应于一段时间内音频文件中的事件。直接使用这个数组作为我们的插值权重调度将导致两个提示之间的非常抖动的运动,因为我们随着时间的推移不断地在 0.0-1.0 之间振荡。这无疑是有趣的,但对于更复杂的音频,它可能最终看起来非常嘈杂。我们希望能够驱动从一帧到下一帧的转换,这样我们就可以清楚地看到提示之间的转换。我们需要一个从 0.0 到 1.0 的单调递增函数
使用该数组的归一化累积和将产生我们需要的效果。
**import numpy as np
cumulative = np.cumsum(onset_env)
cumulative = cumulative / max(cumulative)**
作者图片
当我们从起点移动到终点时,插值时间表会随着时间的推移而增加。我们需要调整这个数组的大小,以匹配我们想要生成的帧数。
**import numpy as np
def resize(onset_env):
x = np.linspace(0, len(onset_env), len(onset_env))
resized_schedule = np.linspace(0, len(onset_env), num_frames)
return np.interp(resized_schedule, x, onset_env)
resized = resize(cumulative, num_frames)**
作者图片
现在,当我们在之间插值时,我们在音频开始事件期间获得快速过渡,否则获得平滑过渡。我们可以通过将音频信号分解为打击乐和谐波分量,并基于这些分量之一构建插值权重,来进一步改善转场。
作者图片
使用初始视频输入创建动画
到目前为止,我们一直使用噪声张量作为输入来创建动画。但是也有可能需要利用现有的视频作为我们动画的基础。
稳定扩散通过图像到图像的转换支持这一工作流程。
图像到图像的工作流程不是使用随机采样的噪声张量,而是首先将初始图像(或视频帧)编码成潜在代码。我们将噪声添加到这个潜在代码中,并将其用作我们的输入噪声张量,同时运行稍微修改的扩散过程。
我们通过引入一个新的参数来修改扩散过程;实力。
“强度”参数控制在扩散过程中保留多少原始图像内容,其范围在 0.0 和 1.0 之间。较低的值(高达 0.5)倾向于更多地保留原始图像内容,而较高的值会改变图像语义的一致性。
作者图片
那么强度参数到底在做什么呢?
去噪过程是迭代的,并且基于调度器运行固定数量的步骤。这意味着,在这个迭代过程中,我们可以在任何点自由地引入噪声张量。强度参数控制我们在这个过程中多晚引入噪声张量。
假设我们正在运行 50 个推理步骤的去噪过程。如果我们将强度值设置为 0.8 ,我们将为初始潜像添加 40 步的噪声,然后对图像进行 40 步的降噪处理。
较高的值意味着运行更多步骤的去噪过程,导致原始图像语义消失。值越小,去噪步骤越少,保留的原始图像越多。
临别赠言
如果你对应用这些技术感兴趣,试试我过去几周一直在做的这个项目, Giffusion 。
Giffusion 应用了我在这篇文章中描述的所有技术,并提供了一个简单的 WebUI,您可以在 Google Colab 中运行它来创建具有稳定扩散的动画 gif 和视频。
如果你对我在这篇文章中是如何制作动画感到好奇,你可以看看我的 Comet 项目,里面有所有的提示、参数和生成的动画。
所以,你有它!通过稳定的扩散,您可以创建令人惊叹的动画,并使您的项目栩栩如生。现在你知道了这个神奇工具的力量,是时候激发你的创造力,做一些漂亮的东西了!
参考
- 机器学习的扩散模型介绍
- 用扩散器稳定扩散
- 可组合扩散
- 针对音频反应的稳定扩散视频
- [图像到图像的工作原理](http://How img2img Diffusion Works)
- Giffusion:生成稳定扩散动画的项目
- 带有我的所有提示和生成图像的彗星项目
增强您的基础数据科学技能
原文:https://towardsdatascience.com/make-your-foundational-data-science-skills-stronger-45ee28574256
在像数据科学这样快速变化的领域,你的学习曲线一定会到达一个感觉非常陡峭的点,甚至可能是不可逾越的。(这种情况可能会发生不止一次。)
在那些时刻,你基础的力量真的很重要——努力掌握基础最终会有回报。如果你是一名数据科学家,想要确保自己为旅途中的挑战做好准备,这里有几篇最近的帖子,提供了实用的见解和灵感。
- 帮助数据职业者脱颖而出的特质有哪些? 卡西·科济尔科夫的帖子总是直截了当,可以付诸行动,这一篇——新系列的第一篇——也不例外。根据她多年的经验,Cassie 概述了那些仅仅涉足数据分析的人和那些处于该领域顶端的人之间的差异,并分享了如何在后者中立足的想法。
- 对于新数据科学家来说,专注是关键。 作为一名自学成才的数据科学家,Soner yld rum不得不通过反复试验来学习许多重要的课程。现在,他慷慨地提供了他的一些主要观点——关于敏捷性的价值和开发部署就绪模型的重要性——以便您可以更有效地推进您的旅程。
Pam Menegakis 在 Unsplash 上拍摄的照片
- 你能说出你的数据是否足够好吗? 评估你正在处理的数据质量的能力至关重要。对于 Seungjun (Josh) Kim 来说,这个过程中最有用的元素之一是异常值检测,他的最新帖子聚焦于你可以在广泛的行业环境中使用的三种可靠方法。
- 如何导航你的第一个时间序列分析项目。 如果(或者,更有可能的是,当时)你发现自己面临一个棘手的时间序列预测问题,你需要一个坚实的路线图来解决它。艾琳·张(Irene Chang)对她的作品集项目的彻底演练正是提供了这一点,并结合了对每个步骤的耐心解释和方便的代码片段,您可以根据自己的需要进行调整。
对于所有如饥似渴的读者来说,我们还没有完成——如果你想了解我们最近的一些杰出作品,请点击这里:
- 在拥挤的就业市场上,实习变得比以前更加重要,而且越来越难找到工作。 Murtaza Ali 的详细指南提供了一个三步走的蓝图,为你的应用提供助力。
- 解释机器学习模型是死路一条吗?瓦莱里娅·丰塞卡·迪亚兹在她对人工智能现状的反思中提出了许多发人深省的问题,并怀疑模型的复杂性是否正在成为技术民主化的障碍。
- 想了解更多关于线性编程的知识吗?本周你很幸运,有两本关于这个主题的优秀入门读物——马克西姆·拉博恩的总论,侧重于数学优化,以及线性规划的商业应用,由 Ezequiel Ortiz Recalde 提供。
- Ayla Kangur 在人工智能、产品管理和团队建设的交叉点上写作,邀请我们了解她的公司最近组织的内部 ML 竞赛,以及她的同事如何从这一倡议中受益。
- 人工智能什么时候会有下一次飞跃?在 TDS 播客上,Jeremie Harris 与人工智能研究员 Jaime Sevilla 聊起了预测 AGI 出现时间表的挑战。
一如既往地感谢您阅读并参与我们作者的工作;特别感谢你们所有选择通过成为灵媒成员来进一步支持他们的人。
直到下一个变量,
TDS 编辑
使用日期时间功能使您的机器学习模型更好地工作
原文:https://towardsdatascience.com/make-your-machine-learning-model-work-better-with-datetime-features-eb21de397fe8
特征工程,机器学习
使用日期时间功能使您的机器学习模型更好地工作
解释如何从日期和时间中提取新要素的现成教程
在 Unsplash 上拍摄的 ThisisEngineering RAEng
毫无疑问,特征工程是构建机器学习模型的最重要的方面之一。研究表明,一个好的模特和一个优秀的模特之间的差别可以通过你设计你的特征的方式来实现。
为了提高模型的准确性,您需要解决数据(不完整或不一致)和模型构建过程中的问题。当精度与您期望的不相似时,您可以使用不同的参数重新训练,或者设计与问题更相关的新功能。
在这篇文章中,我将通过一个实际的例子来讨论如何从日期时间变量中提取新的特征来增加机器学习模型的准确性。
文章组织如下:
- 从日期提取特征
- 时间特征提取
- 实际例子
1 从日期提取特征
有些数据集提供了日期或日期时间字段,在为机器学习模型构建输入要素时通常会将其删除(显然,除非您正在处理时间序列😃).
相反,DateTime 可用于提取新要素,这些新要素可添加到数据集的其他可用要素中。
日期由一天、一个月和一年组成。从这三个部分中,至少可以提取四个不同的特征:
- 一年中的某一天、一月中的某一天或一周中的某一天
- 一年中的月份
- 季节
- 年
在所有情况下,除了年份,所考虑的特征应分为两部分:正弦和余弦,以保证周期性,例如 1 月 1 日接近 12 月 31 日。
1.1 一年中的某一天、一月中的某一天或一周中的某一天
一年中的某一天是指 1 到 365(或 366)之间的数字。当您有一个细粒度的精细数据集,并且在几天内有一个平衡的分布时,可以使用这个特性,否则,使用这个特性可能是危险的,因为它可能会产生过度拟合。
在 Python 中,一年中的某一天可以按如下方式计算:
import **numpy** as npcurrent_date = "2022-01-25 17:21:22"
cdate = datetime.strptime(current_date, '%Y-%m-%d %H:%M:%S')**day_sin** = np.sin(2 * np.pi * cdate.timetuple().tm_yday/365.0)
**day_cos** = np.cos(2 * np.pi * cdate.timetuple().tm_yday/365.0)
对于数据集中更稀疏的日期值,我建议您利用一天作为一个月中的某一天或一周中的某一天。
一年的 1.2 个月
一年中的月份是指 1 到 12 之间的数字。如果您的 Pandas 数据帧中有一个 DateTime 列,则一年中的月份可以提取如下:
df['month_sin'] = np.sin(2 * np.pi * df['date_time'].dt.month/12.0)
df['month_cos'] = np.cos(2 * np.pi * df['date_time'].dt.month/12.0)
1.3 季节
季节是一个分类变量,它包括以下值:春天、夏天、秋天、和冬天。关于前面的特征,不计算季节的周期性。
在 Python 中,可以提取季节,如 Stackoverflow 上这个有趣的帖子中所解释的。
或者,季节可以被视为一个数字特征。在这种情况下,你应该注意周期性,所以你应该计算正弦和余弦。
1.4 年
**通常,当您必须预测未来的值时,年份作为输入特征不是很有用。**然而,为了完整起见,我描述了如何利用它作为输入特性。
如果数据集包含多个年份,则可以使用年份。根据您的需要,它可以是分类变量,也可以是数值变量。
如果您的 Pandas 数据帧中有一个 DateTime 列,则年份可以提取如下:
df['year'] = df['date_time'].dt.year
克雷格·迈莱赫兰在 Unsplash 上拍照
2 时间特征提取
可以从 DateTime 列中提取时间特征。
根据数据集的粒度,可以提取不同级别的时间要素(小时、分钟、秒……)。然而,最常见的时间特性是以小时为基础的。请始终记住,时间特征应分为正弦和余弦,以反映数据的周期性(例如,23:59 接近 0:01)。
在 Python 中,给定一个日期时间变量,可以按如下方式提取一个小时:
import **numpy** as np**hour_sin** = np.sin(2 * np.pi * cdate.hour/24.0)
**hour_cos** = np.cos(2 * np.pi * cdate.hour/24.0)
3 一个实例
这个例子利用了 Kaggle 上的天气数据集,该数据集有 CC0: Public Domain 许可。
本例的目标是构建一个多类分类器,该分类器根据输入要素预测天气状况(由数据集的列摘要给出)。我计算了两种情况下的准确性:有和没有 DateTime 特性。
3.1 加载数据集
该数据集可在 Kaggle 上获得。我把它作为一个熊猫的数据框打开:
import **pandas** as pddf = **pd.read_csv**('../input/weather-dataset/weatherHistory.csv')
作者图片
该数据集包含 96,453 条记录和 12 列。
3.2 探索性数据分析
现在,我去掉对预测没有太大影响的变量。我注意到隔音罩对于所有行都是一样的,所以我可以将其移除。
df['Loud Cover'].**value_counts**()
它给出了以下输出:
0.0 96453
Name: Loud Cover, dtype: int64
我还可以删除每日摘要列,因为它只包含文本。
df.**drop**(['Daily Summary','Loud Cover'],axis=1,inplace=True)
最后,我处理缺失值:
df.**isnull()**.**sum()**
它给出了以下输出:
Formatted Date 0
Summary 0
Precip Type 517
Temperature (C) 0
Apparent Temperature (C) 0
Humidity 0
Wind Speed (km/h) 0
Wind Bearing (degrees) 0
Visibility (km) 0
Pressure (millibars) 0
预测类型列包含一些缺失值。我放下了。
df.**dropna**(inplace=True)
3.3 数据清理
首先,我将分类数据转换成数字数据:
from sklearn.preprocessing import **LabelEncoder**le = LabelEncoder()
df['Precip Type']=le.fit_transform(df['Precip Type'])
df['Summary']=le.fit_transform(df['Summary'])
然后,我标准化输入特征:
from sklearn.preprocessing import **StandardScaler**scaler = StandardScaler()
df[df.columns[2:]] = scaler.fit_transform(df[df.columns[2:]])
我从第二个第三列开始,因为我没有标准化前两列(DateTime 和 Summary)。
3.4 特征工程
现在,我准备提取一年中的某一天和某一小时。我定义了一个函数,在给定日期的情况下,提取天数和小时数的正弦和余弦值:
import **numpy** as np
from **datetime** import datetimedef **discretize_date**(current_date, t):
current_date = current_date[:-10]
cdate = datetime.strptime(current_date, '%Y-%m-%d %H:%M:%S')if t == 'hour_sin':
return np.sin(2 * np.pi * cdate.hour/24.0)
if t == 'hour_cos':
return np.cos(2 * np.pi * cdate.hour/24.0)
if t == 'day_sin':
return np.sin(2 * np.pi * cdate.timetuple().tm_yday/365.0)
if t == 'day_cos':
return np.cos(2 * np.pi * cdate.timetuple().tm_yday/365.0)
现在,我提取新的特征:
date_types = ['hour_sin', 'hour_cos', 'day_sin', 'day_cos']
for dt in date_types:
df[dt] = df['Formatted Date'].apply(lambda x : discretize_date(x, dt))df.drop(['Formatted Date'],axis=1,inplace=True)
我计算特征之间的相关性,以检查是否有一些特征高度相关。在这种情况下,两个功能中的一个可能会被删除。
df.corr()
作者图片
表观温度和温度高度相关,因此我可以降低表观温度:
df.**drop**(['Apparent Temperature (C)'],axis=1,inplace=True)
3.5 列车测试拆分
我将数据分成 X 和 y 两部分,然后分成训练集和测试集:
from sklearn.model_selection import train_test_splitX = df.iloc[:,1:]
y=df.iloc[:,0]
X_train,X_test,y_train,y_test = **train_test_split**(X,y,test_size=0.3,random_state=1)
3.6 模型培训和评估
我测试了两个模型,一个有 DateTime 特性,另一个没有。首先,我用日期时间特征训练模型:
from sklearn.ensemble import RandomForestClassifiermodel = **RandomForestClassifier**(max_depth=32,n_estimators=120,random_state=1)
model.**fit**(X_train,y_train)
y_pred = **model.predict**(X_test)
我计算的准确度是:
from sklearn.metrics import **accuracy_score****accuracy_score**(y_test, y_pred)
它给出了以下输出:
0.6695389319342622
现在,我在没有日期时间功能的情况下训练模型:
model = **RandomForestClassifier**(max_depth=32,n_estimators=120,random_state=1)
model.**fit**(X_train[X_train.columns[:-4]],y_train)y_pred = model.**predict**(X_test[X_test.columns[:-4]])
我计算精确度:
accuracy_score(y_test, y_pred)
它给出了以下输出:
0.5827108161634411
请注意具有日期时间功能的模型如何优于另一个模型。
你可以在我的 Kaggle 简介中获得本教程的完整代码。
摘要
恭喜你!您刚刚学习了如何从机器学习模型中提取日期时间特征!本文中描述的实际例子证明了日期时间特性的存在可以提高机器学习模型的性能。
我留给你一个挑战性的问题:
日期时间特性会引入过度拟合吗?
敬请关注…
你可以通过阅读哈希尔·帕特尔的这篇有趣的文章来了解更多关于特征工程的概念,这篇文章题为什么是特征工程——机器学习的重要性、工具和技术。
如果你已经走了这么远来阅读,对我来说今天已经很多了。谢谢!你可以在这篇文章中读到更多关于我的内容。
相关文章
https://medium.com/geekculture/why-overfitting-makes-machine-learning-algorithms-perform-worse-7414ce71031a
让你的神经网络产生幻觉
原文:https://towardsdatascience.com/make-your-neural-network-hallucinate-a587ceeb4dbe
来自谷歌人工智能的深度梦想项目教程
照片由飞:D 在 Unsplash
在这个项目中,我们将采用一个输入图像,并使用生成模型对其应用一些幻觉效果。本教程基于谷歌人工智能和 Keras 的实现的这篇博客文章。它需要对机器学习和深度神经网络有一些基本的了解。
级别:中级—高级
最后提供了所有代码参考。作者修改了原始代码,并添加了一些额外的实现来适应本教程。
等等,我的狗怎么了?或者是我?—作者图片
(不用担心!在本教程的剩余部分,我将不再在我的狗身上测试这个模型。)
概观
对于高级功能,如使用指导图像控制梦境,请参见继续进一步会话。
为了完成这项任务,我们将:
- 处理输入图像
- 通过预先训练的图像分类模型输入该图像
- 取一些隐藏层的输出并“放大”激活信号
- 用这些放大的神经元重新构建一个新的图像
最大的技术挑战是“放大”激活信号。由于该模型已经被训练来检测和分类图像,我们可以有把握地假设,激活隐藏层可以捕捉有关图像的重要信息:形状,边缘,上下文,…
这意味着模型可以“看到”我们看不到的东西。我们能做的就是把这些信号“放大”到原始的画面,在一个迭代的过程中再次馈入模型。随着我们的图像获得更多的“隐藏上下文”,该模型可以接收这些新信号,甚至发现上下文,从而创建一个反馈循环。
这一步的程序是:
- 创建一个新模型,从一些隐藏的图层中提取特征
- 通过模型输入图像
- 根据这些特征计算激活信号
- 使用这些信号增强输入图像
- 重复第二步
我将使用 TensorFlow/Keras 来完成这项任务。与 PyTorch 相比,TF 的一个优势(在本文发表时)是可以很容易地获得 Keras 隐藏层的输出。因此,构建一个新的模型来提取特征成为一个琐碎的任务。
一般来说,精通这两种框架并了解哪一种最适合您的项目是有利的。
履行
属国
本教程在 Python3 上运行。我使用 Anaconda for Windows 来管理和安装所有的依赖项。
预处理和后处理
我们应用了在训练中使用的相同的预处理。
- 预处理:加载并归一化输入图像到[-1,1]之间
- 后处理:将范围[-1,1]转换为[0,255],并将数据类型转换为 uint8
定义模型
我们将使用 InceptionV3 作为基线模型。
对于这项任务,我们将构建一个新的模型,从我们的基线模型输出一些隐藏层的激活。
我怎么知道选择哪几层?
方法 1: 使用 TF 中的summary()
方法获得图层名称列表。
>>> feature_extractor.summary()
Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_1 (InputLayer) [(None, None, None, 0 []
3)]conv2d (Conv2D) (None, None, None, 864 ['input_1[0][0]']
32)batch_normalization (BatchNorm (None, None, None, 96 ['conv2d[0][0]']
alization) 32)activation (Activation) (None, None, None, 0 ['batch_normalization[0][0]']
...
**方法二:**用 Netron 可视化(这是我比较喜欢的方法):
- 保存模型
>>>feature_extractor.save('extractor.h5')
- 用 Netron 加载并检查模型。
- 在这个模型中有一些“交通路口”,看起来它们应该包含有用的信号,让我们使用这些。
识别“良好”的输出图层-按作者分类的图像
- 网络的每一层都在不同的抽象层次上学习图像。通常,第一层对边缘、颜色和形状等基本特征更敏感。而更深的层可能包含更多关于对象、上下文等的信息
定义损失函数
这就是事情变得有趣的地方。我们将使用以下损失函数:
其中y_i’s
是特征提取器模型的激活输出,d_i's
是这些输出的大小(尺寸的乘积)。
为什么要用这个损失函数?
- 我们想捕捉神经元最“活跃”的地方
- 特征图的范数对应于来自这些层的信号
- 大的损失值意味着特征映射在图像中检测到许多“隐藏的上下文”
- 我们根据输出大小进行归一化,以便所有选择的层对损耗的贡献相等
定义更新规则
在一个典型的深度学习问题中,我们会使用梯度下降来最小化目标损失。然而,这里的情况并非如此:
- 最小化损失函数意味着最小化输出激活的范数。这不是我们的真正目标。
- 激活信号是我们想要注入到我们图像中的东西(模型看到的东西,但是我们看不到)
- 所以我们想保持模型的权重不变。我们将仅使用激活信号来增强我们的输入图像。
→梯度上升!
把所有的放在一起
Google 在原始博客中建议,我们应该迭代地将我们的算法应用于图像输入,并在每次迭代后应用一些缩放。
以下是一些结果:
Fernando Paredes Murillo 在 Unsplash 上拍摄的照片
作者的幻觉
由弗拉基米尔·阿尼奇耶夫在 Unsplash 拍摄的照片
被作者强化的梦
更进一步
为了控制模型如何做梦,我们可以
- 修改变量:渐变步长、缩放步长数、每次缩放的比例以及每步的迭代次数
- 为特征提取选择一组不同的层:同样,我们可以利用 Netron 并挑选出一些候选层
- 修改损失函数:归一化范数是从模型中捕捉隐藏信号的最佳函数吗?
- 为我们的梦想使用向导图像
使用向导图像
除了依赖激活信号,我们还可以使用一个引导图像作为我们迭代的“GPS”。在每一步,来自引导图像的隐藏信号也被注入到我们的图像中。
为了完成这项任务,我们需要做出以下改变:
加载指南图像
将两个图像的大小调整到相同的分辨率
修改损失函数:我们将改为计算图像之间激活的点积。
基本原理:模型可以在两幅图像中“看到”我们肉眼看不到的东西。高损耗意味着来自引导图像的强信号增强了点积。因此,随着梯度上升,我们正在添加额外的“抽象”从指南图像到我们的原始图像。
示例结果:
作者图片
因热带梦幻而增强——作者图片
结论
这个项目是机器学习中生成模型的一个很好的例子。我们不是修改权重以最小化损失,而是使用梯度来增强输入图像以最大化该损失。随着我们反复重复这个过程,图像可以逐渐从模型中获得额外的信号,这些信号以前是我们看不到的。
希望你能从本教程中获得乐趣,并创作出一些美丽的仲夏夜之梦。
资源
谷歌人工智能的博客文章:
https://ai.googleblog.com/2015/06/inceptionism-going-deeper-into-neural.html
TensorFlow 教程:
https://www.tensorflow.org/tutorials/generative/deepdream
Keras 深梦:
https://keras.io/examples/generative/deep_dream/
如果你愿意支持 Thu,你可以考虑注册成为一名中等会员。每月支付 5 美元,你就可以无限制地阅读媒体上的故事。如果你使用他的推荐链接,Thu 也会得到一小笔佣金。
https://medium.com/@tdinh15/membership
让您的 R 代码速度提高 10 倍:3 分钟解释矢量化
原文:https://towardsdatascience.com/make-your-r-code-10x-faster-vectorization-explained-in-3-minutes-9eb4cdd7a49e
矢量化后的代码。利特·约翰斯顿在 Unsplash 上拍照
r 并不以快速编程语言著称。与 C++等速度更快的同类产品相比,它充其量只能算是缓慢的。也就是说,如果您理解了整个语言背后的一个关键概念,您可以让 R 代码运行得更快。
这个概念叫做矢量化,三分钟就能了解到。
在 R 中,向量是包含一个值或一组值的变量的基本类型。它们很常见。如果你曾经给一个变量名分配了一组数字,比如x <- 1:50
,那么你就创建了一个向量。您可以在 vector 中存储最常见的数据类型,包括 doubles、integers、logicals、characters 等等。
向量也可以有额外的属性。例如,向量中的每个值(或“元素”)都可以有一个名称。R like 列表和数据帧中常见的数据结构也是由带有一些额外属性的向量组成的。
因为 R 是围绕向量构建的,所以它的许多运算会自动作用于向量中的所有元素。换句话说,它们是“矢量化”的。为了解释,这里有一个例子,当你创建一个整数向量,然后乘以 2 时会发生什么。
如上所述,乘法运算适用于向量的所有元素。更何况,这并不只是对运营商起作用。许多常见的函数都是矢量化的,这意味着它们以与上面的例子完全相同的方式跨矢量元素工作。例如,您知道粘贴函数是矢量化的吗?
在这一点上,你可能想知道为什么 R 用户不简单地循环向量元素。这是大多数编程语言中的标准方法,那么为什么在 R 中不能这样做呢?这是省时的部分。
事实是,您可以在 R 中使用循环,有时有很好的理由这样做。然而,因为 R 是围绕向量构建的,矢量化表达式的运行速度通常比循环快很多倍。根据您的代码,您通常可以看到矢量化带来 10 倍的速度提升。下面的例子比较了上面粘贴例子中两个代码块的运行时间。
矢量化粘贴表达式与循环的执行时间比较。
这段代码的结果将在一定程度上取决于您机器的规格。在我的计算机上,循环运行平均需要 180 微秒,而矢量化表达式只需要 18 微秒;快十倍!就其本身而言,节省 162 微秒似乎不是什么大事。但是,当你运行这些相同的表达式成千上万次时,这会对你的总运行时间产生巨大的影响。
除了速度,矢量表达式还可以节省空间。 For 循环通常很庞大,需要在循环前初始化变量。矢量化将这些冗长的代码块缩减为几行,甚至是上面例子中的一行。这样,你将拥有更快的代码,需要更少的时间和精力来通读;你不能输!
向量化更进一步
虽然我已经简单介绍了向量化,但是对于高级 R 用户来说,深入研究这个概念是必须的。一旦你有了更多的经验,并且想学习更多关于 R 中的向量和其他数据类型,请阅读 Hadley Wickham 的“高级 R”中的这一章。
如果您是矢量化的新手,首先要做的就是尝试一下。查看您经常使用的函数,看看它们是否是矢量化的。在你的脚本中加入一些矢量化的操作。过一段时间,这将成为你的第二天性,你将能够用向量而不是循环来思考。试一试,享受你在这个过程中节省的时间。
想阅读我所有关于编程、数据科学等方面的文章吗?在这个链接注册一个媒体会员,就可以完全访问我所有的作品和媒体上的所有其他故事。这也直接帮助了我,因为我从你的会员费中得到一小部分,而不需要你额外付费。
你也可以通过订阅这里让我的所有新文章直接发送到你的收件箱。感谢阅读!
制作一个聊天机器人来搜索附近的餐馆
原文:https://towardsdatascience.com/making-a-chat-bot-to-search-for-eating-places-nearby-64df233ce48a
雅各布·卡普斯纳克在 Unsplash 上的照片
我们使用 Rasa 框架和 FOURSQUARE Places API 来制作一个搜索餐馆和咖啡馆的聊天机器人
饿吗?想找家餐馆吗?在这个示例项目中,我们制作了一个简单的聊天机器人,可以帮助您做到这一点。
我们将使用 Rasa 制作聊天机器人。它是一个用于制作基于任务的对话聊天机器人的开源框架。Rasa 使用机器学习模型来理解用户输入,并保持对话。
使用 FOURSQUARE Places API,我们可以在任何地点搜索感兴趣的地方。
结合这两者,我们得到了我们项目的聊天机器人。所以让我们开始吧。
目录:
- 入门
- 获取四方位置 API
- 生成 NLU 训练数据
- 自定义动作
- 域,规则和故事
- 聊天机器人-UI
文章中用到的所有代码都可以在 GitHub 资源库 中找到。
入门指南
首先,我们需要创建一个安装 Rasa 的虚拟环境。如果我们安装了 Anaconda,我们可以使用下面列出的命令。我们应该确保使用 Python 版本 3.7 或 3.8。这个命令列表还会安装一些我们需要的额外的库。
# This command creates the virtual environment
conda create --name env_name python=3.8# This command activates the virtual environment
conda activate env_name# This command installs pip in the virtual environment
conda install pip# And this command installs Rasa in the virtual environment
pip install rasa# We also need to install some additional libraries for the project, which may be done using the command given below.conda install pandas
pip install python-dotenv
pip install geopy
pip install word2number
我们也可以在 Rasa 开源上找到安装说明。
我们将在这个项目中使用的库是:
- 请求 :是一个处理 HTTP 请求的简单库。
- geopy :这是一个简单的 Python 接口,用于许多地理编码 web 服务,如 Google Maps、nomim、MapBox 等。
- 熊猫 :它是一个数据操作库,特别针对表格数据
- word2number :用于将数字字如“五”或“五十五”转换成相应的数字。
- -python-dotenv:用于从项目根目录下的. env 文件中读入环境变量。稍后当我们讨论 FOURSQUARE API 时会有更多的介绍。
一切就绪后,我们现在准备初始化我们的 Rasa 项目。首先激活虚拟环境(我的命名为 rasa),然后制作一个空目录并移入其中,最后输入命令rasa init
。Rasa 会在过程中要求一些提示;我们可以接受默认值。
Rasa 入门(图片由作者提供)
在这个阶段,我们应该有一个目录,其结构如下所示。
.
├── actions
│ ├── actions.py
│ ├── __init__.py
│ └── __pycache__
│ ├── actions.cpython-38.pyc
│ └── __init__.cpython-38.pyc
├── config.yml
├── credentials.yml
├── data
│ ├── nlu.yml
│ ├── rules.yml
│ └── stories.yml
├── domain.yml
├── endpoints.yml
├── models
│ └── 20220210-173055-ambitious-agate.tar.gz
└── tests
└── test_stories.yml5 directories, 13 files
重要的文件和目录有:
- 。/actions/ 目录:该目录下的主要文件有:
- actions.py 文件:Rasa 允许聊天机器人运行自定义 Python 代码,称为自定义‘自定义动作’。这些在这个文件中定义。
- init。py 文件:这个文件告诉 Python 把目录当作一个包
- config.yml 文件:这是配置文件;它定义了模型的各个部分、它们之间的关系以及它们的参数。
- credentials.yml 文件:包含了连接到 slack、脸书等各种渠道的凭证。
- 。/data/ 目录:包含训练数据的目录。有关培训数据格式的详细信息可在处找到[。它包含三个文件:
- nlu.yml 文件:它包含可能的用户输入的标记样本。
- stories.yml 文件:这包含了机器人和用户之间的对话示例。
- rules.yml 文件:它包含应该总是遵循相同路径的用户-机器人对话的小段。](https://rasa.com/docs/rasa/training-data-format)
- domain.yml 文件:这个文件定义了 bot 的环境,它看到什么,它知道什么,以及用什么来响应。
- endpoints.yml 文件:它定义了机器人可以用于各种内部任务的各种端点。
- 。/models/ 目录:包含训练好的模型。默认情况下,推理过程中使用最新的模型。
- 。/tests/ 目录:它包含 test_stories.yml 文件,该文件包含用于评估 bot 性能的示例用户-bot 对话。
对于这个项目,我们将在数据文件夹的三个文件中添加训练数据。我们将在 actions 文件夹的 actions.py 文件中编写一些自定义操作。我们还需要修改 domain.yml 文件。
完成基本的设置后,是时候设置下一个组件了,FOURSQUARE API。
获取 FOURSQUARE Places API
要使用任何一个 FOURSQUARE APIs,首先我们需要在 FOURSQUARE 上创建一个开发者账户。然后我们创建一个新项目并生成一个新的 API 密匙。我们可以在 FOURSQUARE 网站上找到这个过程。
我们需要对 API 密钥保密,所以通常的做法是将其作为环境变量来检索。为此,我们创建一个名为'的文件。env '(是的,。env 是项目根目录中的文件名,而不仅仅是扩展名。的内容。env 文件将类似于如下所示。
API_KEY=replace_this_with_your_api_key
之后,我们可以使用 python-dotenv 库检索这个值,如下所示。确保包括。env 在。gitignore 文件。
from dotenv import dotenv_valuesAPI_KEY = dotenv_values()["API_KEY"]
FOURSQUARE 有许多 API,但我们将只在项目中使用 Places API 的搜索端点。我们可以在下面给出的页面中试用 API。
FOURSQUARE 支持许多不同类别的名胜古迹。在我们的例子中,我们希望将搜索范围限制在餐馆和咖啡馆。为此,我们需要在 categories 参数中传递相关的类别 ID。类别 id 及其相应类别名称的完整列表在这里给出。
生成 NLU 数据
NLU 代表 N 自然 L 语言 U 理解,nlu.yml 文件将包含用户输入的标签示例,模型将使用这些示例来理解用户输入。模型需要从用户输入中学习两件事:
- 用户所说的,叫做意图
- 以及从用户输入中提取的信息,称为实体。
关于如何写训练数据的细节可以在这里和这里找到。
在我们的聊天机器人中,我们将在 API 调用中指定三个参数:ll(纬度、经度)、半径和类别。使用 categories 参数而不是 query 参数有三个好处:
- 从用户输入中提取查询字符串很麻烦
- 确保结果与我们的用例相关
- 我们可以在我们这边执行数据验证,而不依赖于 API
为了填充这三个参数,我们需要从用户输入中提取三个实体:
- place_type:这将被映射到 category 参数。
- 地址:这将被映射到 ll 参数。
- 半径:这将被映射到半径参数。
Rasa 有一个叫做 Forms 的有用特性,可以从用户输入中提取所需的信息。关于如何使用表格的详细信息可以在这里找到。
下面是 nlu.yml 文件的一个示例。完整的数据可以在 Github 上找到。
version: "3.0"
nlu:- intent: greet
examples: |
- hey
- hello
- hi
- hello there- intent: goodbye
examples: |
- bye
- goodbye
- have a nice day- intent: mood_great
examples: |
- perfect
- great
- amazing
- feeling like a king- intent: mood_unhappy
examples: |
- my day was horrible
- I am sad
- I don't feel very well
- sad- intent: giving_place_type
examples: |
- I want to look for [restaurants](place_type)
- I would like to make a search for [restaurants](place_type)
- Look for [both restaurants and coffee houses](place_type)
- Search for them [both](place_type)- intent: giving_address
examples: |
- I live in [Scarborough](address), [Toronto](address), [ON](address), [Canada](address)
- [317 Dundas St W](address), [Toronto](address), [ON M5T 1G4](address), [Canada](address)
- [Dhaka](address), [Bangladesh](address)
- [London](address)- intent: giving_radius
examples: |
- [20](radius)
- [ninety one](radius)
- [fifty](radius) kilometers
- Search within [15](radius) km
自定义操作
定制操作是 Rasa 灵活性背后的主要动力。它们使机器人能够根据用户输入在对话期间运行定制的 python 代码。
要使用自定义操作,我们需要做四件事:
- 在 actions 文件夹的 actions.py 文件中编写 python 代码。关于如何编写自定义函数的详细信息可以在 Rasa 操作服务器文档中找到。
- 通过在 domain.yml 文件中列出自定义操作,让 bot 知道这些操作。
- 取消对 endpoints.yml 文件中以下行的注释。
action_endpoint: url: "http://localhost:5055/webhook"
- 最后,当我们想要使用定制动作时,我们需要在单独的终端中运行命令
rasa run actions
。
我们将在以下任务中使用自定义操作:
- 地址验证:我们将用户的位置提取为一个地名列表,如['纽约','美国']。然后我们使用。join()字符串运算符来生成完整的地址字符串。然后,我们使用 geopy Nominatim 对地址进行地理编码,即将地址映射到一个准确的位置。如果地址无效,则不返回任何结果。如果返回一个结果,我们还存储纬度和经度值,以便传递给 API 调用。
- 半径验证:半径应该是一个数字或一个可以转换成数字的单词,如“六”。我们使用 word2number Python 库来检查这种情况。
- 发出 API 调用:我们使用 Python 请求库发出 get 请求。用于授权的 API 密钥在头中传递,而参数作为 url 的一部分传递。该调用返回一个 json 对象,我们解析该对象以获得所需的值。
这些功能可以在 Github 页面中找到。
领域、规则和故事
domain.yml 文件描述了聊天机器人的环境。它包含所有意图、实体、动作、响应、插槽以及表单的列表。可以在这里找到该文件中包含的内容和形式的详细信息。
到目前为止,我们所做的一切都必须列在这个文件中,聊天机器人才能知道它们。而且,我们还需要做槽和 bot 响应。这个项目的 domain.yml 文件可以在这里找到。
rules.yml 文件描述了应该总是以相同方式进行的简短对话。“激活表单”和“提交表单”是我们项目中的两个主要规则。关于编写规则的详细信息可以在这里找到。
故事是用户与机器人互动的例子。它们帮助模型响应用户输入,即使是长时间的对话。关于如何为 Rasa 写故事的细节可以在这里找到。
现在我们从命令行运行命令rasa train
。
聊天机器人用户界面
测试聊天机器人最简单的方法是在一个终端使用命令rasa shell
,在另一个终端运行命令rasa run actions
。
从终端与聊天机器人互动(图片由作者提供)
这是可行的,但是我们可以通过使用下面显示的聊天机器人 UI 来获得一个更好的界面。
https://elysian01.github.io/Rasa-Chatbot-UI/
首先,我们下载文件,提取其内容,然后在 web 浏览器中打开 index.html 文件。关于如何修改聊天机器人设计的说明可以在网站上找到。
将所有这些放在一起,在一个终端中我们运行下面的命令。
rasa run actions
然后在另一个终端中,我们运行下面的命令:
rasa run --enable-api --cors='*'
最后,我们运行 index.html 文件。
使用聊天机器人用户界面在浏览器中与聊天机器人互动(图片由作者提供)
结论
我们结束了。我们刚刚制作了一个聊天机器人,可以搜索附近的餐馆和咖啡馆。
对于进一步的工作,我们可以:
- 我们可以添加另一个功能,可以帮助查找场馆的细节。
- 我们还可以尝试部署应用程序,并使其为生产做好准备。
目前就这些。感谢您的阅读。
https://rasa.com/ https://developer.foursquare.com/ https://github.com/AhmedFahim-git/simple_chatbot
产生影响有时意味着为了广度而牺牲深度
原文:https://towardsdatascience.com/making-an-impact-sometimes-means-sacrificing-depth-for-breadth-26869f26efec
作者聚焦
阿德里安娜·克莱恩思考了一个跨越医学、机器学习和工程的多学科职业生涯
在 Author Spotlight 系列中,TDS 编辑与我们社区的成员谈论他们在数据科学领域的职业道路、他们的写作以及他们的灵感来源。今天,我们很高兴与 艾德丽安·克莱恩 分享我们的对话。
照片由阿德里安娜·克莱恩提供
Adrienne Kline 是西北大学预防医学系的博士后研究员。她完成了生物医学工程博士学位(机器学习中的成像专业),医学博士学位,之前获得了电气工程学士学位。
她的兴趣在于医学和工程的交叉,特别是利用算法决策支持医学转化应用。她的工作导致了处理缺失数据的新方法、评估机器学习预测可靠性的创新指标以及多模态数据流的信息融合的发展。通过强调结构数据、计算机视觉、强化学习和生成算法,她希望改变医学实践的效率和可靠性。
最近你最感兴趣的话题和项目是什么?
我的工作总是由新颖的应用和方法组成。应用程序,因为我经常想利用现有的工具来完成任务;但是当我想要的工具不存在的时候,我就努力去创造它。目前,我正在研究新的插补和机器学习能力工具,这两者都是方法和数学密集型的。
我在心脏病学领域从事的另外两个项目是生成模型和强化学习。这些都是以应用为基础的,用于提高心力衰竭患者决策的可靠性。此外,当我看到需要的问题时,我会寻找结合硬件和软件的新颖项目。我总是回归理论,解决一个有意义的问题。
作为一个拥有工程、机器学习和医学等多学科背景的人,你是如何决定走哪条职业道路的?
我的机器学习生涯始于我工程本科学位的后期。我的顶点项目是关于脑机接口(BCIs)——利用脑电图(EEG)进行信号/图像处理以控制机器人手。
本科毕业后,我同时被博士和医学博士项目录取,并选择在完成研究生学业的同时推迟医学院的学习。在博士期间,我继续将机器学习应用到我为脊髓损伤患者设计的 BCI 机器人中,这些机器人使用了 fMRI(功能性磁共振成像)和 EEG。
当我带着工程学的视角进入医学院时,我开始体会到医学决策的细微差别和困难。医学是一个要求很高的职业,但当我学会如何像临床医生一样思考时,我开始注意到我希望在当前范式上改进的许多方式。尽管对我焦虑的同学来说很前卫,我还是参加了一场住院医师比赛,因为我认为自己是两个世界之间的桥梁,而不是执业医师。
我喜欢照顾病人,但我的理想角色,发挥我的嵌合体优势,成为一个我可以为我从未谋面的病人提供照顾的角色,为了广度牺牲深度。我总是说我不是在找工作,我是在找一个角色——一个我可以“坐在板凳上”工作,管理一支工程师和科学家团队,与我们的临床医生合作伙伴合作,并且可信地说两种语言的角色。
最终,我希望开发和转化技术(算法、设备、信息),以提高可靠性、降低成本并提高患者护理的效率。
今天的数据从业者有很多职业选择。你会建议他们在开始旅程时问自己什么样的问题?
在这个蓬勃发展和令人兴奋的领域有大量的机会。
我认为人们很容易被过度使用的“人工智能”一词所迷惑归根结底,职业决策是基于个人价值的判断。如果你的动机是薪水,那么你在工业界会更好。如果你想按照自己的节奏前进,也许学术界或创业公司适合你。
也就是说,这曾经是学术界与工业界的较量,但许多公司都有强大、纯粹的 R&D 部门发布白皮书,许多学术界人士在实验室里为他们的研究和出生公司申请专利。因此,在做任何决定时,就像任何好的算法一样,找到对你来说最重要的方面(薪水、向上的流动性、自主性、地域流动性等等。)作为决策的基础。然后根据你的每一个预期选项满足这些内在愿望的程度,对它们进行评分(如果你愿意,也可以对它们进行不同的加权);这就是我如何做出生活和职业决定的。
无论在哪个领域,适应性和灵活性对于保持现状都是绝对必要的,为了发展技能,人们需要在工作中有发展空间。因此,如果你更看重行业,要谨慎行事,确保工作描述中包含一些发展空间;它往往被自动构建到学术学习中心。
不管职业阶段如何,我鼓励人们永远不要放弃数学、统计学、计算机科学和工程学的基础知识。这些基本原则是创新赖以建立的关键。每天都有新的架构和算法出现,如果你只关注树叶,你就会看不到树。总是问自己“那又怎样?”给你的工作打基础,并提醒你回答大问题。
你为什么决定为更广泛的受众撰写关于数据和机器学习的文章?
我关于 TDS 的第一篇文章是关于我创建的一个 Python 包——pspy。现在有一个正式的白皮书,我已经在媒体文章中引用过了。白皮书的提交发生在 2022 年初,直到 7 月份才在 IEEE 会议上提交。但是因为这篇论文需要一个工作包,我觉得它提供了一个独特的机会,让数据科学和统计社区意识到它的存在,而不是通过一个隐藏在看不见的存储库中的自述文件。
奇怪的是,它成了一篇受欢迎的文章,所以我开始写其他文章,灵感来自我们的研究生在实验室问我的问题。我一直喜欢教学,并且非常重视指导。通过创建这些文章的存储库,我有了可供学生参考的资源。这些文章既是教学的资源,也减少了我被问到的问题中的一些重复。
最近几个月,你已经出版了优秀的统计训练营系列。是什么激发了这个想法,你希望你的读者从中学到什么?
我有幸教过本科生和研究生。教学一直是一种有益的经历,我的学生一直是我的灵感来源。训练营建立在我以前教过的统计学研究生入门课程的基础上。
我对训练营的希望是,它可以成为那些上过研究生院的人(可能想更新一下)和那些想从研究生水平的统计学课程中获得知识但没有机会这样做的人容易获得和负担得起的笔记库。通过阅读,人们成为更好的信息生产者和消费者。
无论是在你自己的特定领域还是在更广泛的生态系统中,你希望在近期或中期看到什么样的进步?
在接下来的几年里,我希望看到更多的机器学习部署到翻译应用中。我要补充的告诫是,我希望在一个预测的可靠性或不确定性与首先获得预测的能力同等重要的环境中工作和建设。
了解算法的局限性,也就是说,它适合谁,不适合谁,是非常重要的。这是算法和专家决策者之间的权衡,应该根据具体情况而定。
最后,在医疗领域,我希望看到多模态数据推动更多的患者决策。在医学上,许多决定被称为临床诊断;这意味着它不是一个单一的实验室或影像学研究,而是一个星座的迹象,症状,实验室,成像等。这就是 ML 真正闪光的地方:它可以将不同的数据类型加权到一个统一的诊断中,因为临床医生(以及更广泛的人类)在检查病人时并不擅长执行矩阵代数。
要了解更多关于艾德丽安的工作,了解她的最新文章,请点击媒体、推特和谷歌学术关注她。为了体验阿德里安娜的 TDS 文章,这里有一些突出的:
- 浏览 统计训练营系列中的所有当前帖子。
- 插补方法的实施与限制(2022 年 5 月,9 分钟)
- 用 Dash/Heroku 创建仪表盘 App:精简的流程(2022 年 6 月,5 分钟)****
- 参数与非参数测试,以及何时使用它们(2022 年 4 月,4 分钟)
想和广大观众分享一些你自己的作品吗?我们很乐意收到你的来信。
这个 Q & A 是为了长度和清晰而稍加编辑的。
让 CDR 准备好与真正的量子计算机一起工作
原文:https://towardsdatascience.com/making-cdr-ready-to-work-with-a-real-quantum-computer-fcdf270a6730
做程序员没坏处
量子机器学习要不要入门?看看 动手用 Python 学习量子机器 。
克利福德数据回归(CDR)是一种很有前途的量子误差缓解方法。在之前的帖子中,我们有效地改编了来自开源库 Mitiq 的代码。因此,我们只需进行微小的调整,就可以连接到 IBM 量子云,并降低真实量子计算机的噪音。
作者图片
然而,完成该方法需要一整天的时间。它不需要这么长时间,因为它需要这么多的计算时间。但这需要很长时间,因为我们提交给 IBM cloud 的量子电路已经等待了很长时间才能执行。
IBM 免费提供他们的量子设备。但是,当然,这些是共享设备。当您将一个量子电路发送到 IBM cloud 上的一个量子系统时,在最终执行之前,它会与来自其他用户的作业一起进入一个队列。根据排队作业的数量,可能需要几个小时才能运行作业。
在我们执行 CDR 的过程中,我们向 IBM cloud 提交了 11 个电路。问题是我们没有马上把它们都发出去。但我们总是等到一项工作完成后,再发送下一项。结果,我们的等待时间大大增加了。
幸运的是,IBM 允许我们同时发送多个电路。这将大大减少我们的总等待时间。
在上一篇的代码中,我们使用了从 mitiq 导入的execute_with_cdr
方法。我们用一个定制的执行器作为参数来调用它——real_jakarta
执行器。这个执行器是一个函数,它接受量子电路,运行它,并返回感兴趣的可观察对象的期望值。
让我们简单看一下execute_with_cdr
函数的代码,你可以在这里找到。在第 155 行,我们调用执行器的evaluate
函数,并将一列电路和可观察值作为参数传递。
电路列表是训练电路加上我们要计算其期望值的原始电路。CDR 方法建立在从数据中学习如何减轻噪声的基础上。我们看到,我们可以指定我们想要在更近的检查中使用的训练电路的数量。默认值为10
。所以,我们用 11 个电路的列表调用evaluate
函数。
执行器(参见代码这里的)循环遍历列表,通过调用我们提供的函数来“执行”每个电路。
让我们简单回顾一下所提供的函数,看看我们是如何执行一个电路的。
*# ...*mapped_circuit **=** transpile(circ, backend**=**jakarta)
qobj **=** assemble(mapped_circuit, backend**=**jakarta, shots**=**shots)*# execute the circuit*
job **=** jakarta.run(qobj)counts **=** job.result().get_counts()*# ...*
首先,我们在真实的量子设备(Jakarta)上传输、组装和运行电路。然后,我们在执行器内部直接获得的结果,因此也是在循环内部。这就是问题所在,因为result()
是一种屏蔽方法。它不返回任何内容,但会一直等到作业完成。因此,只要我们的电路在队列中等待,它就会等待。因此,整个循环无法继续。但是它在提交下一个电路执行之前等待结果。
为了防止代码等待,我们必须在使用结果之前将所有电路提交给 IBM cloud。因此,我们需要把execute_with_cdr
功能拆开。
第一步,我们准备训练电路。我们将这个与代码的其余部分分开,因为我们在执行之前和之后都需要这些电路。
我们剥离了这个函数,只包含生成训练电路所需的部分。但是我们接手的那些部分保持不变(见原函数)。
现在,让我们使用这个函数来创建电路列表。出于这个目的,我们首先重用以前帖子中的一些东西,比如创建量子电路的get_circuit()
函数,我们的目标是计算它的可观测期望值。此外,我们定义了这个可观察值(obs
)和无噪声模拟器(sim
)。
一旦我们准备好电路,我们就像最初的 Mitiq 代码一样重新组织它们。
我们将想要运行的电路存储在to_run
变量中。因此,现在让我们创建一个函数,将这些电路一次性提交给 IBM cloud(不等待结果)。
submit_to_jakarta
函数接受to_run
列表、可观察值、每条电路的运行次数(shots
)以及我们想要运行电路的backend
。在这个函数中,我们声明了另一个函数submit_executor
。这是我们作为执行器使用的回调函数。在这个函数的实现中,我们只准备电路(transpile 和 assemble)并发送到backend
后端。执行程序不返回结果,而是返回一个job
。这是提交给给定后端的量子电路的本地引用。submit_to_jakarta
函数收集所有的jobs
并返回它们。
在我们使用这个函数之前,我们需要连接到我们的帐户并获得一个允许我们访问后端(这里是jakarta
)的提供者(您需要使用不同的参数值来连接到ibmq_quito
)。这篇文章更详细地解释了如何做到这一点。
让我们看看调用这个函数时会发生什么。
输出是一个作业对象列表。前往你的 IBM 量子控制台。当您在“最近的工作”中点击“查看全部”时,您会看到这些工作已经到达 IBM,它们正在等待处理。您还可以看到大致的执行时间。在我的情况下,他们今天根本不会运行,因为有超过 140 个其他电路在排队等候。
如果我需要更多的动力来推动我们今天所做的事情,这就是了。
至少,等待这些作业运行给了我足够的时间来完成这篇文章。
当然,在我们等待结果的时候,你不会想让你的电脑一直开着。所以,我们需要坚持这些工作。幸运的是,每个作业都有一个 id,我们可以用它在以后取回。如果您使用 Jupyter 笔记本,您可以简单地将 id 打印到单元格的输出中,并将它们保存在笔记本旁边。
'6218d0f147f37310a6377ee0',
'6218d0f3a2eeaa7a80ad27c8',
'6218d0f48c4ac898e4687d1c',
'6218d0f5a2eeaa782fad27c9',
'6218d0f7f0b807c602db65b0',
'6218d0f8a16487a0ade6eb39',
'6218d0fa3895be83e73e214f',
'6218d0fbf0b8079824db65b1',
'6218d0fc5ac21f18a4473086',
'6218d0fe976d9fc87220be83',
'6218d0ff3f99d49bef399494',
我们看到 11 个作业 id 的列表。
您可以使用它们通过jakarta.retrieve_job("JOB_ID")
功能获得一个新的作业对象。所以,让我们把这些放入一个数组,我们可以使用它。这里,我使用以前运行的 id。
我们可以很容易地将 id 列表映射到实际作业列表。
[<qiskit.providers.ibmq.job.ibmqjob.IBMQJob at 0x7f5c154f7b50>,
<qiskit.providers.ibmq.job.ibmqjob.IBMQJob at 0x7f5c7c5c61c0>,
<qiskit.providers.ibmq.job.ibmqjob.IBMQJob at 0x7f5c154513a0>,
<qiskit.providers.ibmq.job.ibmqjob.IBMQJob at 0x7f5c151c2370>,
<qiskit.providers.ibmq.job.ibmqjob.IBMQJob at 0x7f5c422d01c0>,
<qiskit.providers.ibmq.job.ibmqjob.IBMQJob at 0x7f5c14b50fd0>,
<qiskit.providers.ibmq.job.ibmqjob.IBMQJob at 0x7f5c18978520>,
<qiskit.providers.ibmq.job.ibmqjob.IBMQJob at 0x7f5c14e90400>,
<qiskit.providers.ibmq.job.ibmqjob.IBMQJob at 0x7f5c14e909a0>,
<qiskit.providers.ibmq.job.ibmqjob.IBMQJob at 0x7f5c14e90d00>,
<qiskit.providers.ibmq.job.ibmqjob.IBMQJob at 0x7f5c14e90ac0>]
一旦这些工作完成,我们就可以获得它们的结果,而无需进一步等待。
Result(backend_name='ibmq_jakarta', backend_version='1.0.28', qobj_id='471fdd0a-c273-4ea4-aded-28f0ed4b0ed7', job_id='6216307c89a5f0ba67accdc8', success=True, results=[ExperimentResult(shots=1024, success=True, meas_level=2, data=ExperimentResultData(counts={'0x0': 238, '0x1': 41, '0x2': 49, '0x3': 696}), header=QobjExperimentHeader(qubit_labels=[['q', 0], ['q', 1], ['q', 2], ['q', 3], ['q', 4], ['q', 5], ['q', 6]], n_qubits=7, qreg_sizes=[['q', 7]], clbit_labels=[['meas', 0], ['meas', 1]], memory_slots=2, creg_sizes=[['meas', 2]], name='circuit-3333', global_phase=4.71238898038469, metadata={}))], date=2022-02-23 13:17:22+00:00, status=Successful completion, status=QobjHeader(backend_name='ibmq_jakarta', backend_version='1.0.28'), execution_id='ec06e180-94aa-11ec-ac54-bc97e15b08d0', time_taken=5.931757688522339, error=None, client_version={'qiskit': '0.33.0'})
因此,让我们编写另一个处理作业结果的函数。它需要工作和可观察的事物。同样,我们在这个函数中定义了一个执行实际工作的执行器。
[0.9172392848406065,
0.5629408486360803,
0.4802712135216911,
0.8463795975997012,
0.8463795975997012,
0.7204068202825363,
0.6259272372946627,
0.7322167681560205,
0.7400900667383433,
0.7440267160295047,
0.1417193744818105]
当我们处理作业时,我们会收到一个期望值列表。类似于 Mitiq 的原始函数,我们需要将这些与我们从模拟器中获得的无噪声测量值结合起来。因此,让我们也执行这些步骤,以获得减少误差的测量。
1.0135726520354982
因此,让我们来计算理想值(无噪声)和未优化的测量值。当我们创建要运行的电路列表时,第一个是未修改的电路。我们也可以用这个来计算未优化的测量。
最后,我们可以计算并打印缓解的关键性能指标。
Error (unmitigated): 0.06987552491811488
Error (mitigated with CDR): 0.026457842276776833
Relative error (unmitigated): 0.07078763708873381
Relative error (mitigated with CDR): 0.026803206694106713
Error reduction with CDR: 62.1%.
结果显示误差减少了 60%以上。尽管这不如我们在以前的帖子中实现的模拟测量的缓解效果好,但相对于未缓解的结果来说,这是一个不错的改进。
量子机器学习要不要入门?看看 动手量子机器学习用 Python 。
在这里免费获得前三章。
数据网格:使气候数据易于查找、使用和共享
原文:https://towardsdatascience.com/making-climate-data-easy-to-find-use-and-share-5190a0926407
准确、可用和一致的可互操作气候数据对于解决我们的全球气候危机至关重要。这是 OS-Climate 如何使用数据网格使气候数据更容易查找、使用、共享和管理。
卡塔丽娜·若昂在 Unsplash 上的照片
OS-Climate 正在应对气候和 ESG 数据挑战
OS-Climate (OS-C)正在应用开源功能来帮助组织有效地利用 ESG 数据。结合开源分析工具,企业、金融机构、政府和民间社会可以利用这些数据提高物理风险和弹性、转型分析以及投资组合与净零和适应目标的一致性等领域的透明度和洞察力。OS-C 的愿景是大幅推动全球资本流入气候变化缓解和复原领域。
OS-C 的使命是帮助组织确定他们是否在与巴黎协议目标和 ESG 价值观一致的公司中运营、投资或合作。为了做到这一点,OS-C 正在构建开源工具的技术基础,以组织和提供气候和 ESG 数据。
但道路上有一个根本性的潜在障碍:可信的气候 ESG 数据极难发现、消费、分享和管理。
为了克服这一障碍,OS-C 开始采用“数据网格”原则,目标是让 ESG 和气候数据更容易查找、使用、共享和管理。
这篇文章描述了 OS-C 的数据网格之旅。
可信的气候数据很难找到、消费、分享和管理
ESG 和气候数据是 OS-C 的基础促成因素。但是这些数据几乎每时每刻、每天每年都在变化:
- 随着基础设施被用来获取排放数据,以及越来越多的公司选择发布其排放数据,ESG 和气候数据的数量正在激增。
- 随着更多不同数据的出现,ESG 和气候数据的可变性呈指数级增长。
- ESG 和气候数据的价值正在急剧增加,因为监管机构和投资者都要求访问这些数据。
这种情况因为一个根本性的挑战而变得复杂:ESG 和气候数据很难发现、消费、共享和管理。ESG 和气候数据是:
- 分布在数千个数据源中,很难找到。
- 无结构,难以理解和消费。
- 不一致,难以共享、比较和汇总。
图 1,目标:使 ESG 和气候数据易于查找和使用
OS-C 的目标是克服这些障碍,使 ESG 和气候数据易于查找、消费、共享和管理。为了实现这一目标,OS-C:
- 创建一个数据产品生态系统(也称为数据网),使 ESG 和气候易于查找、理解和信任。
- 建立一个符合数据网格原则的联合来源和治理模型,将公共数据或任何人都可以使用的“开放”数据,以及在气候数据生产者和消费者之间强加定制合同的私人数据结合起来。
- 设计一个符合数据网格原则的“自助式”数据平台,以释放 ESG 和气候数据。
OS-Climate:建立环境、社会和治理/气候数据产品生态系统
数据网格现在正成为解决大型企业大规模数据挑战的一种现代方法。但是,使数据网格对企业如此有价值的原则也使它成为解决 OS-C 及其生态系统合作伙伴正在应对的数据挑战的理想方法。
但是什么是“数据网格”呢?首先也是最重要的一本书是扎马克·德赫加尼(Zhamak Dehghani)的《数据网格,提供大规模的数据驱动价值》(Data Mesh)。这里有许多很棒的文章这里(模式)、这里(架构)、这里(原则)和这里(经验教训),它们将提供关于数据网格更详细的技术信息。
出于我们的目的,我们将数据网格定义为一个交互“数据产品”的生态系统。简单地说,数据产品是将数据视为产品的一种方式。每个数据产品:
- 有一个清晰的边界,
- 参与生产者和消费者的生态系统,
- 使用一个平台来简化数据的查找、消费、共享和管理,
- 发布元数据,实现自助服务和发现,
- 遵守一套明确的治理标准,同时尊重地方自主权。
图 2,OS-C 正在介绍 ESG 和气候数据产品
我们应用数据网格原理的原因基于以下基本原理。
首先,应用数据网格原则将使 ESG 和气候数据产品更易于查找、消费、共享和管理:
- 数据网格定义了数据域,即可信的 ESG 和气候数据的公认边界和位置,这为在高度分散和多样化的 ESG 和气候数据环境中查找数据提供了至关重要的第一步。
- 数据网格识别数据域的所有者,这将使个人或团体能够提出如何使数据可消费。
- Data Mesh 实施了一种“自助式”模型,其中通过标准且一致的机制(例如,通过 API 或其他通用/开放方法)访问数据域**,从而简化了组织内部和组织之间的数据共享**。
- Data Mesh 提供了一个“联合”治理模型,该模型为数据所有者提供了最有效地尊重地区需求和多样化监管要求的本地自主权,同时还使数据的治理和可信度更容易联合。
其次,通过应用这些原则,OS-C 可以建立一个数据产品的生态系统(也称为数据网格),其中每个气候数据产品都是可发现的、可寻址的、可互操作的、自描述的、可信赖的、安全的。
OS-Climate 的 ESG 和气候数据产品生态系统将使用开源组件实现数据网格功能,包括:
- 使查找数据变得容易的数据目录,
- 使访问数据变得容易的通用通信服务,
- 具有明确界限和所有者的分布式“数据产品”,以实现快速决策,以及,
- 每个数据产品中的 API 使得访问和共享数据变得容易。
图 3,OS-C 的 ESG 和气候数据产品生态系统
借助这些数据网格功能,数据产品将使环境、社会和气候数据更易于查找、消费、共享和管理,这将带来更好、更快的洞察,缩短环境、社会和气候数据的上市时间,降低环境、社会和气候数据可用性和可访问性的成本和开销。
操作系统环境:建立联合采购模型
为了与数据网格联合治理原则保持一致,OS-C 正在建立一个“联合采购”模型,该模型结合了公共数据或任何人都可以使用的“开放”数据,以及在气候数据生产者和消费者之间强加定制合同的私有数据。
图 4,OS-C 的气候数据联合采购模型
OS-C 的联合采购模式将努力创建“共享”数据,我们在其中建立了有限使用的优先许可,从而使 ESG 和气候数据更易于消费和共享。我们预见到一种“气候数据即服务”的能力,这种能力默认是开放的,但有访问和使用封闭数据的明确许可。这种方法是仿照 IB1 (破冰船 1)建立的惯例。
OS-Climate:创建自助式 ESG 和气候平台
OS-C 正在建立一个“自助式”平台,使 ESG 和气候数据易于查找、消费和共享。
图 5,走向自助式 ESG 和气候数据平台
OS-Climate 预见了一个开源产品的生态系统,该生态系统提供“从模型到微服务”的能力,涵盖各种关键群体,包括商业领袖、数据科学家、开发人员和运营人员。流行的开源工具被集成到 OS-C 的 ESG 和气候数据价值链中,包括 Spark、Kafka、Jupyter、Trino 和 Prometheus 等等。
OS-C 的目标是让价值链的每个元素都以“自助”的方式构建,从而为全球 ESG 和气候数据社群提供本地自主权和控制权。
图 6,数据网格——支持 ESG/气候数据价值链
为了支持这些功能,OS-C 将投资一个“ESG/气候数据产品工厂”,该工厂拥有可重复的模板和流程,可以轻松构建、保护、部署和管理数据产品。
值得注意的是,OS-Climate 正在将这些数据产品开发为公共产品,这些产品不是为了与商业数据提供商的数据产品竞争,而是为了支持商业提供商的商业创新,如 OS-Climate 成员 S&P 全球和伦敦证券交易所集团
总结想法
气候变化是一场危机,影响着这个奇妙星球上的每一个人。OS-Climate 正在采取几个步骤,使组织、政府和非政府组织更容易发挥作用,帮助解决这场危机。
OS-Climate 是实践和行动的共同体。他们欢迎来自数据工程师、数据科学家、软件工程师、分析师、主题专家、产品领导和项目经理的贡献,无论贡献大小。加入我们来构建、发展和改进 OS-Climate,以实现更好的气候成果!联系 OS-Climate 或点击此处了解更多信息。
关于操作系统-气候
OS-Climate (OS-C)是一个非盈利、非竞争性的组织,其大胆的使命是使用开源工具来统一解决气候变化(我们时代的重大挑战之一)所需的 ESG 和气候数据。
OS-C 的愿景是根据所有相关醋酸领域和基于科学的方法学的最佳奖学金,将全球最佳可用数据、建模、计算和数据科学聚合到一个人工智能增强的物理经济模型中,该模型的功能类似于操作系统,能够在未来与过去截然不同的世界中为气候综合投资提供强大的应用。
OS-C 的目标是迅速加快全球投资从相对 GHG 密集型和易受气候影响的公司、技术和基础设施向财务上可持续和高影响力的缓解、复原和适应项目的转移,特别是在发展中国家,以及设计更好的政策,有效地让资本市场参与应对气候变化。
图 7,OS-Climate——用于 ESG 和气候数据的开源工具
OS-C 平台将加速基于情景的预测分析工具的开发,以管理与气候相关的风险,并加速投资产品的开发,以资助各个地区、部门和资产类别的气候解决方案。OS-C 开源实践和行动社区——使用 Linux Foundation 的高度发展的许可、结构化协作和社区管理工具——将服务于利益相关者社区,并使优先数据和建模需求方面的工作协调一致,将共享资源集中在执行这些优先事项上,并加快采用。
OS-C 是由跨越银行、资产管理公司和技术供应商的国际贡献者团体发起的。OS-C 正在使用开源技术开发满足巴黎协议气候目标所需的数据和工具。更多关于 OS-Climate 及其使命、愿景和路线图的信息可以在这里找到。
下面列出了其他操作系统环境资源:
- 通过开源和数据网格架构释放气候相关数据(文森特·卡尔代拉/埃里克·厄兰森,ODSC 西部,2021 年 11 月 17 日)
- 通过开源气候数据平台为气候融资行动提供动力(文森特·卡尔代拉/埃里克·厄兰森,2022 年红帽峰会)
Eric Broda 是 OS-Climate 的一名架构师,也是 Broda Group Software 的总裁,Broda Group Software 是一家致力于帮助企业加速数据网格之旅的精品咨询公司。
除非另有说明,本文中的所有图片均由 Eric Broda(本文作者)创作或经许可使用。图像中使用的图标是普通的 PowerPoint 图标,不受版权保护。
本文表达的观点仅代表我个人,并不代表我的客户的观点。
根据实时传感器数据做出决策
原文:https://towardsdatascience.com/making-decisions-from-live-sensor-data-1febf8db9464
物联网发展
如何在 Azure 中为实时数据分析设计可扩展的物联网架构
照片由 DeepMind 在 Unsplash 上拍摄
利用数据来洞察问题可能很困难。无论是您拥有的数据量、您可以收集数据的速度,还是数据本身的质量,成为数据驱动都有许多障碍。然而,还有另一个问题,也许是更基本的问题:你根本没有收集到做决定所需的信息。这个可能很难修复。
你经营一家工厂,你想知道你的生产线什么时候会出现故障。你在一家商店工作,想知道大楼里的人流量。你想知道你公寓的温度,这样你就可以相应地调节暖气。这有时是很难得到的数据!然而,有一个解决方案:有针对性地部署物联网(IoT)传感器,旨在收集做出决策所需的特定信息。
这篇文章就是关于这样做的实用性。我们将主要关注能够处理来自众多传感器的实时数据的软件堆栈的创建和部署。这个架构将必须能够分析数据,做出人工智能/人工智能预测,并最终可视化一切。这将集中在 Azure 技术上,尽管我确信有亚马逊(或其他云服务)的等价物。首先,我们将花一点时间预先讨论硬件考虑事项。
这篇文章讲述了我为了解如何管理和使用物联网设备而经历的旅程。该解决方案可能并不完美,但我希望它能让您在构建自己的架构时有所考虑。
本文基于我最近在东北数据科学会议上发表的演讲“从任何事物中获得洞察力”
五金器具
所以,如果我们想把传感器放在现实世界的某个地方,我们会有一些限制。天真地,我认为一个好的 Arduino 或 Raspberry Pi 是我所需要的。然而,现实世界有其他想法。以下是现实世界部署的一些考虑:(一)便宜吗?(ii)您如何为其供电?(iii)您如何将其连接到互联网?㈣安全吗?
一个树莓派可能看起来符合这些类别中的大部分!我花了很长时间开发一个电池供电、支持 3G、装满传感器的系统。但是,事实是这些大多是开发设备。要让工厂、政府大楼、学校等舒适地容纳一大群人是很困难的。它是 IP67 等级的吗?即使在一种情况下,是否防尘?耐摔?你确定电池安全吗?(事实上,锂离子电池相当危险!).坦率地说,一个有电缆和传感器的电路板会让人紧张——他们想知道这是不是合法的?挑战不胜枚举!带试验板的 Raspberry Pi 和生产就绪系统之间的差距是巨大的。当你跨过那道鸿沟时,你可能会意外地发现自己进入了硬件行业。我很晚才知道这个。
这里的解决方案是找到一个生产就绪的传感器设置。我了解了传感器制造商蒙尼特公司。他们提供了一个集线器,通过 3G sim 卡连接到 Azure。理想—我们不需要连接一个公司的 WiFi(把陌生的设备连接到 WiFi 会让网络安全的人很紧张!)该集线器还连接到一组传感器,从温度/湿度到二氧化碳和加速度计。
Monnit 设备有各种各样的传感器可用于与集线器通信。图片作者。
这些传感器具有超长的电池寿命,Monnit 声称(我的测试也支持这一点),它们可以在不充电的情况下持续多年。理想——没有烦人的电缆挂在周围,也不需要人们不断给它们充电!重要的是,它们很便宜,符合工业标准。谈到物联网数据收集,这些是我首选的硬件。
因此,例如,我们可以将加速度计连接到工业锯上来测量振动(一个很好的故障指示器),并让传感器将振动发送到 Monnit hub,mon nit hub 又将数据放入 Azure 进行分析。
软件
硬件决定后,我们需要整理软件。我们的软件解决方案需要考虑多个方面,而使用 1 Raspberry Pi 的较小项目可能不会考虑这些方面:(I)我们如何处理多个设备及其设置,(ii)我们如何处理和存储传入的实时数据,以及(iii)如何使其可扩展,确保它可以与 1 或 1,000 个传感器一起工作。
为此,我们将完全留在 Azure 生态系统中。让我们先来看看我们的架构:
提议的系统架构。图片作者。
我们将这个过程分成三个独立的部分。这样做的目的是有一个通用的“中间”层,清洁的传感器数据和其他实时数据最终可以到达该层。对于在这里访问它的用户来说,它现在应该是“通用的”——他们不需要关心硬件本身。我们可以进出其他传感器或 API 调用,访问数据的人不会受到影响。
让我们浏览一下架构,并解释这些服务是什么以及为什么选择它们。
流式数据输入
- 物联网中枢:这是 Azure 为物联网设备管理提供的服务。在这里,我可以注册设备并控制它们的固件。它还允许我向设备发送信息,并接收信息。在这种情况下,我收到的信息是来自传感器的数据。
- 功能:微软将其描述为“功能即服务”(为什么现在什么都是服务了?).函数是无服务器的计算,允许你编写由一个动作触发的代码,而不需要处理它将以任何方式运行的硬件。留在 Azure 栈中的好处是你不必硬编码文件路径(通常)。通常,你可以将它指向另一个 Azure 服务,并告诉该函数在该服务被使用时触发。例如,当物联网中心收到消息时,或者当文件被放入存储时,可以触发功能。此外,它们非常便宜——一些粗略的数学计算表明,2 亿次函数调用可能花费 35 美元左右。
在这个架构中,我们使用了一个函数来清理一些特别杂乱的数据(稍后会详细介绍)
通用事件处理
- Event Hub :这是(非常松散的),没有设备管理的物联网 Hub。我们希望有地方存放所有的数据。某个通用的地方,其他开发人员可以调用它来访问我们收集的信息,不管它来自什么物联网设备,甚至完全来自另一个来源(即调用 API 来获取实时天气数据)。
已经被清理并落在这里的事件现在可以进行分析了。
现场/历史
- 流媒体分析:微软的命名惯例有时会非常字面化。流分析(SA)是一个处理流数据分析的平台。如果你看看微软为物联网设置提供的大多数架构图,SA 通常直接连接到物联网 Hub。然而,我们发现这很难做到。该服务使用一种类似 SQL 的语言,这种语言非常严格,我们发现在 s a 中清理数据和执行分析是不可能的。有人建议我们开始将一个 SA 实例通过管道传输到另一个,但这很快变得非常昂贵。因此,在 SA 执行分析之前,清理由函数完成。
SA 使用的语言非常适合执行窗口和聚合等操作。你可以快速形成移动平均线,并为下一阶段准备数据:可视化 - Power BI :到目前为止,一切都是现场发生的:SA 由 Event Hub 触发,由 Functions 活动触发,Functions 活动正在观察物联网 Hub 的输出。我们将继续让 Power Bi 实时绘制数据。Power Bi 是模型优先的 Bi 解决方案,而不是可视化优先的 Bi 解决方案。我认识的一位非常聪明的 Bi 开发人员曾经说过这句话,我听得一清二楚,现在我还在不停地重复这句话。我认为 Power Bi 有一个非常强大的建模引擎和一个强大的查询语言(DAX)。最后,这也是非常好的观想。
台式机上的 Power Bi 有一些刷新限制,但有一个仅在线的仪表盘版本是真正实时的。您可以将流分析直接指向这个实时仪表盘,从而简化集成。在上图中,我们定期将数据存入存储器。这在传统的电力商业智能中显露出来。
这种设置允许我们处理实时数据和历史数据。它允许我们从同一系统访问物联网数据和其他实时来源。拥有物联网中心让我们能够进行设备管理。最后,Azure 工具具有很好的可扩展性——函数调用既便宜又快速,你可以在物联网中心免费获得成千上万条消息,Power Bi 将能够处理巨大的数据集。
获得洞察力
到目前为止,我们的解决方案可以处理实时数据,并通过流分析对其进行深入分析。然而,下一步是用 Azure 让我们访问的机器学习堆栈进行更深入的数据分析。
Azure 机器学习,或时间序列分析,是将数据科学应用于物联网数据的好选择。图片作者。
我觉得有趣的两项服务是:
- 时间序列分析:同样,微软的命名惯例非常字面化。时间序列分析(TSA)正如它所说的那样:它允许您将预先构建的标准时间序列分析技术应用于时间序列数据。事实上,您可以直接将其指向 IoT Hub 的一个实例。我在这里的保留来自于使用流分析:关于集成到物联网中心是多么容易的承诺,我最终需要增加功能。这一点也会如此吗?
- Azure 机器学习 : Azure 的机器学习服务棒极了。它是我开发机器学习的首选工具。它具有大量的功能(模型跟踪、管道和几乎所有现代 ML 平台的功能),是实现机器学习模型的好选择。您可以将它指向存储,并使用它对我们刚刚收集的历史数据执行分析。结果可能会在 Power Bi 中浮出水面
正如我在开始时说的,这也是我正在进行的一段旅程。这个末端部分,尖锐的分析部分,是最不发达的。然而,Azure 为探索提供了很多选择。
结论
很难洞察你想要理解的每一个过程。有时候,你根本没有数据来做你想做的决定。然而,在某些情况下,您可以部署廉价、灵活、非侵入性的物联网设备来执行高度有针对性的数据收集。这些数据可以用来帮助你做出正确的决定。
本文的大部分内容都在讨论我构建的软件栈,它用来处理从各种不同传感器收集的实时数据。它非常关注 Azure 技术。它利用物联网中心来处理设备和数据登陆,利用功能来清理一切,利用流分析来获得一些见解,并利用 Bi 来实现实时数据可视化。
展望未来,Azure 提供了两个(可能不止这些!)有趣的工具来自动化决策,并从我们收集的数据中获得更多知识:时间序列分析和机器学习。展望未来,我期待着玩这些更多!
我希望你喜欢这篇文章,并且对创建你自己的架构有用!
安德鲁·布朗斯
请在 Medium 上订阅我,以跟踪任何令人兴奋的未来文章!也可以在这里联系: LinkedIn | Twitter
让深度学习变得气候友好
原文:https://towardsdatascience.com/making-deep-learning-climate-friendly-74ed4f2404e2
如何解决人工智能的碳足迹
佩吉·安克在 Unsplash 上的照片
深度学习正在给世界带来许多好处:解决 50 年的蛋白质折叠问题,检测癌症,改善电网。虽然深度学习推动了很多事情,但我们也需要考虑成本。
在寻求更精确和更通用的模型的过程中,网络规模和功耗出现了爆炸式增长。似乎每年模特的尺寸都在飙升。2020 年,GPT-3 发布,拥有1750 亿参数和 460 万美元的理论培训成本,拥有市场上价格最低的 GPU 云。
不到一年后,谷歌发布了参数超过万亿的 Switch Transformer。OpenAI 发现,同类最佳大型模型所需的计算能力每 3.4 个月翻一番(六年内翻了 300,000 倍)。随着参数的指数增长,能量也有了指数增长。
人工智能研究人员和工程师如何解决这个问题?
迪伦·吉利斯在 Unsplash 上的照片
神经架构
常识告诉我们,模型越大,结果越好。然而,情况并非总是如此。谷歌研究刚刚发布了一份元分析,展示了更高的预训练准确度如何达到饱和点。随着模型精度的提高,其被微调以解决特定任务的能力*(即迁移学习)*并没有同样快速地提高。在某些时候,精度甚至会降低。
当您开始质疑更大的模型是否总是答案时,探索改进当前架构的机会就来了。更多的努力被放在架构选择上,像 进化算法这样的工具被用来为一项任务选择最佳的神经架构,从而大大减少了所需的计算量。
另一个引起兴趣的领域是彩票假说,其中神经网络的密集子网络(“彩票”)可以被训练以实现与整个网络类似的性能。以前,这只能在训练完整个网络后才能完成,但是我在这里详细讨论了这些彩票是如何更早地被发现的。
这些方法挑战了越大越好的观念。与此同时,他们跳过了训练大型模型的成本,只修剪模型的大部分。相反,他们直接跳到最佳架构,使训练模型更便宜、更快、更节能。
数据移动
虽然训练大型模型消耗大量的能量,但是大部分能量都用于推理(80–90%)。理想情况下,像 ResNet50 这样的模型被训练一次,然后在应用程序中使用多次。结果,大量的努力进入了推理阶段。这里最大的领域是数据传输。缩小我们的网络规模是有帮助的,但之后,无论如何数据都必须通过。这有软件和硬件方法。
照片由艾哈迈德·奥德赫在 Unsplash 上拍摄
软件
在软件方面,数据类型选择是关键。虽然某些应用程序可能需要更高精度的数据类型,但在其他应用程序中,它们可能不会提高模型性能。精度较低的数据类型需要传输的数据较少,比如 16 位浮点数与 32 位浮点数。不同的公司正在创建不同的数据类型,进一步减少数据大小和处理需求,如微软的 MSFP。
经常运行的模型也可以利用**批处理。**批量发送请求通常比发送单个请求或数据包更有效。
硬件
在硬件方面,确保数据紧密相连非常重要。深度学习服务器和客户端之间的共享内存比来回发送更有效。在你的 iPhone 上做边缘机器学习更高效(也更安全!)比把这些数据发送到苹果的数据中心要容易得多。数据越接近,需要传输的数据就越少。
也有针对不同深度学习应用的专门芯片,如 Eyeriss ,它通过可重构的片上网络最大化数据重用。
压缩也常用于最小化传输的数据,因为压缩数据比直接发送数据需要更少的资源。这对于稀疏网络来说尤其如此,在稀疏网络中,许多技巧被用来利用稀疏性。稀疏也是剪枝的一个常见动机,因为在存储和计算中 0 值的条目通常可以被忽略。
克林特·王茂林在 Unsplash 上拍摄的照片
数据存储
除了数据移动,数据存储是另一个丰富的硬件节能领域。当深度学习从大脑中获取灵感时,研究人员会查看大脑的能效。大脑的效率高达深度学习的 1000 倍。不像我们数字计算机世界的 1 和 0,大脑的突触看起来更像模拟系统。随着大脑学习和遗忘,信号变得越来越弱。利用这种更加连续的模拟光谱,研究人员已经创造出更接近大脑能量效率的系统(T21)。
以类似的方式,其他形式的计算正在被探索。比如存储光子而不是电子,一次以多种状态表示数据(量子计算)。这些可能是更有效的存储数据的方式。
结束语
深度学习已经为世界做了很多好事,它有潜力做得更多。随着人工智能使用的增长,提高其效率变得至关重要。研究人员正在将神经结构和大小视为一个关键领域。与此同时,他们正在探索降低数据存储和数据传输成本的方法,影响了大多数技术,包括你正在上阅读这篇文章的设备。
在 Matplotlib 中制作经济学家风格的绘图
原文:https://towardsdatascience.com/making-economist-style-plots-in-matplotlib-e7de6d679739
作者照片
在 Matplotlib 中制作经济学家风格的绘图
推动 Matplotlib 制作精美易读的剧情
你可以在 Deepnote 上找到这篇文章的 Jupyter 笔记本。
如果你喜欢数据可视化,你可能已经在他们的网站或杂志上看到过《经济学人》的的图表。几十年来,它们以简单有效的可视化著称,以简单的方式传达有用的信息。在过去的几年里,我一直被他们的形象所鼓舞。无论我是在 Kindle 上、在网上还是在移动设备上看到它们,它们总是引人入胜,而且能让人理解。这是所有数据科学家在他们自己的可视化中应该追求的东西。
2018 年 10 月,《经济学人》为他们的数据可视化发布了一个专门的博客,名为图形细节。如果你浏览他们的文章,你会发现他们的风格在不同的图表类型中保持一致。这并不是因为他们是一个尽可能快的制造可视化的工厂。相反,这种一致的风格旨在简化它们的可视化,并尽可能快地向读者提供关键信息。
作为一名专注于 Python 的数据科学家,在我的日常工作中,我几乎只使用 Matplotlib 进行数据可视化。我喜欢它,因为它是难以置信的可定制的。每一个元素都在你的掌控之中。虽然我肯定《经济学人》的数据记者使用 Matplotlib 进行探索工作或实体模型(或 R 的 ggplot),但我非常怀疑他们只使用 Matplotlib 进行生产可视化。
然而,我非常喜欢将尽可能多的工作流程放在一个地方。这种想法启发我用 100% Python 和 Matplotlib 重新创建了几个经济学家的图表类型。这个项目我有两个目标。首先,我想举例说明如何在 Matplotlib 中执行出色的图表设计。其次,我想展示一个编程绘图库可以用来创建生产绘图。
请注意,我不会为此制作可重用的函数或库。相反,如果您感兴趣,我希望您可以复制任何您可能想要的代码,并自己重用它。
我将重现《经济学人》常用的三种图表类型:条形图、折线图和哑铃图。但在此之前,让我们先来分解一下经济学家的可视化的关键要素。
请注意,这样做只是为了教育和信息的目的。
《经济学家》的风格
为了了解《经济学人》的可视化风格,我们可以使用他们 2017 年的视觉风格指南。这里是环节。让我们来分析一下《经济学人》风格的常见元素。
标题和副标题
图表通常都有标题和副标题。标题通常是文字游戏,比如食品价格上涨的“不开心的一餐”。副标题将是图表内容的简单陈述。它们总是位于左上方,与图表内容的左边缘齐平。标题总是加粗,比副标题大。
网格线
仅显示水平网格线。次要网格线根本不会显示。网格线的间距符合数据的逻辑,易于理解。
轴和刻度标签
仅显示了水平轴。除此之外,图表数据周围没有其他边界框。在 x 轴上,刻度(标签旁边的小线条)仅在需要显示哪些数据连接到哪个刻度标签时显示。y 轴上没有刻度,只有主要的网格线。y 轴刻度标签位于图表区内主要网格线的顶部。
水平条形图是一个例外。在这种情况下,显示了 y 轴,但没有 x 轴。y 轴刻度标签位于数字的正上方。
颜色;色彩;色调
《经济学人》使用两个图表调色板,一个用于网络,一个用于印刷。我们将把重点放在网络上。主色“经济红”(#E3120B)用于顶行和标签框。样式指南中的灰色(#758D99)似乎用于网格线。用于绘图的颜色有:
- 红色,#DB444B
- 蓝色,#006BA2
- 青色,#3EBCD2
- 绿色,#379A8B
- 黄色,#EBB434
- 橄榄,#B4BA39
- 紫色,#9A607F
- 金色,#D1B07C
风格指南中有更多的调色板用于不同的用途,但这是主要的调色板。每种图表类型的颜色顺序都是不同的,下面我将尝试重现这一点。所有地块将使用白色背景的网络样式。
其他文体要点
一条“经济学人红色”的水平线贯穿图表的整个顶部。一个水平的矩形,他们称之为标签,位于左上角。数据源以 75%的不透明度显示在左下角。
我们可以在下面的章节中讨论特定于图表的样式。
条形图
让我们按照《经济学人》的风格制作一个条形图!下面是《经济学人》中条形图的一个例子。我们将使用来自 Kaggle 数据集的按国家分列的 2020 年 GDP 值(链接)。该图表的目的是按 GDP 值显示排名前 5 位的国家。
《经济学人》通常只使用一个垂直条形图,其中一个或两个分类变量是随着时间进行比较的。对于我们正在处理的数据,水平条形图更合适。这是因为国家名称比较长,如果不旋转的话,它们在 x 轴上不太合适。这将使它们更难阅读。解决方案是将数据推过一些,并将标签放在 y 轴上。
让我们导入pandas
和matplotlib
并读入我们的数据。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt# This makes out plots higher resolution, which makes them easier to see while building
plt.rcParams['figure.dpi'] = 100gdp = pd.read_csv('data/gdp_1960_2020.csv')
gdp.head()
这里的 GDP 数据很长。为了便于阅读,我们可以将 GDP 除以 1 万亿。
gdp['gdp_trillions'] = gdp['gdp'] / 1_000_000_000_000
现在,我们只能过滤到 2020 年,并抓住底部 9。我们这样做,而不是按降序排序,因为 Matplotlib 是从下到上绘制的,所以我们实际上希望数据的顺序相反。
gdp[gdp['year'] == 2020].sort_values(by='gdp_trillions').tail(9)
现在我们可以构建我们的图表了。代码在这里有注释。
以及由此产生的图:
作者照片
哇,代码真多!那里有很多东西,值得花时间一行一行地去理解,用评论来理解。
现在让我们继续看折线图。
折线图
折线图可能是《经济学人》中最具标志性的图表。下面是《经济学人》的一个折线图例子。
我们将使用与上述相同的国内生产总值数据,创建一个按国内生产总值排名前 9 名的国家的时间序列图。为了突出中国所走的道路,我们只会用醒目的颜色显示美国和中国,其他 7 个为灰色。
首先,我们可以从前面的代码中得到我们想要过滤的国家的名称。
countries = gdp[gdp['year'] == 2020].sort_values(by='gdp_trillions')[-9:]['country'].valuescountries# Output
array(['South Korea', 'Canada', 'Italy', 'France', 'India',
'United Kingdom', 'Germany', 'China', 'United States'],
dtype=object)
Matplotlib 在时间序列的日期时间方面做得很好,所以我们可以将日期列转换为熊猫日期时间序列。
gdp['date'] = pd.to_datetime(gdp['year'], format='%Y')
我们的数据已经准备好了,开始密谋吧!
作者照片
这次会有更多的代码。通常情况下,《经济学人》的图表更为方形,尤其是印刷版。我决定把它做得更宽,以便更适合一篇文章本身。
哑铃图
最后一个图表,我们做一个哑铃图。这些图表用于显示分类变量的差异,通常是随着时间的推移。这里有一个来自经济学家的哑铃图的例子。
我们将再次使用 GDP 数据。这次是为了显示前 9 个国家从 1960 年到 2020 年的 GDP 差异。
gdp_dumbbell = gdp[(gdp['country'].isin(countries)) & ((gdp['year'] == 1960) | (gdp['year'] == 2020))].sort_values(by='gdp')gdp_dumbbell.head(5)
作者照片
作者照片
结论
虽然我们已经看到在 Matplotlib 中重现经济学人的图表风格是可能的,但这肯定不容易。对于每个图,都需要几十行代码,每个图都经过几次反复试验才得到上面看到的结果。
正如您在代码中看到的,Matplotlib 中数据的绘制相当简单。然而,对齐文本、行和标签需要手动输入非常具体的自定义位置。这是上面代码中值得改进的一点。
这是一次很棒的经历,在这个过程中我学到了一些关于 Matplotlib 的东西。该库的定制能力继续让我惊叹。也许在将来,我会在 Matplotlib 中构建一个更具重现性的方法来创建打印质量图表。
感谢阅读。我主要在 datafantic.com 的写文章,所以一定要看看我写的更多文章。
如果你喜欢媒体,并且想直接支持我的文章,你可以使用我的推荐链接注册成为媒体会员。
参考文献
- 所有国家的 GDP(1960-2020)通过 Kaggle.com。在 GPL v2 许可下发布。
让应用程序开发人员更容易使用机器学习
原文:https://towardsdatascience.com/making-machine-learning-more-accessible-for-application-developers-9765f90c6b22
Towhee 可以帮助加速机器学习应用的开发
许海波在 Unsplash 上拍照
介绍
试图手工制作算法来理解人类生成的内容通常是不成功的。例如,计算机很难仅仅通过分析图像的低级像素来“掌握”图像的语义内容,例如汽车、猫、大衣等。颜色直方图和特征检测器在一定程度上发挥了作用,但对于大多数应用来说,它们很少足够精确。
在过去的十年里,大数据和深度学习的结合从根本上改变了我们处理计算机视觉、自然语言处理和其他机器学习(ML)应用的方式;从垃圾邮件检测到逼真的文本到视频合成,各种任务都取得了令人难以置信的进步,某些任务的准确性指标达到了超人的水平。这些改进的一个主要积极副作用是增加了嵌入向量的使用,即通过在深度神经网络中获取中间结果而生成的模型工件。OpenAI 的文档页面给出了一个很好的概述:
嵌入是一种特殊的数据表示格式,可以很容易地被机器学习模型和算法利用。嵌入是一段文本的语义的信息密集表示。每个嵌入是浮点数的向量,使得向量空间中两个嵌入之间的距离与原始格式的两个输入之间的语义相似性相关。例如,如果两个文本相似,那么它们的矢量表示也应该相似。
下表显示了三个查询图像以及它们在嵌入空间中对应的前五个图像(我使用了 Unsplash Lite 的前 1000 个图像作为数据集):
作者图片
你在上面看到的这些结果是基于 ResNet50 的图像嵌入模型生成的,这是一个众所周知的纯卷积图像分类模型。嵌入不仅限于图像,还可以为各种不同类型的非结构化数据生成,包括图像、音频、时序数据和分子结构。将多种不同类型的数据嵌入同一个空间的模型,也就是通常所说的多模态嵌入模型,也已经存在,并且正在越来越多的应用中使用。
正如我们将在接下来的两节中看到的,生成这些高质量的嵌入可能很困难,尤其是在大规模的情况下。
训练嵌入任务的新模型
理论上,训练一个新的 ML 模型并使用它生成嵌入听起来很容易:采用最新的和最好的预建模型,由最新的架构支持,并用一些数据训练它。很简单,对吧?
没那么快。从表面上看,使用最新的模型架构来实现最先进的结果似乎很容易。然而,这与事实相去甚远。让我们来看看一些与训练嵌入模型相关的常见陷阱(这些也适用于一般的机器学习模型):
- 没有足够的数据:在没有足够数据的情况下,从头开始训练一个新的嵌入模型,容易出现过拟合的现象。在实践中,只有最大的全球性组织有足够的数据来从头开始培训一个新的模型是值得的;其他人必须依靠 微调 ,这是一个过程,在这个过程中,具有大量数据的已经训练好的模型然后使用较小的数据集进行提取。
- 差超参数选择 : 超参数为常量,用于控制训练过程,如模型学习的快慢或单批训练使用多少数据。微调模型时,选择一组适当的超参数极其重要,因为某些值的微小变化可能会导致非常不同的结果。该领域的最新研究还显示,使用改进的训练程序从头开始训练相同的模型,ImageNet-1k 的准确性提高了 5%以上(这是一个很大的进步)。
- 高估自我监督模型:术语自我监督指的是一种训练程序,其中输入数据的“基本面”是通过利用数据本身来学习的,而不需要标签。一般来说,自监督方法非常适合预训练(即,在用较小的标记数据集对模型进行微调之前,用大量未标记数据以自监督方式训练模型),但直接使用自监督嵌入可能会导致次优性能。
解决上述所有三个问题的一个常见方法是,首先使用大量数据训练一个自我监督的模型,然后根据标记的数据对模型进行微调。这已经证明对 NLP 很有效,但是对 CV 还没那么有效。
使用嵌入模型有其自身的缺陷
这些只是与训练嵌入模型相关联的许多常见错误中的一些。这样做的直接结果是,许多寻求使用嵌入的开发人员直接使用学术数据集上预先训练的模型,如 ImageNet (用于图像分类)和 SQuAD (用于问题回答)。尽管目前有大量的预训练模型可用,但为了提取最大的嵌入性能,应该避免几个陷阱:
- 训练和推理数据不匹配:使用由其他组织训练的现成模型已经成为一种流行的方式来开发 ML 应用程序,而不需要数千个 GPU/TPU 小时。理解特定嵌入模型的局限性以及它如何影响应用程序的性能是极其重要的;如果不了解模型的训练数据和方法,很容易误解结果。例如,一个被训练来嵌入音乐的模型在应用于语音时会表现不佳,反之亦然。
- 层选择不当:使用全监督神经网络作为嵌入模型时,特征一般取自激活的倒数第二层(正式名称为倒数第二层)。然而,这可能导致次优性能,具体取决于应用。例如,当使用为图像分类而训练的模型来嵌入徽标和/或品牌的图像时,使用较早的激活可能会导致性能提高。这是因为较好地保留了对非复杂图像分类至关重要的低级特征(边缘和拐角)。
- 不同的推理条件:训练和推理条件必须完全相同,以从嵌入模型中提取最大性能。实际上,情况往往并非如此。例如,来自
[torchvision](https://pytorch.org/vision/stable/index.html)
的标准resnet50
模型在使用双三次插值与最近邻插值进行下采样时会产生两种完全不同的结果(见下文)。
照片由 Patrice Bouchard 在 Unsplash 上拍摄
作者图片
部署嵌入模型
一旦您跨越了与培训和验证模型相关的所有障碍,扩展和部署它就成为了下一个关键步骤。同样,嵌入模型部署说起来容易做起来难。 MLOps 是与 DevOps 相邻的一个油田,专门用于此目的。
- 选择正确的硬件:与大多数其他 ML 模型类似,嵌入式模型可以在各种不同类型的硬件上运行,从标准的日常 CPU 到可编程逻辑(FPGAs)。完整的研究论文分析了成本与效率之间的权衡,强调了大多数组织在这方面面临的困难。
- 模型部署平台:有很多 MLOps 和分布式计算平台可用(包括很多开源的)。弄清楚这些如何适合您的应用程序本身就是一个挑战。
- 嵌入向量的存储:随着应用程序的扩展,您需要为您的嵌入向量找到一个可扩展的、更持久的存储解决方案。这就是 矢量数据库 的用武之地。
我自己学着做!
我为你的热情喝彩!需要记住几个关键的事情:
ML 与软件工程非常不同:传统的机器学习源于统计学,这是一门与软件工程非常不同的数学分支。正则化和特征选择等重要的机器学习概念都有很强的数学基础。虽然用于训练和推理的现代库( PyTorch 和 Tensorflow 是两个众所周知的库)已经使得训练和生产嵌入模型变得非常容易,但是理解不同的超参数和训练方法如何影响嵌入模型的性能仍然非常重要。
学习使用 PyTorch 或 Tensorflow 可能并不直观:如前所述,这些库大大加快了现代 ML 模型的训练、验证和部署。对于经验丰富的 ML 开发人员或熟悉 HDL 的程序员来说,构建新模型或实现现有模型可能非常直观,但对于大多数软件开发人员来说,潜在的概念可能很难理解。还有选择哪个框架的问题,因为这两个框架使用的执行引擎有相当多的差异(个人推荐 PyTorch)。
找到一个适合你的代码库的 MLOps 平台需要时间:这里有一个 MLOps 平台和工具的精选列表。有数百个不同的选项可供选择,评估每个选项的利弊本身就是一个长达数年的研究项目。
说了这么多,我想把我上面的陈述修改为:我为你的热情鼓掌,但是我不建议学习 ML 和 MLOps 。这是一个相当漫长而乏味的过程,会占用最重要的事情的时间:开发一个用户会喜欢的可靠的应用程序。
用 Towhee 加速数据科学
Towhee 是一个开源项目,它帮助软件工程师开发和部署应用程序,这些应用程序仅用几行代码就利用了嵌入式技术。Towhee 为软件开发人员提供了开发他们自己的 ML 应用程序的自由和灵活性,而不必深入嵌入模型和机器学习。
一个简单的例子
一个Pipeline
是由几个子任务组成的单个嵌入生成任务(在 Towhee 中也称为Operators
)。通过在Pipeline
中抽象出整个任务,Towhee 帮助用户避免了上面提到的许多嵌入生成陷阱。
>>> from towhee import pipeline
>>> embedding_pipeline = pipeline('image-embedding-resnet50')
>>> embedding = embedding_pipeline('https://docs.towhee.io/img/logo.png')
在上面的示例中,图像解码、图像变换、特征提取和嵌入归一化是编译到单个管道中的四个子步骤——您无需担心模型和推理细节。Towhee 为各种任务提供了预构建的嵌入管道,包括音频/音乐嵌入、图像嵌入、人脸嵌入等等。有关管道的完整列表,请随时访问我们的 Towhee hub 。
方法链接 API
Towhee 还提供了一个名为DataCollection
的 Pythonic 非结构化数据处理框架。简而言之,DataCollection
是一个方法链 API,它允许开发者在真实世界的数据上快速构建嵌入和其他 ML 模型的原型。在下面的例子中,我们使用DataCollection
通过resnet50
嵌入模型计算嵌入。
对于这个例子,我们将构建一个“应用程序”,让我们用一个数字3
来过滤质数:
>>> from towhee.functional import DataCollection
>>> def is_prime(x):
... if x <= 1:
... return False
... for i in range(2, int(x/2)+1):
... if not x % i:
... return False
... return True
...
>>> dc = (
... DataCollection.range(100)
... .filter(is_prime) # stage 1, find prime
... .filter(lambda x: x%10 == 3) # stage 2, find primes that ends with '3'
... .map(str) # stage 3, convert to string
... )
...
>>> dc.to_list()
DataCollection
可用于开发整个应用程序,只需一行代码。下一节将展示如何使用DataCollection
开发一个反向图像搜索应用程序——继续阅读了解更多。
Towhee 培训师
如上所述,完全或自我监督的训练模型通常擅长于一般的任务。然而,你有时会想要创建一个嵌入模型,它擅长于一些非常具体的事情,例如区分猫和狗。Towhee 专门为此提供了一个培训/微调框架:
>>> from towhee.trainer.training_config import TrainingConfig
>>> training_config = TrainingConfig(
... batch_size=2,
... epoch_num=2,
... output_dir='quick_start_output'
... )
您还需要指定一个数据集进行训练:
>>> train_data = dataset('train', size=20, transform=my_data_transformer)
>>> eval_data = dataset('eval', size=10, transform=my_data_transformer)
一切就绪后,从现有运营商那里培训新的嵌入模型是小菜一碟:
>>> op.train(
... training_config,
... train_dataset=train_data,
... eval_dataset=eval_data
... )
一旦完成,您就可以在应用程序中使用相同的操作符,而无需更改其余的代码。
作者图片
一个示例应用程序:反向图像搜索
为了演示如何使用 Towhee,让我们快速构建一个小型的反向图像搜索应用程序。反向图像搜索是一个众所周知的。让我们开始吧:
>>> import towhee
>>> from towhee.functional import DataCollection
对于这个示例应用程序,我们将使用一个小数据集和 10 个查询图像(此处的可用)。使用DataCollection
,我们可以加载数据集和查询图像:
>>> dataset = DataCollection.from_glob('./image_dataset/dataset/*.JPEG').unstream()
>>> query = DataCollection.from_glob('./image_dataset/query/*.JPEG').unstream()
下一步是计算整个数据集集合的嵌入:
>>> dc_data = (
... dataset.image_decode.cv2()
... .image_embedding.timm(model_name='resnet50')
... )
...
这一步创建嵌入向量的本地集合—数据集中的每个图像一个。这样,我们现在可以查询最近的邻居:
>>> result = (
... query.image_decode.cv2() # decode all images in the query set
... .image_embedding.timm(model_name='resnet50') # compute embeddings using the `resnet50` embedding model
... .towhee.search_vectors(data=dc_data, cal='L2', topk=5) # search the dataset
... .map(lambda x: x.ids) # acquire IDs (file paths) of similar results
... .select_from(dataset) # get the result image
... )
...
我们还提供了一种使用 Ray 部署应用程序的方法。只需指定query.set_engine('ray')
就可以了!
结束语
最后一点:我们并不认为 Towhee 是一个成熟的端到端模型服务或 MLOps 平台,这也不是我们的目标。相反,我们的目标是加速需要嵌入和其他 ML 任务的应用程序的开发。有了 Towhee,我们希望能够在您的本地机器上实现嵌入模型和管道的快速原型化(Pipeline
+ Trainer
),允许仅用几行代码(DataCollection
)开发一个以 ML 为中心的应用程序,并允许轻松快速地部署到您自己的集群中(通过 Ray )。
我们一直在寻找人们加入我们的开源社区——如果你对此感兴趣,请随时通过 Slack 和 Twitter 与我们联系。
这就是所有的人——希望这篇文章是有益的。如果您有任何问题、意见或顾虑,请在下面留下您的评论。敬请关注更多内容!
原载于 2022 年 4 月 9 日https://frankzliu.com。
用文字制作图片
原文:https://towardsdatascience.com/making-pictures-with-words-9aac0b97b356
人工智能文本到图像生成器的崛起
当我第一次听这首歌的时候,我还很年轻。我觉得这是一首有趣又朗朗上口的歌,就像一首顺口溜,尽管我不知道这首歌是关于什么的。但这没什么,我只是一个小男孩,听着收音机,看着录像机上的录像。我真的没有太注意标题或歌词。
Dall-E 2 使用文本提示创建的图像“视频杀死了电台明星,在用迷幻灯光演唱 80 年代流行歌曲风格的乐队中演奏”
很久以后,我才意识到歌词的真正含义。
视频杀了电台明星
视频杀了电台明星在我的脑海里,在我的车里
我们不能倒带,我们已经走得太远
画面传来,伤透了你的心
归咎于录像机视频杀了电台明星(道恩斯/伍利/霍恩)
这首歌是塑料时代专辑的一部分,该专辑的主题是怀旧和对现代技术影响的焦虑。虽然这张专辑发行于 40 多年前(1980 年),但这些主题仍然真实而清晰。
生成图像
当抖音开始在他们的应用程序中推出时,你就知道一项技术已经进入了大联盟。仅在本周,我就阅读了几份人工智能文本到图像生成器的前 10 名名单。这项技术炙手可热,模型和创业公司一夜之间如雨后春笋般涌现。每隔一周或一个月,整个思维方式都会被推翻,昨天令人兴奋的技术已经是旧闻了。
最知名的文本到图像 AI 模型当然是 2021 年 1 月发布的 OpenAI 的 Dall-E 。OpenAI 在 2021 年 12 月发布了另一款机型 GLIDE(没事,后面我再说首字母缩写)而之后没多久, Dall-E 2 在 2022 年 4 月公布。至少可以说,发布的速度非常快。
图片由 Dall-E 2 创建,使用文本提示“一名宇航员以逼真的风格租用一匹马”
谷歌备受期待的 Imagen 于 2022 年 6 月发布,尽管它尚未发布,但大量媒体报道已经开始称赞它如何完全超越了王者 Dall-E 2 。
在这股浪潮中, Meta(脸书)于 2022 年 7 月发布了他们的文本到图像工具,Make-A-Scene 。Make-a-Scene 与中的其他场景稍有不同,因为它允许用户根据自己的选择用一个简单的草图来补充文本提示。
就连微软也赶了上来,宣布推出女娲-Infinity ,这是一种人工智能模型,可以从任何给定的文本、图像或视频输入中生成高质量的图像和视频。
是的,视频。
然而,大公司并没有垄断文本到图像的淘金热。在同一个领域有很多小型创业公司。 Midjourney 是一家小型创业/研究实验室(不到 10 人),拥有一批使用其工具创作艺术作品的用户。它于 2022 年 7 月公开测试。
睡前咖啡是另一个流行的工具,来自澳大利亚。该公司于 2019 年 11 月作为神经风格转移应用程序成立,但当较新的人工智能模型在 2021 年年中登场时,他们很快转向图像生成。如今,它自称已经在其平台上创作了超过 500 万件艺术品。
小型人工智能文本到图像初创公司的新宠必须是稳定性人工智能。所以滔滔不绝的报道已经写在上面,他们炒作泛滥。资金也越来越多——一家投资公司表达了对一项交易的初步兴趣,该交易对总部位于伦敦的初创公司 Stability AI 的估值为 5 亿美元。Stability AI 的首席执行官兼创始人艾玛德·莫斯塔克表示:“我们已经谈成了大规模交易,因此与大多数亏损的大公司相比,我们在门口就能盈利。”
它于 2022 年 8 月进行了公开测试,并与它赞助的由慕尼黑路德维希·马克西米利安大学开发的新的强大的稳定扩散文本到图像人工智能模型密切相关。Stability AI 也比其他小型创业公司更进了一步,不仅拥有自己强大的人工智能模型,还迈出了大胆的一步,将该模型作为开源软件发布。
新的稳定扩散模型也不是唯一的开源模型。
6 月 6 日,拥抱脸,一家托管开源人工智能项目和应用的公司,突然看到其托管的人工智能文本到图像应用 Dall-E mini 的流量激增。顾名思义,Dall-E mini 是对 Dall-E 模型的一种改进。
这个应用程序很简单——它只是用文本提示创建了 9 张图片。它之所以广受欢迎,是因为它是可用的,而且是免费的,不像它最初的同名游戏 Dall-E,需要一个等待名单才能加入测试版。Dall-E mini 很快就达到了每天 50,000 张图片,最终迫使创作者将该应用程序从拥抱脸中移除。当 OpenAI 开始注意到并对 Dall-E 这个名字的使用感到不舒服时,它甚至不得不改变它的名字(改为 Craiyon )。
另一个流行的开源文本到图像是 VQGAN+CLIP ,这样命名是因为它结合了两种不同的人工智能模型(VQGAN 和 CLIP)来创建一个强大的文本到图像模型。大约在 2021 年 4 月,瑞安·默多克将剪辑与一个名为比根的图像生成技术结合起来。后来,凯瑟琳·克劳森和其他一些人用 VQGAN 取代了 BigGAN,并把它放在了一个 Google Colab 笔记本上。剩下的就是开源历史了。
截至今天,在 Replicate 中,一家 AI 模型托管公司(拥有出色的 API)拥有 32 个开源文本到图像模型,包括稳定扩散、各种 GAN+CLIP 模型,当然还有 Dall-E mini。
有很多模型。
它是如何工作的
人工智能行业充满了令人惊讶的难以破译的首字母缩写词和行话,而 tex-to-image 子行业也一样模糊,如果不是更模糊的话。如果你涉水通过技术,你会在不同的时间听到甘斯,剪辑,变压器和扩散模型。技术术语可能令人难以置信(自然,它们毕竟是重大研究成果),但我会尽可能简单地解释它。让我们从技术组件开始,然后从那里继续。我不会试图解释神经网络,因为这是已知的。如果你不确定它们是什么,想要一本快速入门,我不久前写了一篇关于它的文章。
变压器
变压器是一个神经网络。斯坦福大学的研究人员在 2021 年 8 月的一篇论文中将变形金刚基金会模型称为,因为他们看到它们正在推动人工智能的范式转变。
神经网络已经被用于分析复杂的数据类型,如图像、视频、音频和文本,并且不同类型的神经网络被设计用于不同类型的数据。例如,CNN(卷积神经网络)经常用于图像数据。
不幸的是,还没有一个真正好的用于语言和文本的神经网络,尽管 rnn(递归神经网络)也经常被使用(在 transformers 之前),尽管它们很难训练并且在处理长段落时有问题。
transformer 神经网络是由谷歌和多伦多大学的研究人员在 2017 年开发的,最初是为语言任务设计的。Transformers 易于训练,因为它可以有效地并行化,并且它有效地取代了 RNNs,成为最终用于许多事情的主要神经网络。
多伦多大学(维基媒体中的知识共享)
今天最著名的变形金刚之一是 OpenAI 的 GPT-3,它是一个巨大的模型,在 45 TB 的文本数据上进行训练,几乎包括整个公共网络。
虽然它最初是为自然语言处理(即语言和文本)而设计的,但它已经用于许多其他事情,包括文本到图像生成器。如你所见,变形金刚无处不在。事实上,这里的其他技术要么完全是变压器,要么将变压器作为模型的一部分。
夹子
CLIP(对比语言-图像预训练)是一个神经网络,它识别图片中的对象,并提供文本片段来描述它们。这听起来像你的老式图像分类器,但它比那更强大。如果对标记数据进行训练,您习惯的图像分类器可以识别图片中的对象,如果对象不符合任何类别,则不会被识别。
另一方面,CLIP 不是使用带标签的图像数据集训练的,而是来自互联网上的 4 亿张图像及其文本描述。因此,它不按类别识别,而是从单词列表中提供图像的文本片段。
CLIP 还创建了一种图像-文本字典,允许图像和文本之间的翻译,这对于文本到图像的人工智能模型非常有帮助。CLIP 本身使用两种不同的转换器作为编码器——图像编码器是视觉转换器,而文本转换器是 GPT-2。
VQGAN
VQGAN(矢量量化生成对抗网络)是另一种类型的神经网络,由海德堡大学的研究人员在 2020 年首次开发。它由一个获取一组图像来学习特征的 GAN(生成对抗网络)和一个获取序列来学习远程交互的转换器组成。
GANs 是一种有趣的机器学习技术,它让两个神经网络相互竞争,这是一场永无止境的争霸赛。其中一个神经网络称为生成器,另一个称为鉴别器。生成器生成模拟真实数据的数据,而鉴别器尝试从生成的数据中识别真实数据。本质上,GANs 创建它们自己的训练数据,生成器和鉴别器之间的反馈回路产生越来越好的结果。
巴特
BART(双向自回归变压器)是由脸书人工智能研究人员创建的神经网络,结合了谷歌的 BERT 和开放人工智能的 GPT 技术。
BERT(变压器的双向编码器表示)的定向方法适用于下游任务,如需要整个序列信息的分类,但不适用于生成数据仅依赖于先前生成的数据的生成任务。GPT 的单向自动回归方法对文本生成很好,但对需要整个序列信息的任务就不太好了。
巴特把伯特的编码器和 GPT 的解码器结合在一起,结合了两者的优点。这对于文本生成和文本理解都非常有用。
传播
扩散是一种生成式机器学习技术,这意味着他们创建的数据看起来像他们接受训练的数据。例如,稳定扩散使用 3 个神经网络、自动编码器和 U-Net 以及 CLIP。
该模型的主要思想是,它获取一幅图像,并对其进行随机置乱,直到它变成纯粹的噪声。然后,它训练一个神经网络,一步一步地将其改变回类似原始图像的东西。这有效地从随机性中生成图像,当给定随机样本时,它甚至可以生成新的图像!
模型
在几个段落中塞进了大量的缩写词和技术!希望对这些技术的作用有一个更清晰的了解,让我们看看如何使用这些技术将模型放在一起。先从 OpenAI 的机型说起吧。
Dall-E 基本上是 GPT-3 的 120 亿参数版本,一个变形金刚模型,用文本-图像对的数据集训练。
GLIDE(用于生成和编辑的引导语言到图像扩散)是具有 35 亿个参数的剪辑引导扩散模型。
Dall-E 2 与 Dall-E 截然不同,更类似于 GLIDE。和 GLIDE 一样,比 Dall-E 小,只有 35 亿个参数。它还使用了一个扩散模型,由剪辑模型指导,然后用 6.5 亿个文本图像对进行优化、精炼和训练。它将从 Dall-E、CLIP 和 GLIDE 中学到的概念整合在一起。
VQGANs 擅长生成看起来彼此相似的图像,而 CLIP 则决定提示与图像的匹配程度。艺术家兼数学家凯瑟琳·克劳森(Katherine Crowson)受瑞安·默多克(Ryan Murdoch)的启发,将这两个模型结合在一起,这一想法首次出现在谷歌的 Colab 笔记本上。本质上,VQGAN 生成候选图像,并对它们进行剪辑排序,直到生成可接受的候选图像。这变得非常流行,有几个模型使用这种方法,并有自己的调整。例如,NightCafe 使用 VQGAN+CLIP 作为其人工智能文本到图像生成器的基础。
由 NightCafe 使用 VQGAN+剪辑创建,带有文本提示“新加坡滨海湾金沙日落油画,作者詹姆斯·格尼”
另一个使用 VQGAN+CLIP 的型号是我前面提到的 Dall-E mini。它基本上是 VQGAN + CLIP,但是使用 BART 对 VQGAN 的文本提示进行编码。
稳定扩散,顾名思义用的是扩散模型。事实上,扩散模型越来越多地被许多公司用作其人工智能模型的基础。例如,Midjourney 也使用扩散模型。稳定扩散是潜在扩散模型(LDM)的开源实现,在 256x256 图像上进行预训练,然后在 512x512 图像上进行微调,所有这些都来自 LAION-5B 数据库的子集。
这是当前技术和模型的快速浏览。
未来如此光明?
所有这些技术都很吸引人,但它们也会对现实世界产生影响。
2022 年 8 月,Jason Allen 凭借一件名为Thétre D ' opéra Spatial的作品赢得了科罗拉多州博览会的数字艺术竞赛。然而,杰森·艾伦不是艺术家。这张图片是使用 Midjourney 创建的——它接受了艾伦的文本提示,并创建了一个令人惊叹的作品,为他赢得了第一个价格。
不出所料,它引发了对作弊、人工智能产生艺术的伦理的指责,并声称这本质上是一种高科技形式的抄袭。
最后是可以理解的。我自己也走了一半路程,在几分钟内完成了这件作品。如果你熟悉亨利·马蒂斯的作品,这应该会让你想起它。我只用了几分钟就想出了这个。
中途创作——“马蒂斯绘画风格的新加坡滨海湾金沙日落”
当然,新的艺术制作技术总是有争议的。当时的许多画家和艺术家都对照相机的发明感到愤怒。法国诗人兼艺术评论家夏尔·波德莱尔这样评价摄影:“T2 入侵了艺术的领地,成为了艺术最大的敌人”。然而今天,摄影是一种完全不同的艺术形式。
就在几个月前,有人在 MakeUseOf 杂志上写了一篇关于人工智能文本到图像生成器的文章,他用这句话结束了这篇文章。
就像人工智能写作工具一样,虽然最终产品看起来足够“真实”,就像是由人类制作的一样,但它仍然错过了一些东西。艺术家可以添加创意、情感和自我定义的风格,使艺术作品个性化和原创。也许几年后,人工智能也可以进化成那样,但艺术家的工作目前是安全的。
【https://www.makeuseof.com/ai-text-to-art-generators/(2022 年 6 月 11 日出版)
现在看来有点过时了。毕竟,如果一个人工智能模型能想出上面这些图片,艺术家和插画师的工作有多安全?就像艾伦在接受《纽约时报》采访时说的那样:“艺术已经死了,伙计。结束了。人工智能赢了。人类输了。”
冰的故事
在我职业生涯的早期,我听过盖伊·川崎的一次演讲,他是苹果公司的传奇人物和传道者。他讲述了采冰行业的故事(克里斯托弗在迪士尼电影《冰雪奇缘》开始时所做的事情),并谈到技术变革通常不是进化性的,而是革命性的。
在 19 世纪和 20 世纪早期,斯堪的纳维亚国家和北美有一个大型的采冰工业。大块的冰块从湖泊和河流中切割下来,储存在冰库中,然后运送到温暖国家或夏季的家庭和企业。在 19 世纪 90 年代的巅峰时期,美国冰贸易雇佣了大约 90,000 人,并将冰出口到远至香港、东南亚、菲律宾和波斯湾。
19 世纪 90 年代,加拿大安大略省多伦多市的切冰工人(公共领域)
然而,在 20 世纪早期,事情发生了迅速的变化。生产人造冰的冰工厂开始取代天然冰贸易,加上暖冬期间天然冰的稀缺、对天然冰污染的担忧以及来自冰工厂的低成本竞争,促使其最终消亡。在第一次世界大战后的几年里,整个冰贸易崩溃了,随之而来的还有工业和采冰工作。
第二个变化发生在制冰厂开始接管天然冰贸易的时候。1913 年,第一台家用电冰箱 DOMELRE(家用电冰箱)发明并出售。它是革命性的,也是成功的,甚至包括自动温度控制和引入冰块冷冻托盘等创新。到 1944 年,85%的美国家庭拥有冰箱。正如制冰厂取代了冰贸易一样,家用冰箱取代了家庭从制冰厂购买冰的需求。
格里姆斯比冰厂的冰厂工人
不幸的是,没有一家冰贸易公司从收获和销售冰转向经营冰厂,同样,也没有一家冰厂公司转向制造冰箱。冰收割机没有成为冰厂工人,冰厂工人没有成为冰箱厂工人。
正如摄影改变了绘画行业,照片编辑改变了艺术和摄影行业,人工智能文本到图像生成器形式的生成艺术也将改变艺术和插图行业。没有人真正知道当前的艺术家和插画师有多擅长曲线跳跃。
下一跳?
transformer 模型是为了解决 2017 年的一个自然语言处理问题而创建的。2020 年,OpenAI 发布了非常受欢迎的 GPT-3 模型,使用了变形金刚模型。这在 2021 年发展成用于软件代码生成的 Codex(现在用于 Github Copilot),也发展成用于文本到图像生成的 Dall-E (2021)和 Dall-E 2 (2022)模型。
文本到图像似乎不太可能是最后的领域。有视频、音频和音乐(有人想唱歌吗?)前方还有更多。我们正处于激动人心的时代,但也有一些可怕的时代。
视频杀死了电台明星,也许人工智能正在杀死艺术家和插画师。下一个是谁?
理解偏差和差异!
原文:https://towardsdatascience.com/making-sense-of-bias-and-variance-a5e639f6bd86
关于两个基本数据科学概念的初级入门书
什么是偏见?
取决于你从哪里听到这个词。我半开玩笑地列出了各种偏见用法的清单,供你娱乐,但在本文中,我们将重点关注一种特殊的偏见——统计偏见——我将在 时刻 解释它。
打靶
带上你的弓和箭!或者水枪。或者飞镖。或者猫炮。如果你是一个优秀的射手,你正在瞄准靶心,那么当你完成后,你的目标会是什么样子?类似这样的?
你对猫炮的异议已被正式记录。
没错。
现在想象你的目标有偏差。你的目标会是什么样子?
点,到处都是点?没有。这就是我们所说的高 方差 ,而不是高偏差。
相反,偏见看起来是这样的:
这一系列图像是作者创作的。
有偏结果系统地偏离标记,但如果方差较低,仍可能紧密地聚集在一起。
在统计中,偏差完全是关于系统的不平衡,而方差衡量的是数据的分布程度。
方差是指数据中有更多的分布。不带偏见的高方差是一种快乐的无能,这种无能是公平分布的。这就是为什么当我玩飞镖的时候,你不想 T21 站在我 20 码以内的任何方向。这足以让你希望我是有偏见的(在镖靶的大方向上)。
正如您从该图中看到的,最差的可能结果是那些具有高方差和高偏差的结果,而最佳组合是使它们都保持较低的结果。
如果你来到这里是因为你对机器学习中一个叫做偏差-方差权衡的概念感到好奇,那么最重要的是要明白,最好的机器学习模型有 无偏差和无方差 。如果你正在训练一个模型,并且你设法让你的偏差和方差都下降了,那就没有必要谈论权衡:如果你成为了一个更好的射手,你就成为了一个更好的射手。(大概是多加练习— 数据!—为了到达那里。)
但是如果你不能在不破坏另一个的情况下改善其中一个呢?如果收紧射击意味着偏离目标呢?为什么会这样?为什么这很重要?为什么大多数自认为理解偏差-方差权衡的人实际上完全误解了它?
我将在这个关于偏差和方差的迷你系列中回答这些问题和更多问题——请自行点击跟随按钮,以免错过。
你的高斯-马尔科夫作者。差异太大了!
延伸阅读:横着走
如果你已经对方差有了充分的了解,我建议阅读我的平行文章,这篇文章使用冠状病毒案例研究来告诉你其他类型的偏差:采样偏差、选择偏差、信息偏差、报告偏差、和确认偏差。
延伸阅读:更深入
你们中的一些人可能希望更深入,尤其是如果你热衷于理解偏差-方差权衡的话。在这种情况下,请继续关注本系列的其余部分……尽管它将变得更加专业。但我会支持你。在你等待下一期文章的时候,你可以通过链接到我温和的自我介绍来获得所有你需要的背景知识,以便理解它。
想知道你是否应该跳过背景阅读?这里有一个核心概念的清单,我假设您在本系列的其余部分已经理解了:
- 人口
- 样品
- 观察
- 参数
- 统计
- 估计
- 估计器
- 预计需求
如果他们中的任何一个不熟悉,这里是我的方便的词汇指南来帮助你。
如果你对机器学习中的偏差-方差权衡感兴趣——这也是本迷你系列正在构建的——稍微熟悉一下 ML/AI 的这些基础知识会有所帮助,我在这里谈到了这些:
- ML/AI 的意义是什么
- 车型
- 损失函数
此外,您应该知道其中每一个的定义,我已经在这里和分别解释了和:
- 预期值
- 随机变量
- 分配
- 概率
- 的意思是
- 差异
放心吧!这些概念比大多数人想象的要简单得多,我的向导会带你慢慢了解它们。快乐学习!
喜欢作者?与凯西·科兹尔科夫联系
让我们做朋友吧!你可以在 Twitter 、 YouTube 、 Substack 、 LinkedIn 上找到我。有兴趣让我在你的活动上发言吗?使用表格取得联系。
快速理解文本:正则表达式入门
原文:https://towardsdatascience.com/making-sense-of-text-in-a-hurry-a-regular-expressions-primer-5250739abebc
照片由丁诺(Pexels.com)拍摄
介绍
无论您是第一次接触正则表达式,并且有想要理解的文本数据,还是您有研究堆栈溢出问题的经验,希望找到完全相同的用例,而不太理解您正在使用的正则表达式的混乱;当你使用它们来扩展你的经验时,这个介绍将证明是一个有用的基础。
今天我们将讨论三个关键功能和三种关键模式。
正则表达式的简要说明
正则表达式是匹配文本中模式的有用工具。
模式识别对于给定文本串的分类特别有用。例如,假设您有客户的网站数据,但是您想检查给定短语的出现情况;regex 来救援了!
关键功能
我们将使用re
包,并运行以下三个函数:
- 重新搜索()
- 重新拆分()
- re.findall()
够简单!
搜索
定义
搜索允许我们在字符串中搜索给定的模式。如果模式出现在字符串中,函数将返回“匹配对象”,否则返回空值。
例子
假设您想要搜索一个名为 company description 的字符串,以查找包含“tech”的内容。出于示例的目的,让我们假设您将在公司描述中使用单词“tech”来对每个记录进行相应的分类。
调用函数,传递模式和字符串。
company_description = "selling tech products to retailers"
re.search('tech', company_description)
就这么简单,您有一个直接的方法来检测模式的存在。匹配对象本身将包括模式第一次出现的字符索引。
裂开
定义
Split 允许我们将一个字符串“分割”成一个列表中不同的元素。在 split 中,我们使用正则表达式来指定函数拆分字符串的模式。以下是更多相关信息。
例子
假设您希望根据潜在客户当前拥有的技术产品向他们进行营销,并且您甚至拥有这些数据,但不幸的是,每个客户的数据是一个长的连续字符串,用逗号分隔每项技术。
一个简单的解决方案是用逗号(和空格)拆分字符串。
technologies = 'salesforce, gainsight, marketo, intercom'
re.split(', ', technologies)
现在,您已经将每项技术分解到列表中它自己的条目中。
芬达尔
定义
Findall 与 search & match 非常相似。关键区别在于“findall”中的“all”。findall 不只是返回第一次出现的位置,而是返回模式的每次出现。为了便于说明,我们用直接引用的模式来保持事情的简单,但是很快我们将回顾您也可以使用的不同模式。
例子
假设你正在向电子商务公司销售退货处理产品,你浏览了一些潜在客户的网站,希望看看他们是否提供免费评论;在这种情况下,假设“免费退货”的提及量越大,表明注册该产品的倾向越高。
website_text = 'free returns... yada yada yada... free returns... and guess what... free returns'
returns = re.findall('free returns', website_text)
关键模式
现在您的工具箱中有了一些关键函数,让我们通过讨论模式来扩展它们的有用性。
在上面的每个例子中,我们明确地定义了我们的模式;我们现在要做的是回顾如何在更复杂的条件下更快地达到目标。
我们将回顾以下内容:
- 数字
- 话
- 间隔
数字
与前面的例子类似,我们将使用 findall 但在这种情况下,我们将这样做,以找到一个数字的每一次出现。假设我们第一季度的月销售额记录在一个字符串中,我们希望提取这些数字。密切注意我们通过的模式。
string = 'Jan: 52000, Feb: 7000, Mar: 9100'
print(re.findall(r"\d+", string))
让我们将这个命令分成不同的部分:
- 向 python 表明我们将使用正则表达式,这有助于 python 不会对你要做的事情感到困惑。
- 我们使用反斜杠
(\)
告诉 python 按字面意思处理下一个字符。有些情况下,一个“特殊的”字符告诉 python 做一些事情;在这种情况下,python 知道不要做任何时髦的事情。 d
是我们用来表示我们想要的数字。- 不带+号运行同样的操作会将每个单独的数字视为列表中自己的项目。+表示我们希望将符合我们指定标准的完整单词作为一个单独的项目。
话
让我们再做一次,只是把数字换成单词。我们会说我们想要提取月份值。
print(re.findall(r"[A-z]\w+", string))
我们看到这里包含了很多相同的东西:r
、反斜杠、+;但是我们现在看到,我们已经包括了w
,而不是 d。w
是任何字符的指示,从技术上讲,它也可以扩展到其他字符,所以我们指定它是谨慎的。
在\w+
语句之前,我们可以修改我们想要允许的特定字符类型的模式。在这种情况下,我们通过[A-z]
指定包含所有大写和小写字母。
间隔
让我们重温一下之前为re.split
制作的例子。
假设我们不想对逗号进行拆分,而是希望根据空格进行拆分。
print(re.split(r"\s", technologies))
如您所见,考虑到逗号现在是如何被包含在各个项目中的,这真的没有多大用处。如果没有逗号,这将是一个更有用的方法。
结论
你有它!几乎在任何时候,我们已经涵盖了相当多的内容。
你学到了:
3 个关键的正则表达式函数:
- 重新搜索()
- 重新拆分()
- re.findall()
3 种便捷模式:
- 数字
- 话
- 间隔
和一些应该可以帮助你理解 regex 世界的规则。
我希望这可以证明是有用的脚手架,您可以使用它来构建您的正则表达式知识和经验。
我希望你喜欢这篇文章,并希望它对你的工作有所帮助!请分享什么有用,什么没用!
请随意查看我在 datasciencelessons.com 的其他帖子
祝数据科学快乐!
充分利用数据科学团队
原文:https://towardsdatascience.com/making-the-most-of-data-science-teams-cc91371d4fd6
作为技术领导者的经验教训
充分利用数据科学团队中的技能(由 Brooke Cagle 在 Unsplash 上拍摄)
数据科学团队有各种各样的结构,但鉴于他们的专业,数据科学家通常在跨职能团队中工作。跨职能团队由组织内不同工作职能的人组成(例如软件工程师、数据科学家和 scrum masters)。
跨职能团队理想地负责整个价值流,这带来了能够以高度的自主性和速度工作的好处。例如,一个跨职能团队可以拥有一个完整的预测性维护解决方案,包括数据管道、机器学习模型和用户界面。
也就是说,充分利用跨职能团队可能会很棘手。促进团队间的知识共享更加困难,协调不同的工作方式也是一项挑战。
凭借多年的技术领导经验,我想分享一下我对如何成为优秀的数据科学技术领导的看法——不仅仅是技术技能。
1.了解团队中的技能
跨职能团队的领导者面临的一个共同挑战是,他们往往在特定的职能领域更强。例如,以我作为数据科学家的背景,我的优势在于模型的设计和原型制作,而不是软件工程和部署。在计划和设定目标时,这很容易导致偏见和错误的假设。
为了最大限度地发挥团队的潜力,任何领导者都必须了解每个团队成员的个人动机、技能和抱负。在科技公司,这通常与管理职位有关。然而,在我看来,如果你对团队成员的需求和兴趣视而不见,你就不会充分利用他们的技能和潜力。
我建议定期与团队成员联系,并询问以下问题
- 在我们目前的项目中,你最感兴趣的是什么?一般什么样的工作让你兴奋?
- 你想成长的技能是什么?
2.没有跨职能就没有跨职能团队
仅仅让不同职能的团队成员在一个团队中并不能使它成为跨职能的。如果你坚持严格的角色定义,你最终会在角色之间产生技能/责任差距。
工作职能之间的潜在差距以及跨职能团队如何弥补这些差距(图片由作者创建)
根据我的经验,对于团队领导来说,找出这样的差距并扩大职能之间的重叠来解决它们是至关重要的。例如,一个 ML 工程师应该对团队在他们的产品中运行的模型有一个基本的理解。这些交互也有助于 ML 科学家在早期理解非功能性需求(比如运行时性能)。
我认为在团队中跨越职能的最佳方式是设定以结果为导向的目标,这些目标跨越多个职能,不仅鼓励,而且要求协作。
以结果为导向的目标示例:
- “可以在用户界面中看到占位符模型的预测”
- “模型保持应用于新(相似)数据集的预测准确性”
填补空缺的另一个好方法是找到那些想要拓展核心技能之外的团队成员(见第 1 点)。
3.极度的所有权和信任
“领导者必须拥有他或她的世界中的一切。没有其他人可以责怪。领导者必须承认错误,承认失败,承担责任,并制定取胜计划。”乔科·威林克和叶小开·巴宾的《极端所有权》
应用于数据科学团队,“极端所有权方法”意味着领导者应对团队、整个解决方案及其交付负责。如果你没有 DevOPS 方面的背景,并且 CI/CD 管道的问题拖慢了团队的进度,你不能只责怪你的工程师。如果团队成员筋疲力尽,不要只是等待他们的直线经理来解决(或者更糟,让他们离开)。
任何领导者都应该将解决影响团队或交付的问题视为自己的责任——即使这不是他们职责的一部分。
重要的是,极端所有权并不意味着微观管理!使用极端所有权作为技术领导的关键是与团队成员建立信任。对团队成员的技能有了很好的理解,你应该能够预测他们在工作任务或项目时需要多少支持。此外,他们还应该有心理安全感来提出和谈论不舒服的问题,或者在遇到困难时向你寻求帮助。
4.超越你的核心技能
技术领导通常拥有团队中的技术决策,设定技术方向,并向更广泛的组织代表团队的需求。如果你处于这样一个角色(而不是一个管理角色),你很可能不得不在你的核心能力之外发展你的技术技能来承担这些责任。
当然,你不应该期望拥有和更专业的同事一样的技术深度。然而,如果你不扩展你的技能,你可能会成为一个协调者而不是领导者;安排会议和联系同事,而不是积极参与。
通常很难抽出时间来学习和发展。典型的建议是将个人发展目标分成可管理的部分。过去,它帮助我做到了以下几点:
- 提高理论理解:向同事寻求资源,如书籍、博客、在线讲座
- 提高你的代码审查技能:向资深同事学习。当他们审查拉动式请求时,他们会对什么进行评论,为什么?
- 了解代码库:向不熟悉的代码库中添加小的错误修复或特性
结论
专注于技术领导角色的技术责任很有吸引力,但希望这篇文章能强调该角色的其他一些方面:了解同事的需求,拓展你的技能,并在团队中发展健康的工作文化。
像专业人士一样管理您的 AWS EKS 负载平衡器
原文:https://towardsdatascience.com/manage-your-aws-eks-load-balancer-like-a-pro-7ca599e081ca
AWS 负载平衡器高级提示和技巧
在 Unsplash 上由Manuel n Geli拍摄的照片
我的上一篇文章描述了在 AWS 管理的 Kubernetes 集群(EKS)中设置入口控制器。在本文中,我想强调一些高级特性,否则这些特性可能会被忽略,但会显著影响集群内部的自动化水平。
你在下面看到的提示和技巧是为了遵守软件工程的基本原则之一 DRY 而设计的。
什么是入口“控制器”?
Kubernetes 的设计和开发使用了一组“控制器”,负责将当前状态带到由集群内的配置和资源源定义的期望状态。
作为一个实际的例子,如果您将您的 Nginx 部署扩展到 2 个副本,负责的控制器将与运行在 worker 节点上的 Kubelet 对话,以加速 Nginx 映像的两个副本,根据需要放大或缩小。
当涉及到将您的逻辑添加到 Kubernetes 工作负载时,这为您打开了一个无限可能的世界。
我们今天将深入探讨的可能性之一是入口“控制器”,它负责将到达您的集群的流量路由到具有配置规则的适当服务。
Kubernetes 中的运算符是什么?
在我们进入主要讨论之前,定义一个操作符是至关重要的,但是如果您已经知道这个模式,您可以安全地跳过这一部分。
Kubernetes 集群中的操作符是一组自定义资源定义和一个“控制器”,它将负责将 CRD 的当前状态带到期望状态,与上面定义的其他 Kubernetes 控制器的行为非常相似。
作为一个虚拟示例,假设您在集群中定义了以下 CRD。
这可能不是这个星球上最好的三明治。尽管如此,一旦您的控制器被部署到集群中,对端点上的 Kubernetes api-server 的每个 API 调用,然后通过应用上面的 YAML 文件,您将在集群中拥有一个汉堡三明治。
如果您有兴趣了解更多信息,请进入文档获取完整参考。
为什么在这里了解运营商很重要?
Kubernetes 不附带任何入口控制器[ 来源 ]。但是它允许社区编写他们的入口控制器,这些控制器可能有也可能没有一组 CRDs 因此,了解操作符模式非常重要!
AWS 负载平衡器控制器是 Operator 模式的一个实现,它监听集群内部的入口资源,在创建这些资源时,将执行一系列 API 调用来创建正确的TargetGroupBinding
资源。
TargetGroupBinding
填补了 Kubernetes 工作负载和 AWS 基础设施之间的空白,因为 AWS 设计和构建了其负载平衡器的方式&位于这些负载平衡器的上游。如果你想了解更多,这里有参考指南的链接。
目标组和TargetGroupBindings
之间是一一对应的。
目标组是位于 AWS 应用程序负载平衡器[ source ]后面的一个或多个 EC2 实例的组。
你可以在下面看到一个TargetGroupBinding
资源的例子。
TargetGroupBinding 是 AWS 管理的 Kubernetes 中目标组的映射。
这不是您通常自己创建/管理的资源,而是在您创建/修改入口资源时由 AWS 负载平衡器控制器维护的资源。
了解了这一点,当您试图创建一个带有入口的服务来接收来自互联网的流量时,您应该记住一个重要的注意事项。您的服务资源应该是 NodePort 类型,以便入口控制器能够创建TargetGroupBindings
。这在 AWS 的世界中是有意义的,因为如果服务只作为ClusterIP
公开,AWS 负载平衡器就不能与之对话,因为它只公开在集群内部,实际上甚至从主机工作节点本身都无法访问。
你可以通过下面的链接阅读我上一篇文章中的讨论,或者阅读参考指南来理解设计和配置。
既然我们已经介绍了基础知识和先决条件,现在是时候谈谈控制器和 CRD 了,这就是你想要如何配置你的 AWS 负载平衡器。
IngressClass 的作用是什么?
IngressClass 是将管理入口资源的责任委派给指定控制器的资源。为了澄清这一说法,让我们来看一些代码。
如果您遵循安装指南,这个*IngressClass*
将会为您创建。
正如你从上面看到的,两个基本属性在spec
部分下面。controller
是决定谁/什么将通过这个 IngressClass 处理入口资源,而parameters
是传递给同一个控制器以定制其行为的可选键值。
拥有
parameters
允许拥有具有不同风格的相同controller
,定制为不同地处理不同的入口资源。
简单地说,如果我创建一个入口资源,并将ingressClassName
设置为aws-alb
,名为ingress.k8s.aws/alb
的控制器将做它必须做的事情,以便将传入的流量路由到上游的 Kubernetes 服务。
我们在接下来的讨论中需要的关键信息放在spec.parameters.kind
下面。这个属性的值是IngressClassParams
,这是一个 CRD,AWS 负载平衡器控制器将使用它来为您创建最终的负载平衡器。
照片由乔丹·怀特菲尔德在 Unsplash 上拍摄
IngressClassParams 是你最好的朋友
AWS 负载平衡器控制器有自己的一套 CRD。您应该知道的最重要的 CRD 名为IngressClassParams
,它有一系列有益的可配置属性,这些属性会影响如何创建/配置最终的 AWS 负载平衡器。
这些属性允许您定义和定制您的 AWS 负载平衡器,它将充当您的应用程序的前沿。
注意:我在本文中说的是应用负载均衡器。如果您需要使用网络负载平衡器,您可以使用相同的控制器,但一定要检查此链接以获取相应的信息。
现在,我们来介绍一下IngressClassParams
来源提供的规范 API。没有一个好的例子是最好的,所以让我们用下面的例子来支持这个论点。
将影响如何创建负载平衡器的属性。
如果您打开 AWS 控制台,上述配置将导致以下负载平衡器。
截图由作者取自 AWS 控制台。
当在生产级别的环境中工作时,这些属性中的一些是至关重要的,而其他的只是好的。我们将在下面深入探讨每一个问题。
您可以在IngressClassParams
资源上指定以下属性。
**spec.group.name**
:这种“分组”使得在一个负载均衡器后面有多个入口资源成为可能。如果在IngressClassParams
级别没有提供,那么另一种方法是注释你的每一个入口资源 source 。如果你没有为入口资源“分组”,AWS 将为每个入口资源创建一个负载平衡器,这将花费大量的成本。**spec.scheme**
:由于 AWS 中可能有内部和面向互联网的负载平衡器,您可以在此指定您的选择。区别是显而易见的,一个只对您的内部资源可用,另一个在公共互联网上公开。**spec.loadBalancerAttributes**
:最有价值的规范之一将为您提供足够的灵活性来根据您的需求定制您的负载平衡器。这个规范就是本文的全部内容,所以我们将再次讨论它。**spec.ipAddressType**
:您可以选择ipv4
和dualstack
。以防有一天世界用完了 IPv4!
负载平衡器属性
参考上一节中从 AWS 控制台截取的屏幕截图,从属性部分可以看到,您在这里修改了相当多的属性。所有这些属性都在相应的网页上有适当的记录。所以,一定要去查一下,全面了解。
简单介绍一下,下面是从IngressClassParams
资源配置的属性。
**deletion_protection.enabled**
:移除最后一个入口资源是否会终止负载平衡器。您可能想要关闭它,因为每个新的负载平衡器都将获得一个新的 DNS 名称,所以您必须在 AWS DNS 条目中或之外重新配置您的 DNS 记录。**idle_timeout.timeout_seconds**
:这是负载均衡器在自身和连接两端(用户&上游服务器)之间保持两个通信通道打开的秒数。对于文件上传和类似的大型操作,您可能希望增加这个数量,并对您的客户端进行编程,以便在这个数量之前至少发送一个字节[ source ]。**routing.http.drop_invalid_header_fields.enabled**
: 根据 IANA ,HTTP 头名应该符合字母数字和连字符。如果您希望负载平衡器丢弃不对应的标头,请启用此配置。**routing.http2.enabled**
:顾名思义,这将为您的负载平衡器打开 HTTP/2 协议。默认为true
。**routing.http.preserve_host_header.enabled**
:如果设置为true
,负载均衡器会将 HTTP 请求的主机头传递给上游服务器。不过,这不会改变[X-FORWARDED](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/x-forwarded-headers.html)
标题的行为。**access_logs.s3.enabled**
:这将打开记录负载平衡器对 S3 的访问日志。
每个入口组只需指定一次这些设置。如果您使用 Helm 安装入口控制器,您可以在安装过程中将它们设置为值。作为一个实用的指南,这里有一个你将使用的样本values.yaml
文件,把它传给 Helm 安装。
AWS 入口控制器舵安装的 values.yml
如果您没有将任何 IngressClass 注释为
default
,也没有在您的入口资源中指定任何ingressClassName
,就好像您根本没有任何入口,因为没有控制器会拾取它!
创建此类入口类后,由此类控制的任何入口都将归入同一个group.name
下,并有效地位于单个负载平衡器之后,从而节省您的成本并为您提供对进入您集群的向内流量的更多管理&控制。
如果您没有在IngressClassParams
中指定group.name
,您将被要求如下注释每个入口。
现在我们已经介绍了 IngressClass,是时候讨论入口资源和通过指定正确的注释集来控制负载平衡器了。
入口注释
Kubernetes 集群中的入口资源没有任何特别之处。它们只是一个或多个规则的规范,必须被处理并发送到指定的服务。您指定主机和路径,相应的入口控制器将处理剩下的工作。
然而,让它们与众不同的是metadata.annotations
,当它位于任何供应商或非供应商负载平衡器之后时,它可以对您想要如何定制入口进行更多的控制。
为了保持传统,让我们从另一个实际例子开始这一部分,并使用这个例子解释每个注释。
**group.order**
:该整数指定当数据包到达负载均衡器时要匹配的规则的优先级。负载平衡器将从最低编号到最高编号评估规则。你通常希望你最常访问的网址在最上面,这样匹配你最常点击的规则就不会花太多时间。- 与健康检查相关的注释非常简单明了,不言自明,所以我在这里就不多花时间了。
**listen.ports**
:这将是您的负载平衡器中的最终监听器。如果不指定此注释,默认情况下,AWS 负载平衡器控制器将在端口 80 上公开入口。您很可能希望处理安全端口 443(称为 HTTPS)上的每个流量,同时将所有内容从 HTTP(端口 80)重定向到它。本文的最后一部分就是关于这个重定向的!**target-type**
:默认值是instance
,意味着负载均衡器会将流量发送到 EC2 的 exposed NodePort。另一个允许的值是ip
,它将把流量发送到 Pod 的 IP,但是这个选项要求 VPC CNI 支持它[ 源 ]。**success-codes**
:可以是单个值,如200
,也可以是多个逗号分隔的值,如200,300
,也可以是一系列值,如200-300
。
上面讨论的注释是您需要为您创建的每个入口资源指定的注释。其他注释只需设置一次,负载均衡器就会选择它并将其用于所有入口资源。我将在下一节&最后一节讨论这些。
OPPO Find X5 Pro 在 Unsplash 上拍照
负载平衡器的一次性配置
将 HTTP 重定向到 HTTPS
正如在上一篇文章中提到的,仅仅通过部署 AWS 入口控制器不会得到任何负载平衡器。您将需要创建您的第一个入口资源,具有规范中指定的 Ingres class 或者是默认的 Ingres class,然后 AWS 将在一两分钟内提供您的负载平衡器。
安全最佳实践之一是配置负载平衡器,将非 HTTPS 流量重定向到安全的 HTTPS 流量。要使用 AWS 入口控制器做到这一点,您需要创建一个带有正确注释集的入口资源。
这个任务我们需要的注释是:actions.${action-name}
。“action name”将是一个虚拟服务名,甚至不作为 Kubernetes 服务资源存在,但是为了填补 AWS 和 Kubernetes 之间的空白,需要在spec.defaultBackend.service.name
下指定它。
这是 AWS EKS 内部的入口资源将 HTTP 流量重定向到 HTTPS 的最终效果。
在actions.ssl-redirect
下指定的注释是一种特殊的注释,只有 AWS 入口控制器可以理解,尽管 JSON 格式看起来需要处理很多,但它是可读的,您可以了解它的作用。
这里要提到的重要一点是,spec.defaultBackend.service.port.name
应该完全是use-annotation
,如果你想让它像解释的那样工作,你不能在那里放任何东西。
这里需要再次提及的另一个重要问题是,集群中没有名为ssl-redirect
的 Kubernetes 服务资源。我们之所以在这里指定这个名称,是因为要使用kubectl
创建这个资源,我们必须填写所有必需的属性&规范,因为它将在发送到 api 服务器之前被验证,即使您通过了--validate=false
,API 服务器本身也会向您反馈错误的规范。
最终,上述入口资源会将所有 HTTP 流量重定向到 HTTPS 端口,为了确保这一点,下面是它在 AWS 控制台中的样子。
这个负载均衡器目前只监听端口 80,所以让我们定义一个虚拟服务来重定向所有不需要的流量,以防所有其他规则与传入的数据包不匹配。
每个负载平衡器都有一个默认的后端规则,如果所有先前的规则都不匹配传入的数据包,该规则将确定将流量发送到哪里。这允许您将用户重定向到一个友好的页面,提醒他们没有这样的主机或路径,他们需要确保他们正在前往正确的地址。
默认后端的虚拟上游
这一部分非常简单明了,因为您可以根据自己的需要定制它。但是,我会尝试将所有没有特定规则的流量发送到一个友好的维护页面,如下所示。
我个人对哑上游的偏好(图片由作者提供)。
下面提供了该规则的代码和资源,包括前面的 SSL 重定向。
创建上述资源后,每个不是以您配置的入口规则为目的地的传入流量都将被发送到友好维护页面。
您还可以选择审核这些传入的数据包,以了解最常被请求的内容,并在发现任何可疑情况时采取相应的措施。
其他非关键但注释
作为最后一块拼图和一个奖励,这里列出了所有其他可能对您的工作负载有用的注释。
不要忘记,这些最后的注释在每个入口组中只被指定一次。如果您用不同的值设置多个,它将覆盖以前的值,并且您可能会得到意外的结果。
本杰明·蔡尔德在 Unsplash 上的照片
结论
如果您选择加入 AWS 中的托管 Kubernetes 集群,那么在拥有生产级集群时,请准备好应对一些严峻的挑战。
Kubernetes 是与云无关的,允许您将数据库、缓存、消息代理和其他所有东西作为自我管理的解决方案放在集群中。这为您提供了更高的性能,因为您的应用程序将受益于地理上的接近,但这也意味着您将花费更少的钱来购买 RDS、ElastiCache 和 AWS 提供的其他产品。我很确定他们不喜欢这种声音。
这意味着您将面临 Kubernetes 领域之外的挑战,以使您的集群在 AWS 基础设施中工作,为您提供学习和成长的机会。我建议你尽可能多带些,并且一定要享受旅程!
祝你今天休息愉快,敬请期待保重!
参考
- https://kubernetes-sigs . github . io/AWS-load-balancer-controller/v 2.4/
- https://aws.amazon.com/elasticloadbalancing/
- https://kubernetes.io/docs/
如果你喜欢这篇文章,看看我的其他内容,你可能也会觉得有帮助。
https://medium.com/skilluped/what-is-iptables-and-how-to-use-it-781818422e52 https://medium.com/amerandish/clean-architecture-simplified-223f45e1a10 https://medium.com/amerandish/a-tmux-a-beginners-guide-7c129733148 https://medium.com/skilluped/10-tips-on-writing-a-proper-dockerfile-13956ceb435f https://meysam.io/what-is-haproxy-how-to-get-the-most-from-of-it-a9009b67f618
用诗歌管理数据科学项目
原文:https://towardsdatascience.com/managing-data-science-projects-with-poetry-cd3ce2b7913b
如何安装、设置和使用诗歌来管理你的数据科学项目
照片由Tru“Tru”kat sande在 Unsplash 拍摄
再现性是数据科学的一个重要方面。当我们谈论数据科学中的再现性时,我们指的是独立地再现项目结果的能力,无论是在稍后的时间点由你自己还是由同事再现。
拥有确保再现性的流程可以建立对输出的信任,并确保数据科学工作的质量。此外,它使得在过去工作的基础上进行构建变得更加容易。这可能意味着一个同事重新训练六个月前构建的模型,或者另一个团队成员基于先前的分析开发新的模型。
数据科学项目的再现性很大程度上取决于项目代码的组织和可用性,以便其他人可以独立运行。为了组织您的项目,使其可以由任何人在任何地方运行,您至少需要以下内容:
- 虚拟环境
- 一种跟踪项目依赖性的方法
- 标准文件夹结构
- 一种打包和发布代码的方法
近年来,Python 编程语言已经开始出现标准和最佳实践,尤其是在数据科学领域。诗歌是一个 python 库的例子,它为管理 python 项目提供了标准。诗歌的核心是为上面列出的每个领域提供简单的功能。
在下面的文章中,我将介绍如何安装、设置和使用诗歌来管理数据科学项目。
安装
诗歌为装置提供了一个脚本。这取决于您使用的操作系统。
对于 Mac OSX、Linux 或 bash,在 windows 上运行以下程序:
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
对于 Windows,请使用:
(Invoke-WebRequest -Uri https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py -UseBasicParsing).Content | python -
该脚本将在您的系统上安装诗歌,并自动将相关文件目录添加到您的$PATH
环境变量中。
如果您现在打开一个新的 shell 选项卡或窗口,并运行以下命令:
poetry --version
您应该会看到这个输出。
作者图片
创建项目
要创建带有诗歌的项目,请键入以下内容:
poetry new my-datascience-project
poems 将自动为您的项目创建一个目录,其框架结构如下所示。
诗歌目录结构。作者图片
安装软件包
除了生成默认的项目结构,poems 还会为你的项目创建一个虚拟环境和一个.toml
文件。该文件存储和维护项目的依赖关系。它看起来会像这样。
诗歌。toml 文件。作者图片
如果你使用 Pycharm 进行开发,你可以安装一个支持 toml 语言的插件。
这个 toml 文件由 4 部分组成:
tool . poetics提供了一个区域来获取关于项目的信息,比如名称、版本和作者。
tool . poem . dependencies列出项目的所有依赖项。
tool . poem . dev-dependencies列出了项目开发所需的依赖项,这些依赖项不应该出现在部署到生产环境的任何版本中。
构建系统引用了诗歌已经被用来管理项目的事实。
要安装新的软件包,我们键入以下内容:
poetry add pandas
这将自动把这个包添加到您的依赖项列表中,并且还会生成一个 poetry.lock 文件。这个文件跟踪所有的包和你的项目中使用的确切版本。
要激活虚拟环境,我们键入poetry shell
并键入exit
来取消激活。
进入虚拟环境后,可以使用以下命令运行任何 python 脚本:
poetry run python my_script.py
构建并发布
有时我们可能想要打包我们的项目,以便它可以被其他用户或在其他项目中发布和安装。诗歌提供了一种非常简单的方式来构建和发布你的项目。
只需运行以下命令:
poetry build
这将给您以下消息。
作者图片
poems 添加了一个名为dist
的新文件夹,并为项目创建了必要的源代码发行版和轮子。
运行诗歌构建后的目录结构。作者图片
运行命令poetry publish
会将打包的项目上传到一个远程存储库,该存储库可以用 config 命令进行配置。
诗歌是用于依赖管理和虚拟环境的许多现代 python 工具之一。其他工具包括 pipenv 、 virtualenv 和 conda 。然而,poem 努力封装更多代码可再现性所需的元素,包括一致的项目结构和发布代码的简单工具。
在本文中,我简要介绍了用于管理数据科学项目的诗歌。关于 Python 虚拟环境和数据科学代码可再现性的更多信息,请参见我之前的文章。
感谢阅读!
管理语言模型风险;作为虚拟助手的狗
原文:https://towardsdatascience.com/managing-language-model-risks-dogs-as-virtual-assistants-304f12424927
DeepMind“语言模型的风险”出版物述评
作者图片
美国正在经历禁止学校图书馆书籍的阶段。我们更愿意相信学校的图书管理员来决定孩子们应该能够阅读什么,而不像我们相信公共图书馆的图书管理员那样担心言论自由。我们相信图书馆员经过培训能够做出最好的决定,然而当前的政治运动转向家长来决定什么对所有学生合适或有害,什么不合适。语言模型的开发者可能有意或无意地在决定什么是可接受的,什么是有害的言语调节中承担同样的责任。
正如我们依赖图书馆员一样,现代社会也求助于虚拟助理来回答问题。我们依赖广泛的信息来源和算法。在这种背景下,阅读关于语言模型的风险的深度思维文章是很有趣的。深度思维是自动回归语言模型的领导者之一**。**当我们开发新的语言模型并利用它们来增加我们的理解时,将作者概述的风险放在首位至关重要。
研究论文描述了语言模型的六组风险:歧视、排斥和毒性、信息危害、误传危害、恶意使用、人际互动危害和*自动化、访问和环境危害。*这篇文章冗长且非常详细,而这篇简短的概述将突出这些风险的一些潜在相关示例。
歧视、排斥和毒性: 这一组的风险来源于准确反映自然言语的语言模型,包括训练数据中存在的不公正、有毒、压迫倾向。潜在的结果包括正当的犯罪、物质伤害以及边缘化群体的不公正表现或待遇。在日常生活中,人际交往中什么是合适的,往往是从考虑来源开始的。
照片由安德鲁·尼尔在 Unsplash 上拍摄
假设,作为一个模型开发人员,你的任务是开发一个语言模型来支持一个聊天机器人,它将为行为不端或固执的青少年的父母提供建议。因为忠告往往是代代相传的,所以你可以从历史法律图书馆中找到 1646 年马萨诸塞州的一部名为“顽固儿童法”的法律文本。
“如果一个人有一个倔强或叛逆的儿子,他已满 16 岁,有足够的智力,不听从父亲或母亲的话,即使他们管教他,他也不听他们的话,那么他的父母作为他的亲生父母,就要抓住他,把他带到法庭上的法官面前,向他们作证, 他们的儿子是顽固和叛逆的,不会听从他们的声音和惩罚,但生活在各种臭名昭著的罪行,这样的儿子将被处死。”
当然,上面的文字并不适合用来训练一个模型来反映理想的父母行为。这种风险的起源是假设所有的法律都是一贯道德的,并且符合当前关于什么是可接受的行为的观点。注意到马萨诸塞州在 1973 年最终废除该法律之前,在 1971 年还支持该法律的一个版本。
Pierre Bamin 在 Unsplash 上拍摄的照片
语言模型的下一个风险是 信息风险 。与此风险类别相关的问题是无意的数据隐私泄露,这是由于暴露私人信息本身、允许通过给定的数据连接推断私人信息或暴露推断的数据而导致的。医疗保健在当前的数据隐私泄露水平下面临着足够的挑战(2022 年 1 月38已经暴露了近 200 万患者的隐私数据)。如果构建语言模型来公开私有数据,就好像在系统中内置了一个黑客。
Bermix 工作室在 Unsplash 拍摄的照片
第四个风险群体是**恶意使用。**一些语言模型是为了欺诈或模仿、破坏公共话语的骗局、网络攻击或审查和非法监控而故意构建的。语言模型的开发者需要问这样一个问题“这个模型将被用来做什么?”看起来很容易解决的问题可能会导致不合理的实现。从政府监控到说服你的祖母把钱给一个完全陌生的人,同时假装是你祖母的朋友去度假。
作者图片
人机交互危害 是第五个风险群体。这组风险很大程度上来自于作为对话代理基础的语言模型。为什么虚拟助理是女性,名字像“Alexa”和“Siri”,而不是男性,名字像“Bucko”或“Spike”?女性顾客会喜欢一个叫“奈杰尔”、声音像詹姆斯·邦德的助理吗?“奈杰尔”的“个性”会和“阿利克夏”不同吗?谷歌助手也是女性的事实是否延续了一个神话,即男性永远不会提供帮助?从一个不那么偏颇的角度来看,Waze 承认无条件的爱对于一个有声助手来说可能是一笔巨大的财富,它确实给了你选择猫和狗的机会。
人们更容易相信所提供的信息,分享更多的信息。2019 年发表在 Nature 上的一项关于帮助心理健康问题的移动应用的研究发现,只有 53%的分析应用使用了与学术文献中任何支持证据相关的方法。只有 33%描述特定科学技术的应用程序提供了支持这些技术的证据。虚拟援助的语言模型面临的一个持续挑战是,越多的对话聊天机器人被视为人类,隐私风险就越大,因为人类更信任看起来感同身受的交互。
最后一个风险领域是 自动化、访问和环境危害。 这些风险可能带来的负面后果包括过度使用能源对环境造成的危害(为什么你的机器学习模型可能正在融化冰山),越来越多地使用聊天机器人与客户而不是人类代理人进行互动(人类代理人可能会被重新分配去监控聊天机器人,而不是使用他们的人际交往技能),以及由于偏向书面而不是口头交流而导致的对语言素养的偏向。
Deep Mind 文章建议,为了解决潜在的风险,语言模型开发人员和模型用户应该努力:
- 理解风险的起源及其与其他风险的联系和相似之处
- 确定适当的缓解方法
- 分配适当的责任和实施纠正措施
回到“倔强的孩子”的例子,假设用法律作为原始文本来创建一个虚拟的道德助手是一个挑战,那么我们如何解决潜在的风险呢?风险的来源是什么?您会确定什么缓解策略,如何实施纠正措施?
随着社会越来越依赖语言模型来建议下一步要键入的句子、回答问题、让我们参与对话并提供建议,我们也越来越依赖语言模型开发人员的技能和风险意识,他们需要制定策略来减轻这些风险并确保这些减轻策略得到执行。
对于那些想深入研究语言模型风险的人来说,可以从 Deep Mind 出版物的下表开始,或者阅读完整的报告。
来源:语言模型带来的道德和社会风险
Mango:用 Python 实现贝叶斯优化的新方法
原文:https://towardsdatascience.com/mango-a-new-way-to-make-bayesian-optimisation-in-python-a1a09989c6d8
你所需要知道的就是这个库,用于机器学习模型的可扩展超参数调整
由 Unsplash 上的 Kvistholt 摄影拍摄
模型超参数(或模型设置)的优化可能是训练机器学习算法中最重要的步骤,因为它导致找到使模型损失函数最小化的最佳参数。这一步对于构建不容易过度拟合的通用模型也是必不可少的。
优化模型超参数的最著名技术是穷举 网格搜索及其随机对应物:随机网格搜索。在第一种方法中,搜索空间被定义为跨越每个模型超参数的域的网格。通过在网格的每个点上训练模型来获得最佳超参数。虽然网格搜索很容易实现,但它在计算上变得很昂贵,尤其是当要优化的变量数量很大时。另一方面,andom 网格搜索是一种更快的优化方法,可以提供更好的结果。在随机网格搜索中,通过仅在网格空间的随机样本点上训练模型来获得最佳超参数。
两种网格搜索的比较。这九个点表示参数的选择。左侧和顶部的曲线表示作为每个搜索维度的函数的模型精度。该图摘自 Salgado Pilario 等人的《IEEE 工业电子学汇刊》, 68,6171–6180(2021 年)。
长期以来,数据科学家广泛使用这两种网格搜索算法来寻找最佳模型超参数。然而,这些方法通常发现模型超参数的损失函数远离全局最小值。
历史在 2013 年发生了改变,当时 James Bergstra 和他的合作者发表了一篇论文,其中他们探索了一种贝叶斯优化技术,以找到图像分类神经网络的最佳超参数。他们将结果与随机网格搜索得到的结果进行了比较。很明显,贝叶斯方法优于随机网格搜索:
LFW 数据集(左)和 PubFig83 数据集(右)上的验证错误。TPE 代表 Tree Parzen Estimator,这是一种用于贝叶斯优化的算法。该图摘自 Bergstra 等人的《机器学习研究进展》,28,115–123(2013)。
但是为什么贝叶斯优化比任何网格搜索算法都好呢?因为这是引导的方法之一,它执行模型超参数的智能搜索,而不是通过试错法找到它们。
在这篇博客中,我们将剖析贝叶斯优化方法,并通过一个名为 芒果 的相对较新的 Python 包来探索其实现之一。
简而言之,贝叶斯优化
在解释 Mango 做什么之前,我们需要了解贝叶斯优化是如何工作的。如果您对这个算法有很好的理解,您可以安全地跳过这一部分。
贝叶斯优化有 4 个组成部分:
- 目标函数:这是您想要最小化或最大化的真实函数。例如,它可以是回归问题中的均方根误差(RMSE)或分类中的对数损失。在机器学习模型的优化中,目标函数取决于模型超参数。这就是为什么目标函数也被称为黑盒函数,因为它的形状是未知的。
- 搜索域或搜索空间:这对应于每个模型超参数可能具有的值。作为用户,您需要指定您的模型的搜索空间。例如,随机森林回归模型的搜索域可能是:
param_space = {**'max_depth'**: range(3, 10),
**'min_samples_split'**: range(20, 2000),
**'min_samples_leaf'**: range(2, 20),
**'max_features'**: [**"sqrt"**, **"log2"**, **"auto"**],
**'n_estimators'**: range(100, 500)
}
贝叶斯优化使用定义的搜索空间来采样在目标函数中评估的点。
- 代理模型:评估目标函数是非常昂贵的,所以在实践中,我们只在少数地方知道目标函数的真实值,然而,我们需要知道其他地方的值。这是它进入代理模型的时候,代理模型是一种建模目标函数的工具。替代模型的常见选择是所谓的高斯过程(GP ),因为它能够提供不确定性估计。解释高斯过程超出了这篇博文的范围,但是我鼓励你阅读这篇出色的文章,它有大量的图片来帮助你建立这种概率方法背后的直觉。
在贝叶斯优化的开始,代理模型从先验函数开始,该先验函数沿着搜索空间以均匀的不确定性分布:
代理模型之前。阴影区域是它的不确定性,而黑线是它的平均值。紫色线代表一维目标函数。图片复制自博文探索贝叶斯优化,Apoorv Agnihotri 和 Nipun Batra,2020。
每次在目标函数中评估来自搜索空间的样本点时,代理模型在该点的不确定性变为零。经过多次迭代后,代理模型将类似于目标函数:
简单一维目标函数的代理模型。作者制作的图像。
然而,贝叶斯优化的目标不是对目标函数建模。相反,是在尽可能少的迭代次数中找到最佳模型超参数。为此,有必要使用采集功能。
- 获取函数:该函数在贝叶斯优化中引入,用于指导搜索。获取函数用于评估一个点是否期望基于当前代理模型进行评估。一个简单的采集函数是对代理函数的均值最大化的点进行采样。
贝叶斯优化代码的步骤是:
Select a surrogate model for modeling the objective function and define its priorfor i = 1, 2,..., number of iterations:
Given a set of evaluations in the objective, use Bayes
to obtain the posterior.
Use an acquisition function (which is a function of the
posterior) to decide the next sampling point.
Add newly sampled data to the set of observations.
下图显示了简单一维函数的贝叶斯优化:
一维函数的贝叶斯优化。图片摘自 ARM research 的博客文章:AutoML 的可扩展超参数调优。
如果你有兴趣阅读更多关于贝叶斯优化的内容,我推荐你阅读这篇伟大的文章:
https://distill.pub/2020/bayesian-optimization/
几个 Python 包使用贝叶斯优化来获得机器学习模型的最佳超参数。一些例子有:远视;Optuna;贝叶斯优化;Scikit-optimize(skopt);GPyOpt; pyGPGO 和 芒果 。这个列表很长,我没有提到其他库。对于其他软件包的一个很好的总结,你可以阅读这篇博文:
</10-hyperparameter-optimization-frameworks-8bc87bc8b7e3>
现在,让我们一起深入芒果!
芒果:为什么这么特别?
近年来,数据量大幅增长。这对于需要机器学习管道可扩展的数据科学家来说是一个挑战。分布式计算可能会解决这个问题。
分布式计算是指一组计算机在相互通信的同时执行一个共同的任务。这不同于并行计算,在并行计算中,一个任务被分成多个子任务,这些子任务被分配给同一计算机系统上的不同处理器。
并行计算与分布式计算。作者制作的图像。
虽然有相当多的 Python 库使用贝叶斯优化来调优模型超参数,**但是它们都不支持任何分布式计算框架上的调度。**开发芒果的作者的动机是创建一个能够在分布式计算环境中工作的优化算法,同时保持贝叶斯优化的能力。
Mango 的架构在分布式计算环境中运行良好的秘密是什么?Mango 采用模块化设计,优化器与调度器分离。这种设计可以轻松扩展使用大量数据的机器学习管道。然而,这种架构在优化方法方面带来了挑战,因为传统的贝叶斯优化算法是顺序的,这意味着采集函数仅提供单个下一点来评估搜索。
Mango 使用两种方法并行化贝叶斯优化:一种叫做的方法批量高斯过程土匪和 k-means 聚类。在这篇博客中,我们不会解释批量高斯过程。如果你有兴趣了解更多关于这种方法的信息,你可以阅读的这篇论文。
关于聚类方法,使用 k-means 聚类来横向扩展贝叶斯优化过程是由 IBM 的一组研究人员在 2018 年提出的(参见本文了解技术细节)。这种方法由从搜索域采样的聚类点组成,这些点在采集函数中产生高值(见下图)。一开始,这些聚类在参数搜索空间中彼此远离。当替代函数中的最佳区域被发现时,参数空间中的距离减小。k-means 聚类方法横向扩展优化,因为每个聚类用于作为单独的过程运行贝叶斯优化。这种并行化导致更快地找到最佳模型超参数。
Mango 使用聚类方法来扩展贝叶斯优化方法。采集函数上的彩色区域是从搜索空间中具有高采集函数值的采样点构建的聚类。在开始时,聚类被彼此分开,但是它们的距离被缩短,因为代理函数类似于目标。图片来自博文:ARM research 的 AutoML 可扩展超参数调优。
除了在分布式计算框架上工作的能力之外, Mango 还兼容 scikit-learn API。这意味着您可以将超参数搜索空间定义为一个 Python 字典,其中的关键字是模型的参数名称,每个项目都可以用 scipy.stats 中实现的 70 多个发行版中的任何一个来定义。所有这些独特的特征使 Mango 成为想要大规模利用数据驱动解决方案的数据科学家的绝佳选择。
如果你有兴趣了解更多关于 Mango 的内部运作,你可以阅读原文或者访问这个由库的作者写的漂亮的博客:
https://community.arm.com/arm-research/b/articles/posts/scalable-hyperparameter-tuning-for-automl
简单的例子
现在让我们来说明一下 Mango 在优化问题中是如何工作的。你首先需要创建一个 Python 环境,然后通过下面的命令安装芒果:
pip install arm-mango
对于这个例子,我们使用可以直接从 Scikit-learn 加载的加州住房数据集(更多信息请点击此链接):
该数据集包含 20,640 个样本。每个样本有 8 个特征,包括房屋年龄,平均卧室数量等。California housing 数据集还包括每个样本的房价,单位为 100,000。房价分布如下图所示:
在左侧面板中,加利福尼亚数据集中房价的空间分布。右边是同一变量的直方图。作者制作的图像。
注意房价分布有点偏左。这意味着在目标中需要一些预处理。例如,我们可以通过对数或 Box-Cox 变换将目标的分布转换为正常形状。由于减少了目标的方差,这种预处理可能会提高模型的预测性能。我们将在超参数优化和建模过程中执行此步骤。现在,让我们将数据集分为训练集、验证集和测试集:
我们准备用芒果来优化机器学习模型。首先,我们定义搜索空间,Mango和 Mango 从中取值。在这个例子中,我们使用一种叫做极端随机化树的算法,这是一种非常类似于随机森林的集成方法,除了选择最佳分割的方式,这是随机进行的。这种算法通常以略微增加偏差为代价来减少方差。如果你有兴趣了解更多关于极度随机化的树,你可以访问这个 Scikit-learn 文档。
极端随机化树的搜索空间可以定义如下:
一旦定义了参数空间,我们就指定了目标函数。这里我们使用上面创建的训练和验证数据集;但是,如果要运行 k 重交叉验证策略,就需要在目标函数内部实现。
关于上述代码的重要说明:
目标函数旨在找到最小化均方根误差(RMSE)的最佳模型参数。
Scikit-learn 中回归问题的极端随机化树的实现称为extractreesregressor。
注意火车集合中的房价是对数变换的。因此,在验证集上做出的预测被转换回它们的原始规模。
优化模型的超参数所需的最后一步是实例化类调谐器,它负责运行芒果:
在配备 2.3 Ghz 四核英特尔酷睿 i7 处理器的 MacBook Pro 上,代码运行时间为 4.2 分钟。
最佳超参数和最佳 RMSE 分别为:
best parameters: {‘max_depth’: 9, ‘max_features’: ‘auto’, ‘min_samples_leaf’: 85, ‘min_samples_split’: 729}
best accuracy (RMSE): 0.7418871882901833
当在具有最佳模型参数的训练集上训练模型时,测试集上的 RMSE 是:
rmse on test: 0.7395178741584788
**免责声明:**运行这段代码时,您可能会得到不同的结果。
让我们简单回顾一下上面代码中使用的调音器*类。*该类有许多配置参数,但在本例中,我们只尝试了其中的两个:
num_iteration
:这些是 Mango 用来寻找最优值的总迭代次数。initial_random
:该变量设置测试的随机样本数量。重要: 芒果将所有随机样本一起返回。这非常有用,尤其是在优化需要并行运行的情况下。
有关配置芒果的其他参数的更多信息,您可以访问此链接。
这篇博客中的例子使用了一个小数据集。然而,在许多实际应用中,您可能会处理需要并行实现 Mango 的大型数据文件。如果你去我的 GitHub 库,你可以找到这里显示的完整代码以及大数据文件的实现。
芒果很百搭。您可以在广泛的机器和深度学习模型中使用它,这些模型需要并行实现或分布式计算环境来优化它们的超参数。因此,我鼓励你访问芒果的 GitHub 库。在那里,你会发现许多笔记本展示了在不同的计算环境中使用芒果的情况。
带回家的信息
在这篇博客中,我们回顾一下 Mango :一个大规模进行贝叶斯优化的 Python 库。该软件包将使您能够:
- 扩展您的模型超参数优化,甚至可以在分布式计算框架上运行。
- 轻松地将 scikit-learn 模型与 Mango 集成,以产生强大的机器学习管道。
- 使用 scipy.stats 中实现的任何概率分布函数来声明您的搜索空间。
所有这些特性使得 Mango 成为一个独特的 Python 库,它将拓宽你的数据科学工具包。
我希望你学到了新的东西。再次感谢你的阅读!
歧管对齐
原文:https://towardsdatascience.com/manifold-alignment-c67fc3fc1a1c
多个数据集的统一
作者的 3D 艺术
单倍比对是一个寻找共同潜在空间的问题,在这个空间中,我们共同对多个数据集进行降维,以保持这些数据集之间的任何对应关系。在我们深入流形对齐的细节之前,让我们先了解什么是流形。
什么是流形?
n 维流形是具有极限、连续性和正确性的最一般的数学空间,并且允许与 n 维欧几里得空间存在连续的反函数。流形局部类似欧几里得空间,但它们可能不是欧几里得空间。本质上,流形是欧几里得空间的推广。
在拓扑学中,如果两个物体中的一个可以通过
变形成为另一个,而不需要切割或粘合,那么这两个物体就被认为具有相同的形状。形状与
相同的物体称为同胚。流形同胚于欧几里得空间。
流形对齐的问题
在信息革命的时代,我们看到物联网、信息物理系统、生物信息学和机器人领域产生了海量数据。这些数据越来越多模态化,即来自多个来源的一个以上的数据集描述相同的物理或生物过程。自然,这些数据集共享相似或相同的潜在语义结构。流形对齐的问题是关于对齐多个数据集以获得更有意义的表示。从各种模态获得的数据集可能具有不相交的特征,这使得对齐成为一个具有挑战性的问题。
问题陈述
我们可以将流形对齐问题总结为联合降维,约束是由问题中各种数据集之间的对应关系激发的。
如果我们有大小为nt8】×p 的数据集 x 和大小为mt14】×t16】r 的数据集,它们的实例位于同一个流形 z 上,那么对齐的问题就是找到 f 和 g 使得f(xᵢ【t24)接近 g 在这种情况下,我们感兴趣的是映射 f: ℝᵖ → ℝᵏ和 g: ℝʳ → ℝᵏ的一些潜在维度 k 。我们用ℝ来表示实数集。
只有当f(xᵢ*)=g(yᵢ)*时,每个样本点 x ᵢ 和 y ᵢ才可能有精确的对应关系,否则我们可以使用先验对应关系,如任何关于 x ᵢ 和 y ᵢ.的相似性信息【f(x);g(y) 将是 X 和 Y 在新的潜在空间中的统一表示,而 f(X) 和 g(Y) 将是 X 和 Y 的新坐标
解决问题的几个思路:
- 通过将每个数据集的相似点映射到欧几里德空间中的相似位置,除了保留数据集之间的关系之外,还可以保留每个数据集中的单个结构。
- 流形对齐算法可以是无监督的、半监督的或有监督的,这取决于有多少先验信息可用。完全对应允许监督算法,而不完全对应允许半监督或无监督。
- 每个数据集的图拉普拉斯算子将是同一流形的离散近似。因此,这些拉普拉斯算子的对角线连接以及用对应关系填充的非对角线元素是该流形的近似。
- 我们可以使用图嵌入算法来保持局部相似性,并且可以通过联合拉普拉斯算子来编码对应关系。因此,流形对齐可以简化为流形学习问题。
损失函数
在为流形对齐问题开发损失函数时,我们需要考虑两个因素。第一个是损失函数应该保持局部结构和对应信息。第二是在形成联合拉普拉斯算子之后,流形对齐应该等价于拉普拉斯特征映射。
如果我们有 c 数据集 X ⁽ ⁾、 X ⁽ ⁾、…、 X ⁽ᶜ⁾,那么我们可以为数据集内写一个损失项来反映局部相似性的保持,如
其中 F ⁽ᵃ⁾嵌入第 a 个数据集,即 X ⁽ᵃ⁾.的新坐标它表示如果第 I 行和第 j 行相似,即当 w 项大于时, F ⁽ᵃ⁾( i ,)和 F ⁽ᵃ⁾( j ,)应该彼此靠近放置。
对于数据集之间,损失项为
它定义了在⁽ᵃ⁾and 和⁽ᵇ⁾.之间保持通信的成本因此,我们可以有完整的损失函数为
拉普拉斯特征映射的损失函数可以使用联合相邻矩阵写成
其中 F 是所有数据集的矢量表示,而 W 是联合邻接矩阵。这就是说,如果当 W(i,j) 较大时, X ⁽ᵃ⁾和 X ⁽ᵇ⁾的第 I 和第 j 行相似,那么它们在潜在空间 F ( i ,)和 F ( j ,)中的位置应该接近。我们可以把它改写成
其中 L 是所有数据集的联合拉普拉斯算子
因此,流形对齐的优化问题变成
其中 I 是一个s×s 单位矩阵, s 是新空间的维数。
解的最优性
上述优化问题的最优解可以使用拉格朗日乘数来求解
其中f=【f₁,f₂,… fₛ】其中解为 s 最小非零特征向量和 λ 为拉格朗日乘子。
最终算法
给定 c 数据集 X ⁽ ⁾、 X ⁽ ⁾、…、 X ⁽ᶜ⁾都在相同的流形上,相似性函数 S 提供来自相同数据集的两个样本点关于沿流形的测地距离的相似性,以及一些对应信息——可以是来自不同数据集的样本点的相似性的形式(例如,皮尔逊相关、互信息等——只是一个想法,我们需要进一步探索),我们
- 使用相似性函数 S 找到每个数据集的邻接矩阵 W ⁽ ⁾、 W ⁽ ⁾、…、w⁽ᶜ⁾——如果一个实例是另一个实例的 k-最近邻,则可能包括两个实例之间的权重。
- 构造联合拉普拉斯 l。
- 计算sLf=λDf的最小非零特征向量
- 下面的 F 方程的行是 X⁽ᵍ⁾:的新坐标
稍后,我将提供一个使用玩具数据集的编码示例。在此期间,如果读者有任何问题,欢迎在评论中提问。
感谢您的阅读。如果你喜欢我的文章并想支持我的内容,我请求你通过https://rahulbhadani.medium.com/membership订阅 Medium。
参考
- https://web . archive . org/web/20210506132703/http://www . math . LSA . umich . edu/~ jchw/womp talk-manifolds . pdf
- https://www . math . Arizona . edu/~ glickenstein/research/layman fin/node 3 . html
- https://web . archive . org/web/20160909193617/http://OCW . MIT . edu/courses/mathematics/18-965-geometry-of-manifolds-fall-2004/lecture-notes/lesson 1 . pdf
- https://my . Vanderbilt . edu/Stacy fostad/files/2011/10/shape of space . pdf
- https://people . csail . MIT . edu/pkrafft/papers/Wang-et-al-2010-manifold . pdf
- https://sites.google.com/site/changwangnk/home/ma-html
在 Python 中操作列表
原文:https://towardsdatascience.com/manipulating-lists-in-python-8edcfb30b9bd
在 Python 中使用列表创建附加数据结构
图片由像素上的苏西·黑兹尔伍德拍摄
列表和数组是 Python 中最广泛使用的两种数据结构。Python 中的列表只是对象的集合。这些对象可以是整数、浮点数、字符串、布尔值,甚至是字典之类的其他数据结构。数组,特别是 Python NumPy 数组,类似于 Python 列表。主要区别在于 NumPy 阵列速度更快,并且对对象的同质性有严格的要求。例如,字符串的 NumPy 数组只能包含字符串,不能包含其他数据类型,但是 Python 列表可以包含字符串、数字、布尔值和其他对象的混合。因为像计算平均值或总和这样的操作在 NumPy 数组上要快得多,所以这些数据结构在优先考虑速度性能的环境中更常见。
lists 和 NumPy 数组都有大量的内置方法来执行各种任务,包括排序、查找最小值/最大值、截断、追加、连接等等。
列表也可以定义其他数据结构,比如字典,它们在软件工程和数据科学中都有应用。例如,列表可以生成字典,字典可以转换成 json 文件。软件工程师通常使用这些文件类型。
字典也可以转换成数据框,这是数据科学家经常使用的。更重要的是,Python 列表允许您轻松构建对许多数据任务有用的各种数据框。这包括用新的字段扩充现有的数据表,使用构造的列表计算新的数据字段,对通过 API 访问的数据执行探索性的数据分析,等等。对 Python 列表和 NumPy 数组的透彻理解为许多有用的数据任务打开了大门。
列表和数组经常用于生成合成数据等任务。在许多情况下,数据科学团队对真实数据的访问是有限的。当构建依赖于数据 ETL 和机器学习的软件时,合成数据通常是构建应用原型的唯一选择。Python 使您能够生成综合列表,例如姓名、州、身高、职业和任何其他可以表示为字符串的分类值。此外,它还可以生成数字值,如人口、收入和交易金额。对于 Python 中的列表理解,简单的特征工程也很简单。总之,列表和数组都提供了许多操作和生成数据的有用方法。
在这里,我们将调查一些使用列表和数组的最常见的方法和数据结构。这将为初级软件工程师或数据科学家在 Python 中使用列表进行数据操作打下坚实的基础。
构建 Python 列表
用 Python 构建一个列表非常简单。您可以构造一个字符串、浮点值、整数和布尔值的列表。Python 字符串列表是对应于 unicode 字符序列的值列表。浮点列表包含表示实数的值。整数列表包含可以是正数、负数或零的整数值。最后,布尔列表是真/假值的列表。您还可以构造一个混合类型的列表。
让我们首先构建一个包含脸书、亚马逊、苹果、网飞和谷歌公司的字符串列表:
tech_company_names = ['Facebook', 'Apple', 'Amazon', 'Netflix', 'Google']
让我们构建一个整数列表,代表 2021 年这些公司的员工人数。我们的整数列表中的顺序将与我们公司名称列表中的顺序相同。例如,在我们的公司名称列表中,“脸书”是第一个元素的值,在我们的员工列表中,58,604 是脸书的员工数。
tech_company_employees = [58604, 147000, 950000, 11300, 135301]
接下来,让我们构建一个与 2021 年每家公司的收入(以十亿美元计)相对应的浮动列表:
tech_company_revenue = [117, 378, 470, 30, 257]
最后,让我们创建一个布尔列表。我们将使用叫做列表理解的东西来构造我们的布尔值列表。列表理解是一种基于其他列表中的值构建新列表的简单而有用的方法。列表理解的结构通常如下所示:
list = [expression for element in iterable]
表达式可以是 iterable 本身的元素,也可以是元素的某种转换,比如检查条件的真值。这就是我们将要做的来创建布尔列表。该列表将基于我们的技术公司员工列表中的值。如果雇员超过 60,000 人,则该值为 true,否则为 false:
tech_company_employee_bool = [x > 60000 for x in tech_company_employees ]
这将创建以下列表:
[False, True, True, False, True]
也可以构造混合类型的列表。假设我们有公司名称、收入、员工数量和基于员工数量的布尔值。让我们考虑一下微软的混合类型值列表:
new_company_info = ['Microsoft', 163000, 877, True]
我们可以使用 append 方法来更新每个列表。如果我们打印更新的列表,我们会看到添加了新的值:
print('Company: ', tech_company_names)
print('Employees: ', tech_company_employees)
print("Revenue: ", tech_company_revenue)
print("Employee_threshold: ", tech_company_employee_bool)
作者图片
Python 列表还配备了各种有用的方法。例如,我们可以对公司列表(按字母顺序)和员工数量(按升序)进行排序:
tech_company_names.sort()tech_company_employees.sort()
这将我们的列表修改为以下内容:
作者图片
请注意,这将改变这些列表的顺序,使它们不再匹配。更安全的选择是使用内置的 Python 方法 sorted,它返回一个排序列表,我们可以将它存储在一个新的变量中,而不是修改旧的列表。
sort_company = sorted(tech_company_names)sort_employee = sorted(tech_company_employees)print(sort_company)print(sort_employee)
作者图片
构造一个 NumPy 数组
NumPy 是一个用于生成数组的 Python 包,它与 Python 列表有很多区别。最大的区别是 NumPy 数组比 Python 列表使用更少的资源,这在存储大量数据时变得很重要。如果您正在处理成千上万的元素,Python 列表将适合大多数目的。然而,当列表中的元素数量接近数百万或数十亿时,NumPy 数组是更好的选择。
NumPy 对于生成合成数据也很有用。例如,假设在我们的技术公司数据示例中,我们缺少净收入的值,其中净收入是总销售额减去商品成本、税收和利息。我们想以某种方式估算这些值。此外,我们希望从正态分布中对这些估算值进行采样。让我们创建一个 NumPy 数组,其中包含每家公司的净收入列表。
要继续,让我们导入 NumPy 包:
import numpy as np
为了生成我们的样本,我们需要一个平均净收入和净收入标准差的值。让我们做一个简单的假设,跨公司的平均净收入为 800 亿美元,标准差为 400 亿美元。我们将分别把均值和标准差μ和σ称为变量:
mu, sigma = 80, 40
我们还需要指定我们想要生成的值的数量。我们可以简单地将科技公司列表的长度存储在一个新变量中,我们称之为 n_values:
n_values = len(tech_company_names)
我们还应该指定一个随机种子值,以确保我们的结果是可重复的:
np.random.seed(21)
为了生成我们的数组,我们将使用 NumPy random 模块中的常规方法。我们将把平均值(mu)、标准偏差(sigma)和值的数量(n_values)的参数值传递给正常方法,并将结果存储在一个名为 net_income 的变量中:
net_income_normal = np.random.normal(mu, sigma, n_values)print(net_income_normal)
作者图片
在这里,我们为脸书(770 亿美元)、苹果(750 亿美元)、亚马逊(1250 亿美元)、网飞(290 亿美元)、谷歌(1090 亿美元)和微软(110 亿美元)的净收入生成了合成值。由于这些数字是合成的,我们使用汇总统计的合成值来估算所有公司的价值,它们不太现实。
生成这些合成值的一种更准确的方法是使用每家公司的平均净收入和净收入的标准偏差(如果可以获得),从每家公司的唯一正态分布中得出。就目前而言,我们假设我们可以获得所有公司的平均值和标准差的简单方法就足够了。
对于本例,我们假设净收入的分布是正态的(或形状像钟形曲线)。另一种常见模式是胖尾分布,当一个分布包含大量的极端正值或负值时就会出现这种情况。这也称为偏斜度。我们可以使用 NumPy 中的 gumbel 方法生成一个来自胖尾分布的净收入合成值列表:
np.random.seed(64)net_income_fat_tail = np.random.gumbel(mu, sigma, n_values)print(net_income_fat_tail)
作者形象
同样,这里值得注意的是,尽管这些值不太现实,但通过使用实际的汇总统计值并为每家公司生成一个分布,它们可以很容易地得到改善。有了正确的领域专业知识,这些方法可以生成高质量、逼真的合成数据。
使用列表构建字典、JSON 文件、数据帧和 CSV 文件
有了我们生成的列表,我们现在可以构建一个 Python 字典,这是一种将列表存储在键:值对中的有用方法。我们有一个公司名称、员工人数、收入、收入阈值布尔人、正态分布净收入和胖尾分布净收入的列表。让我们为每个列表创建一个字典映射:
company_data_dict = {'company_name': tech_company_names,
'number_of_employees': tech_company_employees,
'company_revenue': tech_company_revenue,
'employee_threshold': tech_company_employee_bool,
'net_income_normal': list(net_income_normal),
'net_income_fat_tail': list(net_income_fat_tail)}
print(company_data_dict)
作者形象
我们看到,在这个数据结构中,我们有键,它们是唯一的字符串,或者我们给每个列表和相应的列表命名。我们可以通过以下逻辑很容易地将这个字典转换成一个 JSON 文件:
import json
with open('company_data.json', 'w') as fp:
json.dump(company_data_dict, fp)
我们可以读取 JSON 文件并打印结果:
f = open('company_data.json')
company_json = json.loads(f.read())
print(company_json)
作者形象
我们也可以很容易地使用熊猫数据框架构造器将 Python 字典转换成熊猫数据框架
import pandas as pdcompany_df = pd.DataFrame(company_data_dict)print(company_df)
作者形象
我们还可以使用 to_csv 方法,使用 panasus 将此数据框写入 CSV 文件:
company_df.to_csv("comapany_csv_file.csv", index=False)
我们可以使用 read_csv 方法读入我们的文件:
read_company_df = pd.read_csv("comapany_csv_file.csv")
并显示我们的数据:
print(read_company_df)
作者形象
我们看到,通过一行代码,我们就可以使用之前创建的列表和字典来生成熊猫数据框架。
这篇文章中的代码可以在 GitHub 上找到。
结论
在 Python 中构造列表和数组对于各种任务都很有用。Python 允许您轻松地创建和操作字符串、布尔值、浮点数和整数的列表。此外,list comprehension 允许您以一种可读和简洁的方式基于另一个列表中的值创建新列表。
NumPy 数组是一种资源效率更高的列表替代方法,列表还配备了用于执行复杂数学运算和生成合成数据的工具。这两种可迭代对象都可以用来构建更复杂的数据结构,比如字典和数据框。此外,从这些由列表创建的数据结构中创建 json 和 csv 文件非常简单。对于每个工程师和数据科学家来说,很好地理解 python 中用于生成和操作列表和数组的工具是必不可少的。
如果你有兴趣学习 python 编程的基础知识、Pandas 的数据操作以及 python 中的机器学习,请查看Python for Data Science and Machine Learning:Python 编程、Pandas 和 sci kit-初学者学习教程 。我希望你觉得这篇文章有用/有趣。
本帖原载于 内置博客 。原片可以在这里找到https://builtin.com/data-science/how-to-create-list-array-python。
处理极坐标数据框中的值
原文:https://towardsdatascience.com/manipulating-values-in-polars-dataframes-1087d88dd436
了解如何使用 Polars 中的various
方法来操作数据帧
由杰西卡·鲁斯切洛在 Unsplash 上拍摄的照片
到目前为止,我已经谈论了如何使用 Polars 数据框架,以及为什么它是一个比 Pandas 更好的数据框架库。继续我们对 Polars 的探索,在本文中,我将向您展示如何操作 Polars 数据框架,特别是:
- 如何更改每一列/行的值
- 如何合计每一列/行的值
- 如何向现有数据框架添加新列/行
准备好了吗?我们走吧!
创建示例数据帧
让我们使用元组列表创建一个 Polars 数据帧:
import polars as plmatrix = [
(1, 2, 3),
(4, 5, 6),
(7, 8, 9),
(10, 11, 12),
(13, 14, 15),
(16, 17, 18)
]df = pl.DataFrame(matrix, columns=list('abc'))
df
数据帧看起来像这样:
作者图片
让我们研究一些方法,您可以调用这些方法来操作数据帧中的值。
使用 apply()方法
apply()
方法可用于:
- 数据帧中的单个列,或者
- 整个数据帧
应用于列
例如,假设您想要将' a' 列中的所有值乘以 2。您可以执行以下操作:
df.select(
pl.col('a').**apply**(lambda x: x*2)
)
列' a 中的所有值现在将乘以 2:
作者图片
在上面的 lambda 函数中,x
将采用列 a 的单个值。当应用于一个列时,apply()
方法逐个发送列的值。这为您提供了在决定如何更改值之前检查每个值的机会。例如,您可以只乘以那些大于或等于 5 的值:
df.select(
pl.col('a').apply(**lambda x: x*2 if x>=5 else x**)
)
这将产生以下输出:
作者图片
一般来说,使用
*apply()*
方法实现逻辑比使用表达式实现逻辑要慢,并且占用更多内存。这是因为表达式可以并行化和优化,表达式中实现的逻辑是用 Rust 实现的,这比用 Python 实现(例如用 lambda 函数实现)要快。因此,尽可能使用表达式,而不是使用apply()
函数。例如,早期的apply()
方法也可以用一个表达式重写:
pl.col('a').apply(lambda x: x*2)
# rewritten as an expression
**pl.col('a') * 2**
请注意,结果只包含一列。如果您希望其余的列也出现在结果中,请使用select()
和exclude()
方法:
**q = (
df
.lazy()
.select(
[
** pl.col('a').apply(lambda x: x*2),
** pl.exclude('a')
]
)
)****q.collect()**
现在结果包含了所有的列:
作者图片
如果您想将所有列乘以 2,使用pl.col(‘*’)
选择所有列:
q = (
df
.lazy()
.select(
**pl.col('*').apply(lambda x: x*2)**
)
)q.collect()
所有列现在将乘以 2:
作者图片
如果要将列' a' 乘以 2,然后将结果存储为另一列,请使用alias()
方法:
q = (
df
.lazy()
.select(
[
pl.col('*'),
pl.col('a').apply(lambda x: x*2)**.alias('x*2')**,
]
)
)q.collect()
结果现在会有一个额外的列:
作者图片
使用 map()方法
另一个类似于apply()
方法的功能是map()
方法。与apply()
方法不同的是,map()
方法将一列的值作为单极性序列发送:
df.select(
pl.col('a').**map**(lambda x: x*2)
)
在上面的 lambda 函数中,x
是包含列 a 的值的 Polars 序列。上述语句产生以下输出:
作者图片
应用于行
注意到目前为止,数据帧中的列应用了apply()
方法。如果您想应用于数据帧中的行该怎么办?在这种情况下,直接调用 dataframe 上的apply()
方法。
为了理解它是如何工作的,我编写了一个test
函数来打印出将apply()
函数应用于 dataframe 时得到的值:
def test(x):
print(x)
return x
df.apply(test)
它返回以下内容:
(1, 2, 3)
(4, 5, 6)
(7, 8, 9)
(10, 11, 12)
(13, 14, 15)
(16, 17, 18)
这意味着apply()
函数在应用于 dataframe 时,会将每一行的值作为一个元组发送给接收函数。这对于一些用例很有用。例如,假设您需要将一行中的所有数字除以 2,如果它们的和大于 10,那么您可以将 lambda 函数写成:
df.apply(lambda x: **tuple([i // 2 for i in x]) if sum(x) > 10 else x**)
结果会是这样的:
作者图片
如果想复制数据帧中的所有列,也可以使用apply()
方法:
df.apply(lambda x: x*2)
数据帧现在有六列:
作者图片
注意
apply()
功能不能应用于 LazyFrame。
对数据帧中的值求和
通常,您需要按行或按列对数据帧中的所有值求和。
按列
合计每列值的最简单方法是在数据帧上使用sum()
方法:
df.sum()
作者图片
要将上述结果附加到现有数据帧,使用concat()
方法:
pl.**concat**([df, df.sum()])
作者图片
按行
要合计每行所有列的值,使用sum()
方法,并将axis
参数设置为1
:
df.sum(**axis=1**)
结果是一个 Polars 系列:
作者图片
将 Polars 系列视为数据帧中的一列
您也可以使用select()
方法来选择您想要求和的列:
df.**select(pl.col('*'))**.sum(axis=1)
以下代码片段将系列作为新列添加到 dataframe 中:
df['sum'] = df.select(pl.col('*')).sum(axis=1)
df
作者图片
如果您不想使用方括号索引(Polars 中不推荐使用),请使用select()
方法:
df.**select**(
[
pl.col('*'),
df.select(pl.col('*')).sum(axis=1).alias('sum')
]
)
https://weimenglee.medium.com/membership
我将在即将到来的新加坡 ML 会议(2022 年 11 月 22-24 日)上主持一个关于 Polars 的研讨会。如果你想在 Polars 数据框架上快速起步,请在https://ml conference . ai/machine-learning-advanced-development/using-Polars-for-data-analytics-workshop/上注册我的研讨会。
摘要
我希望这篇文章为您使用 Polars 数据框架增加了一些弹药。以下是何时使用apply()
和map()
方法的快速总结:
- 对表达式调用
apply()
方法,将函数应用于数据帧中 列 中的 单个 值。 - 在表达式上调用
map()
函数,将函数应用于数据帧中的 列 。 - 在数据帧上调用
apply()
方法,将函数应用于数据帧中的 行 s 。
保存这篇文章,并在下次使用 Polars 数据框时用作快速参考!
多种语言,一个深度学习模型
原文:https://towardsdatascience.com/many-languages-one-deep-learning-model-69201d02dee1
多语言理解比你想象的容易!
(来源)
*我们如何让基于自然语言的深度学习系统理解多种语言的数据?*一种简单的方法是在每种期望的语言上训练一个单独的深度学习模型。但是,这种方法非常繁琐。*如果我们需要创建一个接收来自 100 多种不同语言的数据的系统,该怎么办?*承担训练 100 个深度学习模型的成本,对于大多数从业者来说是不可行的。
幸运的是,最近的深度学习研究表明,多语言理解可能比看起来更简单。在 BERT [1]出现后不久,研究人员意识到双向转换器模型能够同时从多种语言中学习。这种方法使多种语言的深度学习变得简单——只需训练一个模型来处理所有语言。
“最近的发展表明,有可能建立通用的跨语言编码器,可以将任何句子编码到共享的嵌入空间中。”
—来自【2】
这些多语言模型几乎与 BERT 相同。然而,他们的自我监督预训练是在包含来自 100 多种语言的文本数据的多语言语料库上进行的。尽管对于训练这些多语言模型存在一些额外的考虑,但是总体方法与训练单语 BERT 模型惊人地相似。
多语言深度学习模型对于语言理解的主要好处有两个方面:
- 简单性:单一的模型(而不是每种语言的独立模型)更容易使用。
- 归纳转移:多种语言的联合训练使得跨语言模式的学习成为可能,这有利于模型性能(特别是在低资源语言上)。
对于深度学习实践者来说,这些模型非常有用,因为它们表现得惊人地好,并且不需要任何深入的语言理解。在这个概述的过程中,我将解释基于 BERT 的多语言模型的当前工作,包括对这种模型的行为/倾向的分析和对最近提出的模型的解释。
使用自我监督、屏蔽的语言建模目标对多语言 BERT 模型进行预训练(由作者创建)
多语言理解的基础
在自然语言处理(NLP)领域,大量的研究工作已经投入到制作通用(单语)句子表示(例如, FastText 、 Word2Vec 、GloVe【8】等)中。).BERT [1]的出现——在我的上一篇文章中解释过——彻底改变了这个领域,揭示了预先训练的双向变压器模型产生可微调的句子表示,以非常高的准确度解决语言理解任务。
除了单语方法,许多 NLP 研究人员已经研究了跨语言的单词或短语对齐问题[9,10],机器翻译[11]和跨语言表示学习[12]。其核心是,这样的研究提出了这样一个问题:有没有可能创建一个跨语言共享嵌入空间的通用句子编码器?
朝着这个方向发展,最近的研究发现,伯特风格、预先训练的变形金刚可以产生有用的多语言句子表示。通过简单地使 BERT 的预训练语料库多语言化,这些模型获得了对许多语言的体面理解,并可以进行微调以解决跨语言任务(例如,对多种语言的句子进行分类)。第一种方法叫做多语言 BERT (mBERT ),实际上是与 BERT 同时提出的。
多语言 BERT (mBERT)
最初的 BERT 出版物[1]没有提到多语言学习。然而,BERT 的公开版实际上包含了一个多语种版本的 BERT,名为(创造性地)多语种 BERT (mBERT) 。
该模型与 BERT 几乎相同,唯一的区别是:
- 多种语言的联合预培训(详见下文)
- 跨语言使用共享的令牌词汇表和嵌入空间
简而言之, mBERT 是一个单一的模型,它学习跨大量语言的统一表示。
预培训方法。 mBERT 接受了来自超过 100 种语言(而不仅仅是英语)的维基百科数据的预训练。为了构建预训练数据集,作者在维基百科上找到数据量最大的 100 种语言,然后将这些数据串联起来,形成多语言预训练数据集。mBERT 学习的所有语言都是基于大小为 110K 的单个单词块词汇嵌入的。
为每种语言分配一个概率,预训练更新从一种语言中抽取一部分文本数据,用于根据该概率进行的自监督更新,允许 mBERT 在整个预训练过程中接触所有不同的语言。不出所料,预训练数据集中的某些语言自然代表性不足——它们的数据比其他语言少。为了减轻这种不平衡的影响,每种语言的概率被指数平滑如下:
- 取每种语言的概率(例如,如果 21%的预训练语料库是英语,则英语具有 21%的概率)
- 用因子 s 对这个概率取幂(例如,mBERT 使用 s=0.7,得到 0.21⁰.7 = 0.335)
- 基于这些指数化的概率,重新归一化(即除以总和)每种语言的概率
这种平滑方法 (i) 略微降低了采样高资源语言的概率,而 (ii) 略微增加了采样低资源语言的概率。尽管平滑的量由 s 的选择来控制,但是这种技术确保了在预训练期间低资源语言不会被代表不足(反之亦然);见下文。
语言概率的指数平滑
对于那些感兴趣的人来说,语言概率的指数平滑法受到了用于时间序列预测的类似技术的启发。
**mBERT 真的很有用。**在 XNLI 数据集上评估时,发现 mBERT 在解决跨语言任务方面相当有效。跨多种语言的联合预训练带来了显著的性能优势,其中低资源语言的分类任务得到了最大的提升。这些结果表明,多语言预训练有助于正向归纳迁移——同时训练多种语言实际上比训练每种语言的单独模型更好。
应当注意,许多用于多语言(和单语)理解的现有方法依赖于专门的语言学方法。mBERT(和 BERT)的提议极大地简化了日常实践者解决 NLP 任务的过程。
数据集
有几个数据集可用于多语言深度学习模型的下游评估。这些数据集既包括分类风格的问题,也包括更复杂的问题,如命名实体识别或问题回答。
跨语言 NLI (XNLI)语料库。 XNLI 是流行的 multiNLI 语料库的多语言版本,是目前最流行的评估多语言语言理解的数据集。multiNLI 数据集包含 433,000 对已经用文本蕴涵信息进行了注释的句子(即,句子是否相互矛盾,相互蕴涵,或者是中性的)。multiNLI 数据集不同于其他自然语言推理数据集,因为它包含跨众多语音和文本体裁的句子。
XNLI 语料库是 multiNLI 的一个版本,已被翻译成 15 种不同的语言,由:
- 使用人类来翻译开发和测试集
- 机器翻译训练集
在 XNLI 数据集上进行训练的几种不同方法在文献中经常被报道,包括:
- **翻译-训练:**对每种语言的训练集使用单独的模型进行训练和评估
- **翻译-测试:**开发和测试集被翻译成英语,然后在训练集上微调单个英语模型并用于评估
- translate-train-all: 在所有不同语言的训练集的机器翻译版本上微调单个多语言模型,然后使用每种不同语言进行评估。
也可以使用零测试设置来执行评估,在零测试设置中,某一语言被排除在训练集之外,但仍包括在评估中。
**其他数据集。**虽然 XNLI 非常受欢迎,但还有其他几个数据集用于评估多语言理解。例如,命名实体识别 (NER)的 CoNLL 数据集包含英语、荷兰语、西班牙语和德语的翻译。此外,对于问题回答, MLQA 基准采用了流行的小队基准用于英语,并将其扩展到西班牙语、德语、阿拉伯语、印地语、越南语和中文版本。最后,多语言模型仍然通常在 GLUE 基准上进行评估,以便更好地比较它们与单语模型的性能。最近还提出了更广泛的多语言基准,如 XTREME 。
出版物
我现在将概述几个研究多语言 BERT 模型的出版物。这些出版物中的两个提出了对底层模型的潜在修改,而其他的分析模型行为并研究对更复杂应用的扩展(例如,命名实体识别)。
跨语言语言模型预训练[2]
XLM 的语言嵌入(摘自[2])
与 mBERT 的提议同时,[2]的作者开发了跨语言语言模型(XLM)。XLM 是第一个使用生成性和跨语言预训练相结合的思想来创建多语言、基于转换的语言理解模型的模型之一。XLM 与伯特共享相同的架构,除了额外的“语言”嵌入被添加到模型输入中的每个令牌;见上图。
与 mBERT 类似,XLM 有一个共享的标记化方法和跨所有不同语言的嵌入空间。由于这种共享的嵌入空间,不同语言之间共享的标记级模式很容易学习。
“这极大地改善了共享相同字母表或锚标记(如数字或专有名词)的语言之间嵌入空格的对齐方式”
—来自【2】
与 mBERT 相反,作者考虑了 XLM 训练前数据的两个来源:
- 来自每种不同语言的无监督的原始文本数据(与 mBERT 预训练数据相同)。
- 不同语言的平行句子集(即一种语言的句子与另一种语言的相同句子成对出现)。
上面提到的第二种数据集类似于机器翻译数据集——它只包含成对的句子,其中每个句子都被翻译成了另一种语言。显然,获得这样的句子对比获得原始文本更困难,因为它需要翻译文本数据(通过机器或人工注释器)。
利用这两个数据来源,为 XLM 提出了几个不同的培训前任务:
- 因果语言建模(CLM): 给定一个序列中的一组单词,预测下一个单词(这是一个普通的语言建模任务)。
- 蒙面语言建模(MLM): 相同,自我监督 MLM 任务在 BERT 内部使用。
- 翻译语言建模(TLM): 类似于 MLM,但是使用不同语言的两个平行句子作为输入(与单语输入相对)。
直观上,TLM 任务可以学习语言之间的对应关系。如果某个单词在一种语言中被屏蔽,该模型可以关注翻译句子的相应区域来预测被屏蔽的单词;见下文。
(来自[2])
因为 CLM 任务需要使用单向自我关注,三个独立的 XLM 模型使用 CLM、MLM 和 MLM+TLM 进行预训练。当对下游任务进行评估时,这些 XLM 模型在跨语言检测和机器翻译基准上取得了最先进的结果。使用 MLM 或 MLM+TLM 预培训目标会产生最佳效果。
(来自[2])
用 CLM 训练的 XLM 模型也显示出在下游语言建模应用中产生困惑改进。XLM 最显著的成绩提高发生在低资源语言上,揭示了多语言预训练对这类语言产生积极的归纳迁移。
在零元迁移领域,XLM 在推广新语言方面表现出色。特别是,由于跨语言的共享词汇,XLM 可以利用语言之间的标记相似性来推理未明确包括在其训练集中的语言。
多语言 BERT 的语言中立性如何?[3]
随着 mBERT 的公开发布,许多研究人员对模型的行为和属性产生了疑问。BERT 修改这么少怎么支持多种语言?从不同语言中学习到的表征是否对齐(即不同语言中的相似词有相似的表征)?通过更具体的调整来支持多种语言,可以提高 mBERT 的性能吗?
为了解决这些问题,作者在[3]中采用了 mBERT 模型,并研究了它在三个下游任务中的行为:
- **语言识别:**对句子的语言进行分类
- **句子检索:**在不同语言的文本语料库中查找句子的翻译版本
- **词语对齐:**计算同一句子翻译版本中对应词语的对齐度
- **机器翻译质量评估:**在不访问参考译文的情况下计算(机器生成的)译文的质量(即,这是一项更加细致/困难的任务。有关更多信息,请参见[3]的第 4 节)
在对这些不同任务中的每一个进行微调之后,作者进行了广泛的分析,从而对模型的行为产生了深刻的见解。有趣的是,人们发现 mBERT 表示由语言中立和特定于语言的成分组成— 由 mBERT 产生的表示并不完全是语言中立的。
例如,mBERT 能够以高准确度识别输入句子的语言。但是,当 mBERT 的表示居中/规范化时(基于语言中的所有其他表示),模型:
- 在对源语言进行分类方面要差得多
- 更擅长执行句子检索
这一发现揭示了一些语言特有的信息清楚地包含在 mBERT 的表征中。因此,mBERT 学习的模式并不是所有语言都通用的。尽管尝试了多种改进的微调方法,作者在使 mBERT 的表示更加语言中立方面并不成功。因此,产生具有改进的语言中立性的模型是未来的工作。
(摘自[3])
尽管有这些发现,但作者观察到,语言相似的语言在 mBERT 的嵌入空间中聚集在一起(见上文),并且 mBERT 擅长解决跨语言、单词级的语义任务(例如,单词对齐和句子检索)。尽管努力完成更复杂的任务,如机器翻译质量评估,mBERT 仍然能够在一定程度上捕捉跨语言的相似性。换句话说,mBERT 工作得很好,但必须加以改进,才能直接应用于更大范围的跨语言任务。
用 BERT 实现通用语命名实体识别[4]
如果你和我一样,关于这篇论文你会问的第一个问题是— *这个题目是什么,有什么含义?*首先,牛津语言对“通用语”的定义是:
"母语不同的人之间作为共同语言使用的一种语言."
虽然标题有点花哨,但上面的定义实际上很好地概括了本文的目的— 找到一个可以跨多种语言同时执行命名实体识别的单一模型。特别是,[4]的作者在多种语言的 NER 数据集上联合训练了一个 mBERT 模型(如下所示)。
NER 的 mBERT 建筑(摘自[4])
人们发现 mBERT 完全有能力解决这样的任务。该模型可以在不同语言的 NER 任务上联合训练,这产生了 NER 性能的改善,尤其是在低资源语言上。
作者还表明,mBERT 能够执行高精度的零炮 NER 推断。鉴于先前的姆伯特/XLM 模型可以以类似的方式进行零炮推断,这似乎并不令人惊讶。然而,NER 是一个复杂的令牌级任务,与分类任务相比很难解决。尽管它简单易用,但在这个复杂的领域中,mBERT 的表现还是令人惊讶的好。
(摘自[4])
试图分析 mBERT 令人印象深刻的零射击性能,作者表明 mBERT 的令牌嵌入在预训练后排列良好,这意味着不同语言中的相似单词/短语具有相似的嵌入。因此,即使某种语言被排除在微调过程之外,嵌入空间中的这种对齐使得有用的信息能够被提取并用于推断。在微调过程中,对齐性能似乎会下降,但这可以通过简单地冻结早期网络层来解决。
大规模无监督跨语言表征学习[6]
[6]中的作者提出了 XLM-罗伯塔(简称为 XLM-R)模型,这是一个基于罗伯塔的多语言 BERT 模型(即 BERT 的变体)[7]。然而,作者没有建立在公开可用的 mBERT 模型上,而是从头开始建立自己的模型,强调在预训练过程中做出不同的选择可以导致更好的多语言理解下游模型。
(摘自[6])
伯特[1]和 XLM [2]接受了维基百科数据的预训练,这些数据是跨多种语言聚合的。[6]中的作者声称,这种预训练数据集限制了模型性能。取而代之的是,他们使用公共抓取库——一个公开的网络抓取数据数据库——构建了一个多语言文本数据语料库。语料库包括来自 100 多种语言的数据,比基于维基百科的数据集大两个数量级;见上文。
类似于 mBERT,XLM-R 模型在这个更大的语料库上被预训练,根据指数平滑的语言概率使用语言采样。然而,作者确实对基础模型和训练方案做了一些改变:
- 共享令牌词汇表的大小从 110K 增加到 250K。
- 采用更通用的标记化方法来去除 mBERT 所需的特定于语言的预处理。
- 执行的预训练量显著增加(以前的模型显然训练不足!).
XLM-R 模型在多语言推理和问题回答任务上都明显优于 mBERT 和 XLM,确立了其作为多语言理解的首选模型的地位,并揭示了在源自普通爬行的较大语料库上进行预训练是非常有益的。同样,低资源任务的性能似乎从使用 XLM-R 中受益最大,但 XLM-R 在高资源语言上的表现也与单语模型有竞争力。
(摘自[6])
在消融实验中,XLM-R 使用不同数量的语言进行训练。作者揭示了一种现象,他们称之为多语制的诅咒。如果在保持模型容量/大小不变的情况下增加用于训练模型的语言数量,模型的性能最初会有所提高,但随后随着语言数量变得过大,性能会开始下降。虽然这个问题可以通过简单地增加模型大小来缓解,但它揭示了共同学习的语言的数量是使用这些多语言模型的从业者必须记住的一个考虑因素。
外卖食品
尽管多语言理解似乎是一项艰巨的任务,但最近的深度学习研究表明,当前的语言理解方法可以很好地处理这个问题。特别是,稍加修改的 BERT 版本可以通过多语言语料库进行联合预训练,然后进行微调,以惊人的准确度解决跨语言任务。
通过分析这些模型,已经表明:
- 单个多语言 BERT 模型可以学习/理解大量(即> 100 种)不同的语言。
- 多种语言的联合预训练产生了显著的性能优势(特别是对于缺乏大量训练数据的语言)。
- 以这种方式训练的多语言模型在高资源语言(例如,英语或德语)上仍然具有与单语模型竞争的性能
- 多语言 BERT 模型可以以零触发的方式推广到新的语言,因为它们跨语言高度一致、共享词汇/嵌入。
先前对多语言理解(以及一般的 NLP)的研究依赖于大量详细的语言理解。大多数任务通常使用复杂的、专门的架构来解决。有了(多语言)BERT,语言理解任务——甚至跨多种语言——可以用一个简单易懂的模型来解决。
**代码。**微调/使用这些模型比你想象的计算成本更低!因此,我再次强调 BERT 模型对任何深度学习实践者的实用价值。如果你有兴趣尝试这些方法中的任何一种,我在这里推荐 XLM-R 链接的代码示例。
**延伸阅读。**虽然我在这篇概述中涵盖了几篇论文,但我在研究过程中发现了许多其他真正有趣的论文。我最喜欢的一些是:
- Xtreme:用于评估跨语言泛化的大规模多语言多任务基准——包含 40 种语言的多语言、多任务语言理解基准[ 论文 ][ 代码
- mT5:一个大规模多语言预训练文本到文本转换器——流行的 T5 转换器架构的多语言变体[ 论文 ][ 代码
- 用于语音识别的无监督跨语言表征学习—多语言语音识别模型[ 论文
结论
非常感谢你阅读这篇文章。如果你喜欢,请在 twitter 上关注我,或者订阅我的深度(学习)焦点时事通讯,在那里我挑选了一个双周一次的深度学习研究主题,提供了对相关背景信息的理解,然后概述了一些关于该主题的热门论文。我是 Cameron R. Wolfe ,ale gion的研究科学家,莱斯大学的博士生,研究深度学习的经验和理论基础。你也可以看看我在 medium 上的其他著述!
文献学
[1] Devlin,Jacob 等,“Bert:用于语言理解的深度双向转换器的预训练”arXiv 预印本 arXiv:1810.04805 (2018)。
[2]兰普勒,纪尧姆和亚历克西斯·康诺。"跨语言语言模型预训练."arXiv 预印本 arXiv:1901.07291 (2019)。
[3]利博维克、Jindřich、鲁道夫·罗萨和亚历山大·弗雷泽。“多语伯特的语言中立性如何?."arXiv 预印本 arXiv:1911.03310 (2019)。
[4] Moon,Taesun,等.“用 bert 实现通用语命名实体识别”arXiv 预印本 arXiv:1912.01389 (2019)。
[5] Lample,Guillaume 等,“命名实体识别的神经结构”arXiv 预印本 arXiv:1603.01360 (2016)。
[6] Conneau,Alexis 等人,“无监督的跨语言表征学习的规模。”arXiv 预印本 arXiv:1911.02116 (2019)。
[7]刘,,等.“Roberta:一种稳健优化的 bert 预训练方法”arXiv 预印本 arXiv:1907.11692 (2019)。
[8]潘宁顿、杰弗里、理查德·索赫尔和克里斯托弗·曼宁。"手套:单词表示的全局向量."2014 年自然语言处理经验方法会议录(EMNLP)。2014.
[9] Mikolov,Tomas 等人,“单词和短语的分布式表示及其组合性”神经信息处理系统进展 26 (2013)。
[10] Ammar,Waleed 等人,“大规模多语言单词嵌入”arXiv 预印本 arXiv:1602.01925 (2016)。
[11] Johnson,Melvin 等,“谷歌的多语言神经机器翻译系统:实现零镜头翻译。”计算语言学协会汇刊 5(2017):339–351。
[12] Conneau,Alexis 等人,“XNLI:评估跨语言句子表征”arXiv 预印本 arXiv:1809.05053 (2018)。
映射、过滤、归约-用于功能数据管道设计的高阶函数
原文:https://towardsdatascience.com/map-filter-reduce-higher-order-functions-for-functional-data-pipeline-design-929935222475
深入探讨高阶函数,以及如何使用它们为函数式数据管道设计数据转换
概述
在我上一篇关于“控制流”的函数式编程特性的文章中,我提供了函数组合的概述,并讨论了高阶函数和递归作为“函数迭代”形式的使用。
在本帖中,我们将深入探讨高阶函数,以及它们如何用于设计函数式数据管道。
在此之前,我们先来看看典型的数据管道是什么样子的。
数据处理和数据管道设计模式
数据处理的经典方法是编写一个使用数据管道设计模式的程序。
数据管道设计模式(图片来自作者)
在典型的数据管道中,我们从源中读取数据,以某种期望的方式转换数据,并收集输出作为新数据。这就是通常所说的“提取、转换、加载”(ETL)过程。
- 提取:在数据提取阶段,从源系统中提取数据并验证其正确性。
- Transform :在数据转换阶段,一系列的函数被应用到提取的数据,以便为装载到目标数据存储做准备。
- Load :在数据加载阶段,数据被加载到目标数据存储中——它可以是平面文件(比如 CSV 或 Parquet)或数据仓库。
用于数据转换的高阶函数
下面是 S3 桶中从数据库源到平面文件的典型 ETL 过程的简化示意图:
ETL 过程的草图(图片由作者提供)
数据提取和数据加载阶段取决于程序外部的源数据和目标数据存储的状态,而数据转换阶段则取决于程序本身内部的输入数据和应用于数据的函数。因此,数据转换可以自然地表达为由一系列函数组成的函数操作,也称为函数组合。
对于支持函数作为一级对象的编程语言,函数组合可以用高阶函数的形式来表示。虽然我们可以编写自己的高阶函数,但有一些有用的内置高阶函数通常用于数据转换:
map
filter
reduce
本文的重点是探索这些内置的高阶函数,并讨论如何将它们用于设计功能性数据管道。
map
map
函数接受一个函数作为输入,将该函数应用于值集合中的每个元素,并返回一个新的函数输出值集合。
map
功能示意图(图片由作者提供)
例如,当我们用一个addSmile
操作映射一个形状集合时,我们通过将操作应用于形状来“映射”集合中的每个形状。使用addSmile
操作映射形状的输出被合并到一个新的形状集合中,并在输入形状中添加了一个微笑。
关于 Python 和 Scala 中的map
实现的详细讨论可以在我之前的帖子中找到。
filter
filter
函数接受返回布尔值(也称为谓词)的函数作为输入,将该函数应用于值集合中的每个元素,并将从该函数返回true
的元素作为新集合返回。
谓词应该接受一个与集合中的元素类型相同的参数,用该元素评估结果,并返回true
到将元素保留在新集合中或false
将其过滤掉。
filter
功能示意图(图片由作者提供)
例如,当我们将hasFiniteEdges
条件应用于形状集合时,我们根据集合中的每个形状是否满足hasFiniteEdges
条件来“过滤”它。从hasFiniteEdges
函数返回true
的形状被合并到一个新的形状集合中,不满足条件的形状从集合中被过滤掉。
当我们查看 Python 内置函数filter
的文档时,会发现filter
函数接受一个谓词函数和一个 iterable 作为输入参数,并根据谓词函数返回 true [1]的 iterable 的元素构造一个迭代器。
在 Scala 中,包scala.collections
中的每个集合类及其子集都包含由 ScalaDoc [2]上的以下函数签名定义的filter
方法:
def filter(p: (A) => Boolean): Iterable[A] // for collection classes
def filter(p: (A) => Boolean): Iterator[A] // for iterators that access elements of a collection
函数签名的意思是,filter
接受一个谓词输入参数p
,该参数将类型A
的一般输入转换为布尔值,并返回一个新的迭代器,该迭代器由满足谓词p
的可迭代集合(类型A
)的所有元素组成。
为了使用函数方法从现有的数字集合中创建一个新的偶数集合,可以使用filter
函数通过对每个元素应用一个偶数谓词并将满足偶数谓词条件的元素收集到一个新的集合中来过滤掉集合中的奇数。
- 在 Python 中:
def isEven(x):
return x % 2 == 0def main(args):
collection = [1,2,3,4,5]
evenNums = list(filter(isEven, collection))
print(evenNums)
- 在 Scala 中:
object FilterEven { def isEven(x: Int): Int = {
x % 2 == 0
} def main(args: Array[String]) {
val collection = List[1,2,3,4,5]
val evenNums = collection.filter(isEven)
println(evenNums)
}}
在这两个实现中,filter
函数接受一个应用于值集合中每个元素的输入谓词,并返回一个包含满足该谓词的元素的新集合。
reduce
reduce
函数接受一个组合函数(通常为二元运算)作为输入,将该函数应用于值集合中的连续元素,并返回一个累积结果。
reduce
功能示意图(图片由作者提供)
例如,当我们将composeAll
操作应用于形状集合时,我们通过使用composeAll
操作将部分结果和迭代中的形状折叠成单个结果,并将合成结果用作下一次迭代的部分结果,从而将集合中的形状“缩减”成单个结果。在迭代结束时,使用composeAll
操作减少形状的输出作为单个结果返回(形状的组合)。
当我们在functools
库中查看 Python reduce
函数的文档时,会发现reduce
函数接受一个带有两个参数和一个 iterable(带有一个可选的初始值,可以在计算中放在 iterable 的项之前)的函数作为输入参数,并从左到右【3】将函数累积应用于 iterable 的项。
在 Scala 中,包scala.collections
中的每个集合类及其子集都包含由 ScalaDoc [2]上的以下函数签名定义的reduce
方法:
def reduce(B >: A)(op: (B, B) => B): B
函数签名的意思是,reduce
接受一个结果类型为B
(类型为A
的超类型)的二元运算符op
,该运算符将集合中的两个元素转换为类型为B
的值,并返回在集合中所有元素(类型为A
)之间应用该运算符的结果。二元运算符op
需要返回存储在集合中的相同数据类型A
或类型B
的超类型,以便reduce
方法可以对操作的结果和集合中正在应用操作的下一个元素执行后续操作。
仔细观察reduce
方法的 ScalaDoc,会发现以下操作条件:
- 二元运算符必须是关联的。
- 对元素执行操作的顺序可能是不确定的。
二元运算符的关联属性是一个必要条件,以确保在归约过程中执行运算的顺序不会改变不同运行的结果——只要被运算元素的顺序不变。
虽然关联属性对于从归约函数返回确定性结果是必要的(引用透明的必要条件),但它不是充分条件,因为在非交换二元运算符的情况下,如减法和除法,运算的顺序会产生不同的结果。
如果我们想要指定对集合的元素执行操作的顺序该怎么办?
在 Scala 中,我们在集合类中有reduceLeft
和reduceRight
方法,它们分别以从左到右和从右到左的顺序对集合中的所有元素累积应用二元运算符。reduceLeft
和reduceRight
方法由 ScalaDoc [2]上的以下函数签名定义:
def reduceLeft[B >: A](op: (B, A) => B): B
def reduceRight[B >: A](op: (A, B) => B): B
与reduce
的函数签名相比,reduceLeft
和reduceRight
方法的函数签名的主要区别在于:
- 结果类型为
B
(类型为A
的超类型)的二元运算符op
获取部分结果(类型为B
)和集合中的下一个元素**(类型为****A**
) ,并将它们简化为类型为B
的值。 - 二元运算中部分结果和集合中元素的顺序表示运算符沿着集合应用的方向。
为了使用函数方法获得数字集合的累积和,可使用reduce
函数,通过在集合的连续数字之间应用加法运算符并执行运算,将数字集合缩减为单个值。
- 在 Python 中:
def main(args):
from functools import reduce
collection = [1,3,5,2,4]
totalSum = reduce(lambda x,y: x + y, collection)
print(totalSum)
- 在 Scala 中:
object SumNumbers { def main(args: Array[String]) {
val collection = List[1,3,5,2,4]
val totalSum = collection.reduce((x, y) => x + y)
println(totalSum)
}}
在这两个实现中,reduce
函数接受一个二元运算符,该运算符应用于值集合中的连续元素之间,并返回一个结果。
使用二进制加法运算符对数字集合进行从左到右的归约(Scala 中的reduceLeft
方法)的计算方式如下:
[1,3,5,2,4].reduceLeft((x, y) => x + y) // initialize var acc = null
(((1 + 3) + 5) + 2) + 4 // take first value, acc = 1
((4 + 5) + 2) + 4 // acc = 1 + 3 = 5
(9 + 2) + 4 // acc = 4 + 5 = 9
11 + 4 // acc = 9 + 2 = 11
15 // acc = 11 + 4 = 15 returned upon end of collection
类似地,从右到左归约(Scala 中的reduceRight
方法)按以下方式计算:
[1,3,5,2,4].reduceRight((x, y) => x + y)// initialize var acc = null
1 + (3 + (5 + (2 + 4))) // take first value from right, acc = 4
1 + (3 + (5 + 6)) // acc = 4 + 2 = 6
1 + (3 + 11))// acc = 6 + 5 = 11
1 + 14 // acc = 11 + 3 = 14
15 // acc = 14 + 1 = 15 returned upon end of collection
由于加法运算符是关联和交换的,运算符应用的顺序不会影响结果,因此reduce
方法可以安全地用于 Scala 实现中。
注意递归模式如何在归约过程中计算加法运算?reduce
函数是函数式编程中fold
的一个特例,指的是一族高阶函数,将递归数据结构中的元素递归组合成一个结果。
内置的收集方法sum
、product
、min
和max
是基于reduce
函数[4]及其对应的二元运算符定义的:
- 总和:
+
- 产品:
*
- 最小:
math.Ordering.min
- 麦克斯:
math.Ordering.max
虽然reduce
函数有助于使用关联二元运算符将值集合缩减为单个值,但使用这些内置集合方法代替reduce
可以提高可读性,而无需为数据管道设计中的预期用例重新发明轮子。
把它们放在一起
总而言之,内置高阶函数在构建数据管道内的数据转换序列时非常有用:
map
用于将函数应用于集合中的所有元素filter
用于根据布尔条件选择集合中的元素reduce
通过在连续元素之间使用一个关联运算符,将元素集合简化为一个结果
当map
和filter
返回一个可以用后续高阶函数处理的新集合时,reduce
返回一个累积值。因此,在设计具有功能组合的功能数据管道时,通常将reduce
方法及其衍生方法作为最终转换步骤来实施。
下一步是什么
在本帖中,我们将了解:
- 数据管道设计模式
- 用于数据转换的高阶函数:映射、过滤、归约
在我们探索高阶函数的 map-filter-reduce 三元组时,我们简要地提到了与函数式编程相关的两个有趣的概念:
reduce
作为fold
的特例- 集合类作为带有内置方法的数据容器
在本系列接下来的几篇文章中,我将详细阐述这些概念及其在设计函数式数据管道中的应用。
想要更多关于我作为数据专业人员的学习历程的幕后文章吗?查看我的网站:https://ongchinhwee.me !
参考
[1] 内置函数— Python 3.10.2 文档
[2] Scala 标准库 2 . 13 . 8—Scala . collections . iterable
[3] functools —可调用对象上的高阶函数和操作— Python 3.10.2 文档
[4]trait iterable once ops—Scala/iterable once . Scala at v 2 . 13 . 8
原载于 2022 年 2 月 5 日https://ongchinhwee . me。
地图切片:定位区域、嵌套/父切片、坐标和边界框
原文:https://towardsdatascience.com/map-tiles-locating-areas-nested-parent-tiles-coordinates-and-bounding-boxes-e54de570d0bd
使用位置或缩放级别来确定要使用的图块,并以编程方式计算它们的坐标。
在 Unsplash 上 Deva Darshan 拍摄的照片
在可视化地理信息时,数据通常被分割成段——跨越整个地球并根据缩放级别递减的块。这意味着作为用户,我们可以只加载我们感兴趣的信息。
然而,问题在于希望从 tileset 中提取数据,而不必每次都手动重新定位地图。为此,我们有两种方法:
- 使用缩放级别和地理坐标查找单幅图块
- 找到一个缩放级别上的图块,并从另一个缩放级别定位任何相应的图块。
- 最后,我们可以颠倒整个过程,以获得瓷砖坐标,因此包围盒
基于坐标定位图块
查找相关图块的最简单方法(除了手动操作加载的地图)是从感兴趣的地方开始,决定我们想要的细节,并直接搜索它。
选择缩放级别
第一步是决定我们在地图上需要多少细节。OpenStreetMap 的下表给出了每个级别的规模。对于此示例,我将查看纽约,因此将基于代表列的“区域示例”选择缩放级别 11。
显示不同缩放级别及其相对比例的表格。来源:https://wiki.openstreetmap.org/wiki/Zoom_levels
从坐标计算图块
接下来,我们需要获得我们感兴趣的城市的纬度和经度。对于纽约,这是:40.7128° N, 74.0060° W
。
我们现在可以使用上面的下列函数来确定我们需要什么样的图块,如下等式:
import mpmath as mp **def** get_tile(**lat_deg,lon_deg,zoom**): *''' A function to get the relevant tile from lat,lon,zoom)'''*
lat_rad = mp.radians(lat_deg)
n = 2 ** zoom
xtile = n * ((lon_deg + 180) / 360)
ytile = n * (1 - (mp.log(mp.tan(lat_rad) + mp.sec(lat_rad)) / mp.pi)) / 2 **return** 'tile %d/%d/%d '%(zoom,xtile,ytile)
注意:由于我们坐标系的性质,由于这是向西 74 度,我们需要在计算时将其输入为负数。 *40.7128,* ***—*** *74.0060*
返回的卫星视图左(© Mapbox, © OpenStreetMap
)和缩小的谷歌地图(地图数据:2022 谷歌)在右边。
将get_tile(*40.7128,* ***—*** *74.0060 , 11)*
与 Mapbox 卫星 tileset 一起使用,我们得到了纽约最左边的图像tile: 11/602/770
。这可以与谷歌地图进行比较,以确保我们有正确的位置。
从另一缩放级别查找嵌套或父平铺
我们可能面临的另一个问题是希望同一区域的分辨率更高或更低。在这种情况下,我们可能只有一个单独的位置,而没有产生它所需的坐标。我们可以简单地使用“n”来调整缩放级别,而不是逆向工程上述等式:
def tile_translator(z,x,y,newzoom):*'''Find the linking (left top) most tile from another zoom level'''* n = 2 ** z
x /= n
y /= n n2 = 2 ** newzoom
x *= n2
y *= n2 return '%d/%d/%d'%(newzoom,x,y)
在英国东北部(10/510/330)
随机取一块瓷砖,我们可以根据newzoom
参数的值生成它的父元素和子元素。
tile_translator 的输出(
10,510,330,9). — via © Mapbox, © OpenStreetMap`
在需要右边单元格的坐标的情况下,我们可以将 1 加到x
(511)并将+1
加到y
(331)以得到向下的单元格。如果探索子块,这个过程也是有用的(见下文)。
tile_translator(
10,510,330,11) — via © Mapbox, © OpenStreetMap`的输出
将所有这些放在一起会产生以下结果:
def get_children(z,x,y):
''' Return the children of a tile '''
tile = list(tile_translator(z,x,y,z+1).split('/').map(float)) return [[tile[0],tile[1]+i,tile[2]+j] for i in [0,1] for j in [0,1]]
查找图块边界
最后,我们可以颠倒这个过程来获得每个瓷砖的角点:
def tile2lon(z,x,y) :
return x / 2**z * 360 - 180;def tile2lat(z,x,y) :
n = mp.pi - 2 * mp.pi * y / 2**z;
return float((180 / mp.pi) * (mp.atan(0.5 * (mp.exp(n) - mp.exp(-n)))))
将数字x
和y
加 1,我们现在可以计算包围我们的图块的边界框:
def tile_bbox(z,x,y):
'''Return the bounding box of a tile'''
w = tile2lon(z,x,y)
s = tile2lat(z,x,y)
e = tile2lon(z,x+1,y)
n = tile2lat(z,x,y+1) return [w,s,e,n]
结论
我们已经学习了如何使用经度和纬度以编程方式定位图块,以及在另一个缩放级别上找到相应的图块。其用途包括提取特定领域的信息,然后用于机器学习(文章即将发表)。
资源
代码的完整副本,以及一些进一步的阅读材料。
密码
关于 Tilesets 的更多信息
https://docs.microsoft.com/en-us/bingmaps/articles/bing-maps-tile-system?redirectedfrom=MSDN https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
“MAPIE”准确地解释了你希望别人如何向你解释
原文:https://towardsdatascience.com/mapie-explained-exactly-how-you-wished-someone-explained-to-you-78fb8ce81ff3
一个 Python 库,可以将任何模型的预测转化为置信区间
[图片由作者提供]
当您为数值目标变量构建预测模型时,Scikit-learn、XGBoost、LightGBM、CatBoost、Keras 等经典 Python 库会生成点预测。不幸的是,
点预测总是错的。
事实上,假设您有一个预测旧金山房屋销售价格的模型。该模型已经预测出一栋房子将以 746,632.15 美元的价格出售。这是实际价格的可能性有多大,精确到每一分钱?实际上,零。
知道房子将以 70 万美元到 80 万美元之间的价格出售,有 95%的把握,这要有用得多(也安全得多)。其中 95%的置信度大致意味着——如果我们可以观察所有可能的宇宙——在 95%的情况下,售价实际上在 70 万美元到 80 万美元之间。这被称为区间预测。
那么,我们如何在 Python 中从点预测得到区间预测呢?这就是 MAPIE 发挥作用的地方。
什么是 MAPIE,如何使用它
MAPIE 是一个用于获取区间预测的 Python 库。名字代表“模型不可知预测区间估计器”,其中重要的部分是“模型不可知”。事实上,与分位数回归或贝叶斯推断等更传统的方法相反, MAPIE 允许您保留您最喜欢的高度精确的模型。
所以,我们来看看怎么用。为了在一些真实数据上测试 MAPIE,我将使用来自 StatLib 的加州住房数据集,该数据集可以在 Scikit-learn 中直接获得(在 BSD 许可下)。
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split**# Get California housing dataset, and split it in 3 sets** X, y = fetch_california_housing(return_X_y=True, as_frame=True)X_train_and_cal, X_test, y_train_and_cal, y_test =
train_test_split(X, y, test_size=1/3)X_train, X_cal, y_train, y_cal = train_test_split(
X_train_and_cal, y_train_and_cal, test_size=1/2)
重要的是,我们有三组数据:
- 训练数据:预测模型学习的数据。
- 校准数据:MAPIE 校准间隔的数据。
- 测试数据:我们用来评估区间好坏的数据。
在我们的例子中,每个集合由 6,880 个观察值和 8 个特征组成。
数据帧的形状。[图片由作者提供]
目标变量是加州各区的房价中值,以几十万美元表示。
测试集的前 5 个房屋的值。[图片由作者提供]
所以第一栋房子值 35.5 万美元,第二栋房子值 7.07 万美元,以此类推。
现在我们已经准备好了数据,让我们对训练数据拟合预测模型(我将使用 Scikit-learn 的随机森林回归器),对校准数据拟合 MAPIE 回归器。
from sklearn.ensemble import RandomForestRegressor
from mapie.regression import MapieRegressor**# Fit model on training data** model = RandomForestRegressor().fit(X_train, y_train)**# Fit MAPIE on calibration data
# Important: calibration data must be different from training data!**
mapie = MapieRegressor(estimator=model, cv="prefit"
).fit(X_cal, y_cal)
在这一点上,我们可以最终使用 MAPIE 回归器的“预测”方法,它为观察值产生预测间隔(为了方便起见,我将它们存储到 Pandas 数据帧中):
import pandas as pd**# Get interval predictions on test data, with alpha=5%** y_test_pred_interval = pd.DataFrame(mapie.predict(X_test, alpha=.05)[1].reshape(-1,2), index=X_test.index, columns=["left", "right"])
每个区间由区间的左端和右端定义。例如,这些是测试集的前 5 个观察值的预测间隔:
测试集的前 5 次观察的预测间隔。[图片由作者提供]
“阿尔法”的含义
如果您查看最后一段代码,您会注意到一个名为alpha
的参数。如何解读这个参数?alpha
是公差。它回答了这个问题:
“我们愿意接受多少‘错误’?”
我所说的“错误”是指超出预测区间的观察结果。例如,如果alpha
设置为 10%,这意味着我们预计不会有超过 10%的观测值超出 MAPIE 预测的区间。
alpha = 10%时的输出示例:10 次观察中有 1 次超出其预测区间。[图片由作者提供]
我们如何确保 MAPIE 预测的区间实际上与容差相匹配?这就是测试集存在的目的。统计y_test
的观测值落在预测区间之外的次数并与alpha
进行比较就足够了;
out_of_interval = ((y_test < y_test_pred_interval["left"]) |
(y_test > y_test_pred_interval["right"])
).sum() / len(y_test)
让我们对不同的alpha
值重复此过程。这是结果:
将 alpha 与超出预测区间的观察值百分比进行比较。[图片由作者提供]
区间外观察值的百分比实际上非常接近alpha
的相应值。这可能感觉像是魔术,但却出奇的简单!在下一段中,我将向您展示这是如何工作的。
引擎盖下的 MAPIE
MAPIE 背后的想法是从模型对一组数据(称为校准数据)产生的错误中学习。一旦我们知道我们应该期望什么样的误差(对于给定的容差),将它加在点预测的两侧就足够了,以获得一个区间预测。
MAPIE 基本算法包括 6 个步骤:
MAPIE 算法。[图片由作者提供]
这些步骤是:
- 根据训练数据拟合模型。
- 对校准数据进行预测。
- 计算模型对校准数据产生的绝对误差。
- 从前一点得到的绝对误差分布中得到 1-
alpha
分位数。 - 对测试数据进行预测。
- 通过将点 4 获得的分位数与点 5 获得的预测值相减(相加)来计算区间的左(右)端。
我们可以用几行 Python 代码轻松再现该算法:
**# 1\. Fit model on training data** model = RandomForestRegressor().fit(X_train, y_train)**# 2\. Make prediction on calibration data** y_cal_pred = model.predict(X_cal)**# 3\. Compute absolute errors made by the model on calibration data** y_cal_error = np.abs(y_cal - y_cal_pred)**# 4\. Get 1-alpha quantile from the distribution of absolute errors
# Note: this is a single number** quantile = y_cal_error.quantile(q=.95, interpolation='higher')**# 5\. Make prediction on test data** y_test_pred = model.predict(X_test)**# 6\. Compute left (right) end of the interval by
# subtracting (adding) the quantile to the predictions** y_test_interval_pred_left = y_test_pred - quantile
y_test_interval_pred_right = y_test_pred + quantile
这是y_cal_error
的直方图:
校准误差直方图和α= 5%的分位数。[图片由作者提供]
当alpha
为 5%时,quantile
等于 1.18。这意味着随机森林回归器产生的误差在 5%的情况下高于 1.18 亿美元(在 95%的情况下低于 1.18 亿美元)。此时,通过简单地将随机森林所做的预测加上和减去 118k $,就可以获得区间预测:
MAPIE 中点预测与区间预测的关系。[图片由作者提供]
就是这样。如果你已经理解了这段代码,那么恭喜你:你已经掌握了 MAPIE 背后的逻辑!
这个库还包含了这个基本算法的一些细微变化,我们将在下一段中看到。
其他模式
到目前为止,我们一直在“prefit”模式(cv="prefit"
)下使用 MAPIE。“Prefit”意味着模型(在我们的例子中是一个随机森林)是预先拟合的。但是,根据我们选择的参数,还有其他的可能性。这是MapieRegressor
的大致结构:
MapieRegressor(estimator=model, cv=cv, method=method)
因此,决定 MAPIE 行为的参数是cv
和method
。让我们分别来看:
cv
:交叉验证的折叠次数(除非等于“prefit”,此时不进行交叉验证);method
:“幼稚”、“贱”、“加”、“最大化”之一。该参数仅在cv
不同于“前缀”时使用。“naive”类似于“prefit”,但校准数据与训练数据相吻合。“base”类似于“prefit”,但是预测误差是从交叉验证过程中获得的。“plus”类似于 base,但每个区间都是测试预测的分位数加上验证残差。“最小最大值”类似于“加号”,但是使用了来自交叉验证模型的最小和最大预测值。
这里就不深究各种方法的细节了。我们将在一些真实数据集上尝试它们,看看它们的表现如何。
我们用来评估 MAPIE 输出质量的标准是与 **alpha**
的正距离。的确,既然alpha
被定义为我们愿意接受的错误百分比,那么问题就出在% out of interval
大于alpha
的时候。
因此,我们感兴趣的量是:
df["breach"] = (df["% out of interval"] — df["alpha"]).clip(lower=0)
在上面的示例中,这将是:
将 alpha 与超出区间的观察值百分比进行比较。[图片由作者提供]
因此,列 **breach**
的简单平均值是 MAPIE 预测的间隔有多差的指标:该数字越高,MAPIE 性能越差。
现在,让我们看看这种方法在不同的数据集上是如何变化的。除了上面引用的加州住房数据集,我还使用了来自 Pycaret 的“帕金森”、“钻石”和“交通”数据集,这是在麻省理工学院许可下。
各种数据集的 MAPIE 与 alpha 的平均正偏差(越低越好)。左上:帕金森数据集(Pycaret)。右上:加州数据集(Scikit-learn)。左下角:钻石数据集(Pycaret)。右下角:交通数据集(Pycaret)[图片由作者提供]
请注意,当你使用cv="prefit"
时,不使用method
,这就是为什么列prefit
总是不变的原因。
简而言之,你要一直避开 **method="naive"**
,因为它的表现一直不好。关于剩下的选项,method="base"
、method="plus"
和method="minmax"
工作得很好,但是你应该考虑到它们是基于交叉验证的,所以如果你处理真正的大数据,它们需要更多的时间。
总的来说,cv="prefit"
的性能几乎和其他选项一样好,而且速度更快。此外,它在现实生活中更方便,在现实生活中,您已经有一个拟合的模型,并希望从中“提取”区间预测。
MAPIE 的弱点
我们都知道,直觉上,有些预测比其他的更难。这应该反映在间隔的宽度上。有理由预期,预测越不确定,区间越大。
但是 MAPIE,至少是它的基础版本,并不满足这个属性。事实上,为了获得区间,我们在预测中加入和减去了校准误差的分位数。但是分位数是一个数字,因此所有观测值的区间宽度是相同的。
因此,基本上,这就像你只是估计一个与预测模型相关的区间,然后将它复制粘贴到所有的观察值上。
当然,使用method="plus"
或method="minmax"
,输出间隔将不会有相同的确切宽度。但这只是一种“扰动”,最终的区间不会有实质性的不同。事实上,让我们来看看加州住房数据集的预测区间宽度的分布:
加州住房数据集上 method="plus "(左)和 method="minmax "(右)时预测区间宽度的分布。[图片由作者提供]
正如你所看到的,间隔的宽度没有太大的变化。采取“加”的方法(左):至少 75%的区间在 220k $和 223k $宽之间:它们的宽度几乎相同。
外卖食品
MAPIE 是一个 Python 库,用于将模型做出的点预测转化为区间预测。它是模型不可知的,因此您可以保留您最喜欢的 ML 模型,并且仍然具有强大的统计保证,即预测区间符合您设置的容差。
然而,你必须意识到,从根本上来说,间隔的宽度都是一样的。因此,与其为每个观察值估计一个时间间隔,不如只估计一个时间间隔,然后将该时间间隔应用于所有观察值。
如果你想知道如何预测与每次观察相关的风险的“定制”区间,你可以阅读我下面的文章“共形分位数回归”:
感谢您的阅读!我希望你喜欢这篇文章。如果你愿意, 在 Linkedin 上加我 !
绘制新型冠状病毒污水覆盖率图
原文:https://towardsdatascience.com/mapping-sars-cov-2-wastewater-coverage-48bff31e4e92
数据工程创建美国县水测试繁荣图。包括 pandas set()操作。
作者图片
背景
废水(下水道)数据可用作新冠肺炎病例的早期预警。这是一项开发良好的技术,可以对废水中的新型冠状病毒 RNA 片段进行计数,将计数与同一区域先前的值进行比较,以检测增加/减少,甚至可以对 RNA 进行测序,以确定病毒变体。
美国两套最全面的废水追踪数据由 CDC 国家废水监测系统(NWSS) 和 Biobot Analytics 汇编和保存。(在美国还有其他更本地化的数据源,如州或大学。)
但是看着原始的 CDC 和 Biobot 数据集可能会令人沮丧。它们具有不同的布局,使用联邦信息处理标准(FIPS)县代码而不是可读的名称,并且没有按照位置的逻辑顺序排列。很难看出它们是否包含您想要查找的县,也很难判断特定的县是在一个文件中还是在两个文件中(或者都不在)。而且它们一点也不直观:美国的哪些地区在数据中得到了很好的体现,哪些地区被遗漏了?
我最近完成了一个数据工程项目来回答这些问题。目标是:
- 在每个数据集中创建按字母顺序排列的县列表。
- 创建按字母顺序排列的县列表,这些县与两个源重叠并跨越两个源-交叉点和联合点。
- 制作美国的互动地图,显示每个县的废水数据集。使用颜色编码快速查看数据稀疏的区域。
由于数据集和县覆盖范围经常变化,我将每周重新运行此代码并更新地图。
数据工程
这个项目的 Python/pandas 代码直观上很简单:删除除 FIPS 县代码之外的所有关于水测试的信息;添加可读的州县名称;使用 Python set()操作创建县列表;并输出一个包含该信息的文件,该文件将成为地图的输入。当然,大多数应该简单的编程项目都有一两个缺点,这一个也不例外。
为了删除我们不关心的许多列数据,pandas 提供了两个选项:列出要删除的字段或要保留的字段。在这种情况下,后者要容易得多。
NwssDF = NwssDF[["county_fips"]] # don't need any other columnsBiobotDF = BiobotDF[["fipscode"]] # don't need any other columns
NWSS 数据集有一个棘手的问题。county_fips 字段有时包含不止一个 fips 代码,例如"['08013', '08123']"
。在关系数据库术语中,这违反了第一范式。就我们的目的而言,这种格式使得不可能将文件与任何其他由 FIPS 代码键入的数据相结合。
修复方法是分解这些行,使每一行只有一个 FIPS 代码,然后清除周围的字符。
NwssDF["county_fips"] = NwssDF["county_fips"].str.split(",")
NwssDF = NwssDF.explode("county_fips") NwssDF["county_fips"] = NwssDF["county_fips"].str.strip("[]' ")
Biobot 数据集还包含一个问题,即它抑制了 FIPS 代码中的前导零。所以数据中有9001
为费尔菲尔德 CT,而不是正确的09001
。Biobot 已经承诺解决这个问题,但在此之前,这个代码解决了这个问题。
BiobotDF.loc[BiobotDF["fipscode"].str.len() == 4, "fipscode"] = \
"0" + BiobotDF["fipscode"]
使用 FIPS 代码连接/合并包含可读字符串STATE | COUNTY | FIPS
的字段,这在县列表和地图上非常有用。如何做到这一点的细节在源代码和我最近关于 FIPS 代码和县名的文章中。
使用 Python set()操作获取各种县集:
all_counties = set(AllCountiesDF["STATE_COUNTY_FIPS"])
biobot_counties = set(BiobotDF["STATE_COUNTY_FIPS"])
nwss_counties = set(NwssDF["STATE_COUNTY_FIPS"])biobot_nwss_union = biobot_counties.union(nwss_counties)biobot_nwss_intersection = \ biobot_counties.intersection(nwss_counties)biobot_only = biobot_counties.difference(nwss_counties)nwss_only = nwss_counties.difference(biobot_counties)missing_counties = all_counties.difference(biobot_nwss_union)
使用 sorted()函数将集合写入输出文件,该函数将集合转换为按字母顺序排列的列表,并使用*运算符分隔列表以进行打印。比如:
with open(BIOBOT_LIST_FILE, 'w') as f:
print(*sorted(biobot_counties), file=f, sep="\n")
从而产生可读的县文件: NWSS 、生物机器人、仅 NWSS、仅生物机器人、交集、联合、缺失(不在 NWSS 或生物机器人)。
最后,创建一个数据帧和输出文件,它将被输入到地图。
MapDF = AllCountiesDF # copy of DF with all US countiesMapDF["WBE_WHO"] = "none" # default for "who has wastewater data"MapDF.loc[MapDF["STATE_COUNTY_FIPS"].isin(biobot_only),
"WBE_WHO"] = "biobot" # Biobot onlyMapDF.loc[MapDF["STATE_COUNTY_FIPS"].isin(nwss_only),
"WBE_WHO"] = "nwss" # NWSS onlyMapDF.loc[MapDF["STATE_COUNTY_FIPS"].isin(biobot_nwss_intersection),
"WBE_WHO"] = "biobot-nwss" # bothMapDF = MapDF.rename(columns={"CountyFIPS":"FIPS"})
# to match Flourish naming
MapDF.to_csv(MAP_FILE, encoding='utf-8', sep='\t', index=False)
制作地图
制作地图的大部分工作是由 fluore 软件完成的,该软件提供了一个非编程接口来创建可视化效果,包括地图。Flourish 已经有了美国各县的几何图形,用颜色代码填充区域的简单设置,以及当用户悬停在某个区域上时自动弹出的窗口。
下面是如何创建我制作的同样的地图,你可以随意修改…
- 在https://fluid . studio创建一个帐户。
- 创建新的可视化效果。向下滚动到美国各县,并选择它作为您的起点。在左上角给你的地图命名。
- 转到预览/点层,并禁用它。如果需要,您也可以删除数据/点处的所有相关数据。
- 转到数据/区域,删除名称、标签、值和组的现有列条目。您将为这些分配新的值。
- 在数据/区域中,删除除几何图形和 FIPS 之外的所有列。现在,您的地图已缩减为仅包含县轮廓,可以接收废水数据了。
- 将上传选项更改为上传数据并合并。上传/合并您在上面创建(或复制)的地图数据文件。确保现有数据和传入数据的合并列都是 FIPS。
- 在数据/区域中,设置几何=几何列(可能是 A);name = STATE _ COUNTY _ FIPS 列(大概是 C);和值= the 世卫组织列(可能是 D)。
- 在预览/区域中,设置比例类型=分类。选择调色板。(我使用了带有十六进制颜色值的自定义覆盖,但这不是必需的。)
- 在预览/页脚中,设置数据源名称和 URL。
- 在“导出和发布”中,您可以将地图发布到 fluorescent 服务器上并获取其公共 URL,和/或下载 PNG 或 JPG 格式的图像文件。
未来的工作
这项工作的两个有价值的扩展是:
- 在美国添加更多的县。看到整个国家的地图都充满了废水追踪,这将是一件很棒的事情,水测试结果将成为未来新冠肺炎病例的可靠预测器,以及它们将在哪里发生。疾控中心正在积极开展这项工作,覆盖的县的数量逐月增加。
- 添加更多来自全球的国家。有两个项目(引用如下)正在跟踪国际废水测试,它们覆盖的国家数量正在稳步增加。然而,这项工作的一个重要组成部分是为所有国际测试结果创建一个单一、统一的数据集,以便于分析和可视化。
欲了解更多信息
https://biobot.io/data/[(生物机器人数据)](https://biobot.io/data/)
https://Data . CDC . gov/Public-Health-supervision/NWSS-Public-新型冠状病毒-废水-数据/2ew6-ywp6 (疾控中心 NWSS 数据)
https://ucmerced . maps . ArcGIS . com/apps/dashboards/c 778145 ea 5 bb 4 daeb 58d 31 afee 389082和 https://sphere.waterpathogens.org/map(国际废水地图)
https://twitter.com/COVIDPoops19(关于加州大学默塞德分校废水的热门推特消息)
https://help.flourish.studio/category/10-flourish-basics(蓬勃发展入门)
用 Python 绘制世界飞行路线
原文:https://towardsdatascience.com/mapping-the-worlds-flight-paths-with-python-232b9f7271e5
用 Python 可视化连接世界各大洲的飞行路线
图片作者。
由于 covid 航空旅行在过去两年中明显减少,但它正显示出恢复昔日辉煌的迹象。2019 年,据估计,飞机运送的乘客总数达到 45 亿,创下历史新高。尽管如此,这些飞机的航线可能与大多数人的想法相去甚远,但想象和了解热门航线在哪里、涉及哪些国家以及国际旅游枢纽在哪里是很有趣的。在本教程中,我将解释如何使用开源数据和 Python 生成显示世界航线的醒目地图。这是一个数据操作和数据可视化的练习,你应该对像Pandas
、GeoPandas
、Matplotlib
和Shapely
这样的库有更好的理解。
数据探索
数据来自于 openflights 数据库,并且可以在数据库内容许可下获得。简而言之,这意味着欢迎你按照自己的意愿使用这些数据,当且仅当你和都承认来源和并以免费许可的方式许可任何可供公众使用的衍生作品。
这种观想是一种数据操作的练习。openflights 数据库(这个数据库可能有点牵强)在一个文件中提供了一个机场列表,在另一个文件中提供了每个机场之间的航线数量明细。因此,数据集不包含每条路线的具体坐标,这是我们需要自己推导的。在一个理想的世界中,我们将拥有由一年中飞行的每架飞机的位置更新组成的飞行数据,并且我们可以构建该数据的 2D 直方图来展示飞行路径。不幸的是,这个数据很难得到,所以我们不得不凑合着用一个机场列表和每个机场之间的航线数量。我们的目标是创建一个描述每条路线的线串地理数据框架。我们据此做出的假设是,飞机直接从一个机场飞到另一个机场。
第一个数据集是机场数据。该数据包含机场及其位置的列表以及一些其他信息。总共有 7608 个独特的机场。重要的是要注意机场iata
,它是每个机场的唯一标识符,以后将用于在路线中链接机场。
可以将数据绘制成散点图,以显示世界上机场的位置,或者至少是包含在该数据集中的机场的位置。像往常一样,在全球范围内,这张地图很大程度上只是成为了人口密度的一个代理,但看到世界机场的分布仍然很有趣。
根据 openflights 数据库的世界机场位置。图片作者。
第二个数据集是包含路由列表的 csv。这些航线采用iata
对的形式,连接源机场和目的地机场。
数据操作
我们想要操作我们的数据,以便我们有一个Linestrings
的DataFrame
来连接每条路线的源机场和目的地机场。我们将采用的为每条路线创建LineStrings
的方法如下。
- 创建一个重复的机场
DataFrame
,将一个标记为来源,一个标记为目的地。 - 使用路线来源
iata
代码将来源DataFrame
与路线合并。 - 使用路线目的地
iata
代码将目的地DataFrame
与路线合并。 - 使用现在合并到路线
DataFrame
中的源机场和目的地机场的纬度和经度值创建一个代表该路线的LineString
。
下面我们复制了最初的机场DataFrame
,并给其中一个机场的列标上了_source
和_destination
。
这两个DataFrames
然后与路线DataFrame
合并,以给出具有目的地和源机场的纬度/经度值的DataFrame
。注意下面显示的列,每条路线有四列,lat_source
、long_source
、lat_destination
和long_destination
。
然后使用shapely.geometry.LineString
方法,用这些值为每条路线生成一个带有LineStrings
的GeoDataFrame
。这需要一个纬度和经度对的列表,并创建一条连接这些点的线。
数据可视化
这些数据现在可以绘制成航线图。不幸的是,不可能简单地按原样绘制数据,因为在绘制线条时需要考虑地球的曲率。下面的地图显示了每个机场之间的直线,这是不正确的。
不考虑地球曲率的世界飞行路线。图片作者。
Cartopy
可用于操纵线条的绘制方式。transform=ccrs.Geodetic()
方法转换LineStrings
来解释地球的曲率。我还将投影更改为罗宾逊投影,因为这是绘制全球数据集的更现实的方法之一,说明了地球不是平的这一事实。我还把alpha
的值降低到 0.1,让它看起来更清晰一点。
瞧。世界飞行路线。图片作者。
结论
我们有它,一个美丽的地图显示了如何产生引人注目的数据可视化显示世界航线。这是计划展示如何使用 Python 使地理空间数据看起来令人惊叹的许多文章中的第一篇,请订阅以便您不会错过它们。我也喜欢反馈,所以请让我知道你会如何做不同或建议改变,使它看起来更棒。我每周都会在我的推特账户上发布数据可视化,看看地理空间数据可视化是不是你的菜【https://twitter.com/PythonMaps
medium 提供的额外收入确实有助于这个项目继续进行,所以如果你是一个粉丝,想取消你的帐户,然后用我的推荐重新注册,那就太好了。https://pythonmaps.medium.com/membership
描绘你最喜欢的高端连锁杂货店
原文:https://towardsdatascience.com/mapping-your-favorite-high-end-grocery-chains-5fe3d234cfd4
全食超市 vs. Trader Joes:他们的选址策略有多大不同?
布里塔尼·伯恩斯在 Unsplash 拍摄的照片
多年来,我在 Trader Joes 购物,喜欢他们低成本、自有品牌的销售方式。我不想要 14 种不同大小、价格和成本的花生酱。我想要一种成分干净的。交易者 Joes 比周围任何人提供的都好。
我也“避开”全食超市,因为它是不适合我的高端、昂贵的连锁店。在我看来,尽管这两家商店在很多方面很相似,但它们服务于两种不同的受众。然后我查看了数据,注意到他们选择商店的地点有显著的相似之处。事实上,他们的“典型”商店邮政编码几乎没有明显的区别。请亲自查看下面的数据。
商店邮政编码的中值。右手边是全国基准。作者图片
即使我知道这是一个相似的观众,我没有意识到这是相似的。看看这两家商店的人口、收入和年龄有多接近,以及它们与全国基准相比如何。
那么,知道了以上,数据能告诉我们这两个品牌的位置是什么呢?更重要的是,数据不能告诉我们什么,我们在哪里可以依靠我们的知识和直觉得出比数据本身更好的结论?
想自己对比数据? 查看本交互可视化 。
数据告诉我们的是
数据告诉我们,这两个品牌都位于人口和收入高的城市和郊区——这并不是一个惊人的见解或意外。让我们更仔细地看看典型的邮政编码人口统计。
典型(中值)邮政编码概况;图片作者。
一个典型的邮政编码地区的人口大约是全国基准的 10 倍。周围地区的人口(距邮政编码中心约 10 英里)几乎是基准的 30 倍,人口密度也比基准高出许多倍。这表明这些地点位于人口稠密的城市地区及其周围。典型的家庭收入比国家基准高出 35%——这也不足为奇。
经济活动指数是一项定制措施,旨在了解通常会吸引客流量的区域中其他企业的存在(如零售机构、健身房、餐馆等)。).接近 10 的数字表明一个地区有更多的经济活动,而接近 0 的数字表明该地区有更少的消费企业(想想一个更农村或住宅区)。两家公司都位于经济高度活跃的地区。这符合我(有限的)经验——它们通常位于购物中心或繁华的商业区。
“白领”工作(专业、管理、科学等)的就业百分比。)是全国基准的两倍多,这可能也与收入有关。典型年龄符合国家基准。
典型的家庭规模;作者图片
在这些企业经营的邮政编码区,大约有四分之一的家庭有孩子,两者的平均家庭规模都在 2.5 左右。与全国 28%的有孩子家庭(每户 2.7 人)相比,这些数字略低于平均水平。虽然范围很广,但我们可以说它们的位置稍微有利于没有孩子或只有一个孩子的两人家庭。
逐代人口;作者图片
就一代人而言,千禧一代的比例高于全国基准(约 20%)——这与早期关于家庭规模的调查结果一致。不是所有的千禧一代都到了做父母的年龄,也不是所有的千禧一代都选择在这个阶段做父母,所以看到更高比例的千禧一代和更小的家庭规模似乎是合适的。
全食超市和贸易商在热图上的位置;作者图片
最后,我们开始看到两个品牌在地理位置上的差异。如果我们仔细观察,我们可以看到全食超市在东北部和中西部(主要在芝加哥周围)的商店(暗红色)更集中,而 Trader Joes 往往在南加州更集中。
事实上,数据证明了这一点。这是在分析的数据中两个商店之间唯一真正的区别 Trader Joes 大约有一半的位置在西部地区,相比之下 Whole Foods 只有大约 33%。Whole Foods 的商店更多地分布在美国各地,而 Trader Joes 的商店更多地集中在西部(或者更恰当地说,在加利福尼亚州,占西部地区商店的大约 70%)。
数据没有告诉我们的是
在之前的分析中,我们研究了 Target 和沃尔玛以及它们的位置对其战略的影响。差异是清晰而明显的——目标位于周围客户群更富裕的城市及其周边的高密度区域。沃尔玛位于更多的郊区和农村地区,密度较低,收入低于平均水平。这些商店针对不同地区的不同顾客。
在没有更好了解的情况下,数据可能表明全食超市和 Trader Joes 瞄准了同一地理概况内的相同客户,有时在该国略有不同的地区。毕竟,数据清楚地表明了这两个品牌之间的相似之处。
在这里,我们可以开始深入思考这种情况,进一步理解它们之间的区别。我们可以用自己的经验、知识和直觉来补充数据,并得出比数据或自己的经验更好的结论。
在我看来——在没有数据支持的情况下——我会说,在相同的人口统计中,Trader Joes 的目标客户是想要健康食品的价格敏感型客户,而 Whole Foods 的目标客户是愿意为优质食材付费的健康敏感型客户。这两个客户群可以存在于同一个地点及其周围——事实上,他们经常没有重叠,我想你可以将这些商店定位在彼此靠近的地方,而不会互相蚕食。
以马萨诸塞州波士顿为例,虽然全食超市的数量越来越多,但该市 Trader Joes 的四个地点中有三个离全食超市很近,这表明地理距离并不总是限制靠近另一家类似企业。换句话说,了解你的目标客户——以及你的竞争对手——超越产品系列和人口统计资料,可以让你找到另一个类似的企业,同时两者都取得成功。
波士顿市中心 Trader Joes 和 Whole Foods 的位置;作者图片
位置数据(包括人口统计和附近的兴趣点)可以让我们清楚地了解谁住在一个地区,以及哪些其他类型的企业将该地区称为家。
然而,位置智能只是选址这个复杂等式的一部分。企业投入大量的时间、精力和资源来选择合适的地点。数据可以也应该成为讨论的一部分。不仅仅是孤立的数据——人口、收入、年龄等单一数据点。—但数据在上下文中(它与地方和国家基准相比如何?)和有方向的数据(它如何符合你的策略?根据你的产品和公司,谁是你的目标人群?我们能找到他们居住的地区吗?)将信息从数据转换为见解。
数据很少能帮你做决定,这个例子也不例外。然而,它可以将你的搜索范围从较大的地理区域缩小到较小的地理区域,从而更有效地利用你的时间和资源,此外还可以描绘出一个网站或地区的更完整的画面(你的竞争对手在哪里?附近还有哪些其他类型的企业,尤其是那些与您的企业互补的企业,比如距离餐馆仅几步之遥的冰淇淋店?).
有兴趣谈论位置数据&情报?在 jordan@jordanbean.com 给我发邮件或者在 LinkedIn 上联系我。
想自己下载数据?全食商店 的数据在这里 , 的交易员 Joes 在这里 ,还有美国人口普查局的人口统计数据。
三月版:数据和我们吃的食物(和饮料,和测量)
原文:https://towardsdatascience.com/march-edition-data-and-the-food-we-eat-56a2861c8b0c
烹饪,就像数据科学一样,融合了规则和违反规则,精确的测量和创造力
乔治·特罗瓦托在 Unsplash 上的照片
当我们遵循一个配方时,我们实际上是在将一个算法或 ML 模型投入生产吗?微调超参数就像用勺子蘸一锅煨肉汤来检查它是否调味充分吗?烟是从你的烤箱里冒出来的吗…元数据?这些问题对你这个有成就的数据专家来说可能听起来很傻,但对我这个曾经讨厌数学但一直喜欢烹饪的编辑来说很有意义。
人们经常告诉我们要走出自己的舒适区,但对于这个月刊来说——恰逢疫情和一个非常不和平的世界的两周年纪念日——踮着脚尖回到那个区域感觉是有道理的,或者至少是可以理解的。我希望你能迎合我对好面包和不可靠类比的热情,来分享一些数据科学和食品交叉领域的最佳 TDS 文章。
一旦你开始挖掘 TDS 档案时,总是如此,选择是困难的。下面的一些精选是带我们进入数据驱动的烹饪兔子洞的激情项目(真正的兔子在这个过程中受到伤害:零)。还有人以食物为出发点,探索编程、机器学习、物联网(IoT)。没有一个讨论饺子的,很可惜;如果你正在研究一些关于 varenyky、gyoza、manti 或他们任何胖胖的表亲的前沿数据科学研究,请发送给我们。(如果您正在进行其他主题的前沿研究,也请将它发送给我们。)
祝大家度过一个平安祥和的月份,
主编本·胡伯尔曼
英国烘焙展:兰登森林版
逐步介绍随机森林,这样您就可以看到每个成分的组合。
由帕米拉·吴 (8 分钟)
双盲咖啡研究
一位浓缩咖啡专家讨论了为什么他们避免使用标准测试方法。
由罗伯特·麦肯芦荟 (6 分钟)
发酵数据可视化
自学 Python 的一个夏天。
由罗斯等待 (10 分钟)
数据科学家购买葡萄酒指南
分离出让葡萄酒好喝的化学物质。
芭芭拉·瓦纳基 (7 分钟)
城市健康:预测发展中国家的粮食不安全(第二部分)
我们能否应用机器学习技术来着手解决一个重大的全球性问题?
由 Dea Bardhoshi (7 分钟)
用 IoT 和 ML 制作更好的奶酪
使用低成本物联网和机器学习解决方案优化奶酪制作流程。
科恩拉德·普雷托里斯 (14 分钟)
衡量饭菜的相似度
在食品领域应用 Word2Vec 生成食品嵌入。
由亚龙·瓦扎纳 (4 分钟)
人工智能设计的“超级食物”可能有助于预防癌症
现代机器学习技术可以发现生物活性分子,其中一些类似于抗癌药物,并帮助设计营养,让我们活得更长更健康。
迈克尔·布朗斯坦、基里尔·维塞尔科夫和加布里埃拉·斯博多内(14 分钟)
感谢你这个月加入我们,感谢你对我们作者的所有慷慨支持,包括我们 2 月份加入我们的最新成员:向萨利赫萨利赫、亚历山大魏、安托万维拉提、基隆斯皮灵、让-马克布切特、莉亚辛普森和雷麦克伦登、尼古拉斯加西亚阿拉穆尼问好 坎·布恰克,姆尔纳尔·古普塔,普拉蒂克·奇卡拉,艾伯特·维博沃,梅茜(玛格丽特)摩尔,本杰明·富尔,穆罕默德·阿米·哈契查,佩德罗·费拉里,杰弗里·纳夫 https://medium.com/u/ca780798011a?source=post_page-----56a2861c8b0c-------------------------------- 奥斯卡·利奥,尼克拉斯·朗,凯西·程,瓦莱拉斯·班格特,维娅莱塔·阿普加尔,亚当·科瓦奇,莱昂·孙,阿尔文·陈,安德里·戈日洛夫斯基,安德鲁·约瑟夫 早坂、苏布拉曼亚·乔希、朱利亚·维隆、维贾伊·亚达夫、艾薇·刘、古斯塔夫·希尔、罗兰·施策尔、理查德·罗伯茨、马丁·哈贝丹克 伊肯纳·奥格博古、罗宾·奥普达姆、丹尼尔·费恩、塔里克·萨马里、阿维·查德-弗里德曼、维克托·德尔维涅、安什·博尔迪亚、拉斯·特尔·布拉克、罗伯托·扎普 梅拉尔多·安东尼奥、格拉汉姆·麦克尼科尔、克鲁蒂·克里什纳帕、安德烈亚斯·马丁森、莱昂纳多·卡瓦尼斯、亚尼夫·瓦克宁、卢卡·克里萨、卢卡·里布、斯拉夫
具有实时浮游植物热点的野生动物监测系统
原文:https://towardsdatascience.com/marine-wildlife-monitoring-system-with-real-time-phytoplankton-hotspots-9b361dbd2d13
“海洋野生动物跟踪器”v0.2 版简介
图片由作者提供。蓝鲸的运动和十月浮游生物热点[1]
**“海洋野生动物跟踪器”**是由 GIS4 野生动物运动分析开发的地理框架,将于 2022 年 4 月在海洋保护项目中实施。由于 Argos 卫星的实时反馈,该框架能够以接近实时的方式检索野生动物跟踪数据集。野生动物的运动可以显示为动画,它可以包括直接来自数据库的历史数据集,该数据库具有无限的存储空间(由于 Movebank 而免费)。此外,它还与哥白尼海洋服务中心有联系,可以检索海洋生物地球化学和物理变量的数据集。其目的是为海洋科学家提供一种工具,支持海洋野生动物的保护、海洋保护区(MPAs)的评估和轨迹的运动分析(这要归功于后端移动的熊猫)
GIS4 野生动物运动分析提供个人定制平台。用户可以包括他们自己的分析算法和他们自己的补充数据集,如特殊位置或限制。“海洋野生动物追踪器”产品的特别之处在于,它有一个报警系统,可以向用户的个人手机警告海洋野生动物面临的威胁。此外,该框架可以作为 web 地图嵌入,并可以显示野生动物跟踪期间的实时运动。
查看使用海洋野生动物追踪工具制作的在线网络地图产品:
一月份大鲸鱼迁徙和浮游植物热点
“海洋野生动物追踪器”使用凭据工作,并提供对用户数据集的安全访问。一旦在海洋保护项目中实施,该软件只需要根据用户对可视化和警报系统配置的反馈进行维护。
蓝色经济模式的解决方案
GIS4 野生动物的蓝色经济模式旨在向更多用户开放平台。一旦该软件在 2022 年进行测试,那么它将被部署到网上,供更广泛的社区使用。该平台旨在与专注于保护的机构合作,帮助其保持在线。我们的愿景是在全球范围内实现这一目标。
图片由作者提供。2018 年浮游植物的年度变化和蓝鲸的运动轨迹[1]
请继续关注进展
如果你愿意跟踪“海洋野生动物跟踪 v0.2 Pro”的进展,你可以订阅时事通讯。它将概述该平台是如何开发和实施的。
查看简讯: GIS4 野生动物简讯
图片由作者提供。野生动物追踪者肖像。
代码
后端分析已经发表在地理空间数据科学的短文中,这些短文提供了一些特定情况下的编码工作流。该解决方案不仅适用于海洋动物,也适用于鸟类等不同物种。在这里查看一些关于产品如何在后台工作的代码:
黑背海鸥迁徙期间寻找筑巢地
本文是理解停止检测算法如何工作以及如何配置其参数的关键。
GBIF.org(2021 年 3 月 15 日)全球定位系统追踪北海南部海岸繁殖的小黑背鸥和银鸥https://doi.org/10.15468/dl.6vsh8k
在蓝鲸的活动中发现觅食点
本文可能有助于在您自己的工作流程中自动化停止检测算法。它使得算法参数的配置更容易,并帮助用户做更多的测试。
[## 蓝鲸 GPS 追踪中的停止检测—移动熊猫 0.6
towardsdatascience.com](/stop-detection-in-blue-whales-gps-tracking-movingpandas-0-6-55a4b893a592)
Irvine LM、Palacios DM、Lagerquist BA、Mate BR、Follett TM (2019)数据来自美国加利福尼亚附近的蓝鲸和长须鲸的摄食行为,以及对猎物斑块的影响。 Movebank 数据仓库。DOI: 10.5441/001/1.47h576f2
可视化阿德利企鹅在南极洲的活动
这篇文章支持动物追踪数据集在两极的可视化:南极和北极。它给出了关于将空间数据投影到正投影坐标参考系统的说明。
在这张网络地图中查看阿德利企鹅的动向
该实验可视化显示了动物运动期间的停止检测算法。这有助于理解冰冻海洋上的觅食活动。
检查阿德利企鹅动作中的停止检测
按年份过滤巨头鲸迁徙路线
本文介绍了如何根据 GPS 轨迹创建线串几何。然后,它给出了按年份过滤的鲸鱼迁徙路线。
查看本网络地图中的巨头鲸迁徙路线
发现决定不迁徙的巨头鲸的觅食活动
本文旨在利用停止检测算法发现觅食活动,并参考 Silva 等人(2013)。算法的使用是实验性的,结果的验证需要海洋科学家的批判性观点。但是,该分析与研究杂志的结论相吻合,所以这篇文章展示了可以用来洞察鲸鱼迁徙的其他方法。
在网络地图中停止探测巨头鲸的迁徙
结论
GIS4 wildlife 提供了一个地理框架“海洋野生动物跟踪器”,旨在支持海洋保护、海洋保护区评估和运动分析。这一想法是在基于赞助的蓝色经济模式下工作,其愿景是向海洋科学界开放。
关于代码使用的文章展示了与出版的研究期刊的结论相匹配的内容。它们主要代表使用停止检测算法的教程和实验,结果的验证需要海洋科学家、生物学家、鸟类学家等的批判性观点。代码文章旨在提供有关“海洋野生动物跟踪器 v0.2 Pro”如何使用以及它如何支持野生动物跟踪项目中的研究和洞察力发现的可见性。
成为这个鼓舞人心的项目的一部分,支持海洋保护。如果你需要进一步的交流,请在这篇文章中留言或者在我们的网站上直接交流。
数据许可证
本文中可视化的数据集在 attribute 4.0 International(CC BY 4.0)和 CC0 1.0 Universal 下获得许可。
参考
[1] Irvine LM、Palacios DM、Lagerquist BA、Mate BR、Follett TM (2019)来自美国加利福尼亚州附近的蓝鲸和长须鲸摄食行为的数据,以及对猎物斑块的影响。 Movebank 数据仓库。DOI: 10.5441/001/1.47h576f2
[1] Sauzede R .、H. Claustre、J. Uitz、C. Jamet、G. Dall'Olmo、F. D'Ortenzio、b .根蒂利、A. Poteau 和 C. Schmechtig,2016:一种基于神经网络的方法,用于合并海洋颜色和 Argo 数据,以将表面生物光学特性扩展到深度:颗粒后向散射系数的检索,J. Geophys。海洋资源,121,doi:10.1002/2015JC011408。从蓝云虚拟实验室获取的浮游植物产品
环境署-WCMC 和自然保护联盟(2022 年),受保护的星球:世界保护区数据库(WDPA)和其他有效的区域保护措施世界数据库(补充)
营销组合建模 101
原文:https://towardsdatascience.com/marketing-mix-modeling-101-d0e24306277d
营销组合建模(MMM)的非技术性介绍—第 1 部分
“我花在广告上的钱有一半都浪费了;问题是我不知道是哪一半。”—约翰·沃纳梅克
约翰·沃纳梅克是一位成功的商人,是广告和其他许多领域的先驱。他在费城开设了第一家百货商店。他的百货连锁店被出售给多家公司,最终成为今天梅西百货的一部分。在他那个时代(1838 -1922),营销测量很可能是猜测,可能依赖于直觉。如果沃纳梅克先生正在天堂观看,他会很高兴,他会有一个更加精确和科学严谨的答案。可以帮助他的事情之一是营销组合建模,也就是嗯。
作者图片
MMM 试图帮助营销人员解决数百万美元的问题:衡量营销活动的效率,以及如何在未来优化预算分配。MMM 可以帮助回答这样的问题:
——KPI 的驱动力是什么?
—每个营销渠道的广告支出回报率(ROAS)如何?
—如果我在某些渠道或整体营销上花费更多或更少,KPI 会是什么?
—我应该在每个渠道上分配多少支出才能在下一个周期实现 KPI 最大化?
内容摘要
在这篇文章(MMM 系列的第 1 部分)中,我计划用以下主题对 MMM 进行非技术性的介绍:
- MMM 模型包含哪些数据和变量
- 理想的 MMM 心智模型中最重要的元素是什么
- 哪些模型属性的选择非常适合所提出的心智模型
MMM 数据
MMM 是一个聚合模型,其数据粒度通常在渠道、国家/地区和/或产品级别,而不是用户级别。每天的支出数据可能很少且不稳定,因此每周或每月的汇总数据非常常见。业务 KPI 通常具有年度季节性和趋势性,因此该模型通常需要至少 2 年的历史数据(尤其是每周级别或每月级别)。也就是说,一切还是要看数据,对于某些数据量大、变化快的业务,每天和更短的周期也可能行得通。
MMM 模型通常包括以下目标和特征。
MMM 数据类别和示例-作者图片
感兴趣的 KPI 指标是响应变量。它可以是零售的销售额,在线商店的网站访问量,或者帐户注册数,谷歌搜索量等。
媒体变量应包括所有主要的营销渠道,并有可观的支出。例如付费搜索等在线媒体、付费社交媒体、电视等传统媒体和/或户外和直邮等离线媒体。建模单元可以是 spend,这更容易获得,因为它通常是内部跟踪的。但是,支出数据不考虑媒体成本。基于行动的变量,如点击、印象和 GRP 数据(如果可用且可靠)可能是更好的选择。
KPI 的其他重要驱动因素也应包括在模型中,以控制其影响。一些内部业务变量可以是促销、推荐和定价。其他外部控制包括季节性、趋势、重要节日等变量,以及可能导致 KPI 变化的事件(如 covid 或产品发布)、相关经济因素和竞争对手行为。
MMM 心智模型
所有的模型都是错的,有些是有用的。有用的模型是那些心理模型最接近现实的模型。以下要素对于一个好的 MMM 模型很重要,它们决定了应该采取什么样的模型形式。
- 收益递减
收益递减说明了一个事实,即花在广告上的每一美元都不会产生相等的回报,随着我们在一个渠道上花更多的钱,一美元的回报将由于饱和而不断减少。开始时,有许多唾手可得的果实,最初的支出可以产生高回报。最终,随着市场越来越饱和,或者消费者对广告越来越厌倦,营销产生的回报会少得多,继续花钱就不值得了。这可以通过以媒体支出为 x 轴、KPI 为 y 轴的响应曲线图(见下例)来捕捉,这样我们就可以直观地看到支出的增加,以及 KPI 如何随之变化。
关于响应曲线应该是什么(C 曲线、S 曲线等),有一些假设。下面的例子显示了响应曲线的一个最流行的假设:曲线在开始时更陡峭,最终变得更平坦。尽管我们看到 KPI 持续增长,但每增加一个 KPI 的成本会大幅增加。重要的是要了解在什么水平的支出,ROAS 不再理想,我们应该停止支出。模型应该采取能够模拟这种凹形的形式。
回复治疗-作者图片
- 遗留效应
广告的延续效应指的是消费者被广告感动的时间和消费者因为广告而转变的时间之间的时间差。假设有一天,我在回家的路上看到广告牌上有一个保险计划的广告,但是我不能马上买,因为我正在开车。这个广告仍然对我有一些挥之不去的影响。最后,一个星期后,我找到了一些时间,成为了他们的客户。这个广告对我的影响持续了一周。将我的经验扩展到成千上万的其他消费者,我们也可以有一个结转效应的平均衰减函数,也称为 adstock。参见下面的示例。该示例频道的广告效果在最初的一周大约为 40%,然后在接下来的一周下降到 20%,等等。
按周分类的广告——按作者分类的图片
虽然广告支出发生在第 0 周,但实际的广告效果直到后来才实现,因此在构建模型时,需要使用衰减率将第 0 周的支出公平分摊到接下来的 8 周。
存货、支出和转化支出——按作者分类的表格
adstock 衰减率需要针对不同的信道和目标。
广告库存可能因渠道而异。人们可以在一些频道上转换得更快,尤其是数字频道。例如,付费搜索渠道的用户可以很快转化,因为他们有更高的购买意愿,一次点击就可以带他们到商店或他们正在寻找的产品。另一方面,来自离线渠道的用户可能转换得慢得多,因为它可能需要一些时间来响应离线媒体。
adstock 也可能因业务而异。有些业务可能比其他业务慢。例如,如果你想成为 Lyft 的一名司机,需要时间来获得你的驾照和车辆检查和批准。因此,从接触广告到被认可为驾驶员之间的转换滞后时间较长。与注册网飞等视频流媒体服务不同,从看到广告到订阅可能只需要不到 5 分钟的时间。
- 协同效应
1+ 1 > 2.你可能会担心,我怎么能称自己为拥有如此数学技能的数据科学家。但有时多项营销活动的组合可能会比每项活动单独运作的总和产生更大的效果,这就是营销的协同效应。
在这个信息爆炸的世界里,许多消费者不仅仅被一个媒体渠道所感动。例如,用户可以在回家的路上看到广告牌上的广告,然后他可能会在晚饭后看电视时看到关于同一产品的第二个广告。如果他只看了一个广告,他可能不会转化,但看了两个广告后,他可能会转化。
- 趋势、季节性、节假日、事件和其他控制
除了目标和媒体特征之外,重要的是要理解业务 KPI 通常是季节性的和流行的,并且可能受到某些事件、假期和其他因素的高度影响。
快速增长的企业通常在目标上有上升趋势。零售商店可能在周末有更多的销售,而在一周内销售较少。一家面包店的销售额可能会突然发生变化,因为他们最近新开了三家分店。圣诞节促进了某些生意,因为许多人购买礼物。此外,由于竞争对手的大力促销,我们可能会看到销售额下降,当经济衰退时,开设投资账户的人会减少。
这些方面大多与营销活动没有什么关系,必须由模型很好地捕捉,以便进行无偏见的估计和合理的预测。
- 纳入 的实证和实验结果
一个理想的 MMM 模型不应该纯粹由观测数据驱动,还应该结合实验结果、以前的经验见解、研究,甚至以前的模型。这将有助于模型解决数据问题,并产生与外部见解更一致且更具普遍性的结果。
通常,真实世界的数据,尤其是更细粒度的截面级别的营销数据可能非常稀疏,这可能会仅基于观察到的数据产生非常不合理的读数。在这种情况下,经验见解、以前的研究和行业基准可能会有所帮助。此外,具有较差数据质量的信道或 geo 可以使用来自具有稳健数据的类似信道或 geo 的信息来指导估计。例如,显示可以类似于编程显示。如果显示通道具有稀疏数据,我们可以使用从显示器上的编程显示中获得的信息来获得更合理的读数。新一代模型还可以利用以前版本模型的结果,并保持洞察力的一致性。
可信的实验结果被认为是理解因果关系和增量的圣杯。许多公司定期对营销渠道进行实验,以了解渠道增量。测试结果可能仅代表某一支出水平或短时间范围或渠道的子集。使用模型的测试结果可以利用两个世界的优势,并帮助校准模型结果。
模型属性的选择
模型属性和损失函数的许多选择可以适合上述心理模型。下面的图表总结了一些我认为可以满足心智模型关键要求的选择。
模型形式与心智模型—作者列表
为了捕捉不同信道之间的协同效应,假设信道可以以任何组合对用户产生协同效应,我们可以在 adstock 变换之后使用具有媒体的乘法模型形式。
媒体变量应使用 adstock 进行转换,考虑广告效果的预期长度、广告影响的预期峰值时间和衰减率。
然后,我们可以在函数的两端应用对数变换(加 1 以避免对数为零),方程就变成线性的了。对数转换为我们提供了凹形的介质成本曲线,也使扭曲的数据更加正常。可以通过对媒体数据应用附加变换来调整曲线的形状。
在上述等式之上,模型还应该是支持多变量和分解趋势、季节性、回归和误差项的时间序列模型。
贝叶斯框架是将外部信息整合到模型中的自然选择。贝叶斯框架允许使用具有期望变化水平的信息先验,以便结合先验知识和观测数据来估计后验概率。虽然媒体影响在短期内不会太不稳定,但营销杠杆和市场动态也可能会迅速变化,尤其是对新公司而言。在这种情况下需要时变系数,以便模型更加动态。
Orbit 是一个强大的 python 包,它可以构建支持上述属性的 MMM 模型。我有一个单独的介绍轨道的教程这里(这是一个关于轨道的一般用法的教程,不是专门为建造 MMM 的)。你可以在这里找到关于 MMM 系列第二部分的第二个故事,了解建立 MMM 模型时的挑战和陷阱,以及 MMM 与其他营销测量方法的比较。我还计划编写第 3 部分,使用 Orbit 用模拟数据构建一个 MMM 模型。
参考
[1] Dominique M. Hanssens,Leonard J. Parsons,Randall L. Schultz,市场反应模型——计量经济学和时间序列分析
[2] Edwin Ng,Zhishi Wang,,贝叶斯时变系数模型及其在营销组合建模中的应用
谢谢你,并关注我更多!
感谢您阅读到目前为止,并了解营销组合模式。如果你对 MMM 模型感兴趣,请关注更多,因为我计划为 MMM 写一系列故事。
敬请关注,关注我,订阅电子邮件了解更多关于数据科学和其他有趣话题的故事!
营销组合模型 102——好的、坏的和丑陋的
原文:https://towardsdatascience.com/marketing-mix-models-102-the-good-the-bad-and-the-ugly-f5895c86b7c3
营销组合模型(MMM)(和其他流行的营销测量方法)介绍第 2 部分
图片来自 Pixabay
公司每年在广告上花费数十亿美元,这使得营销活动的衡量和优化成为营销人员的一项重要任务。营销组合建模(MMM)通过提供营销活动如何驱动业务 KPI 的自上而下的视图来帮助回答这个问题。
在我的另一个故事营销组合建模 101 中,我介绍了什么是 MMM、MMM 数据、MMM 心智模型以及合适的模型形式。在这篇文章中,我将重点介绍
- MMM 与其他一些衡量营销效率的方法相比如何?
- 构建 MMM 时有哪些挑战,有哪些潜在的解决方案?
MMM 与其他营销测量方式的对比
营销的最终目的是影响或改变消费者的行为。衡量营销活动的效率就是要了解营销活动对 KPI 的增量效应是什么。在一个完美的世界中,我们希望有一对反事实:一个被广告感动的用户会发生什么,而如果同一用户没有被同样的广告感动,同时保持其他一切不变,会发生什么。然而,我们并不是生活在平行宇宙中(或许我们生活在平行宇宙中,但我并不知道。).
没有人会两次踏入同一条河。因为这不是同一条河,他也不是同一个人。—赫拉克利特
虽然只有随机实验被认为能够回答因果关系,我们仍然可以依靠其他建模技术来接近营销的增量。一些方法有助于衡量营销增量/效率:实验、营销组合建模(MMM)、多点接触归因建模(MTA)和混合方法——三者的全部或两者的组合。
实验
实验是理解因果关系的圣杯。有了随机化,它应该是我们能得到的最接近真实反事实的东西。实验和准实验广泛应用于市场测量中,以测试活动和渠道的增量。类型包括用户级测试、地理级市场测试、综合控制测试等。
在用户级测试中,用户被随机分配到控制组和治疗组。治疗组会暴露在广告下,而对照组不会。测试结束后,分析师可以比较两组之间的关键指标(例如转化率),并进行统计测试,以查看治疗组和对照组之间观察到的差异是否显著。例如(举一个简单的例子),如果一家公司想测试某种促销活动是否有效,以及多少折扣是理想的,他们可以有多个组——一个完全没有促销活动的控制组,促销活动减少 10 美元的处理组 1,以及促销活动减少 30 美元的处理组 2。
在某些情况下,用户级测试是不可行的,类似市场级测试或综合控制测试的准实验方法可能会奏效。例如,如果存在严重的网络效应,治疗将影响治疗组和对照组。就像优步一样,如果一个促销活动只针对某些司机,它可能会影响平台上的供应情况,并影响控制组中的司机。或者,如果我们想测试广告牌的效率,我们不能遮住某些人的眼睛,不让他们看到。但是,我们可以在一个城市进行治疗,而不在另一个城市进行治疗,并使用对照城市构建一个模型化/预测的反事实,与治疗城市的观察结果进行比较。
如果操作正确,使用实验/准实验的优势包括:
- 这是真正理解一个活动或一个渠道的增量的最科学的方法。
- 它可以灵活地理解活动级别、渠道级别、地理位置级别或营销有效性和边际性的整体级别(取决于创造性的实验设计)
- 它可以为其他归因模型提供有价值的验证和校准
- 它可以绕过一些模型难以解决的问题,例如付费搜索频道的自我选择偏差(在本文的后面部分会有更多相关信息)。
- 对于历史很短但数量足够多的渠道和活动来说,实验比模型更有效。
也有缺点:
- 对于许多渠道来说,实验可能是不可行的。原因可能是通道没有足够的容量或足够的功率来获得统计上显著的结果,或者通道不便于实验。准实验也受许多外部因素的影响,这些因素可能会影响治疗组和其预测因素之间的关系,从而使测试不准确。
- 实验可能只能代表测试期间的市场情况和营销策略,而不能预测未来。它也可能受季节性和趋势的影响。仅仅使用实验结果来优化未来的预算分配,很难建立一个全面的、包罗万象的衡量框架。
- 一个人不可能在给定的时间框架内运行无限数量的测试,尤其是对于较小的企业,或者当需要测试许多不同的处理方法时。
- 这是主观的用户数据隐私倡议,如苹果没有 IDFA。没有用户级的跟踪数据,很难确定地将转化归因于活动,因此没有可靠的提升结果。
多点触摸归因模型(MTA)
转化前被多个广告接触的用户—作者图片
MTA 模型旨在将转化归因于转化前接触用户的不同渠道。在用户转化之前,他/她可以被多个频道的广告所触动。例如,用户可能会看到 Instagram 广告,并在转换前收到电子邮件广告。你如何将转化归因于不同的广告?如何确定哪个广告在推动用户转化方面是增量的,或者都不是?MTA 模型可能会有所帮助。
有许多方法可以建立 MTA 模型。有许多流行的方法:
1)简单但不精确的基于规则的方法,例如相等的权重(给所有通道相等的信用)、时间衰减(以固定/动态速率给远离转换的接触点较少的信用)、位置(给最后和/或第一接触通道更多的权重,假设它们对用户的决策具有更大的权重,并将剩余的信用分配给中间的其他通道)
2)合作博弈论与 Shapley 值。在博弈论中,Shapley value 是一种将积分公平分配给联盟中多个玩家(营销渠道)的解决方案(推动转化)。
3)机器学习模型,例如逻辑回归、树模型或神经网络。
4)上述的混合
优势:
- MTA 模型是用户级和自底向上的。通过将用户级别聚合到所需的粒度级别并执行群组级别的分析,他们可以提供更精细和灵活的见解。
- MTA 模型通常处于日线水平,能够对每日市场做出更快速的反应。
- 允许广告客户深入了解用户旅程,更好地理解特定受众群体和粒度策略的细微差别和协同作用。
缺点:
- MTA 模型只能覆盖数字媒体。你不能将广告牌广告接触归因于特定的用户。对于一家在线下媒体上投入大量资金的公司来说,这不会有什么好处。
- 用于 MTA 模型的数据可能很难获得。MTA 数据旨在模拟整个用户广告旅程。然而,在现实中,由于跟踪困难,真实世界的数据可能是部分的,并且仅包括部分触摸点。
- 大多数 MTA 方法利用点击数据,而不是印象,这往往会给更多点击渠道更多的信任,如搜索。这也会使分析出现偏差。
- 像实验一样,MTA 也对用户数据隐私倡议持主观态度,比如苹果没有 IDFA,脸书没有 AMM。在可预见的未来,谷歌也将加入这一行列。没有用户级的跟踪数据,就无法建立 MTA 模型。
- 它可能没有回答因果关系的问题,因为相关性不同于因果关系。
嗯
正如本文开头和我的 MMM 101 故事中提到的,MMM 也衡量不同营销渠道对 KPI 的影响。
与其他两种方法/模型相比,MMM 是一种聚合的自顶向下模型,具有以下优点和缺点:
优势:
- MMM 提供了一个整体框架来评估营销渠道,控制许多其他因素,包括季节性、趋势、经济等。
- MMM 可以生成有助于预算优化和分配的成本曲线。
- MMM 不仅覆盖数字频道,还覆盖线下媒体,比 MTA 覆盖面更广,比市场水平测试更可靠。
- MMM 对用户数据隐私倡议并不主观,因为它不需要用户级别的数据,这使得它是面向未来的。
缺点:
- 相关性不等于因果性。MMM 本质上是一个回归模型。它没有反事实。虽然我们可以模拟假设情景,但它仍然不等同于随机控制实验。没有实验结果的校准,单独的 MMM 可能很难说它是否是因果关系。
- MMM 需要相对较大的预算和较长的数据历史来进行可靠的读取。稀疏的数据和短暂的数据历史会使结果有偏差且不稳定。
- MMM 倾向于低估上漏斗通道,高估下漏斗通道。
- MMM 是一个聚合级别,这使得它不太灵活,不太可能获得更细粒度的洞察,并且不会显示用户旅程的细微差别。
统一 测量框架
以上都有一些利弊,没有人是完美的。我们可以将一种方法与另一种方法结合或校准,以利用所有方法的优势,并尽可能避免问题。例如,使用实验结果作为 MMM 的先验,并使用 MTA 结果来验证和校准 MMM。理论上,统一的方法应该比只使用一种方法更有效,但它需要更多的努力、规划、人才和预算,这对于许多公司来说在成本和收益方面可能没有意义。
挑战和陷阱
在高度了解 MMM 在营销测量领域的地位后,在本节中,我将深入探讨构建 MMM 模型时面临的挑战,以及一些挑战的解决方案。一些挑战可能不是 MMM 模型特有的,而是与一般回归相关的。
- 数据挑战
MMM 最具挑战性的数据限制包括三个方面:可用性、稀疏性/杂乱性以及有限的范围和数量。低质量的数据是无法治愈的,但是我们可以做一些事情来降低对模型的影响。(也就是说,垃圾进垃圾出,下面提出的解决方案只适用于通过最低要求的数据。)
- 可用性:想要高质量的数据可能很难获得。例如,许多 MMM 模型使用媒体支出作为特征,然而,支出并不完美,因为它没有考虑媒体成本。在同样的支出水平下,我们以较低的媒体成本所能达到的范围要比以较高的价格所能达到的范围大得多。印象、点击和 grp(电视)可能是比支出更好的功能,但数据可能不可用或不可靠。
可以从第三方数据中获取基于行动的数据,或者尝试建立更好的内部数据跟踪系统。没有基于行动的数据,花费数据仍然足够,但是,数据科学家应该注意媒体成本,并在模型形式和使用模型洞察力时解决它。 - 稀疏和混乱:媒体数据也可能是稀疏和混乱的(不管是否使用支出作为特征),尤其是在更细粒度的层面上。这是营销预算工作的本质。例如,由于新品上市,支出可能会大幅增加,但之后的支出不会保持一致。对于某些渠道,由于市场动态或业务战略变化,预算可能会关闭一段时间,然后重新打开。
在某些情况下,我们可以将被认为彼此行为相似的较小位置汇集在一起,并使用分层贝叶斯模型(相同的逻辑可以应用于类似的渠道)来估计它们,或者使用从实验或外部来源获得的可信先验来通知模型,以便数据不是决定系数的唯一因素。
了解数据中的问题也有助于数据科学家利用模型做出更明智的决策 EDA 分析的重要性(我将有一篇关于它的单独文章)。 - 有限的范围和数量:MMM 模型通常需要至少 2 年的每周数据(越长越好)和大量的媒体支出。如果我们有 2 年的周数据,只有 104 个数据点(假设一个横截面),我们可能有 10–20 个媒体渠道和外部因素,面临一个大 p(特征数)小 N(观察数)的问题。
如果更长的历史或更细粒度的时间维度不在考虑范围内,一种可能的方法是在业务用例允许的情况下,通过合并较小的渠道并优先考虑较大的渠道来减少功能的数量。
这是上面提到的 MMM 模型的弊端之一,没有足够的范围和历史数据量,不如不建立 MMM 模型,寻求其他营销测量的解决方案。
2.自我选择偏差和内生性
用技术术语来说,当模型中的一个回归变量与误差项高度相关时,内生性就发生了。这违反了基本的回归模型假设,并将导致有偏见的估计。
对于 MMM 来说,当一个频道中存在自我选择时,就会发生这种情况。最好的例子就是付费搜索,尤其是品牌付费搜索。有时,用户心中已经有了想要购买的品牌,并在网上搜索该品牌名称。然后她点击了付费搜索广告,进行了购买。这将招致广告支出,然而,购买将不是递增的,因为她无论如何都会购买。类似的案例在联盟营销中也经常发生。就拿我这个鬼鬼祟祟节俭的顾客来说吧。在我买东西之前,我会查看代销商应用程序中是否有返现优惠。我只是想看看我是否能节省一些钱,但返现并不影响我的购买决定。
自我选择偏差问题导致了另一个漏斗效应问题。漏斗上渠道获得的信贷比下游渠道少。例如,当用户首先在电视上看到广告,并想在线购买时。然后,他在谷歌上搜索该产品,并从付费搜索广告中购买。如果没有得到很好的处理,该模型可以将更多的信用归于搜索。
有几种方法可以解决内生问题并获得更准确的估计:
1)使用来自具有高选择偏差的渠道的可靠实验的信息先验来指导模型,并防止模型高估内生渠道。
2)使用仪器变量来更好地控制偏差。工具变量的基本思想是用其他与误差项不相关但与内生变量相关的预测因子来预测内生变量,并用预测替代内生变量。哇,那是一个令人费解的句子吗?LOL。一个简单的例子是两阶段最小二乘法。工具变量可能很难找到或构建。
3)使用 Google 开发的选择偏差修正方法(论文此处)。谷歌团队使用搜索广告环境的因果图,并根据因果推理文献中的后门标准(Pearl 2013)推导出一种统计学原理方法来纠正偏差。他们发现相关搜索量满足后门标准,并使用搜索查询作为控制。校正后,搜索系数比原始模型的搜索系数小得多,并与实验结果一致。
4)使用行业基准数据来减少从未修正模型直接估计的原始系数。这可能是最不可靠和最不推荐的方法,但可能是一个快速和肮脏的解决方案。
3.多重共线性
营销人员自然会以相关的方式在不同的渠道中分配他们的广告支出。例如,两个渠道可能一起发布,以相互加强或支持产品发布。
当构建模型时,高度相关的输入变量会导致模型具有高方差,并导致信道贡献的不稳定和不准确的分解,从而发生多重共线性。我能想到的检测多重共线性的方法有以下几种:1)检查解释变量之间季节性和趋势控制的相关性,2)检查方差膨胀因子(VIFs),即将每个要素与所有其他要素进行回归,并计算 VIF 以确定是否存在严重的多重共线性,3)更改模型中的要素组合并检查系数中是否存在高方差。
如果多重共线性问题严重,我们可以
- 考虑减少特征的数量,并通过丢弃不相关的变量、合并相似的变量或使用 PCA(主成分分析)等更高级的方法来删除导致问题的变量。
- 对变量进行一些转换或标准化,看看是否有帮助。
- 在某些情况下,将规则应用于模型(如岭回归或套索回归)也可能有所帮助。
4.型号选择
在我的另一个故事营销组合建模 101 中,我讨论了 MMM 的良好心智模型需要考虑哪些方面。我就在这里总结一下
- 报酬减少
- 阿德斯托克
- 季节性、趋势、假期和外部因素,如经济和竞争
- 不同媒体之间的协同效应
- 结合经验和实验见解进行模型校准,并提高解释因果关系的能力
在我们开始用模型拟合任何数据或开始解决数据问题或内生性或多重共线性问题之前,所选的模型形式至少应满足上述要求。
谢谢你,并关注我更多!
感谢你到目前为止的阅读,祝贺你已经完成了关于营销组合模型和其他营销测量方法的第二个故事。(此处看第一个。)我打算写一篇关于使用 Orbit 包的教程(你可以在这里找到 Orbit 的入门和基础教程)和模拟数据构建一个 MMM 模型,作为我 MMM 系列的第三个故事。如有兴趣,请关注更多!
敬请关注,关注我,并订阅电子邮件以获取更多关于数据科学和其他有趣主题的故事!
参考
[1] 使用类别数据改进媒体组合模型的分层贝叶斯方法由谷歌团队完成,2017
【2】媒体组合建模中的挑战和机遇由谷歌团队完成
【3】媒体组合建模中付费搜索的偏差修正,2017
【4】市场反应模型—计量经济学和时间序列分析**,** Dominique M. Hanssens,Leonard J. Parsons,Randall L. Schultz,
马尔可夫链:多步转移
原文:https://towardsdatascience.com/markov-chains-multi-step-transitions-6772114bcc1d
使用 Chapman-Kolmogorov 方程理解马尔可夫链中的多步转移
沃洛季米尔·赫里先科在 Unsplash 上的照片
介绍
在我以前的文章中,我们介绍了 马尔可夫属性 的概念,并将其发展为解释一个 马尔可夫链 **。**我建议现在的读者在阅读这篇文章之前先浏览一下那篇文章:
然而,在坚果壳中,马尔可夫性质是当转移到下一个状态的概率仅取决于当前状态时。系统是 无记忆 。马尔可夫链是有限状态空间下马尔可夫性质下的时间离散变迁的序列。
在本文中,我们将讨论 Chapman-Kolmogorov 方程以及如何使用它们来计算给定马尔可夫链的多步转移概率。
马尔可夫链和转移矩阵
考虑以下状态空间为 {A,B,C} 的马尔可夫链:
作者制作的图像。
这个马尔可夫链有如下关联的 转移矩阵 :
由作者在 LaTeX 中生成的矩阵。
这些值告知我们从状态 i (行)移动到状态 j (列)的概率。然而,这些概率仅针对单步过渡。比如说,分两步从状态 **B 到状态 A 的概率是多少?**我们可以通过参考马尔可夫链图来解决这个问题:
LaTeX 中生成的方程。
然而,当状态空间变大,我们需要计算两个以上的转换时,这种方法变得越来越困难。有一种更简单、更通用的方法来表达多步跃迁,使用 T 和 Chapman-Kolmogorov 方程,我们接下来将深入探讨。
查普曼-柯尔莫哥洛夫方程
我们可以使用以下公式概括多步转换:
LaTeX 中生成的方程。
也就是我们刚刚在时间 m 处于状态 i 时,在时间 n 进入状态 j 的概率。这个方程可以用查普曼-科尔莫戈罗夫方程求解:
LaTeX 中生成的方程。
其中 l 为介于 m 和 n 之间的整数值,P 为状态间转移的概率, k 为只能占用状态空间中的值的索引变量。
让我们通过一个例子来实现上面马尔可夫链的Chapman-Kolmogorov 方程。我们希望分两步从状态 B 到状态 A:
LaTeX 中生成的方程。
我们得到了和上面一样的概率!
我将当前时间设置为零。这是允许的,因为状态的历史与马尔可夫链无关,所以将当前时间设置为零不会失去表达式的通用性。
直觉上,我们只是将两步转换分解为一组一步转换,因为我们知道它们的概率。然后,我们将它们结合起来计算两步跃迁概率。
如果我们想分三步计算转换,那么 l 的值可以是 1 或 2 。因此,我们必须应用两次查普曼-科尔莫戈罗夫方程来表达一步转换公式。这个过程被称为递归**,因为我们不断地向后计算概率。**
多步转移矩阵
转移矩阵是一个正方形矩阵,其中每个单元给出了在一步中从状态*到 j 的概率。*解读上面的Chapman-Kolmogorov 方程,可以看到,从状态 i 到 j 中的两步等于转移矩阵的平方:
LaTeX 中生成的方程。
请注意,对于从 B 到 A 的条目(1,0)* (我使用的是从零开始的索引),我们得到的概率是 0.25,正是我们上面得到的相同结果!***
因此,要得到多步转移概率,你所要做的就是将一步转移矩阵本身乘以你需要的转移次数!****
结论
在本文中,我们展示了如何使用 Chapman-Kolmogorov 方程推导出马尔可夫链的多步转移概率*。在我的下一篇文章中,我们将看到当转换的数量变大并趋于无穷大时会发生什么!***
和我联系!
- 要在媒体上阅读无限的故事,请务必在这里注册! 💜
- T49😀
- 领英 👔
- 推特 🖊
- github🖥
- https://www.kaggle.com/egorphysics🏅
(所有表情符号都是由 OpenMoji 设计的——开源的表情符号和图标项目。许可证: CC BY-SA 4.0
马尔可夫链简单地解释了
原文:https://towardsdatascience.com/markov-chains-simply-explained-dc77836b47e3
马尔可夫性和马尔可夫链的直观而简单的解释
照片由郭佳欣·阿维蒂西安在 Unsplash 上拍摄
介绍
马尔可夫链出现在许多领域:物理学、遗传学、金融,当然还有数据科学和机器学习。作为一名数据科学家,你可能听说过这个词**‘马尔可夫’**在你的研究或一般阅读中出现过几次。它是自然语言处理和强化学习中的典型统计技术。在本文中,我们将简单解释什么是马尔可夫链,以及它的含义。
马尔可夫性质
对于任何被认为是马尔可夫/马尔可夫的建模过程,它必须满足 马尔可夫特性 。这个性质表明下一个状态的概率只取决于当前状态,当前状态之前的一切都是无关的。换句话说,整个系统完全是无记忆。
从数学上来说,这可以写成:
LaTeX 中生成的方程
其中 n 是时间步长参数和 X 是在给定的状态空间中取值的随机变量状态空间是指事件的所有可能结果。例如,一次掷硬币在其状态空间中有两个值:s = {正面,反面} 和从一种状态转移到另一种状态的概率为 0.5。
马尔可夫链
使用马尔可夫属性的过程被称为马尔可夫过程。如果状态空间是有限的并且我们使用离散时间步这个过程被称为马尔可夫链。换句话说,它是在给定的状态空间中呈现状态的随机变量序列。
在本文中,我们将考虑时间齐次离散时间马尔可夫链,因为它们最容易处理并建立背后的直觉。确实存在非时齐马尔可夫链,其中状态之间的转移概率不是固定的,而是随时间变化的。
下面显示的是带有状态空间{A,B,C} 的示例马尔可夫链。箭头上的数字表示在这两种状态之间转换的概率。
图片作者。
比如你想从状态 B 到状态 C,那么这个转换有 20%的几率。从数学角度来看,我们得出以下结论:
LaTeX 中生成的方程
概率转移矩阵
我们可以通过为给定的马尔可夫链构建一个概率转移矩阵来简化和概括这些转移。转移矩阵具有行 i 和列 j ,因此 i,j 索引值给出了从 i 到 j 的转移概率:
LateX 中生成的方程
上述马尔可夫链的转移矩阵是:
由作者在 LaTeX 中生成的矩阵。
所以(0,1)项告诉我们从 A 到 B 的转移概率是 0.4。这与我们在上面的马尔可夫链图中得到的结果一致。
摘要
在本文中,我们介绍了马尔可夫性质的概念,并使用这一概念来构建和理解一个基本的马尔可夫链。这种随机过程出现在数据科学和机器学习的许多方面,因此对它有所了解是很重要的。在我的下一篇文章中,我们将深入研究马尔可夫链和过程的更复杂和更特殊的部分。
希望你喜欢这篇文章!
和我联系!
- 要在媒体上阅读无限的故事,请务必在这里注册!T35💜**
- 😀
- LinkedIn👔
- 碎碎念 🖊
- github🖥
- https://www.kaggle.com/egorphysics🏅
(所有表情符号都是由 OpenMoji 设计的——开源的表情符号和图标项目。许可证: CC BY-SA 4.0
马尔可夫链:平稳分布
原文:https://towardsdatascience.com/markov-chains-stationary-distribution-bedd67140112
用 Python 模拟解释和推导马尔可夫链的平稳分布
马库斯·温克勒在 Unsplash 上的照片
介绍
在我以前的文章中,我们开始发展我们对 马尔可夫链 的直觉和知识。到目前为止,我们已经讲述了: 马尔可夫性质、 转移矩阵 和多步转移利用 查普-柯尔莫哥洛夫方程 。我建议在继续写这篇文章之前通读这些文章:
但是,简单总结一下上面的文章:马尔可夫链是离散时间内有限状态空间中的一系列转移,其中转移的概率只取决于当前状态。这个系统是完全无记忆的。
转移矩阵显示状态空间中状态间转移的概率。Chapman-Kolmogorov 方程告诉我们如何计算多步跃迁概率。****
在本文中,我们将讨论当我们采用大量离散时间步长时,转移矩阵会发生什么。换句话说,我们将描述马尔可夫链如何随着时间趋于无穷大而发展。
马尔可夫链和转移矩阵
首先,让我们快速回顾一下我以前文章中的内容。下面是一个状态空间为 {A,B,C}的马尔可夫链。
作者制作的图像。
该马尔可夫链具有以下转移矩阵:
由作者在 LaTeX 中生成的矩阵。
其中每个单元格传达在马尔可夫属性下从状态 i (行)到状态*(列)的转移的概率。然而,该矩阵仅用于单步过渡。如果我们想分两步从 i 到 j 呢?*
这个问题可以通过 Chapman-Kolmogorov 方程来解决,它告诉我们这就是转移矩阵的平方。
由作者在 LaTeX 中生成的矩阵。
如果我们想计算三步概率,我们就要对转移矩阵求立方。一般来说对于 n 步:
作者在 LaTeX 中生成的方程。
现在,随着 n 变大会发生什么?我们接下来会回答这个问题。
稳定分布
随着时间的推移,处于某些状态的可能性比其他状态更大。从长远来看,该分布将达到一个平衡,并具有处于每个状态的相关概率。这就是所谓的固定分配。
它是稳定的原因是,如果你将转移矩阵应用于这个给定的分布,结果分布是与之前相同的*:***
作者在 LaTeX 中生成的方程。
其中 π 是某个分布,它是列数等于状态空间中的状态的行向量,而 P 是转移矩阵。
特征值分解
有些人可能会把上面的方程认作是 π 是 P 的一个特征向量*,其特征值为 1 。这确实是真的,所以我们可以用 【特征值分解(谱定理) 来求解。***
注意:在这篇文章中,我假设读者已经理解了矩阵的特征值以及如何找到它们。如果没有,我建议阅读一些基础教程来获得一些直觉。
让我们看看上面的例子马尔可夫链,它有一个 3x3 转移矩阵。根据上面的转换矩阵,我们想要求解下面的等式:
作者在 LaTeX 中生成的方程。
其中 λ 为特征向量对应的特征值。利用 三角形法则 ,这等于:
作者在 LaTeX 中生成的方程。
所以我们的特征值是 0,-0.5 和 1* 。我们知道我们的解只在特征值等于 1 的情况下有效,所以我们现在将使用它来找到我们相应的特征向量,这将是我们的平稳分布:***
作者在 LaTeX 中生成的方程。
这里,下标指的是当我们有一个稳定分布时,处于相应状态的概率。上述方程可以通过高斯消去求解得到如下结果:
作者在 LaTeX 中生成的方程。
将上述向量归一化,我们的平稳分布为:
作者在 LaTeX 中生成的方程。
这意味着从长远来看,我们在这三个州中的任何一个州都有同等的可能性!
注意:这不是平稳分布的密集深度推导,因为我不想把它变成教科书!不过网上有很多关于特征值分解的深入例子。
Python 模拟
我们刚刚展示了理论上的稳定分布,现在我们将使用 Python 中的模拟来根据经验计算它。****
下面的代码模拟了开始分布为{1,0,0}的 50 步后处于每个状态的概率:****
***# import packages
import numpy as np
import matplotlib.pyplot as plt# set transition matrix
transition_matrix = np.array([[0, 0.4, 0.6],
[0.5, 0.3, 0.2],
[0.5, 0.3, 0.2]])# set initial distribution
initial_dist = np.array([1,0,0])# simulate 50 time steps
for _ in range(50):
update = initial_dist @ transition_matrix
initial_dist = update
print(initial_dist)***
输出:
***[0.33333333 0.33333333 0.33333333]***
这与我们上面推导的平稳分布完全相同!
完整代码可以在我的 GitHub 上找到:
***https://github.com/egorhowell/Medium-Articles/blob/main/Statistics/markov_stationary.py
摘要
在这篇文章中,我们描述了什么是平稳分布,以及如何为马尔可夫链推导出平稳分布。在我的下一篇文章中,我们将讨论马尔可夫链的不同性质。
和我联系!
- 要在媒体上阅读无限的故事,请务必在这里注册! 💜
- T35😀
- 【领英】 👔
- 碎碎念 🖊
- github🖥
- https://www.kaggle.com/egorphysics🏅
(所有表情符号都是由 OpenMoji 设计的——开源的表情符号和图标项目。许可证: CC BY-SA 4.0***
科技领域大规模裁员——人工智能的冬天来了吗?
原文:https://towardsdatascience.com/mass-layoffs-in-tech-is-the-ai-winter-coming-d7f9968006d
意见
面对经济逆风,亚马逊、Meta 和 Twitter 等科技巨头削减了数千个工作岗位。这对人工智能的未来意味着什么?
照片由 Unsplash 上的 Aditya Vyas 拍摄
直到最近,公司还在努力吸引和留住数据科学领域的优秀员工。在线业务在封锁时期蓬勃发展,世界突然依赖于包裹递送、云环境、在线会议空间和虚拟娱乐。科技巨头报告了创纪录的利润,将多余的现金投入到雄心勃勃的人工智能项目和创新中。
每个合格的数据科学家都是高价值的商品,公司竭尽全力阻止员工加入伟大的辞职运动。不管有没有电晕,天空似乎是科技行业的极限。
然后,几乎在一夜之间,LinkedIn 突然被寻找另一份工作的有经验的数据科学家淹没了。几天之内,Twitter 解雇了一半的员工,亚马逊和 Meta 都在大规模裁员中削减了超过 10,000 个职位,更多的公司要么冻结招聘,要么大幅缩减员工人数[2]。在全球范围内,估计已经有 200,000 名技术工人失业,这个数字在未来几个月内可能会上升。
突然之间,数据科学界似乎陷入了低谷。我们正在走向另一个人工智能冬天吗?
什么是 AI 冬天?
首先,什么是 AI 冬天?维基百科[4]将其定义为:
"人工智能研究资金和兴趣减少的时期."
通向这样一个冬天的道路概述如下:
“这是一个连锁反应,首先是人工智能界的悲观情绪,然后是媒体的悲观情绪,接着是资金的大幅削减,然后是严肃研究的结束。”
更广泛地说,人工智能的冬天可以被归类为 Gartner 炒作周期【5】中的一个低谷,在这个周期中,当事实证明过高的预期无法满足时,人们对某项技术的兴趣急剧下降。
Gartner 炒作周期。如果过高的期望得不到满足,这项技术就会失去兴趣,资金也会被抽走,从而阻碍进一步的发展
据报道,主要的 AI 冬季发生在 1974-1980 年和 1987-1993 年,人们一直预测另一次萧条迟早会到来。
总而言之,要实现人工智能冬天,在一段较长的时间内,必须满足以下两个条件:
- 资金减少
- 降低利息
根据记录,炒作周期存在的经验证据充其量也是站不住脚的,但为了这篇文章,我们将配合一下。
为什么数据科学家会被解雇?
先说经费减少。科技公司创纪录的裁员自然降低了进一步开发人工智能的能力。
显然,不是所有被解雇的人都是数据科学家,也不是所有数据科学家都设计 AI。尽管如此,大多数从事技术工作的人确实在日常工作中以这样或那样的方式使用人工智能。
在更多的应用角色中,你甚至可能不会直接注意到创新。然而,从长远来看,考虑一下如果没有更有效的矩阵乘法发明、更快速的梯度计算、透明地解释自动化决策的实践,会发生什么……你使用五年前的工具包会有多有效?
当这类创新停滞时,整个行业都会停滞不前,数据科学家的影响力也会大打折扣。人工智能与数据科学的许多分支交织在一起,大规模裁员的影响将渗透到该领域的所有缝隙中。自然,那些不幸失业的人受到的影响最大,然而我们所有人都会受到人工智能创新能力丧失的影响。
从商业常识的角度来看,裁员的原因很简单:
- 高成本削减:数据科学以高工资和丰厚奖金著称;这也是很多人试图进入这个领域的原因之一。因此,削减对公司的运营成本有着重大而直接的影响。
- 降低 R & D 的优先级:尽管“数据科学”的概念相当宽泛,但该领域的许多人都以某种方式参与了研究开发。在危机时期,研发活动总是受到打击,人们关注的是短期生存,而不是长期愿景和投机努力。
- 修正表现不佳:科技股最近经历了大幅下跌。corona 似乎将推动不断扩张的数字世界发生永久性变化,科技行业也随之扩张。然而,实际表现与乐观的预期并不相符。
一些具体的例子?
- Meta 向元宇宙投入了数十亿美元——仅今年一年就在这个项目上损失了近 100 亿美元[6]——还看不到盈亏平衡点。
- 根据马斯克的说法,Twitter 目前每天损失 400 万美元。
- 亚马逊最近成为历史上第一家亏损一万亿(!)的市值,微软紧随其后。
- 谷歌继续经历利润缩水,部分原因是广告市场饱和,部分原因是创新失败[9]。
在更细的层面上,特定的团队或产品无法产生利润,无论成员的素质或想法的才华如何。稍后会详细介绍。
最终,裁员决定通常只是一个团队花费多少和产生多少的问题。有办公室政治和商业愿景,但底线最终很重要。
马克西姆·霍普曼在 Unsplash 上的照片
艾温特或'只是'另一个危机?
人工智能资金的减少是不可否认的,但从表面上看,裁员有明显的宏观经济原因。全球经济出人意料地迅速从科罗纳危机中复苏——部分原因是政府机构近乎无限的资助——但乌克兰战争引发了另一系列问题,包括供应链进一步中断和能源价格飙升。通货膨胀率飙升,消费者有了购买力,人们变得恐惧……这就是一场危机所需要的所有要素。
经济逆风和裁员齐头并进,因此仅仅削减员工成本不足以构成人工智能的冬天。然而,如果我们仔细看看被解雇的*,我们可能会认为最近的发展不仅仅是为风暴做准备。是时候考虑一些例子了:*
- Twitter 的整个道德人工智能团队的解散引起了广泛的关注,因为该团队被认为是透明和公正人工智能的领导者[10]。裁员可能会被解读为独角戏,但类似的有针对性的裁员可能也会在其他科技公司出现。
- Meta 的概率团队完全解散了,他们的工作主题是概率和微分编程,这可以帮助 ML 工程师。据报道,这是一个世界级的专家团队,但似乎它缺乏足够明显的影响[11]。
- 据报道,亚马逊解雇了其**机器人和设备部门的大部分人员,**标志着重新定位于被证明能够产生现金流的服务。
在这些决策中,应该考虑到科技巨头——尽管显然不是慈善家——有大量现金可供支配。因此,停止人工智能项目对于短期生存来说并不重要,这意味着他们对长期的盈利能力或价值失去了信心。
终止项目随时都在发生,但此时一批插头正在被拔出。对许多公司来说,这是几十年来最大规模的裁员;很难夸大当前事件的重要性。
处于过程中间,缺乏关于重组努力的规模和范围的全面声明,现在看 AI 将走向何方还为时过早。然而,鉴于即使是世界级的人工智能专家也不再能保证得到一份工作,这似乎不仅仅是简单地预测经济挫折。
由卡莱斯·拉巴达在 Unsplash 上拍摄
下一步是什么?
未来如何发展显然取决于许多因素:战争、能源危机、反通胀措施的成功、消费者的情绪等等。尽管如此,日冕期间经历的 V 型复苏(快速内爆后同样快速的反弹)似乎不太可能。一个 U 型模式(逐渐衰退、停滞、缓慢复苏)似乎是我们所能期待的最好结果。鉴于科技劳动力的大幅减少,我们需要相当长的时间才能回到 2022 年开始时的水平。
所有这些是否意味着一个迫在眉睫的人工智能冬天?资金和人力的减少似乎是既定的,许多人工智能部门的有针对性的取消和精简肯定可以解释为对人工智能或至少该领域分支的兴趣减少。
话虽如此,AI 发展当然不会停止。甚至以前的冬天也没有完全停止人工智能的发展。此外,上一个冬天发生在 90 年代初。当今的人工智能如此庞大,在日常生活中如此根深蒂固,很难想象人工智能发展会出现真正的“突破”。
尽管大规模裁员、许多人工智能计划的终止以及公司目前的短期关注不太可能而不是损害人工智能的进展,但经济逆风似乎是比普遍丧失对人工智能的信心更强大的驱动力。因此,一个严重的人工智能冬天不太可能出现——人工智能仍然有太多的优势。
也就是说,在我们未来的日子里,多一条毯子可能没什么坏处。
罗伯特·蒂曼在 Unsplash 上拍摄的照片
喜欢这篇文章?你可能也会喜欢下面这些:
参考
[1]https://www.bbc.com/news/business-57979268
[2]https://www . computer world . com/article/3679733/tech-裁员-in-2022-a-timeline.html
[3]https://www.trueup.io/layoffs
https://en.wikipedia.org/wiki/AI_winter
[5]https://www . Gartner . com/en/research/methodologies/Gartner-hype-cycle
[6]https://www . ign . com/articles/meta-正在元宇宙损失数十亿美元,而它并没有好转
[7]https://www . Reuters . com/markets/deals/twitters-massive-revenue-drop-adds-heavy-debt-burden-2022-11-07/
[8]https://www . Bloomberg . com/news/articles/2022-11-09/Amazon-hits-unwanted-milestone-with-one-万亿-value-lost
[9]https://www . the verge . com/2022/10/25/23423560/Google-Q3-2022-增长-收入-萎缩-利润-收益
https://www.wired.com/story/twitter-ethical-ai-team/
[11]https://venturebeat . com/ai/why-meta-and-twitters-ai-and-ml-offsets-matter-the-ai-beat/
[12]https://www . business today . in/latest/corporate/story/after-meta-Twitter-and-Microsoft-now-Amazon-starts-fireing-employees-352547-2022-11-11
[13]https://www . nytimes . com/2022/11/14/technology/Amazon-offsets . html
[14]https://TechCrunch . com/2022/11/11/面临-经济-逆风-亚马逊-整合-机器人-项目/
[15]https://edition . CNN . com/2022/05/12/investing/recession-shapes-alphabet/index . html
Python 中图像处理和深度学习准备的大量教程#1
原文:https://towardsdatascience.com/massive-tutorial-on-image-processing-and-preparation-for-deep-learning-in-python-1-e534ee42f122
随意操纵和变换图像
照片由Prasad PanchakshariUnsplash
介绍
我们来这里是为了一件悲伤的事情。真的很悲伤。我们在这里学习如何拍摄美丽、令人惊叹的图像,并把它们变成一堆丑陋的小数字,以便它们对所有那些没有灵魂、没有头脑的机器来说更像样。
我们会把动物身上的颜色剥去,变成黑色和白色。抢过色彩鲜艳的花,抢走它们的美丽。我们将看到令人不安的 x 射线图像,并想办法让它们变得更加令人不安。有时候,我们甚至可以用计算机算法来画硬币。
换句话说,我们将学习如何执行图像处理。我们的荣誉图书馆将在整篇文章中以 Scikit-Image (Skimage)的形式出现。
这是一篇由两部分组成的文章。你可以在这个之后在这里阅读第二部分。最后还有一个链接。
**https://ibexorigin.medium.com/membership
获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:
https://alphasignal.ai/?referrer=Bex
基础
1.什么是图像?
除了文本,图像数据可能是最常见的。那么,计算机是如何理解你在埃菲尔铁塔前的那张自拍的呢?
它使用称为像素的小正方形单元的网格。像素覆盖很小的区域,并且具有代表颜色的值。图像中的像素越多,质量就越高,需要更多的内存来存储。
就是这样。图像处理主要是操纵这些单个像素(有时是一组像素),以便计算机视觉算法可以从中提取更多信息。
2.使用数字和浏览的图像基础
图像在 Matplotlib 和 Skimage 中都作为 NumPy ndarrays 加载。
数字阵列总是给游戏带来灵活性、速度和力量。图像处理也不例外。
Ndarrays 可以轻松检索图像的一般细节,如尺寸:
我们隐藏的image
高 853 像素,宽 1280 像素。第三维表示 RGB(红、绿、蓝)颜色通道的值。最常见的图像格式是 3D 格式。
您可以通过常规的 NumPy 索引来检索单个像素值。下面,我们尝试对图像进行索引,以检索三个颜色通道中的每一个:
照片由 Pixabay 从像素拍摄
照片由像素的 Pixabay 拍摄
红色通道为 0,绿色通道为 1,蓝色通道为 2——非常简单。
我已经创建了两个函数,[show](https://gist.github.com/BexTuychiev/e65c222f6fa388b22d7cf80eb561a1f4)
和[compare](https://gist.github.com/BexTuychiev/37898e5a44fae4851f6fc6e951e560d7)
,它们显示一幅图像或者并排显示两幅图像进行比较。我们将在整个教程中广泛使用这两个函数,所以您可能想看看我超链接的代码。
按照惯例,ndarray 的第三维是用于颜色通道的,但是这个惯例并不总是被遵守。Skimage 通常会提供一些参数来指定这种行为。
图像不同于通常的 Matplotlib 图。它们的原点不在左下方,而是在左上方的位置(0, 0)
。
>>> show(image, axis=True)
照片由 Pixabay 从像素拍摄
当我们在 Matplotlib 中绘制图像时,轴表示像素的顺序,但我们通常会隐藏它们,因为它们不会给查看者带来太多的价值。
3.常见转换
我们将执行的最常见的图像转换是将彩色图像转换为灰度图像。许多图像处理算法需要灰度 2D 阵列,因为颜色不是图片的定义特征,并且计算机已经可以在没有它的情况下提取足够的信息。
照片由来自 Pexels 的 Jovana Nesic 拍摄
>>> gray.shape(853, 1280)
当将图像转换为灰度时,它们会失去第三维空间——颜色通道。相反,图像数组中的每个单元格现在都表示一个uint8
类型的整数。它们的范围从 0 到 255,给出了 256 种灰度。
你也可以使用像[np.flipud](https://numpy.org/doc/stable/reference/generated/numpy.flipud.html)
或[np.fliplr](https://numpy.org/doc/stable/reference/generated/numpy.fliplr.html#numpy.fliplr)
这样的 NumPy 函数,随心所欲地操纵图像,就像操纵 NumPy 数组一样。
照片由 Hanna 从 Pexels 拍摄
保罗·巴塞尔从 Pexels 拍摄的照片
在[color](https://scikit-image.org/docs/dev/api/skimage.color.html)
模块中,您可以找到许多其他变换函数来处理图像中的颜色。
4.颜色通道直方图
有时,查看每个颜色通道的强度有助于了解颜色分布。我们可以通过分割每个颜色通道并绘制它们的直方图来实现。以下是执行此操作的函数:
除了几个 Matplotlib 细节,你应该注意一下hist
函数的调用。一旦我们提取了颜色通道及其数组,我们将它展平成一个 1D 数组,并将其传递给hist
函数。面元应该是 256,每个像素值一个面元- 0 是全黑,255 是全白。
让我们为我们的彩色风景图像使用函数:
照片由来自 Pexels 的 Pixabay 拍摄
>>> plot_with_hist_channel(colorful_scenery, "green")
照片由像素的 Pixabay 拍摄
>>> plot_with_hist_channel(colorful_scenery, "blue")
照片由 Pixabay 从像素拍摄
您也可以使用直方图找出图像转换为灰度后的亮度:
大多数像素值较低,因为风景图像有点暗。
我们将在接下来的章节中探索直方图的更多应用。
过滤
1.手动阈值
现在,我们开始有趣的事情——过滤图像。我们将学习的第一个操作是阈值处理。让我们加载一个示例图像:
stag = imread("images/binary_example.jpg")
>>> show(stag)
来自像素的 cmonphotography 拍摄的照片
阈值处理在图像分割、对象检测、寻找边缘或轮廓等方面有许多应用。它主要用于区分图像的背景和前景。
阈值处理最适用于高对比度灰度图像,因此我们将转换 stag 图像:
# Convert to graysacle
stag_gray = rgb2gray(stag)
>>> show(stag_gray)
来自像素的 cmonphotography 摄影
我们将从基本的手动阈值处理开始,然后转向自动阈值处理。
首先,我们看看灰度图像中所有像素的平均值:
>>> stag_gray.mean()0.20056262759859955
请注意,上面的灰色图像的像素通过将其所有值除以 256,在 0 和 1 之间进行归一化。
我们得到一个平均值 0.2,这给了我们一个可能想要使用的阈值的初步想法。
现在,我们使用这个阈值来屏蔽图像阵列。如果像素值低于阈值,则它的值变为 0 —黑色,否则变为 1 —白色。换句话说,我们得到一张黑白二进制图片:
来自像素的 cmonphotography 照片
在这个版本中,我们可以更清楚地区分牡鹿的轮廓。我们可以反转蒙版,使背景变成白色:
由 cmonphotography 从像素拍摄的照片
2.阈值处理—全局
虽然尝试不同的阈值并观察它们对图像的影响可能很有趣,但我们通常使用比我们的眼球估计更鲁棒的算法来执行阈值处理。
有许多阈值算法,所以可能很难选择一个。在这种情况下,skimage
有try_all_threshold
函数,它对给定的灰度图像运行七种阈值算法。让我们加载一个示例并进行转换:
照片由丽莎·福蒂斯从派克斯拍摄
我们将看看是否可以通过使用阈值来改进郁金香的特征:
正如你所看到的,在这张图片上,一些算法工作得更好,而另一些则很糟糕。otsu
算法看起来更好,所以我们将继续使用它。
现在,我想把你们的注意力拉回到最初的郁金香图片上:
>>> show(flower)
照片由 Pexels 的丽莎·福蒂斯拍摄
这幅图像的背景不均匀,因为太多的光线来自后面的窗户。我们可以通过绘制灰色郁金香的直方图来证实这一点:
>>> plt.hist(flower_gray.ravel(), bins=256);
正如预期的那样,大多数像素的值都在直方图的远端,这表明它们大部分都很亮。
为什么这很重要?根据图像的亮度,阈值算法的性能也会改变。为此,阈值算法通常有两种类型:
- 全局—适用于背景均匀一致的照片
- 局部—适用于不同图片区域中具有不同亮度级别的图像。
郁金香图像属于第二类,因为右边部分比另一半亮得多,使其背景不均匀。我们不能对其使用全局阈值算法,这也是为什么[try_all_threshold](https://scikit-image.org/docs/dev/api/skimage.filters.html#skimage.filters.try_all_threshold)
中所有算法的性能如此之差的原因。
我们一会儿将回到郁金香的例子和局部阈值。现在,我们将加载另一个具有更精细亮度的实例,并尝试自动设置阈值:
照片由 Pixabay 从像素拍摄
我们将使用一个通用的全局阈值算法[threshold_otsu](https://scikit-image.org/docs/stable/api/skimage.filters.html#skimage.filters.threshold_otsu)
:
照片由 Pixabay 从像素拍摄
效果好多了!
3.阈值处理—局部
现在,我们将使用局部阈值算法。
局部算法不是查看整个图像,而是关注像素邻域,以解决不同区域中亮度不均匀的问题。skimage
中常见的局部算法为[threshold_local](https://scikit-image.org/docs/stable/api/skimage.filters.html#skimage.filters.threshold_local)
函数:
来自 Pexels 的丽莎·福蒂斯的照片
你必须玩弄offset
的论点来找到你需要的最佳图像。offset
是从局部像素邻域的平均值中减去的常数。这个“像素邻域”由local_threshold
中的block_size
参数决定,该参数表示算法在每个方向的每个点周围寻找的像素数量。
显然,同时调整offset
和block_size
是不利的,但是局部阈值处理是产生比手动或全局阈值处理更好结果的唯一选择。
让我们再举一个例子:
照片由龟背竹从派克斯拍摄
如你所见,在阈值化之后,板上的笔迹更加精细。
4.边缘检测
边缘检测在许多方面都是有用的,例如识别物体、从物体中提取特征、计数等等。我们将从基本的 Sobel 滤波器开始,它在灰度图像中找到对象的边缘。我们将加载硬币的图片,并对其使用 Sobel 滤镜:
照片由来自 Pexels 的德米特里·德米多夫拍摄
索贝尔是非常简单的;你只需要在灰色图像上调用它,就可以得到上面这样的输出。我们将在后面的章节中看到 Sobel 的更复杂的版本。
5.缓和
另一种图像过滤技术是平滑。许多图像,如下面的鸡,可能包含随机噪声,对 ML 和 DL 算法没有任何有价值的信息。
例如,鸡周围的毛发会给图像添加噪声,这可能会使 ML 模型的注意力偏离主要对象本身。在这种情况下,我们使用平滑来模糊噪声或边缘,并降低对比度。
chickens = imread("images/chickens.jpg")
>>> show(chickens)
照片由维克托·伯恩赛德从 Pexels 拍摄
最流行和最强大的平滑技术之一是[gaussian](https://scikit-image.org/docs/stable/api/skimage.filters.html#skimage.filters.gaussian)
平滑:
照片由维克托·伯恩赛德从派克斯拍摄
您可以通过调整sigma
参数来控制模糊效果。如果你正在处理一个 RGB 图像,不要忘记设置multichannel
为真。
如果图像分辨率太高,平滑效果可能肉眼看不到,但在遮光罩下仍然有效。
你可以在这里阅读帖子的下一部分。
https://ibexorigin.medium.com/membership
或者订阅我的邮件列表:
https://ibexorigin.medium.com/subscribe
你可以在LinkedIn或Twitter上联系我,友好地聊一聊所有的事情数据。或者你可以读我的另一个故事。这些怎么样:
</10-minute-guide-to-julia-for-die-hard-python-lovers-a2fcf7dcb860> </6-pandas-mistakes-that-silently-tell-you-are-a-rookie-b566a252e60d> </8-booming-data-science-libraries-you-must-watch-out-in-2022-cec2dbb42437> **
Python 中图像处理和深度学习准备的大量教程#2
原文:https://towardsdatascience.com/massive-tutorial-on-image-processing-and-preparation-for-deep-learning-in-python-2-14816263b4a5
随意操纵和变换图像
照片由 Ihsan Adityawarman 在 Pexels 上拍摄
这是我关于图像处理的第一篇文章的第二部分。请阅读第一篇了解背景和设置。
我们将从对比度增强开始第二部分。
6.对比度增强
某些类型的图像(如医疗分析结果)对比度较低,难以发现细节,如下所示:
图片由 Pixabay 提供
在这种情况下,我们可以使用对比度增强来使细节更加清晰。有两种类型的对比度增强算法:
- 对比度扩展
- 直方图均衡
我们将在本帖中讨论直方图均衡化,它依次有三种类型:
- 标准直方图均衡化
- 自适应直方图均衡
- 对比度受限的自适应直方图均衡(CLAHE)
直方图均衡将图像对比度最高的区域扩展到亮度较低的区域,对其进行均衡。
哦,对了,你可以通过从最高像素值中减去最低像素值来计算图像的对比度。
>>> xray.max() - xray.min()255
现在,让我们试试来自exposure
模块的标准直方图均衡化:
图片由 Pixabay 提供
我们已经可以更清楚地看到细节了。
接下来,我们将使用 CLAHE (这是一个有趣的发音单词!),它为图像中不同的像素邻域计算许多直方图,即使在最暗的区域也能获得更多细节:
图片由 Pixabay 提供
这张看起来好很多,因为它可以在背景中显示细节,在左下角显示更多丢失的肋骨。您可以调整clip_limit
来获得更多或更少的细节。
https://ibexorigin.medium.com/membership
获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:
https://alphasignal.ai/?referrer=Bex
7.转换
数据集中的影像可能有几个相互冲突的特征,如不同的比例、未对齐的旋转等。ML 和 DL 算法希望您的图片具有相同的形状和尺寸。因此,您需要学习如何修复它们。
旋转
要旋转图像,使用transform
模块中的rotate
功能。我选择了真正的时钟,所以你可能会更好地记住角度符号:
照片由来自 Pexels 的 RP Singh 拍摄
照片由 RP Singh 从 Pexels 拍摄
重新缩放
另一个标准操作是缩放图像,它主要在图像比例不同的情况下有用。
我们使用一个类似的[rescale](https://scikit-image.org/docs/stable/api/skimage.transform.html#skimage.transform.rescale)
函数来完成这个操作:
照片由 Pixabay 从像素拍摄
当图像分辨率较高时,过度缩小可能会导致质量下降,或者像素随意地摩擦在一起,从而产生意外的边缘或拐角。为了考虑这种影响,您可以将anti_aliasing
设置为 True,这将使用高斯平滑:
https://gist.github.com/f7ae272b6eb1bce408189d8de2b71656
照片由像素的 Pixabay 拍摄
和以前一样,平滑并不明显,但在更精细的层次上它会很明显。
调整大小
如果您希望图像具有特定的宽度和高度,而不是按某个因子缩放,您可以通过提供一个output_shape
来使用[resize](https://scikit-image.org/docs/stable/api/skimage.transform.html#skimage.transform.resize)
函数:
照片由切瓦农摄影从 Pexels 拍摄
图像恢复和增强
在文件转换、错误下载或许多其他情况下,一些图像可能会扭曲、损坏或丢失。与其放弃这个想法,你可以使用skimage
来说明损坏的原因,并使图像完好如初。
在这一节中,我们将讨论一些图像恢复技术,从修复开始。
1.修补
修复算法可以智能地填充图像中的空白。我找不到损坏的图片,所以我们将使用这张鲸鱼图片,并手动在上面放置一些空白:
whale_image = imread("images/00206a224e68de.jpg")>>> show(whale_image)
>>> whale_image.shape(428, 1916, 3)
下面的函数创建了四个漆黑的区域来模拟图像上丢失的信息:
我们将使用来自inpaint
模块的[inpaint_biharmonic](https://scikit-image.org/docs/stable/api/skimage.restoration.html#skimage.restoration.inpaint_biharmonic)
函数来填充空白,传入我们创建的mask
:
正如你所看到的,在看到有缺陷的图像之前,很难判断缺陷区域在哪里。
现在,让我们制造一些噪音📣!
2.噪音📣
如前所述,噪声在图像增强和恢复中起着重要作用。有时,您可能会有意将它添加到如下所示的图像中:
照片由阮俊从派克斯拍摄
我们使用[random_noise](https://scikit-image.org/docs/dev/api/skimage.util.html#skimage.util.random_noise)
函数给一幅图像洒上随机的颜色斑点。因此,这种方法被称为“盐和胡椒”技术。
3.减少噪音—去噪
但是,大多数情况下,您希望从图像中去除噪声,而不是添加噪声。有几种去噪算法:
- 全变差(TV)滤波器
- 双边去噪
- 小波去噪
- 非局部均值去噪
在本文中,我们只看前两个。先来试试电视滤镜,有[denoise_tv_chambolle](https://scikit-image.org/docs/stable/api/skimage.restoration.html#denoise-tv-chambolle)
的:
照片由 Tuan Nguyen 从 Pexels 拍摄
图像的分辨率越高,去噪的时间就越长。您可以使用weight
参数控制去噪效果。现在,让我们试试[denoise_bilateral](https://scikit-image.org/docs/stable/api/skimage.restoration.html#skimage.restoration.denoise_bilateral)
:
照片由 Tuan Nguyen 从 Pexels 拍摄
它不如电视滤镜有效,如下图所示:
照片由 Tuan Nguyen 从 Pexels 拍摄
4.超像素和分段
图像分割是图像处理中最基本也是最常见的课题之一,广泛应用于运动和目标检测、图像分类等领域。
我们已经看到了一个分割的例子——对图像进行阈值处理,从前景中提取背景。本节将学习做更多的事情,比如将图像分割成相似的区域。
要开始分段,我们需要了解超像素的概念。
一个像素,就其本身而言,只是代表一个很小的颜色区域,一旦从图像中分离出来,单个像素就没用了。因此,分割算法使用多组对比度、颜色或亮度相似的像素,它们被称为超像素。
一种试图寻找超像素的算法是简单线性迭代聚类(SLIC ),它使用 k 均值聚类。让我们看看如何在skimage
库中可用的咖啡图像上使用它:
from skimage import datacoffee = data.coffee()>>> show(coffee)
我们将使用segmentation
模块中的[slic](https://scikit-image.org/docs/dev/api/skimage.segmentation.html?highlight=slic#skimage.segmentation.slic)
函数:
from skimage.segmentation import slicsegments = slic(coffee)>>> show(segments)
slic
默认查找 100 个段或标签。为了把它们放回图像上,我们使用了[label2rgb](https://scikit-image.org/docs/dev/api/skimage.color.html#skimage.color.label2rgb)
函数:
from skimage.color import label2rgbfinal_image = label2rgb(segments, coffee, kind="avg")>>> show(final_image)
让我们将这个操作封装在一个函数中,并尝试使用更多的段:
分割将使计算机视觉算法更容易从图像中提取有用的特征。
5.轮廓
物体的大部分信息存在于它的形状中。如果我们能检测出一个物体的线条或轮廓,我们就能提取出有价值的数据,如它的大小、标记等。
让我们来看看在实践中使用多米诺骨牌的形象寻找轮廓。
dominoes = imread("images/dominoes.jpg")>>> show(dominoes)
照片由 Pixabay 从像素拍摄
我们将看看是否可以使用skimage
中的[find_contours](https://scikit-image.org/docs/stable/api/skimage.measure.html#skimage.measure.find_contours)
函数隔离瓷砖和圆圈。这个函数需要一个二值(黑白)图像,所以我们必须先对图像进行阈值处理。
产生的数组是表示等高线坐标的(n,2)数组列表:
我们将把操作包装在一个名为mark_contours
的函数中:
为了在图像上绘制等高线,我们将创建另一个名为plot_image_contours
的函数,它使用了上面的函数:
照片由像素的像素拍摄
正如我们所看到的,我们成功地检测到了大多数轮廓,但我们仍然可以看到中心的一些随机波动。在将多米诺骨牌的图像传递给我们的轮廓寻找函数之前,让我们应用去噪:
照片由 Pixabay 从像素拍摄
就是这样!我们消除了大部分的噪声,导致不正确的轮廓线!
高级操作
1.边缘检测
之前,我们使用 Sobel 算法来检测对象的边缘。在这里,我们将使用 Canny 算法,因为它更快、更准确,所以应用更广泛。像往常一样,函数[canny](https://scikit-image.org/docs/dev/api/skimage.feature.html#skimage.feature.canny)
需要一个灰度图像。
这一次,我们将使用包含更多硬币的图像,从而检测更多边缘:
照片由来自 Pexels 的德米特里·德米多夫拍摄
为了找到边缘,我们只需将图像传递给canny
函数:
照片由来自 Pexels 的德米特里·德米多夫拍摄
该算法发现了几乎所有硬币的边缘,但它非常嘈杂,因为硬币上的雕刻也被检测到。我们可以通过调整sigma
参数来降低canny
的灵敏度:
照片由来自 Pexels 的德米特里·德米多夫拍摄
可以看到,canny
现在只找到了硬币的大致轮廓。
2.角点检测
另一种重要的图像处理技术是角点检测。在图像分类中,角点可能是对象的关键特征。
为了找到角点,我们将使用哈里斯角点检测算法。让我们加载一个样本图像并将其转换为灰度:
照片由 Pixabay 从像素拍摄
我们将使用[corner_harris](https://scikit-image.org/docs/dev/api/skimage.feature.html#skimage.feature.corner_harris)
功能来生成一个测量图像,该图像遮盖了角所在的区域。
from skimage.feature import corner_harrismeasured_image = corner_harris(windows_gray)>>> show(measured_image)
现在,我们将把这个屏蔽的测量图像传递给corner_peaks
函数,这次它返回角坐标:
该函数找到了 79 个角,每个角之间的最小距离为 50 个像素。让我们将到目前为止的操作封装在一个函数中:
现在,我们将创建另一个函数,使用上述函数生成的坐标绘制每个角:
照片由像素的皮克斯拜拍摄
不幸的是,算法并不像预期的那样工作。这些标记被放置在砖块的相交处,而不是寻找窗户的角落。这些路口都是噪音,让他们没用。让我们对图像进行降噪,并再次将其传递给函数:
现在,这样好多了!它忽略了砖块的边缘,找到了大部分的窗户角落。
结论
唷!多好的帖子!你和我都值得表扬!
写这两篇文章的时候,我很开心。当然,在真正的计算机视觉问题中,你不会同时使用所有这些。你可能已经注意到了,我们今天学到的东西并不复杂,最多只需要几行代码。棘手的部分是将它们应用到实际问题中,并实际提高模型的性能。
这一点来自艰苦的工作和实践,而不是很好地包装在一篇文章中。感谢您的阅读!
在这里阅读第一部分。
您可以使用下面的链接成为高级媒体会员,并访问我的所有故事和数以千计的其他故事:
https://ibexorigin.medium.com/membership
或者订阅我的邮件列表:
https://ibexorigin.medium.com/subscribe
你可以在LinkedIn或者Twitter上联系我,友好的聊一聊万物数据。或者你可以读我的另一个故事。这些怎么样:
</10-minute-guide-to-julia-for-die-hard-python-lovers-a2fcf7dcb860> </6-pandas-mistakes-that-silently-tell-you-are-a-rookie-b566a252e60d> </8-booming-data-science-libraries-you-must-watch-out-in-2022-cec2dbb42437>
使用 Csvkit 包在终端中控制 CSV 文件
原文:https://towardsdatascience.com/master-csv-files-in-the-terminal-with-the-csvkit-package-70984d394501
使用 Excel 并不总是一个好主意。
照片由西格蒙德在 Unsplash 拍摄
您的旅程概述
- 为什么要在终端处理 CSV 文件?
- 快速设置
- Excel 黑仔(in2csv)
- 数据 Scapel (csvcut)
- 好看(csvlook)
- 牛逼统计(csvstat)
- 巧妙搜索(csvgrep)
- 包装
1 —为什么要在终端中处理 CSV 文件?
很少有事情能像在命令行终端中工作一样令人生畏。许多数据科学家没有计算机科学背景,因此不习惯这样。甚至看起来在命令行终端工作已经是过去的事情了😧
我在这里告诉你,这是假的。事实上,随着像 Azure Cloud Shell 这样的基于云的 Shell 的增加,学习命令行比以往任何时候都更有价值。事实是命令行是一个效率助推器。对文件夹使用图形用户界面(GUI)对于简单的事情来说是最容易的,但是对于较难的事情来说就变得越来越复杂了。
对于数据科学家/数据工程师来说,有许多终端工具可以快速处理终端中的数据。在这篇博文中,我将告诉你一个我最喜欢的叫做 csvkit 的东西。这是一个在终端中处理 CSV 文件的工具。如果您正在处理 CSV 文件或 Excel 文件形式的月度报告,这将非常有用。使用 csvkit,您可以轻松地:
- 将 excel 文件转换为 CSV 文件
- 打印格式良好的 CSV 文件
- 剪切特定的列
- 获取有关列的统计信息
- 使用正则表达式在列内搜索
还有更多!您也许可以用 Excel 做同样的事情。然而,使用 csvkit 做事情的真正价值在于您可以自动化这个过程(例如使用 cronjobs)。
我将向您介绍如何使用 csvkit,以便您可以在终端中处理 CSV 文件。具体来说,我会教你我认为最重要的五个 csvkit 命令。如果你更喜欢视觉学习,那么我也在 csvkit 上制作了一个免费的 youtube 视频系列你可以看看😃
**先决条件:**您应该已经安装了运行 BASH 的命令行终端。如果你使用的是 Linux,那么这应该不成问题,而 Windows 用户可能想看看 cmder 或者用 WSL2 启用 Linux 子系统。
2—快速设置
要开始使用 csvkit 包,您需要安装它。csvkit 包是一个 Python 包,所以您需要做的就是运行(假设您已经安装了 Pyhon 的包安装程序 pip ):
pip install csvkit
如果不想全局安装 csvkit 包,那么可以考虑使用 virtualenv 来设置一个虚拟环境。要检查安装是否正常,您可以运行以下命令:
in2csv --help
如果一切安装正确,您现在应该会看到命令in2csv
的帮助页面。命令in2csv
是 csvkit 为我们安装的几个命令之一。
让我们打开一个名为csvkit_sandbox
的新文件夹,你可以在这里玩你的新玩具:
mkdir csvkit_sandbox
cd csvkit_sandbox
最后,您需要一些实际使用的数据:让我们下载 csvkit 文档也在使用的数据:
curl -L -O https://raw.githubusercontent.com/wireservice/csvkit/master/examples/realdata/ne_1033_data.xlsx
运行上面的命令后,您应该在您的文件夹中安装了文件ne_1033_data.xlsx
。如果你不信任我,你可以运行ls
来检查你的csvkit_sandbox
文件夹的内容😉
Excel 黑仔(in2csv)
我们要看的第一个命令是in2csv
命令。这个命令将一个 excel 文件转换成一个 CSV 文件(废话!).这非常有用,因为数据科学家经常获得 excel 格式的报告,并且必须将它们转换为 CSV 文件才能有效地使用它们。
要使用in2csv
命令,您只需编写:
in2csv ne_1033_data.xlsx
如果您像这样使用该命令,那么您只需将所有输出转储到终端。即使对于中等大小的文件,这也很糟糕。更好的选择是将输出重定向到一个文件,如下所示:
in2csv ne_1033_data.xlsx > new_file.csv
现在,在您的csvkit_sandbox
文件夹中应该有一个名为new_file.csv
的新 CSV 文件。我有时使用的一个技巧是在同一个命令中添加一个rm
命令来删除原始文件,如下所示:
in2csv ne_1033_data.xlsx > new_file.csv && rm ne_1033_data
如果您运行上面的命令,那么在您的csvkit_sandbox
文件夹中应该只有文件new_file.csv
。如果您想签出文件new_file.csv
,那么尝试命令:
head new_file.csv
该命令将文件new_file.csv
中的第一行输出到终端。
照片由 Mohamed Nohassi 在 Unsplash 拍摄
4—数据手术刀(csvcut)
在上一节中,您从 excel 文件中检索了所有列,并将其转换为 CSV 文件。但是通常您不需要所有的列来进行进一步的分析。如果是这样的话,那么和我们一起拖无用的列将是一件麻烦事。不要害怕!使用命令csvcut
可以选择 CSV 文件的某些列:
csvcut -c county,total_cost,ship_date new_file.csv**Output:**
county,total_cost,ship_date
ADAMS,138.0,2008-07-11
ADAMS,138.0,2008-07-11
ADAMS,138.0,2008-07-11
ADAMS,138.0,2008-07-11
ADAMS,138.0,2008-07-11
ADAMS,138.0,2008-07-11
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BURT,499.0,2013-06-20
...
上述命令仅从 CSV 文件new_file.csv
中选择列county
、total_cost
和ship_date
。
但是如果您不记得列名了呢?然后,一个快速的技巧是运行命令:
csvcut -n new_file.csv**Output:** 1: state
2: county
3: fips
4: nsn
5: item_name
6: quantity
7: ui
8: acquisition_cost
9: total_cost
10: ship_date
11: federal_supply_category
12: federal_supply_category_name
13: federal_supply_class
14: federal_supply_class_name
可选参数-n
给出了列的名称。整洁,对不对?命令csvcut
对于选择想要继续的列非常有用。
5-看起来不错(csvlook)
梅尔·埃利亚斯在 Unsplash 上拍摄的照片
当您将最后一部分中的 CSV 文件输出到终端时,它看起来不是很好。将原始 CSV 文件打印到终端时,这样读起来通常会很糟糕:
csvcut -c county,total_cost,ship_date new_file.csv**Output:**
county,total_cost,ship_date
ADAMS,138.0,2008-07-11
ADAMS,138.0,2008-07-11
ADAMS,138.0,2008-07-11
ADAMS,138.0,2008-07-11
ADAMS,138.0,2008-07-11
ADAMS,138.0,2008-07-11
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BUFFALO,499.0,2008-09-24
BURT,499.0,2013-06-20
...
命令csvlook
前来救援。您可以使用管道操作符 |
将命令csvcut
的输出作为命令csvlook
的输入,如下所示:
csvcut -c county,total_cost,ship_date new_file.csv | csvlook**Output:** | county | total_cost | ship_date |
| ---------- | ---------- | ---------- |
| ADAMS | 138.00 | 2008-07-11 |
| ADAMS | 138.00 | 2008-07-11 |
| ADAMS | 138.00 | 2008-07-11 |
| ADAMS | 138.00 | 2008-07-11 |
| ADAMS | 138.00 | 2008-07-11 |
| ADAMS | 138.00 | 2008-07-11 |
| BUFFALO | 499.00 | 2008-09-24 |
| BUFFALO | 499.00 | 2008-09-24 |
| BUFFALO | 499.00 | 2008-09-24 |
| BUFFALO | 499.00 | 2008-09-24 |
| BUFFALO | 499.00 | 2008-09-24 |
| BUFFALO | 499.00 | 2008-09-24 |
| BUFFALO | 499.00 | 2008-09-24 |
| BUFFALO | 499.00 | 2008-09-24 |
| BURT | 499.00 | 2013-06-20 |
...
那看起来好多了!现在,用肉眼解析输出要容易得多。
还记得你用管道操作符|
将csvcut
的输出传递给csvlook
的输入的技巧吗?这在 csvkit 中非常常见,并且是创建更复杂管道的关键🔥
6 —令人敬畏的统计数据(csvstat)
当作为数据分析师或数据科学家处理 CSV 文件时,我们经常希望提取汇总统计数据。有数百种方法可以提取 CSV 文件中各列的中值和平均值。你应该用哪一个?将数据导入 Excel?用熊猫进行快速数据分析?
我想你知道我想说什么。当然,csvkit 对此有一个命令叫做csvstat
。命令csvstat
从 CSV 文件中的每一列提取基本统计数据:
csvstat new_file.csv**Output:**1\. "state"
Type of data: Text
Contains null values: False
Unique values: 1
Longest value: 2 characters
Most common values: NE (1036x)2\. "county"
Type of data: Text
Contains null values: False
Unique values: 35
Longest value: 10 characters
Most common values: DOUGLAS (760x)
DAKOTA (42x)
CASS (37x)
HALL (23x)
LANCASTER (18x)3\. "fips"
Type of data: Number
Contains null values: False
Unique values: 35
Smallest value: 31001
Largest value: 31185
Sum: 32176888
Mean: 31058.772
Median: 31055
StDev: 23.881
Most common values: 31055 (760x)
31043 (42x)
31025 (37x)
31079 (23x)
31109 (18x)
...
如果您仔细阅读上面的输出,那么您会发现csvstat
根据列类型给出不同的统计信息。如果你的某一列是文本格式,那么谈论那一列的标准差就没什么意义了。
命令csvstat
执行类型的推理。它尝试推断列的类型,然后找到合适的汇总统计信息。根据我的经验,这非常有效。
有时,您不想要所有列的统计数据,而只想要几列。为此,使用命令csvcut
过滤出必要的列,然后通过管道将其传递给csvstat
以获得统计数据:
csvcut -c total_cost,ship_date new_file.csv | csvstat**Output:**1\. "total_cost"
Type of data: Number
Contains null values: False
Unique values: 92
Smallest value: 0
Largest value: 412000
Sum: 5553135.17
Mean: 5360.169
Median: 6000
StDev: 13352.139
Most common values: 6800 (304x)
10747 (195x)
6000 (105x)
499 (98x)
0 (81x)2\. "ship_date"
Type of data: Date
Contains null values: False
Unique values: 84
Smallest value: 2006-03-07
Largest value: 2014-01-30
Most common values: 2013-04-25 (495x)
2013-04-26 (160x)
2008-05-20 (28x)
2012-04-16 (26x)
2006-11-17 (20x)Row count: 1036
7 —智能搜索(csvgrep)
我极力推荐的最后一个命令是csvgrep
命令。命令csvgrep
允许您搜索 CSV 文件以找到特定的记录。如果你习惯了终端命令grep
,那么csvgrep
命令也差不多。
要查看它的运行情况,让我们在 CSV 文件中搜索 county HOLT :
csvgrep -c county -m "HOLT" new_file.csv**Output:** NE,HOLT,31089.0,1005-00-073-9421,"RIFLE,5.56 MILLIMETER",1.0,Each,499.0,499.0,2008-05-19,10.0,WEAPONS,1005.0,"Guns, through 30 mm"NE,HOLT,31089.0,1005-00-589-1271,"RIFLE,7.62 MILLIMETER",1.0,Each,138.0,138.0,2006-03-08,10.0,WEAPONS,1005.0,"Guns, through 30 mm"NE,HOLT,31089.0,1005-00-589-1271,"RIFLE,7.62 MILLIMETER",1.0,Each,138.0,138.0,2006-03-08,10.0,WEAPONS,1005.0,"Guns, through 30 mm"
...
成功了!现在你只能得到县霍尔特的记录。
- 参数
-c
用于选择列。 - 参数
-m
用于查找与匹配的字符串*。*
Pro 提示:
csvgrep
命令也允许使用可选参数-r
进行正则表达式搜索。查看 csvgrep 文档了解更多相关信息!
我给你的最后一个建议是,试着组合几个 csvkit 命令来获得很酷的结果。看看这个。
csvcut -c county,total_cost new_file.csv | csvgrep -c county -m "HOLT" | csvlook**Output:**
| county | total_cost |
| ------ | ---------- |
| HOLT | 499.00 |
| HOLT | 138.00 |
| HOLT | 138.00 |
| HOLT | 138.00 |
| HOLT | 138.00 |
| HOLT | 138.00 |
| HOLT | 138.00 |
| HOLT | 58.71 |
首先,我只选择了带有csvcut
的两列county
和total_cost
。然后我用命令csvgrep
搜索县 HOLT 。最后,我用命令csvlook
很好地显示了结果。现在轮到你和✌️搭档了
8 —总结
照片由斯潘塞·伯根在 Unsplash 上拍摄
希望你开始像我一样喜欢 csvkit。同样,如果你想学习一些更酷的技巧和诀窍,那么查看 csvkit 文档或 csvkit 上我的 youtube 视频。
**喜欢我写的?**查看我的其他帖子,了解更多 Python 内容:
- 作为数据科学家如何写出高质量的 Python
- 用漂亮的类型提示使你罪恶的 Python 代码现代化
- 用 Python 可视化缺失值非常简单
- 使用 PyOD 在 Python 中引入异常/离群点检测🔥
- 5 个专家建议让你的 Python 字典技能突飞猛进🚀
如果你对数据科学、编程或任何介于两者之间的东西感兴趣,那么请随意在 LinkedIn 上加我,并向✋问好
主数据完整性清理您的计算机视觉数据集
原文:https://towardsdatascience.com/master-data-integrity-to-clean-your-computer-vision-datasets-df432cf9e596
辅导的
处理数据泄漏。降低贴标成本。减少计算时间和费用。
JESHOOTS.COM在 Unsplash 上拍照
数据完整性是最近一段时间公司和工程师最关心的问题之一。
我们必须处理和理解的数据量只会变得更加重要,手动查看数百万个样本是不可持续的。因此,我们需要能够帮助我们浏览数据集的工具。
本教程将介绍如何清理、可视化和理解计算机视觉数据集,如视频或图像。
我们将制作一部关于我家最珍贵的东西——我的猫的视频。我们的最终目标是从视频中提取关键帧,这些帧最近可以被发送到标签和训练你的模型。从视频中提取关键信息并不简单,因为视频属性不断变化。例如,在开始时,视频是高度静态的,从中间开始有很多动作。因此,我们需要一种智能的方法来理解视频的属性,以消除重复的图像,找到离群值,并对相似的照片进行聚类。
GIF 来自我的 sassy 猫的视频【作者 GIF】。
利用 FastDup,一个用于理解和清理 CV 数据集的工具,我们将向您展示如何解决上述问题。
最终目标
我们将提供一个教程,对我的野蛮女友的视频进行解码,并从中提取所有帧作为图像。我们将使用 FastDup 来可视化图片数据集上的不同统计数据。
主要目标是从数据集中移除相似/重复的图像。在下一节中,我们将详细说明为什么清除数据集的重复项至关重要。最终,我们将查看移除所有相似照片前后的异常值。
为什么删除重复图像很重要?
当数据变大时,手动解析和理解所有的样本是非常困难的。因此,您需要更多创新的方法来查询数据集以找到重复的图像。
当使用表格数据时,可以用 Pandas、SQL 等工具绕过这个问题。但是,当建模图像是一个挑战。因此,我们需要从不同的角度来处理这个问题。使用 FastDup,我们可以快速计算数据集上的一组嵌入,用它我们可以执行数据检索、聚类、离群点检测等。最终,我们可以轻松地查询和可视化我们的数据集,以更好地理解它。
我们将消除相似图像的重要性归结为三个主要观点:
#1 数据泄露
在处理图像时,尤其是处理视频时,许多示例都是相似的。一个稳健的策略是使用嵌入来检测相似的照片,并相应地删除重复的照片。保留副本容易导致数据泄露。比如在拆分数据时,很容易不小心在列车拆分中加入图像 A,在测试拆分中加入图像 B,其中 A 和 B 是相似的。在处理大型数据集时,经常会遇到这样的数据泄漏问题,不幸的是,这会导致错误的结果,并可能导致在生产中部署模型时性能不佳。
#2 降低贴标成本
大多数表现良好的 ML 模型使用有监督的方法来学习。因此,我们需要标签来训练它们。
在数据集中保留相似的影像会使数据集变大,但没有任何积极效果。在这种情况下,更多的数据不会增加任何价值,因为它不会增加任何新的信息。而是你只是花钱花力气给相似的图像贴标签。
#3 减少计算时间&成本
正如我们在第 3 点中讨论的。,数据需要添加额外的信息来帮助建模。因此,通过在您的数据集中保留相似的样本,您的训练时间将会更长且成本更高。最终,它将导致更少和更长的实验,由于缺乏反馈而导致更差的模型。
什么是 FastDup?
FastDup 是计算机视觉中的一个 Python 工具,具有许多奇妙的特性,可以洞察大量的图像/视频集合。
它们的一些主要特征:
- 发现异常
- 查找重复项
- 使聚集
- 图像/视频之间的交互
- 计算嵌入
它是无人监督的,可伸缩的,并且在你的 CPU 上运行很快。这太棒了,因为 GPU 很贵,是小型团队的最大障碍之一。
现在,因为我们看到了理解和清理数据集的重要性,所以让我们开始本教程,并使用 FastDup 来看看我们如何在我的 sassy 猫的视频中解决这些问题。
我们走吧👇🏻
从视频中提取图像
下面是一些反映我们资源位置的常量。我们有输入视频的路径、提取帧的目录,以及 FastDup 计算和缓存数据集信息(如嵌入、离群值和各种统计数据)的目录。FastDup 提取的信息只计算一次,可以随时访问。
我们将视频逐帧提取到给定的输出目录中。该视频的 FPS 为 30,长度为 47 秒。 skip_rate 是我们从视频中提取帧的速率。这种情况下, skip_rate = 1 。因此,我们逐帧提取视频。如果 skip_rate = 5,我们将提取第 0 帧、第 5 帧、第 10 帧等等。
frames_count = extract(input_video_path=INPUT_VIDEO_PATH, output_dir=VIDEO_EXTRACTION_1_SKIP_DIR)FPS = 30
Frame Count = 1409
Video Length = 47 seconds
Extract Frame Count = 1409
使用 FastDup 可视化数据
首先,让我们了解一些我们将在本文中继续使用的术语:
- **成分:**相似图像的聚类。一个组件总是有多个图像。
- **相似图像:**不完全相同但包含相同信息的图像。大多数图像不会是彼此完美的复制品。因此,必须有一种机制来查找密切相关的图像。
现在我们调用 FastDup 来分析我们的数据集,并在给定的目录中缓存所有的分析数据。
compute_fastdup_analytics(images_dir=VIDEO_EXTRACTION_1_SKIP_DIR, analytics_dir=FASTDUP_ANALYTICS_1_SKIP_DIR)
统计数字
使用 FastDup,您可以根据不同的统计数据有效地计算数据集的直方图,这些统计数据包括:平均值、标准差、最小值、最大值、大小等。
fastdup.create_stats_gallery(FASTDUP_ANALYTICS_1_SKIP_DIR, save_path="stats", metric="mean")
视频内图像平均值的直方图【图片由作者提供】。
fastdup.create_stats_gallery(FASTDUP_ANALYTICS_1_SKIP_DIR, save_path="stats", metric="stdv")
视频内图像标准差直方图【图片由作者提供】。
最有趣的指标是模糊,它可以快速突出模糊图像及其统计数据:
关于视频内模糊图像的统计[图片由作者提供]。
极端值
Fastdup 利用嵌入和 K-Means 将图像聚类到嵌入空间中。因此,我们可以快速找到相似图像的集群(用他们的行话来说就是“组件”)和离群值。
让我们来计算异常值:
离群值可视化帮助我们看到令人兴奋的场景。在我们的例子中,异常图像是模糊的。只要样本中的信息是有效的,使用有噪声的样本来提高模型的稳健性是一种好的做法。
视频中的前 5 个异常值,用 FastDup 快速计算[图片由作者提供]。
复制
最后,让我们计算并找出我们从视频中提取了多少相似的图像:
FastDup 基于组件的大小创建直方图,其中组件是相似图像的集群。
视频中的前 3 个组件逐帧提取。最重要的部分是包含最相似图像的部分。
正如所料,如果我们导出视频中的所有帧,我们最终会得到许多重复的帧。我们可以清楚地看到包含最多副本的前 3 个组件。此外,我们可以看到组件大小的分布。6 个分量有超过 40 个图像,这表明在大约 6 个场景中,视频非常静态。
此外,通过计算 num_images / FPS ,我们可以估计静态场景的秒数。
我们如何删除所有重复的内容?
一种简单的方法是增加 skip_rate 来消除相似相邻帧之间的冗余。让我们看看表现如何。
提取具有较高跳过率的帧
正如我们之前所说的,让我们使用 skip_rate = 10 来看看我们是否可以解决相似性问题。该视频的 FPS 为 30,经过几次实验,我们发现 skip_rate = 10 是一个平衡的数字。
frames_count_10_skip = extract(input_video_path=INPUT_VIDEO_PATH, output_dir=VIDEO_EXTRACTION_10_SKIP_DIR, skip_rate=SKIP_RATE)FPS = 30
Frame Count = 1409
Video Length = 47 seconds
Extract Frame Count = 140
我们必须重新计算 FastDup 分析,以可视化新组件:
现在让我们来想象一下组件:
视频摘录中跳过率为 10 的前 3 个组件。最重要的部分是包含最相似图像的部分。
在尝试了多种跳过率之后,我们解决了大部分相似性问题。我们可以看到,在只有 9 个和 8 个样本的 2 个组件中发现了重复图像。我们用这种简单的方法做了一件体面的工作。
此外,一个巨大的好处是提取时间快了 10 倍。当有大量视频时,这是需要考虑的一个重要方面。
但遗憾的是,这种方法一点也不健壮。原因如下:
增加跳级率的危险
#1.信息的丢失
视频中的活动总是以不同的速率移动。我们可以找到 2-3 个视频的最佳 skip_rate ,在保留足够的必要样本和去除冗余数据之间找到最合适的平衡。尽管如此,因为现实世界是混乱的,这个数字可能会在新视频的某个时候打破。在不知情的情况下,你会跳过视频中的关键帧。
#2.你无法控制相似性问题
因为视频是动态的,固定的跳过率只适合你的一些视频。因此,为数据集中的每个视频寻找不同的跳过率是不可持续的。此外,在所有视频中使用一个较高的 skip_rate 值是行不通的。
即使对于我们的视频,这是非常简单的,我们只能删除一些重复的。
如上所述,我们看到,使用更高的跳过率不会做这项工作。因此,我们需要一种更加智能和动态的方法来找到相似的图像并删除重复的图像。
我们很幸运,因为 Fastdup 正在这么做。让我们看看怎么做。
使用 FastDup 排除重复项
使用 FastDup,我们可以使用更巧妙的方法。我们已经知道图像的聚类嵌入空间内的分量。因此,我们知道哪些图像在同一个聚类中,这转化为相似的照片。FastDup 为我们提供了仅保留一个映像/组件的功能。
这种方法好 10 倍,因为它独立于视频中的动态。相对于视频语义的不规则性,它是鲁棒的和灵活的。
我们必须再次提取所有帧并重新计算 FastDup 分析。
FPS = 30
Frame Count = 1409
Video Length = 47 seconds
Extract Frame Count = 1409
-------------Deleted frames count: 1202
Deleted 85.308730% of the frames.
哇哦。85%的图像代表冗余数据,这是一个很大的数字。想象一下,不用标注 1000 张图片,只需要标注 150 张。这节省了很多时间。
清理完目录映像后,让我们重新计算组件,看看它们是什么样子。
compute_fastdup_analytics(images_dir=VIDEO_EXTRACTION_1_SKIP_DIR, analytics_dir=FASTDUP_ANALYTICS_1_SKIP_DIR)
AssertionError: No components found with more than one image/video
那是什么?我们出错了吗?
是的,我们做到了,这是完美的。这意味着 FastDup 不再找到任何组件。因此,数据集不再包含相似的图像。
仅此而已。简单对吗?是不是很酷?
最终策略。将 Fastdup 与更高的提取率相结合。
我称赞说,使用嵌入来删除相似的图像是最好的方法。但不幸的是,逐帧导出视频需要很长时间。因此,最好的方法是将这两种策略结合起来。但这一次,我们将使用 2 的低跳过率,因为现在跳过的目标不是消除表里不一,而是加快提取时间。我们仍将使用 FastDup 来处理清理过程。
frames_count_2_skip = extract(input_video_path=INPUT_VIDEO_PATH, output_dir=VIDEO_EXTRACTION_2_SKIP_DIR, skip_rate=SKIP_RATE)FPS = 30
Frame Count = 1409
Video Length = 47 seconds
Extract Frame Count = 704compute_fastdup_analytics(images_dir=VIDEO_EXTRACTION_2_SKIP_DIR, analytics_dir=FASTDUP_ANALYTICS_2_SKIP_DIR)
视频摘录中跳过率为 2 的前 3 个组件。最重要的部分是包含最相似图像的部分。
如你所见,使用 skip_rate = 2 ,我们仍然有很多相似的图像。但是导出时间快了两倍。
delete_duplicates(FASTDUP_ANALYTICS_2_SKIP_DIR, total_frames_count=frames_count_2_skip)Deleted frames count: 346
Deleted 49.147727% of the frames.
很好。这次只有总帧数的 50%是重复的。
移除相似图像前后的异常值
观察清理前后异常值的 IoU 是很有趣的。理论上,离群值在删除重复项后基本保持不变,因为数据集的一般语义信息应该保持不变。不幸的是,异常值之前和之后之间的 IoU 只有大约 30%,这表明移除所有重复图像改变了几何空间,这可以更好。
0.31
通过向您展示离群值的例子,我想强调的是,尽管这种方法是创新的并且非常有用,但它仍然需要改进。我们刚刚开始以数据为中心的运动;因此,我们必须保持关注,看看它会给未来带来什么。
结论
谢谢你看我的教程。如果你读过它,你会发现控制你的数据集是多么容易。
我们展示了如何使用 FastDup 处理一个真实世界的视频(我的一只时髦的猫),以快速找到计算机视觉数据集中的相似图像和离群值。
我们强调了理解数据集并根据您的业务问题正确处理相似图像的重要性。否则,您可能会遇到数据泄漏、更高的标注成本以及更高的计算时间和成本。问题转化为错误的度量和更少的实验,最终导致更差的模型。
这里是 GitHub 资源库的链接。
💡我的目标是让机器学习变得简单直观。如果你喜欢我的文章,我们可以在 LinkedIn 上联系,了解关于#数据、 #ml 和 #mlops 的每日见解。
https://pauliusztin.medium.com/membership
数据网格中的主数据管理
原文:https://towardsdatascience.com/master-data-management-in-data-mesh-594d21f3ee10
如果它是稳定的并且确实重要,那么考虑使用 MDM
使用数据网格架构的企业通常有大量的域,每个域都有自己的系统和数据。这意味着增加了复杂性,因为数据分布很广,同一数据可能存在多个版本。集成,例如提供客户的 360 度视图,因此需要更多的努力,因为它需要您集成和协调来自不同领域的相同数据的所有不同的独立部分。另一个挑战是,不同领域之间的数据可能不一致,数据质量水平也可能存在差异。为了应对这些挑战,您应该在数据网格架构中应用主数据管理(MDM)。
面向领域的架构中的主数据管理工作方式不同,因为它是分布式的。一致性更难实现,因为您依赖于域内主数据的管理。
如果数据是快速和流动的,将它分成更小的片段,并留给域处理。如果数据是稳定的并且确实很重要,那么考虑使用 MDM。
面向领域的主数据管理
尽管许多 MDM 解决方案可以管理参考数据和主数据,但我建议明确区分这两者。参考数据是用于定义、分类、组织、分组或归类其他数据(或价值层次,如产品和地理层次之间的关系)的数据。相比之下,主数据讲的是核心概念。每种类型通常都有不同的管理方式。
参考数据
实现域间数据一致性的一种方法是,在分发数据产品时,要求您的域符合集中管理的参考数据。货币代码、国家代码或产品代码通常是可疑的。这些参考数据列表被发布在例如主数据存储或中央储存库中。当您的任何域跨其他域分发数据产品时,域使用企业参考数据中的企业参考标识符来对数据进行分类。本地数据到企业数据的映射允许其他域快速识别数据产品中的主数据。这种映射活动还意味着域需要查找或交换集中管理的参考数据,以了解哪些本地数据需要映射到中央主数据。
主数据
对于主数据,方法略有不同。主数据的一个重要方面是主标识号,它将主数据和域中的数据联系在一起。这些数据元素对于追踪哪些数据已经被掌握以及哪些属于一起是至关重要的。识别唯一数据和分配主标识符只能集中完成,而不能在系统内部本地完成。它需要将来自不同系统的主数据放在一个 MDM 解决方案中。
主数据管理领域(鸣谢:Piethein Strengholt)
当对 MDM 活动进行分组并使用主数据存储作为集中式存储库时,您还可以创建新的 MDM 域。这种 MDM 域指的是一个特定的数据主题,其中主要关注主数据的识别和控制。众所周知的例子有客户、产品、地理位置、财务和风险以及员工数据。来自这些 MDM 域的主数据有望返回到域中。这些数据的分发类似于数据产品的分发。
您可以确定主数据管理的范围,并允许不同的数据产品分发方法。例如,在某个范围边界内的一些数据产品不必符合企业主数据,而分布在该范围之外的数据产品必须符合。或者反过来应用这种模式:只有在特定的范围内才需要严格遵守。
主识别号
识别和维护这些关系不仅对于了解已经掌握了哪些数据非常重要,而且对于了解哪些数据可以快速链接到其他数据也非常重要。如果操作系统中的本地(域)键发生变化,将所有内容绑定在一起的唯一元素将是主标识符。因此,建议您也建立从 MDM 解决方案到当地管理部门的反馈回路。
在分发主标识符时,建议您不要将 MDM 主标识符外推至所有源系统,这可能会产生不一致的问题。只有受主数据管理区域约束的应用程序或系统才应该从 MDM hub 获取主标识符。不受 MDM 约束的系统应该使用自己的本地(域)完整性。
域级主数据管理
当您寻找重叠数据时,您可能会发现不同程度的重叠。有些数据是通用的,跨越许多领域;其他数据具有有限的重叠,并且仅跨越几个领域。为了区分重叠的重要性和数量,您还可以通过在特定范围内创建主数据的局部视图,将 MDM 扩展到域级 MDM。这对于在一些(但不是所有)域之间共享的数据非常有用。
域级主数据管理(鸣谢:Piethein Strengholt)
对于重叠的域来说,重要的是管理它们的数据,但是没有中心依赖性。这方面的 MDM 解决方案最好以服务的形式提供。将基础设施抽象出来,将 MDM 作为服务提供给域,可以极大地简化使用。如果您正在使用中央 MDM 基础设施,建议您对每个单独的域或范围应用隔离视图。
通过可重用组件实现 MDM 一致性
主数据协作和可重用性的另一种方式是代码共享。这里共享的不是主数据,而是底层代码(代码片段和脚本)来生成输出并促进有效的重用。这些代码存储在一个集中的开放的存储库中,包括版本控制,允许领域团队贡献和改进已经发布的内容。
这种模型的好处是,业务逻辑只在领域内应用,这允许团队在他们认为合适的时候偏离、改进或使用稍微优化的逻辑版本。此外,随着社区的改进进入中央代码库,这些输出可以重新生成。这种模型的一个缺点是一致性,因为允许团队修改他们的代码会使得在团队之间比较结果更加困难。
后续步骤
主数据管理的重要性显而易见:只有当用户使用的数据一致且正确时,他们才能做出正确的决策。MDM 确保跨域级别的一致性和质量。组织需要找到一个平衡点。引入过多的主数据或参考值区域会引入过多的跨域对齐。根本没有企业数据,无法比较任何结果。
开始在您的组织中实现 MDM 的一个实用方法是从最简单的主数据管理方式开始:实现存储库。有了存储库,您可以通过了解哪些数据需要调整或哪些数据质量差来快速交付价值,而无需调整任何域系统。
下一步将是设定更清晰的范围。不要因为选择所有数据而陷入企业数据统一的陷阱。从增加价值最多的主题开始,如客户、合同、组织单位或产品。只选择最重要的领域来掌握。属性的数量应该是十个,而不是几百个。在您与您的领域达成一致之后,调整流程和治理。让所有领域都清楚地了解您在时间表和评审方面的协议。还要处理元数据,因此主数据被编目,域知道来自什么源系统的什么数据元素是候选的,以及这些元素如何在数据管道中流动。
最后一步,也是最终目标,是实现共存:改进直接反馈到您的领域。这一步是最困难的,因为它需要对架构进行许多更改。域需要能够处理来自集中管理的 MDM 解决方案的修正和改进。
用这三种有用的技术掌握熊猫的数据转换
原文:https://towardsdatascience.com/master-data-transformation-in-pandas-with-these-three-useful-techniques-20699f03e51d
对过滤、操作和功能的深入研究
由 Milad Fakurian 在 Unsplash 上拍摄的照片
回想一下您最后一次使用格式良好的数据集的情况。命名良好的列、最少的缺失值和适当的组织。拥有不需要清理和转换的数据是一种很好的感觉——几乎是自由的。
好吧,这很好,直到你从你的白日梦中抽离出来,继续在你面前破碎的行和无意义的标签的绝望混乱中修补。
没有干净数据(原始形式)这种东西。如果你是一个数据科学家,你知道这一点。如果你刚刚开始,你应该接受这一点。为了有效地使用数据,您需要转换数据。
我们来谈谈三种方法。
过滤——但解释得当
让我们来谈谈过滤——但比你可能习惯做的更深入一点。作为最常见和最有用的数据转换操作之一,有效地过滤是任何数据科学家的必备技能。如果你了解熊猫,这可能是你学会做的第一个手术。
让我们回顾一下,用我最喜欢的,奇怪的通用例子:一个学生成绩的数据框架,恰当地称为grades
:
作者图片
我们将过滤掉任何低于 90 分的分数,因为在这一天,我们决定成为只迎合优等生的训练不足的教育者(请永远不要这样做)。完成此任务的标准代码行如下:
grades[grades['Score'] >= 90]
作者图片
只剩下杰克和赫敏了。酷毙了。但是这里到底发生了什么?为什么上面这行代码行得通?让我们更深入一点,看看上面外层括号内的表达式的输出:
grades['Score'] >= 90
作者图片
啊,好吧。有道理。这一行代码似乎返回了一个 Pandas Series 对象,该对象包含由每一行返回的内容确定的布尔值(True
/ False
)。这是关键的中间步骤。之后,就是这一系列的布尔值被传递到外层的括号中,并相应地过滤所有的行。
为了完整起见,我还将提到使用loc
关键字可以实现相同的行为:
grades.loc[grades['Score'] >= 90]
作者图片
我们选择使用loc
有很多原因(其中之一是它实际上允许我们通过一个操作来过滤行和列),但是这打开了 Pandas 操作的潘多拉魔盒,最好留给另一篇文章来处理。
目前,重要的学习目标是这样的:当我们过滤熊猫时,令人困惑的语法不是某种怪异的魔法。我们只需要把它分解成两个组成步骤:1)获得满足我们条件的行的布尔序列,2)使用该序列过滤出整个数据帧。
你可能会问,这为什么有用?嗯,一般来说,如果你只是使用操作而不了解它们实际上是如何工作的,这很可能会导致令人困惑的错误。过滤是一种非常有用且非常常见的操作,您现在知道它是如何工作的了。
我们继续吧。
λ函数的美妙之处
有时,您的数据需要转换,而这并不是 Pandas 的内置功能。尽管你可能会尝试,但是再多的搜索栈溢出或者勤奋地研究 Pandas 文档也不能揭示你的问题的解决方案。
输入 lambda 函数——这是一个有用的语言功能,与 Pandas 完美地集成在一起。
快速回顾一下,lambdas 是如何工作的:
>>> add_function = lambda x, y: x + y
>>> add_function(2, 3)
5
Lambda 函数与常规函数没有什么不同,只是它们的语法更简洁:
- 等号左边的函数名。
- 等号右边的
lambda
关键字(类似于传统 Python 函数定义中的def
关键字,这让 Python 知道我们正在定义一个函数)。 - 参数在
lambda
关键字之后,冒号的左边。 - 冒号右边的返回值。
现在,让我们将 lambda 函数应用于实际情况。
数据集通常有自己的格式特点,具体到数据输入和收集的变化。因此,您正在处理的数据可能有您需要解决的奇怪的特定问题。例如,考虑下面的简单数据集,它存储了人们的姓名和收入。姑且称之为monies
。
作者图片
现在,作为该公司的主数据殿下,我们得到了一些绝密信息:该公司的每个人都将获得 10%的加薪,外加 1000 美元。这种计算可能过于具体,无法找到具体的方法,但对于 lambda 函数来说就足够简单了:
update_income = lambda num: num + (num * .10) + 1000
然后,我们需要做的就是将这个函数与 Pandas apply
函数一起使用,这样我们就可以将一个函数应用于所选系列的每个元素:
monies['New Income'] = monies['Income'].apply(update_income)
monies
作者图片
我们完事了。这是一个全新的数据框架,包含了我们需要的所有信息,只有两行代码。为了使它更加简洁,我们甚至可以直接在apply
中定义 lambda 函数——这是一个值得记住的很酷的技巧。
我会简单地说明这一点。
Lambdas 非常有用,因此,你应该使用它们。尽情享受吧!
系列字符串操作函数
在上一节中,我们讨论了 lambda 函数的多功能性,以及它们可以帮助您使用数据完成的所有很酷的事情。这很好,但是你应该小心不要忘乎所以。如此沉迷于一种熟悉的做事方式,以至于错过了 Python 赋予程序员的更简单的捷径,这种情况非常普遍。当然,这不仅仅适用于 lambdas,但我们暂时会坚持这一点。
例如,假设我们有以下名为names
的数据帧,其中存储了人们的名字和姓氏:
作者图片
现在,由于数据库的空间限制,我们决定不存储一个人的整个姓氏,而只存储他们的姓首字母会更有效。因此,我们需要相应地转换'Last Name'
列。对于 lambdas,我们这样做的尝试可能如下所示:
names['Last Name'] = names['Last Name'].apply(lambda s: s[:1])
names
作者图片
这显然是可行的,但是有点笨拙,因此不像 Pythonic 那样好。幸运的是,有了 Pandas 中的字符串操作功能,还有另一种更优雅的方式(为了下一行代码的目的,假设我们还没有用上面的代码修改过'Last Name'
列):
names['Last Name'] = names['Last Name'].str[:1]
names
作者图片
哒哒!熊猫系列的.str
属性让我们通过指定的字符串操作拼接系列中的每个字符串,就像我们单独处理每个字符串一样。
但是等等,还有更好的。由于.str
有效地让我们通过系列访问字符串的正常功能,我们还可以应用一系列字符串函数来帮助快速处理我们的数据!例如,假设我们决定将两列都转换成小写。下面的代码完成了这项工作:
names['First Name'] = names['First Name'].str.lower()
names['Last Name'] = names['Last Name'].str.lower()
names
作者图片
这比定义自己的 lambda 函数并在其中调用字符串函数要简单得多。并不是说我不喜欢 lambdas——但是每样东西都有它的位置,在 Python 中简单应该总是优先考虑的。
这里我只介绍了几个例子,但是有大量的字符串函数供您使用。
自由地使用它们。他们很棒。
最终想法和总结
这里有一个小小的数据转换备忘单:
- 认真过滤。了解到底发生了什么,这样你才知道自己在做什么。
- 爱你的小羊羔。它们可以帮助你以惊人的方式操纵数据。
- 熊猫和你一样热爱弦乐。有许多内置的功能,你也可以使用它。
这里是最后一条建议:没有“正确”的方法来过滤数据集。这取决于手头的数据以及您希望解决的独特问题。然而,虽然每次都没有固定的方法可以遵循,但是有一些有用的工具值得您使用。在本文中,我讨论了其中的三种。
我鼓励你出去多找一些。
想擅长 Python? 在这里 获得独家、免费获取我简单易懂的指南。想在介质上无限阅读故事?用我下面的推荐链接注册!
https://murtaza5152-ali.medium.com/?source=entity_driven_subscription-607fa603b7ce---------------------------------------
参考
[1]https://www . about datablog . com/post/10-most-used-string-functions-in-pandas
掌握 Python 理解
原文:https://towardsdatascience.com/master-python-comprehensions-4ef1c66b452d
以简单性和可读性为目标编写代码
照片由 Unsplash 的 Ksenia Makagonova 拍摄。
Python 最显著的优势之一是它是一种高度表达的语言。没有太多的样板文件,开发人员可以想出优雅的解决方案,如果结构良好,读起来就像一个故事。
这就是语言之美,在 Python 的禅中得到了很好的展现。如果我必须强调其中的一句话,那就是“简单胜于复杂”。我们应该尽可能地坚持这样一条黄金法则,即代码被阅读的次数要比它被编写的次数多得多,无论是被作者、同事还是外部贡献者阅读。因此,直截了当,避免华丽的辞藻,帮助你的队友和未来享受你今天写的东西。
这篇文章将遵循这一思想,通过一系列例子介绍处理数据时最常见的任务之一:转换列表,重点关注简单性和可读性。
环
语句是 Python 的控制流工具之一,可以帮助我们迭代序列中的项目。在每一步中,我们都拥有原始数据中的一个元素。
第一个示例是将一系列数字中的偶数元素加 1。
虽然这是一个相对简单的问题,但我们需要执行多个操作:
- 实例化一个空列表。
- 迭代
data
的每个元素。 - 检查元素是否是偶数。
- 在每一步更新
added
对象。
它完成了工作,但是我们可以使代码更紧凑,更容易理解。我们的主要目标是通过浏览代码来传达我们的意图。
地图和过滤器
高阶函数接受函数作为参数或者返回函数作为结果。这有点拗口,但是利用 Python 允许我们像对待任何其他对象一样对待函数是很重要的。
map
将帮助我们在序列的每个元素上运行一个函数,而filter
添加了跳过某些项目的逻辑。为了简单起见,我们将使用lambda
来编写我们的函数,但是这个例子也将使用命名定义。
从这个片段中重要的是,能够编写一行程序并不意味着我们应该这样做。
请注意理解最后几行中发生的事情是多么容易,以及编写有意义的名称的重要性。人们可能不理解filter
是做什么的,但是通过使用名称even
,我们帮助我们的读者继续下去,而不要跳到任何实现细节。按照这个逻辑,我应该在这里做得更好,并且在 lambda 表达式中使用num
而不是x
。
列表理解
Python 中我最喜欢的特性之一是理解。它们在简单直接地表达转换方面非常出色,开发人员可以通过使用不同的符号将这些转换应用于列表、集合、字典和生成器。
我们可以定义集合,并通过函数和条件动态地构建它。
请注意,阅读这段代码时主要关注的是实际的逻辑。一眼就能看出代码的目的是什么。然后,我们可以包装这个逻辑,将data
对象转换成不同的结构。
我发现自己在理解中反复使用的一个小技巧是从一个列表中提取一个元素,如果它存在的话。
这个要点提取理解逻辑,并把它变成一个迭代器。在内部,Python 使用next
从迭代器中的一个元素跳到另一个元素。因此,我们可以应用我们的过滤逻辑,返回我们感兴趣的元素,或者获取它,或者如果它不存在,获取None
。这种方法让我们可以在代码的后面优雅地处理类型提示和条件。
回到理解。这是否意味着我们应该为循环 或更高阶的函数抛弃*?一点也不。开发人员需要评估不同方法的逻辑有多复杂。每种工具都有它的位置,有了经验,人们就会知道哪种设计变得更容易维护。*
三元运算符
我们已经看到了如何使用条件来过滤掉元素。然而,另一种典型的做法是将条件直接应用于每个元素,作为转换的一种方式。三元运算符有助于我们做到这一点。
通过检查每个元素,我们已经根据项目的属性将数字列表转换为字符串列表。这种情况可能看起来过于简单,但是将这种逻辑与过滤和数据描述符(如 Pydantic )组合在一起就成了一个极好的方法。
可读性强、直截了当的永远是赢家。在这种情况下,它比其他选择更优雅。不是因为它更短,而是因为它更好地传递了代码的目的。
赋值表达式
我们将以一个来自 PEP 572 的精彩附加节目来结束这个节目。Python 3.8 中提供的赋值表达式允许开发人员为表达式结果命名(赋值变量),这在处理理解时特别有用。
让我们想象一个函数,它可能为一些输入返回None
。然后,我们需要将该函数应用于给定的列表,并过滤掉缺失的值。
简单的方法意味着运行该函数两次:
- 首先,检查输出是否为用于滤波的
None
,以及 - 然后,存储有效的返回值。
如果函数需要很长时间来运行或者需要一些外部组件,我们应该完全避免这种方法,因为执行两次这个过程是不可能的。
然而,使用赋值表达式(又名 walrus 操作符),我们可以获得理解的所有好处,而不会牺牲函数方面的任何性能。该函数只执行一次,我们可以在本地使用res
变量来构建结果列表。
结论
Python 是一种非常灵活的语言:有很多途径可以达到相同的结果。因此,了解可供我们选择的方案至关重要。否则,如果你有锤子,一切看起来都像钉子。
在这篇文章中,我们看到了如何使用以下方法过滤和转换数据的不同选择:
- 对于循环,
- 映射和过滤,以及
- 三元运算符和赋值表达式的理解。
难的是没有金科玉律。我们需要找到传达我们意图的最佳方式。这不是聪明的问题,而是清晰的问题。
使用助手类掌握数据科学工作流
原文:https://towardsdatascience.com/mastering-data-science-workflows-with-helper-classes-1134afbd0600
用于 EDA、特征工程和机器学习的 Python 助手类
由像素上的像素生成的图像
在计算机编程中,类是组织数据(属性)和函数(方法)的一种有用方式。例如,您可以定义一个类,该类定义与机器学习模型相关的属性和方法。这种类型的类的实例可能具有诸如训练数据文件名、模型类型等属性。与这些属性相关的方法可以是拟合、预测和验证。
除了机器学习,一般来说,类在数据科学中有广泛的应用。您可以使用类来组织各种 EDA 任务、功能工程操作和机器学习模型培训。这是很理想的,因为如果写得好,类会使理解、修改和调试现有的属性和方法变得容易。如果类方法被定义为完成一个明确定义的任务,这一点尤其正确。定义只做一件事的函数和类使理解和维护这些方法更加直接,这通常是一种好的做法。
虽然使用类可以使维护代码变得更加简单,但是随着复杂性的增加,它也会变得更加难以理解。如果你喜欢组织基本 EDA、特征工程和模型训练的属性和方法,一个单独的类可能就足够了。但是,当您为每种类型的任务添加更多的属性和方法时,这些对象的初始化会变得非常模糊,尤其是对于阅读您代码的合作者。考虑到这一点,随着复杂性的增加,为每种类型的任务(EDA、特征工程、机器学习)设置助手类而不是单个类是理想的。当开发复杂的 ML 工作流时,应该有单独的 EDA、特征工程和机器学习课程,而不是单一的课程。
在这里,我们将考虑每一种类型的任务,并看看如何编写一个单一的类,使我们能够执行它们。对于 EDA,我们的课程将允许我们读入数据,生成直方图和散点图。对于特征工程,我们的类将有一个采用对数变换的方法。最后,对于机器学习,我们班将有适合,预测和验证方法。
从这里我们将看到当我们添加额外的属性和方法时,类实例化和方法调用变得更加难以阅读。我们将为每种任务类型添加额外的方法和属性,并说明当我们增加复杂性时可读性如何受到损害。从这里我们将看到如何将我们的类分成更容易理解和管理的助手类。
对于这项工作,我将在 Deepnote 中编写代码,这是一个协作数据科学笔记本,使运行可重复的实验变得非常容易。我们将使用医疗成本数据集。我们将使用患者属性,如年龄、体重指数和子女数量来预测医疗费用。这些数据在数据库内容许可 (DbCL: Public Domain)下公开免费使用、修改和共享。
面向对象的簿记模型类型
首先,让我们导航到 Deepnote 并创建一个新项目(如果您还没有帐户,可以免费注册)。
让我们创建一个名为“helper_classes”的项目,并在该项目中创建一个名为“helper_classes_ds”的笔记本。另外,让我们将 insurance.csv 文件拖放到页面左侧的“文件”面板中:
作者截图
我们将通过定义一个类来继续,该类包含机器学习工作流中的一些基本步骤。让我们从导入我们将使用的所有包开始:
作者创建的嵌入
让我们定义一个名为“MLworkflow”的类,它包含一个初始化字典的 init 方法,我们将使用字典来存储模型预测和性能。我们还将定义一个存储医疗成本数据的类属性:
class MLworkflow(object):
def __init__(self):
self._performance = {}
self._predictions = {}
self.data = pd.read_csv("insurance.csv")
接下来,我们将定义一个名为“eda”的方法来执行一些简单的可视化。如果您为变量直方图传递一个值“True ”,它将为指定的数字特征生成一个直方图。如果您为变量 scatter_plot 传递值“True ”,它将根据目标生成数字特征的散点图:
class MLworkflow(object):
...
def eda(self, feature, target, histogram, scatter_plot):
self.corr = self.data[feature].corr(self.data[target])
if histogram:
self.data[feature].hist()
plt.show()
if scatter_plot:
plt.scatter(self.data[feature], self.data[target])
plt.show()
接下来,我们将定义另一个名为“data_prep”的方法,它定义了我们的输入和输出。我们还将定义一个名为 transform 的参数,我们可以用它对数字列进行对数变换:
class MLworkflow(object):
...
def data_prep(self, features, target, transform):
for feature in features:
if transform:
self.data[feature] = np.log(self.data[feature])
self.X = self.data[features]
self.y = self.data[target]
我们还将定义一个拟合方法。它将为训练和测试拆分数据,其中 test_size 可以由“split”参数指定。我们还将提供适合线性回归或随机森林模型的选项。这显然可以扩展到任意数量的模型类型:
class MLworkflow(object):
...
def fit(self, model_name, split):
X_train, X_test, y_train, y_test = train_test_split(self.X, self.y, random_state=42, test_size=split)
self.X_test = X_test
self.y_test = y_test
if model_name == 'lr':
self.model = LinearRegression()
self.model.fit(X_train, y_train)
elif model_name == 'rf':
self.model = RandomForestRegressor(random_state=42)
self.model.fit(X_train, y_train)
然后,我们将定义一个 predict 方法,在我们的测试集上生成预测。我们将把结果存储在我们的预测字典中,字典键将是模型类型:
class MLworkflow(object):
...
def predict(self, model_name):
self._predictions[model_name] = self.model.predict(self.X_test)
最后计算每种模型类型的性能。我们将使用平均绝对误差作为我们的性能指标,并使用名为 validate 的方法将值存储在我们的性能字典中:
class MLworkflow(object):
...
def validate(self, model_name):
self._performance[model_name] = mean_absolute_error(self._predictions[model_name], self.y_test)
完整的类如下:
作者创建的嵌入
我们可以定义这个类的一个实例,并生成一些可视化效果:
作者创建的嵌入
然后,我们可以定义一个实例,并构建线性回归和随机森林模型。我们首先定义类的一个实例,并用我们希望使用的输入和输出调用数据准备方法:
model = MLworkflow()
features = ['bmi', 'age']
model.data_prep(features, 'charges', True)
然后,我们可以通过调用 fit 方法来构建一个线性回归模型,使用线性回归的 model_name 参数值“lr”和 20%的 test_size。然后,我们在模型实例上调用预测和验证方法:
model.fit('lr', 0.2)
model.predict('lr')
model.validate('lr')
我们可以对随机森林模型做同样的事情:
model.fit('rf', 0.2)
model.predict('rf')
model.validate('rf')
因此,我们的模型对象将有一个名为 _performance 的属性。我们可以通过模型对象访问它,并打印字典:
作者创建的嵌入
我们看到我们有一个字典,其中的关键字“lr”和“rf”的平均绝对误差值分别为 9232 和 9161。
簿记模型类型和用单个类分类分段的训练数据
虽然用来定义这个类的代码足够简单,但是随着复杂性的增加,阅读和解释起来会变得很困难。例如,如果除了能够监控 model_types 之外,我们还希望能够在数据中的不同类别上构建模型,那会怎么样呢?例如,如果我们希望只对女性患者训练线性回归模型,或者只对男性患者训练随机森林模型,该怎么办?让我们看看如何编写这个修改后的类。类似于在我们定义 init 方法之前,我们初始化必要的字典。我们将添加一个名为 models:
class MLworkflowExtended(object):
def __init__(self):
self._performance = {}
self._predictions = {}
self._models = {}
self.data = pd.read_csv("insurance.csv")
eda 和数据准备方法基本保持不变:
class MLworkflowExtended(object):
...
def eda(self, feature, target, histogram, scatter_plot):
self.corr = self.data[feature].corr(self.data[target])
if histogram:
self.data[feature].hist()
plt.show()
if scatter_plot:
plt.scatter(self.data[feature], self.data[target])
plt.show()
def data_prep(self, features, target, transform):
self.target = target
for feature in features:
if transform:
self.data[feature] = np.log(self.data[feature])
fit 方法包含相当多的变化。它现在接受变量 model_category 和 category_values 以及我们的随机森林算法的默认值。它还检查类别值是否在初始化的字典中。如果不是,就用一个空字典初始化它们。结果是字典的字典,其中最外面的键是分类值。分类键映射到的值是包含算法类型及其性能的字典。其结构如下:
_performance = {'category1':{'algorithm1':100, 'algorithm2':200}, 'category2':{'algorithm1':300, 'algorithm2':500}
我们还将过滤指定类别的数据。对应于此逻辑的代码如下:
def fit(self, model_name, model_category, category_value, split, n_estimators=10, max_depth=10):
self.split = split
self.model_category = model_category
self.category_value = category_value
if category_value not in self._predictions:
self._predictions[category_value]= {}
if category_value not in self._performance:
self._performance[category_value] = {}
if category_value not in self._models:
self._models[category_value] = {}
self.data_cat = self.data[self.data[model_category] == category_value]
剩下的逻辑和我们之前的差不多。完整的功能如下:
def fit(self, model_name, model_category, category_value, split, n_estimators=10, max_depth=10):
self.split = split
self.model_category = model_category
self.category_value = category_value
if category_value not in self._predictions:
self._predictions[category_value]= {}
if category_value not in self._performance:
self._performance[category_value] = {}
if category_value not in self._models:
self._models[category_value] = {}
self.data_cat = self.data[self.data[model_category] == category_value]
self.X = self.data_cat[features]
self.y = self.data_cat[self.target]
X_train, X_test, y_train, y_test = train_test_split(self.X, self.y, random_state=42, test_size=split)
self.X_test = X_test
self.y_test = y_test
if model_name == 'lr':
self.model = LinearRegression()
self.model.fit(X_train, y_train)
elif model_name == 'rf':
self.model = RandomForestRegressor(n_estimators=n_estimators, max_depth = max_depth, random_state=42)
self.model.fit(X_train, y_train)
self._models[category_value] = self.model
注意,这个函数要复杂得多。
预测和验证方法是相似的。不同的是,我们现在也按类别存储预测和性能:
def predict(self, model_name):
self._predictions[self.category_value][model_name] = self._models[self.category_value].predict(self.X_test)
def validate(self, model_name):
self._performance[self.category_value][model_name] = mean_absolute_error(self._predictions[self.category_value][model_name], self.y_test)
完整的类如下:
作者创建的嵌入
然后,我们可以根据模型类型和类别进行不同的实验。例如,让我们在单独的女性和男性数据集上构建一些线性回归和随机森林模型:
作者创建的嵌入
我们可以对 region 类别做同样的事情。让我们对西南和西北进行实验:
作者创建的嵌入
虽然这样做很好,但是运行某些实验的代码变得难以阅读。例如,当拟合我们的随机森林时,第一次阅读我们代码的人可能不清楚传递给 fit 方法的所有值是什么意思:
model.fit('rf','region', 'northwest', 0.2, 100, 100)
随着我们增加类的功能,这可能会变得更加复杂。
簿记模型类型和带有助手类的分类分段训练数据
为了避免这种日益增加的复杂性,求助于基于 ML 工作流的每个部分定义的助手类通常是有帮助的。
我们可以从定义一个 EDA 助手类开始:
作者创建的嵌入
然后,我们可以使用 eda 类来访问特征工程类中的数据:
作者创建的嵌入
接下来,我们将定义我们的数据准备类。在数据准备类的 init 方法中,我们将初始化字典来存储模型、预测和性能。我们还将使用特征工程类将对数变换应用于 bmi 和年龄。最后,我们将修改后的数据和目标变量存储在数据准备属性中:
class DataPrep(object):
def __init__(self):
self._performance = {}
self._predictions = {}
self._models = {}
feature_engineering = FeatureEngineering()
feature_engineering.engineer(['bmi', 'age'], 'charges', True, False)
self.data = feature_engineering.data
self.target = feature_engineering.target
def dataprep(self, model_name, model_category, category_value, split):
self.split = split
self.model_category = model_category
self.category_value = category_value
if category_value not in self._predictions:
self._predictions[category_value]= {}
if category_value not in self._performance:
self._performance[category_value] = {}
if category_value not in self._models:
self._models[category_value] = {}
接下来,我们将在数据准备类中定义一个数据准备方法。我们将从定义训练/测试分割、模型类别和类别值的属性开始。然后,我们将检查类别值是否存在于我们的预测、性能和模型字典中。如果不是,我们将为新类别存储一个空字典:
class DataPrep(object):
...
def dataprep(self, model_name, model_category, category_value, split):
self.split = split
self.model_category = model_category
self.category_value = category_value
if category_value not in self._predictions:
self._predictions[category_value]= {}
if category_value not in self._performance:
self._performance[category_value] = {}
if category_value not in self._models:
self._models[category_value] = {}
然后,我们将筛选我们的类别,定义输入和输出,为训练和测试拆分数据,并将结果存储在数据准备属性中:
class DataPrep(object):
...
def dataprep(self, model_name, model_category, category_value, split):
...
self.data_cat = self.data[self.data[model_category] == category_value]
self.X = self.data_cat[features]
self.y = self.data_cat[self.target]
X_train, X_test, y_train, y_test = train_test_split(self.X, self.y, random_state=42, test_size=split)
self.X_test = X_test
self.y_test = y_test
self.X_train = X_train
self.y_train = y_train
完整的数据准备课程如下:
作者创建的嵌入
最后,我们定义一个模型训练类,它允许我们访问准备好的数据、训练我们的模型、生成预测和计算性能:
作者创建的嵌入
我们现在可以用我们的类层次结构进行一系列实验。例如,我们可以建立一个随机森林模型,只根据对应于女性患者的数据进行训练:
作者创建的嵌入
我们还可以建立一个线性回归模型,只根据女性患者的相关数据进行训练。此模型的性能将添加到现有的性能字典中:
作者创建的嵌入
我们可以对男性患者做同样的事情。以下是线性回归的结果:
作者创建的嵌入
对于随机森林:
作者创建的嵌入
我们看到,我们有一个几个实验及其相应的模型类型、类别级别和模型性能值的字典。
这篇文章中使用的代码可以在 GitHub 上找到。
结论
在这篇文章中,我们讨论了如何使用面向对象编程来简化数据科学工作流程的各个部分。首先,我们定义了一个 ML 工作流类,它支持简单的 ed a、数据准备、模型训练和验证。然后我们看到,当我们向类中添加功能时,类实例上的方法调用变得难以阅读。为了避免阅读和解释代码的问题,我们设计了一个由一系列助手类组成的类层次结构。每个助手类对应于 ML 工作流中的一个步骤。这使得理解方法变得容易,因为它们与高级任务相关,这有助于可读性和可维护性。我鼓励你在自己的 ML 项目中尝试一下。
掌握动态编程
原文:https://towardsdatascience.com/mastering-dynamic-programming-a627dbdf0229
理解基本原理,知道何时以及如何应用这种优化技术
除特别注明外,所有图片均为作者所有
A 你期待去 FAANG 这样的大科技公司面试吗?如果是这样,请准备好进行一轮或多轮技术编码评估。
事实上,越来越多的公司已经开始在数据科学家、机器学习工程师或软件工程师职位的面试过程中纳入技术编码评估。
这种评估通常测试候选人的数据库和 SQL 数据操作技能,或者数据结构和算法的问题解决和编程技能。后者通常包括动态编程,这是一种优化技术,可以产生时间或空间复杂度降低的高效代码。
让我们行动起来,开始学习动态编程。
内容
1。动态编程的特点2。动态规划题型3。实现动态编程的方法 ∘自上而下的方法 ∘自下而上的方法4。动态编程示例 ∘示例 1:爬楼梯 ∘示例 2:入室抢劫犯 ∘示例 3:樱桃皮卡什么概要
1.动态规划的特征
我们许多人都面临着识别动态编程问题的困难。我们如何知道我们能否用动态编程解决一个问题?我们可以问自己以下问题。
- 我们能把这个问题分解成更小的子问题吗?
- 有没有重叠的子问题?
- 如果是,我们能否最优地解决较小的子问题,然后用它们构造一个最优解来解决主问题?
- 在解决问题的同时,当前步骤的决策是否影响后续步骤的结果和决策?
如果你对以上问题的回答是肯定的,那么你可以应用动态规划来解决给定的问题。根据维基百科,
一个问题必须具备两个关键属性,才能应用动态规划: 最优子结构 和 重叠子问题
不要混淆动态编程与分治或贪婪算法。
分治 也是通过组合子问题的最优解来解决一个问题。但《分治法》中的子问题是不重叠。
一个 贪婪算法 将试图做出贪婪的选择来为每一步提供局部最优解。这可能无法保证最终的解决方案是最优的。贪婪算法永远不会回顾并重新考虑它的选择,而动态编程可能会根据对前面步骤的回顾来修改它的决定。
2.动态规划问题的类型
动态编程题是什么样子的?
让我们来看看一些最常见的动态编程问题。
第一种类型的动态规划问题,也是经常遇到的一种,是为一个给定的问题找到一个最优解。例子包括寻找最大利润、最小成本、最短路径或最长公共子序列。
第二类动态规划问题是寻找达到某种结果的可能性,到达某一点的可能性,或者在规定的条件下完成一项任务的可能性。
第三种类型通常被称为动态规划计数问题(或组合问题),如寻找执行或完成一项任务的方法的数量。
注意,我们讨论的所有三种类型的动态规划问题也可以以矩阵的形式作为二维问题出现。更复杂的问题可能涉及多个方面。
3.实现动态规划的方法
我们可以用自底向上或自顶向下的方法解决动态规划问题。不管哪种情况,我们都需要定义并得出问题的基本情况。
一般来说,自上而下的方法是从查看全局开始的。从高层开始,它逐渐深入到更小的子问题的解决方案,最后是构建解决方案的基础案例。
与自上而下的方法相反,自下而上的方法从小处着手,首先查看基础案例,然后在整体解决整个问题之前,一步一步地构建更大的子问题的解决方案。
想象以下情况:
自上而下 :“我想做一个好吃的,看起来很华丽的生日蛋糕。如何实现这一点?嗯……我可以用甘美的香蒂伊奶油、浆果果盘、鲜花和新鲜草莓来装饰蛋糕。如何准备它们?我需要用糖搅打浓奶油,切草莓,煮浆果果盘。但是蛋糕在哪里?哎呀,我需要准备蛋糕面糊,然后烘烤它。怎么会?用糖和鸡蛋搅打黄油,然后拌入面粉和牛奶。每种成分的用量是多少?我需要根据所需的比例来衡量它们”。
自下而上 :“我先从测量和准备蛋糕配料开始。所有的材料都准备好了,我把黄油和糖、鸡蛋搅拌在一起,然后加入面粉和牛奶。接下来,我把它放进烤箱,烤 30 分钟。烤好蛋糕并让它冷却后,我煮浆果果盘,切新鲜草莓,用糖搅打浓奶油。此后,我在蛋糕上涂上尚蒂伊奶油,在上面铺上浆果果盘,最后放上并摆好切好的草莓和可食用的花”。
你能注意到自下而上的方法是如何从基础开始有序地做事的吗?
由塞巴斯蒂安·科曼摄影在 Unsplash 上拍摄
自上而下的方法
自顶向下方法的实现使用带记忆的递归。
记忆化是一种存储计算结果的技术,这样当程序再次需要它们时,就可以直接检索和使用它们。这对于重叠的子问题特别有用,因为它有助于避免两次执行相同的计算,从而提高效率并节省计算时间。哈希表或数组通常用于记忆。
只要当前状态不是基本情况,这种方法将根据我们定义的递归关系进行递归函数调用。
自下而上的方法
为了实现自底向上的方法,我们需要从基础案例开始,以特定的顺序迭代问题的所有状态。
通过这样做,我们从小到大一步一步地构建解决方案,这样当前步骤的答案可以很容易地使用从先前步骤计算的可用子问题解决方案来计算。这个过程会一直持续下去,直到我们构建出完整的解决方案。
自下而上的方法也被称为列表法。由于这种方法会以特定的顺序遍历每一步并执行计算,因此很容易将结果列表成数组或列表,这样就可以通过相关索引方便地检索它们,以便在后续步骤中使用。
对于某些情况,可以避免制表以节省空间和内存。我们将在后面的示例 1 和示例 2 中看到这一点。
那么使用哪种方法呢?
通常使用这两种方法都可以。毕竟,动态编程不容易理解。你可以使用对你来说更自然的方法,也可以使用你更习惯的方法。
虽然有些人可能发现使用自顶向下的方法用递归关系和代码构建解决方案更容易,但自底向上的迭代过程有时会执行得更快,因为它没有递归的堆栈开销。大多数时候,这种差异是微不足道的。不用说,如果你很有技巧,能够编写两种代码,那么就选择时间和空间复杂度较低的一种。
4.动态编程示例
在本帖中,我们将详细讨论三个解决动态编程问题的例子。
示例 1:爬楼梯
先说一个简单易懂的例子,爬楼梯。
在这个问题中,我们有一个楼梯,它会带我们走
n
步到达顶端。每次,我们只允许爬 1 步或 2 步。在中,我们可以用多少种不同的方式爬到山顶?
让我们从思考基本情况开始。如果我们要迈出第一步,有多少种方法可以做到?当然只有一条路,那就是爬 1 级台阶。
进入第二步怎么样?有两种方法可以做到这一点。我们可以两次爬 1 级台阶,也可以一次爬 2 级台阶。
爬楼梯问题的基本情况
很好!到达第一步和到达第二步构成了这个问题的基本情况。那么,第三步,第四步,等等呢?
对于第三步,只有 3 种可能的方法可以到达那里。它们是:
- 爬 1 级台阶 3 次,或
- 先爬 1 步,再爬 2 步,或
- 先爬两步,再爬一步
我们怎么得到这个数字?就是把第一步和第二步的结果相加(1 + 2 = 3)。这告诉我们,从基础案例出发,我们可以构建其他步骤的解决方案。假设climb(n)
是返回到达步骤n
的路径数的函数,那么
climb(n) = climb(n-1) + climb(n-2)
现在我们有了,这是这个爬楼梯问题的递推关系。当前步骤采用前两步的计算结果,并将它们相加。
基本上,这就是我们如何将一个问题分解成子问题,解决子问题将构建主问题的解决方案。
你能看出这些子问题是重叠的吗?
climb(6) = climb(5) + climb(4)
climb(5) = climb(4) + climb(3)
climb(4) = climb(3) + climb(2)
climb(3) = climb(2) + climb(1)
为了得到climb(6)
的结果,我们需要计算climb(5)
和climb(4)
。为了得到climb(5)
的结果,我们需要再次计算climb(4)
。
从下图我们可以清楚的看到,为了得到climb(6)
,我们要计算climb(4)
两次,计算climb(3)
三次。这将导致 O(2^n).的时间复杂度
重叠子问题— climb(6)
无记忆
有了记忆化,我们可以摆脱多余的计算,只检索先前存储的结果。时间复杂度将降低到 O(n)。
climb(6)
带记忆功能
好了,我们有了基本情况和递归关系,现在是时候使用自顶向下的方法编写实现了。
这里,为了记忆,我们使用 hashmap (Python 字典)来存储计算结果。如果n
的结果存在,我们跳过计算并从 hashmap 中检索结果。
该方法的时间和空间复杂度均为 O(n)。这是因为我们实际上经历了每一步,并且对于每一步,我们都将计算结果存储在 hashmap 中。
爬楼梯的自上而下解决方案
下面是使用自下而上方法的解决方案。
通常,我们可以使用一个数组来列出每一步的计算值。然而,对于这个问题,由于当前状态只依赖于前两个状态,我们可以有效地只使用两个变量,case1
和case2
,在迭代for
循环时跟踪它们。
通过这样做,我们能够实现恒定的空间复杂度 O(1)。时间复杂度将与自顶向下的方法相同,O(n)。
爬楼梯的自下而上解决方案
例子 2:入室抢劫犯
这是动态编程问题的另一个经典例子,入室抢劫。
在这个问题中,我们得到了一个整数数组,代表一个强盗可以抢劫的每所房子的钱数。这些房子排列在一条街上。不过有一个限制,即强盗不能抢劫邻近的房子。例如,如果房屋的索引从 0 到 4,强盗可以抢劫房屋 0、2、4 或房屋 1、3。
强盗能从这些房子里抢劫的最大金额是多少?
让我们用一个示例输入*house = [10, 60, 80, 20, 8]*
来演示一下。
同样,我们从考虑基本情况开始。如果只有一套房子,house[0]
,我们怎么办?我们只会抢劫,从house[0]
那里拿钱。这是因为没有其他选择可以考虑。
现在,如果我们有两栋房子,house[0]
和house[1]
,我们抢哪栋?我们不能抢劫两家,因为它们相邻。我们要么抢house[0]
要么抢house[1]
。好吧,当然,我们会抢劫装有更多钱的房子。为了找到包含更多钱的房子,我们应用max(house[0], house[1])
。
注意上面是如何构成这个问题的基础的?
入室抢劫问题的基本情况
现在,如果有很多房子呢?那我们该怎么办?我们这么想吧。如果我们在house[n]
,我们有两个选择,即是否抢劫房子。
抢还是不抢这房子?
- 假设我们在
house[9]
。如果我们决定抢劫house[9]
,那么我们将得到的是来自house[9]
的钱加上我们之前得到的 2 栋房子的钱。 - 如果我们决定不抢劫
house[9]
,那么我们所拥有的仅仅是我们在house[8]
之前从抢劫房屋中获得的金额。
入室抢劫问题的递推关系
为了找出哪个选项能产生最大金额,我们对这两个选项应用了max()
函数,这就形成了我们的递归关系:
rob_house(n) = max(rob_house(n-2) + house[n], rob_house(n-1))
有了基本情况和递归关系,我们现在可以使用自顶向下的方法来编写解决方案。该方法的时间和空间复杂度为 O(n)。
自上而下的入室抢劫解决方案
对于自底向上的方法,我们再次使用两个变量case1
和case2
来跟踪前两个状态,而不是将数组列表。
还记得只有一栋房子和有两栋房子的基本情况吗?case1
用第一个房子的值初始化,case2
用max(house[0], house[1])
的结果初始化。
下图说明了我们有 3 个房子时的训练。
对于n = 2
所在的第三个房子,以及后续的迭代,我们可以通过应用max(case1 + house[n], case2)
来计算result
。因为我们正在回收这两个变量,所以在当前计算之后,case1
将被替换为case2
,并且当前计算的结果将被赋给case2
。
仅使用两个变量允许我们实现恒定的空间复杂度 O(1)。时间复杂度将与自顶向下的方法相同,O(n)。
自下而上解决入室抢劫
示例 3:樱桃皮卡
让我们来尝试一个更有挑战性的问题,樱桃皮卡。
在这个问题中,有一个
n x n
网格,其中每个单元格中的值可以是1
(单元格中有一颗樱桃)0
(单元格是空的),或者-1
(有一根刺,一个挡住去路的障碍物)。从单元格
(0,0)
开始,我们应该移动到位于(n-1, n-1)
的目的地。如果一路上有樱桃,我们就把它们捡起来。如果我们从一个细胞中拿起樱桃,这个细胞就变成了一个空细胞,0
。我们只允许向下或向右移动,如果有荆棘,我们将无法通过。在
(n-1, n-1)
到达目的地后,我们必须返回起点(0,0)
。回程时,我们只允许向上或向左移动,捡起剩余的樱桃,如果有的话。如果我们在
(n-1, n-1)
找不到到达目的地的方法,那么我们就收不到任何樱桃。我们被要求归还我们能够收集的最大数量的樱桃。
首先,我们想到的是建模一个解决方案,如问题所述,在向前的行程中拾取樱桃,然后返回。
但是,让我们用从这里采用的方法来尝试一种新的方法。
也就是说,我们将只在向前的行程中遍历,而不是先向前再返回。为什么?这是因为在这个问题中,向前的行程和向后的行程实际上是一样的。换句话说,我们可以在只能向下移动或 right️的向前行程中,以及在只能向上移动或 left️.的返回行程中,遍历相同的路径这有意义吗?
向前行程或返回行程可以穿过相同的路径
接下来,我们可以用两个人同时移动的想法简单地做一次向前的旅行,而不是做两次旅行。因此,在任何时间点上,这两个人都会移动相同的次数。
假设人员 1 在位置(r1,c1)
并且人员 2 在位置(r2,c2)
移动一定次数后,我们有r1 + c1
等于r2 + c2
。听起来很奇怪?看看下面的插图。
这意味着我们可以在我们的问题中使用r1
、c1
、r2
、c2
作为状态变量。但是我们真的需要这四个吗?对于长度为n
的网格,它将产生 O(n⁴)状态。
我们可以改进并将状态变量减少到只有三个,即r1
、c1
、c2
。但是r2
怎么样?好吧,我们还需要r2
,我们可以从等式中导出它的值。这个状态空间约简将把这个问题的时间和空间复杂度从 O(n⁴)降低到 O(n)。
r1 + c1 = r2 + c2Derive r2 as:
r2 = r1 + c1 - c2Where
r1 is the row index of *person1*
c1 is the column index of *person1*
r2 is the row index of *person2*
c2 is the column index of *person2*
那么,这个问题中的三个状态变量是怎么算出来的呢?因为我们有两个人同时移动,所以每次移动有四种可能性,如下所示:
使用三个状态变量(r1、c1、c2)来表示每次移动的四种可能性
现在,在对概念有了清晰的理解和足够的信息后,让我们构建自顶向下的解决方案。
这一次,我们将使用来自[functools](https://docs.python.org/3/library/functools.html#module-functools)
模块的 Python 的[lru_cache()](https://docs.python.org/3/library/functools.html#functools.lru_cache)
装饰器,而不是使用 hashmap 进行记忆。
在我们的解决方案中,我们将创建一个get_cherry()
递归函数,并使用一个名为cherries
的变量来累加采摘的樱桃。
假设人员 1 在(r1,c1)
位置人员 2 在(r2,c2)
位置,并且两个位置都没有刺,我们首先将(r1,c1)
到cherries
的值相加。因为没有刺,所以值为0
或1
。
其次,为了避免重复计算,只有当人员 2 在不同的位置时,我们才会将(r2,c2)
的值加到cherries
上。
到目前为止,我们已经在人员 1 和人员 2 的当前位置积累了樱桃。我们也需要对后续位置进行同样的累积。
还记得我们之前说过的吗,每一步都有四种可能?为了确定哪条路径可以获得后续位置的最大樱桃数量,我们可以对四个选项应用max()
函数。这就形成了这个问题的递归关系。
樱桃采摘的递归关系
递归函数调用将继续,直到到达目标单元格。当这种情况发生时,函数将返回该单元格的值。因为这是基本情况,所以不会进行进一步的递归函数调用。
樱桃采摘自顶向下解决方案
哇。这是个完美的计划,不是吗?但也有例外。
(r1,c1)
或(r2,c2)
有刺怎么办?如果人员 1 或人员 2 越界了呢?
显然,我们想尽可能避开那些地方。如果发生这种情况,函数将返回一个负无穷大,表示不值得采取特定的路径来寻找最佳答案。负无穷大本质上是最小值,所以max()
函数会采用另一个值最大的选项。
最后,如果人员 1 或人员 2 无法到达目的地怎么办?嗯,这是可能的,它可能会发生由于荆棘阻碍。在这种情况下,没有樱桃可以收集,最后的答案将是0
。
对于这个问题,我将把它留给好奇的读者去思考自底向上的解决方案。谁知道呢,你也许能想出一个绝妙的解决方案。
下一步是什么?
无论如何,去练习解决动态编程问题。
迈出第一步。如果你不能破解解决方案,不要烦恼。查看一些论坛,在那里您通常可以找到有用的提示和指导。
最知名的可以练习的平台是 LeetCode 。其他还有 GeeksforGeeks 、hackere earth和 HackerRank 。
我们练习得越多,学到的就越多。我们学得越多,就越容易!
摘要
☑️:在这篇文章中,我们讨论了动态编程的特征以及适用于动态编程的关键属性。
☑️:我们揭示了经常被问到的动态编程问题的典型类型。
☑️我们探讨了自顶向下和自底向上方法之间的差异,以及以何种方式实现它们。
☑️:我们通过 3 个例子学习了如何解决动态编程问题,其中包括详细的解释和解决方案。
参考
[1] LeetCode,爬楼梯,入室抢劫,樱桃皮卡
[2] GeeksforGeeks,动态编程
[3] Prateek Garg , 动态编程介绍 1
[4]编程,动态编程
动态编程很有趣。有些问题可能是令人生畏和难以置信的,但是学习解决它们并掌握技巧可以打开一个可能性的彩虹!
由 Unsplash 上的不锈钢图片拍摄
***Before You Go...****Thank you for reading this post, and I hope you’ve enjoyed learning dynamic programming as much as I do. Please leave a comment if you’d like to see more articles of this nature in the future.**If you like my post, don’t forget to hit* [***Follow***](https://peggy1502.medium.com/) *and* [***Subscribe***](https://peggy1502.medium.com/subscribe) *to get notified via email when I publish.**Optionally, you may also* [*sign up*](https://peggy1502.medium.com/membership) *for a Medium membership to get full access to every story on Medium.*📑 *Visit this* [*GitHub repo*](https://github.com/peggy1502/Data-Science-Articles/blob/main/README.md) *for all codes and notebooks that I’ve shared in my post.*© 2022 All rights reserved.
准备好深入潜水了吗?跳上 掌握动态编程 II
https://peggy1502.medium.com/mastering-dynamic-programming-ii-73149d26b16d
有兴趣阅读我的其他数据科学文章吗?查看以下内容:
https://pub.towardsai.net/building-a-product-recommendation-engine-with-aws-sagemaker-321a0e7c7f7b
掌握 Git:修改提交
原文:https://towardsdatascience.com/mastering-git-amending-commits-f46c2afc9508
如何使用 git amend 更改您之前的提交
照片由 Praveen Thirumurugan 在 Unsplash 拍摄
Git 是所有数据科学家和软件工程师都应该知道如何使用的工具。无论您是独自从事一个项目,还是作为一个大型分布式团队的一部分,了解如何使用 Git 从长远来看都可以为您节省大量时间。git 的关键功能之一是能够撤销、重做或编辑已经做出的更改。当第一次并不总是如预期的那样工作时,这可以用来确保在每次提交中具有清晰定义的变更的干净的提交历史。实现这一点的工具是git --amend
,它使您能够修改您的最后一次提交,而无需在原始提交的基础上进行新的提交。这样做的好处是,您可以通过添加或删除文件和/或修改提交消息来改变上次提交的内容。
更改提交消息
使用--ammend
函数的一个最简单的方法是简单地改变最后一次提交的消息。当您过早提交、临时提交或在撰写消息时忘记遵循某些团队约定时,这可能是有益的。更改提交消息可以确保它准确地描述了以前的提交,以便其他人可以确切地知道该更改包含什么。
要修改消息,当当前没有暂存任何内容时,您只需运行:
git commit --amend
这将打开您链接到 git 的编辑器,允许您修改之前的提交消息。如果您甚至不想打开编辑器,更改整个提交消息,您可以简单地添加-m
选项,这将允许您在同一个命令中指定新消息。这可以通过以下方式实现:
git commit --amend -m "New commit message"
更改提交的文件
在某些情况下,您想要更改的不仅仅是附加到最后一次提交的消息。这可能是因为您忘记存放文件,在最初的提交中出错,或者因为一些额外的小更改应该在最后一次提交而不是新的提交中捆绑。这也可以通过--amend
功能来完成。
为此,你所要做的就是简单地使用git add file_name
将文件暂存起来,然后使用git commit --amend
。典型的工作流程可能采取以下形式:
git add some_amazing_python.py
git commit -m "Some amazing work"#edit the files to remove an unecessary import
#and make it cleanergit add some_amazing_python.py
git commit --amend --no-edit
第二次升级文件时,使用git commit --amend
会将第二次更改与第一次更改捆绑在一起。
您还会注意到,--no-edit
标志被添加到了git commit -amend
调用中。这样做是因为我们不希望在这次更改中更改提交消息,只更改文件。当然,如果你想改变消息,你可以移除这个标志来打开一个编辑器,或者使用-m
选项来改变消息而不打开编辑器。
除了简单地向现有文件添加更改之外,您还可以从文件中删除上次提交的更改。这可以通过git rm
功能而不是git add
来删除文件。
警告!!!
git commit -amend
通过删除以前的提交并创建一个新的提交来工作,如下图所示:
作者图片
因此,使用git commit --amend
会改变存储库的历史。如果资源库已经公开共享,比如在 GitHub 上,并且其他开发人员已经在现有提交的基础上进行构建,这可能会使事情变得困难和混乱。这是因为在公开共享和使用时更改提交的历史将会产生合并冲突,这在将来很难解决。作为开发人员,您不想处理的一件事是合并冲突。所以如果改动已经公开,就不要用--amend
。
唯一的例外是,如果你正在做你自己的分支,别人还没有接触过。这应该可以避免合并冲突的问题,但是在推送远程存储库时仍然会给自己带来困难。这是因为本地存储库和远程存储库之间的历史将会不同,这意味着必须通过强制提交到远程存储库来解决这个问题。这可以使用git push -f
来完成,其中-f
强制提交并用本地提交历史覆盖公共提交历史。只要确保这是在正确的分支上完成的!
结论
git --amend
是一个非常有用的命令知道吗!如果遗漏了什么,它允许您更改提交消息,或者调整前一次提交中的更改。这可以为您和其他开发人员确保一个清晰的提交历史,并且在犯小错误的时候可以派上用场。只要确保你没有在已经公开的提交中使用它,或者当你在之前的提交中改变代码的行为时(这应该是一个新的!).
来源
[1]https://www.atlassian.com/git/tutorials/rewriting-history
[2]https://stack overflow . com/questions/179123/how-to-modify-existing-un pushed-commit-messages
如果你喜欢你所读的,并且还不是 medium 会员,请使用下面我的推荐链接注册 Medium,来支持我和这个平台上其他了不起的作家!提前感谢。
https://philip-wilkinson.medium.com/membership
或者随意查看我在 Medium 上的其他文章:
https://python.plainenglish.io/a-practical-introduction-to-random-forest-classifiers-from-scikit-learn-536e305d8d87
掌握 Git: Git 精选
原文:https://towardsdatascience.com/mastering-git-git-cherry-pick-fbeb23eea04d
如何将单个提交精选到您当前的分支
由 Praveen Thirumurugan 在 Unsplash 上拍摄
Git 是所有数据科学家和软件工程师都应该知道如何使用的工具。无论您是独自从事一个项目,还是作为一个大型分布式团队的一部分,了解如何使用 Git 从长远来看都可以为您节省大量时间。git 的一个关键功能是能够从任何地方选择任何提交并复制到当前分支的头部。这可以通过命令git cherry-pick
来完成,该命令可用于撤销更改、引入新功能、解决 bug 或确保提交附加到正确的分支。这当然是有用的,但你必须小心,不要在合并或重定基础可能更有益的情况下过度使用它。
樱桃采摘
git cherry-pick
是一个有用的工具,它允许您从存储库中的任何地方复制一个提交,并将其附加到当前分支的头部。这使得它成为一个非常有用的工具,可以以多种方式使用,但避免过度使用通常是最好的做法。这是因为该命令会创建重复的提交,这会造成混乱的历史记录,并且在大多数情况下,合并或重置会更有好处。那么什么时候应该使用它:
- 作为同事在不同的分支从事类似的工作。在这种情况下,您的同事开发的类或函数可以帮助您当前的进展或将来的系统集成。为了将这种功能引入到您当前的分支中,并避免代码的重复,您可以简单地挑选出那些允许您继续工作的特定功能。这将集成功能,特别是如果它们是前端和后端系统,同时也减少了将来可能出现的合并冲突的数量。
- 减少 bug。在某些情况下,当开发一个新特性时,你可能会发现一个来自另一个分支甚至主线的 bug(希望不是!).在这一点上,最好是尽快为客户获得一个 bug 补丁,这可能意味着在您当前的分支上开发补丁。在这一点上,您可以将您的变更精选到主分支或另一个分支中,以尽快解决 bug。
- 为了不失去功能。在某些情况下,新功能可能不会在您希望的时候合并到 main 中。在这些情况下,为了获得您想要的特性,最好使用精选来挑选该功能。这样就保证了这个特性不会丢失!
使用 git 精选
使用git cherry-pick
相对简单,因为您需要做的就是找到您感兴趣的提交引用,然后当您检查出您想要将功能引入的分支时,使用git cherry-pick commit_reference
。例如:
作者图片
如果在提交G
中有一些功能,在当前的特性分支中会很有用。您可以使用:
git cherry-pick G
这将改变提交历史,使得G
现在也将出现在当前头部的尖端,如下所示:
为了能够识别您想要挑选的相关提交及其相关的提交引用,您可以使用命令git log
或git reflog
,它们将向您显示提交历史。
附加功能
为了改进git cherry-pick
的工作,我们还可以添加标志来改变功能,使它对我们想要的更有用:
- 选项将导致 git 在应用精选操作之前提示提交消息。这将允许您更改提交消息,如果您想反映它在该位置正在做什么。
--no-commit
flag 将执行精选,但不是进行新的提交并将其添加到当前文件头,而是简单地移动文件内容,而不进行实际的提交。当您只想检查更改或查看更改如何与您的功能集成而不提交时,这可能很有用。-x
将在原始提交消息中添加一行“cherry picked from commit …”,用于无冲突提交。这在从公开可用的分支中挑选樱桃时会很有用。
您还可以通过指定git cherry-pick branch_name
来指定想要将最新提交移动到哪个分支。这将从当前分支获取最新提交,并将其放在指定分支的末端。
此外,如果您想同时挑选多个提交,您可以添加它们的提交引用,用空格分隔。尽管不建议这样做,因为理想情况下,您应该一次只复制一个提交。
结论
git cherry-pick
是一个强大的命令,如果正确使用,它会非常有用。它允许您将提交复制到当前分支的头部,当您想要共享功能但不想执行合并时,这对于解决在其他地方发现的错误或提交到错误的分支时非常有用。当合并或重定基础是更好的实践时,例如当多个提交可能被移动时,不应该使用它。您可以使用列出先前提交的git log
命令找到您想要移动的提交的提交标识。
来源
[1]https://www.atlassian.com/git/tutorials/cherry-pick
[2]https://www.gitkraken.com/learn/git/cherry-pick
如果你喜欢你所读的,并且还不是 medium 会员,请使用下面我的推荐链接注册 Medium,来支持我和这个平台上其他了不起的作家!提前感谢。
https://philip-wilkinson.medium.com/membership
或者随意查看我在 Medium 上的其他文章:
https://python.plainenglish.io/a-practical-introduction-to-random-forest-classifiers-from-scikit-learn-536e305d8d87
掌握 Git:“Git stash”
原文:https://towardsdatascience.com/mastering-git-git-stash-cf4042dca068
如何使用 git stash 来存储您不准备提交的更改
由 Praveen Thirumurugan 在 Unsplash 上拍摄
Git 是所有数据科学家和软件工程师都应该知道如何使用的工具。无论您是独自从事一个项目,还是作为一个大型分布式团队的一部分,了解如何使用 Git 从长远来看都可以为您节省大量时间。虽然 git 对于清楚地查看您对存储库所做的更改的历史很有用,但是在某些情况下,您还没有准备好提交您对历史所做的更改。当 git 告诉您需要提交或者您将丢失您的更改时,这可能是一个挑战。当您想要更改分支或者从远程存储库中提取更改时,Git 通常会这样做。那么你能做什么呢?git stash
来救援了!stash
命令允许您“隐藏”您还没有准备好提交的更改,这样您就可以保存更改,而不必将它们添加到历史中。这将允许您轻松地更改分支或从远程存储库中提取,而不必担心丢失您的更改。那么它是如何工作的呢?
Git Stash
git 的正常工作流程是编辑文件,准备变更,然后提交。这个工作流将创建一个清晰的提交历史,其中的工作明显地建立在彼此的基础上。它允许你进入你正在开发的特性的流程,做出清晰的改变,并且在一个编码会话中做出你通常的惊人的代码贡献。当然,这是理想的工作流程,但是在现实中,作为团队的一部分在工作环境中进行开发意味着您必须经常在一天中切换任务或职责。当您正在进行代码更改时,这可能会令人沮丧,因为您还没有准备好提交,但同时又不想丢失您的工作。您可能会被要求更改您正在处理的内容,因为出现了一个需要尽快解决的 bug,另一个开发人员要求您查看另一个分支,对您正在处理的分支进行了新的提交,您只想休息一下。此时,在您可以做任何其他事情之前,git 可能会告诉您对您所做的更改做一些事情。
如果您还不想创建新的提交(尽管您可以随时返回并编辑提交或消息),您可以使用git stash
保存未提交的更改。这个过程允许您处理存储库中的其他任务,而不会丢失到目前为止所做的更改。然后,当您想要恢复这些更改时,可以简单地重新应用这些更改,而不会影响提交历史。厉害!它是如何工作的?
使用 Git Stash
默认情况下,当您调用git stash
时,您对跟踪文件所做的所有更改都将被转移到一个存储中。此时,您当前的工作目录将阻止返回到上次提交后的阶段。这意味着在那之后你所做的所有更改都将被保存在一个存储库中,你可以在以后访问它。
你藏起来的东西会包含:
- 对暂存文件所做的更改
- 对跟踪文件所做的更改(但未转移)
但是您需要知道,它不会自动包含:
- 工作目录中尚未暂存的新文件
- 有意忽略的文件
这是您需要注意的事情,尤其是当您处理涉及新文件的变更时。为了包括这一点,您可以使用标志-u
,它将包括未被跟踪的文件,以及-a
,它也将包括被忽略的文件。
一旦你创建了隐藏,做了你需要在其他地方做的改变,然后回到你工作的最初的分支,你会想知道你如何能把那些隐藏的改变拿回来?有两种方法可以做到:
git stash apply
—将获取您存储在存储库中的变更,将它们应用到当前签出分支的工作目录中,并且将保持存储库的完整性。当您将更改拉至不同于最初开发的分支时,或者您正在编辑更改,但您可能希望保留最初的更改以备不时之需时,这很有用。重要的是,在它被清理之前,这个藏匿点在未来仍然存在,这意味着如果出现问题,你可以转换回原来的藏匿点。git stash pop
—还会将存储在存储中的更改应用到当前工作目录,但这将在应用更改后删除存储。这可以在你不在乎以后保留存储时使用,或者如果你计划对存储进行更改,并且不想在任何时候恢复到原始存储时使用。
这非常有用,尤其是对于存储您不准备与团队其他成员共享的变更。
然而,必须注意的是,这并不能代替将您的变更提交到存储库中。储藏只能适度进行。这是因为跟踪变更会变得很困难,尤其是在有多个 stashes 的情况下,因为它们的历史和它们包含的内容不像提交那样清楚。这意味着应该谨慎使用它们,只在需要的时候使用,否则更改应该提交!
附加功能
除了使用起来相对简单之外,git stash 是一个非常强大的工具,可以通过多种方式进行修改。
git stash --patch
(或使用-p
标志)将打开一个编辑器,允许你交互地选择你想要保存的变更和你想要保存在你的工作目录中的变更。当您不想隐藏所有的更改时,这很有用,因为您可能想要提交一些更改。git stash save "message"
将允许你添加一条消息到你的秘密物品中。当您有多个隐藏并且想要清楚地告诉变更包含什么或者它们为什么被隐藏时,这是特别有用的。如果你经常使用 stash,这是一个很好的实践,当你回去应用 stash 的变化时,它会使你的生活变得容易得多。git stash show
将显示您最近收藏的内容(文件更改的数量、添加的数量和删除的数量)。这有助于大致了解最新的存储内容,但是如果你想了解文件中的所有变化,你可以通过-p
,它将显示与git diff
相同的输出- 如果你已经藏了不止一次,
git stash list
会显示所有已藏物品的列表。这将显示隐藏来自哪个分支和他们的隐藏索引(你如何能在隐藏之间选择)。默认情况下,在分支和创建存储的提交之上,所有存储都被标识为“WIP”(正在进行的工作)。 git stash show stash@<index>
可用于显示特定存储的变化,例如git stash show stash@{2}
将显示第三个存储的变化。这可以扩展到前面的命令,比如git stash pop stash@{2}
,它将弹出第三个存储,而不是默认的最后一个存储更改。git stash drop
可用于从隐藏条目列表中删除单个隐藏条目。当您知道您在将来的任何时候都不会使用这些更改,并且保持您的 git 存储库没有任何未使用的更改时,这是非常有用的。git stash clear
将其扩展到删除所有隐藏的更改,但要谨慎使用,因为在此之后很难恢复任何隐藏!- 最后
git stash branch <branch_name>
可以用来从最新的存储中创建一个分支,并删除最新的存储。当您将存储应用到您的分支的最新版本之后出现冲突时,这可能是有用的(这可能发生,并且可能非常混乱!)
结论
如果 git 警告你不能做某件事,因为你没有提交修改git stash
可以帮助你!它的工作原理是将更改存储到您不准备创建提交的文件中,确保当您更改分支、发出拉请求或只想提交您已经完成的部分工作时,工作不会丢失。这有时会很有用,可以存放多份,但要确保不要过度使用!与提交不同,隐藏可能很难跟踪,而且它们会被全面地记录,所以当您准备好发布您的更改时,就提交吧!
来源
[1]https://www . atlassian . com/git/tutorials/saving-changes/git-stash
[2]https://git-scm.com/docs/git-stash
如果你喜欢你所读的,并且还不是 medium 会员,请使用下面我的推荐链接注册 Medium,来支持我和这个平台上其他了不起的作家!提前感谢。
https://philip-wilkinson.medium.com/membership
或者随意查看我在 Medium 上的其他文章:
https://python.plainenglish.io/a-practical-introduction-to-random-forest-classifiers-from-scikit-learn-536e305d8d87
掌握 Git:合并和重建基础
原文:https://towardsdatascience.com/mastering-git-merge-and-rebase-f2a7c5c348a9
它们是什么,如何使用它们?
照片由 Praveen Thirumurugan 在 Unsplash 上拍摄
Git 是所有数据科学家和软件工程师都应该知道如何使用的工具。无论您是单独从事一个项目,还是作为大型分布式团队的一部分,从长远来看,了解如何使用 Git 都可以为您节省大量时间。这是因为 git,以及更广泛的版本控制,允许您跟踪对代码库所做的更改,并与其他人有效地协作。你想逆转你所做的改变,用 git 很容易做到,你想从现有的代码中分离出一个新的特性,git,你想在你做改变的同时运行一个有效的产品,git,你想和其他人在同一段代码上工作,git 和 github!这真的很神奇。
学习其中一个重要的关键功能是合并和重组,将分支工作引入主线。阅读本文的大多数人都会在某个地方遇到过合并或重组(当然,有些比其他的更容易、更简洁)。这可能是因为你自己正在开发一个特性,你把它引入了你的主线,或者是其他人把他们已经完成的工作引入了一个共享的主线分支。这意味着理解 merge 和 rebase 是如何工作的,它们的区别是什么,以及如何使用它们来为自己服务,以便将来使用 git 时更加有效,这是非常重要的。
合并和重定基础之间的关键区别在于它如何处理代码历史,以及您是否需要新的提交。在合并的情况下,维护代码的历史,包括分支最初从主线分叉的点,并且需要新的提交来将分支合并在一起。相反,rebase 只是将新分支的提交放在主线的顶部,而不管分支的来源。这保持了主线中的线性,可以很容易地导航,但这是以保留提交的直接历史为代价的。您使用哪一个通常是个人偏好或取决于项目负责人,但为了以防万一,了解如何使用这两者是值得的!
安装
如果你已经安装了 git 和 gitbash,那太好了!不用担心设置,你应该准备好了。如果没有,请随意查看下面的帖子,其中涵盖了如何安装 git 和开始使用 git 命令。
合并
一旦设置好 git 和 gitbash,我们就可以继续创建一个测试文件夹,我们将使用它来查看 git merge 会发生什么。我们将简单地称这个文件夹为git_test_merge
,但是你想怎么叫都可以!一旦创建了文件夹,您就需要cd
进入并创建一个新的空存储库。
为此,您将需要使用命令git init
来创建新的存储库。然后你应该看到文件夹中创建的.git
文件,并且main
或master
(如果你使用的是旧版本的 git)应该出现在你所在的文件夹目录之后。为此,您应该已经运行了这三个命令:
mkdir git_test_mergecd git_test_mergegit init
这将在文件夹git_test_merge
中创建一个 git 存储库,并为您提供该存储库的所有功能。出于这个测试的目的,我们将首先创建一个文件,添加一些行并创建两个提交,然后再创建一个分支,稍后我们将合并到这个分支中。这只是为了展示提交的结构以及分支如何处理这些提交。
要创建一个文件并向其中添加一行,我们可以使用下面的命令echo "This is a test file" >> test_file.txt
。这将创建文件test_file.txt
,并附加第一行“这是一个测试文件”。如果你想检查这是不是真的,你可以使用命令nano test_file.txt
来检查它是否工作。
一旦您创建了文件,我们希望使用git add test_file.txt
提交该文件,然后我们可以使用git commit -m "Create new test_file"
提交我们所做的更改。这将在我们的存储库中创建第一个提交。
这应该采用以下命令的形式:
echo "This is a test file" >> test_file.txtgit add test_file.txtgit commit -m "Create new test_file"
我们可以通过使用gitk
使用内置的 git GUI 来检查这种情况。在这种情况下,将出现以下内容:
这是一个 GUI,向您显示以前的提交以及它们是如何交互的。到目前为止,我们只在主分支上创建了一个提交,如下所示。它还向您展示了是谁在何时提交的,以及与此提交相关的文件中的更改。虽然这是非常有用的功能,但我们将只关注之前提交的可视化表示,这样我们就可以看到git merge
和git rebase
都发生了什么。
在我们继续之前,我们可以创建第二个提交,这样在我们创建一个新的分支来合并之前,我们可以在我们的master
分支上看到一个清晰的历史。这可以通过以下方式简单实现:
echo "This is the second line of the test_file" >> test_file.txtgit add test_file.txtgit commit -m "Add second line to the test_file"
此时,我们应该在master
分支上看到两个提交,这创建了一个清晰的历史。然后我们想要创建一个分支,稍后我们想要将它合并回master
分支。出于我们的目的,这可以称为merge_branch
,但是通常以您试图创建的特征命名。我们可以使用以下命令创建这个分支:
git checkout -b merge_branch
这将创建分支(由于-b
命令)并由于checkout
命令检查分支。
如果您再次使用gitk
,您应该会看到如下内容:
其中我们现在有一个master
和一个merge_branch
,它们目前都处于“向 test_file 添加第二行”提交状态。我们还可以看到,我们已经检出了merge_branch
,因为它是粗体的(不是很清楚,但它在那里)。
为了检查 merge 是如何工作的,我们可以为这个分支创建两个新的提交。这将像以前一样通过创建一个新文件并向其中添加两行来完成。您可以自己尝试一下,看看是否可以复制我们在上面完成的过程,如果不能,那么您可以遵循下面的命令:
echo "This is a new file" >> merge_file.txtgit add merge_file.txtgit commit -m "Create a merge_file"echo "This is the second line of the merge file" >> merge_file.txtgit add merge_file.txtgit commit -m "Add a second line to the merge file"
然后我们可以再次使用gitk
命令来查看merge_branch
应该比主分支提前两次提交,如下所示:
如果我们现在简单地进行合并,我们将不得不使用git checkout master
检查master
分支并运行git merge merge_branch
。结果看起来会像这样:
这将为您提供一个完整的合并,很好,但它并没有真正显示您想要的合并。以这种方式运行它将会把来自merge_branch
的所有提交放到master
分支上。这并没有显示太多,因为在现实中,master
分支可能会有进一步的提交,这会使过程变得复杂。
因此,我们可以通过检查master
分支并创建一个新的提交来复制它。这可以通过以下方式实现:
git checkout masterecho "This is the third line" >> test_file.txtgit add test_file.txtgit commit -m "Add third line to the test_file"
然后我们需要创建合并,将merge_branch
引入到master
分支中。我们这样做是因为位于我们想要将提交放入的分支中。在这种情况下,我们已经检查了master
分支,所以我们是在正确的地方。
然后我们执行git merge merge_branch
,指定我们想要将merge_branch
合并到主分支中。然后,它会要求将提交消息添加到合并中,因为合并将被注册为提交。这种情况下,我们可以简单的命名为merge merge_branch into master
。
一旦完成,我们就将merge_branch
合并到master
分支中。我们可以再次使用gitk
将它形象化如下:
在这里,提交的图形保留了提交来自两个分支的位置,这两个分支源自提交“将第二行添加到 test_file”。我们还可以看到它们在那之后如何分化的历史,直到我们将merge_branch
合并回master
。
这样做的好处是它维护了提交历史和顺序。您知道提交的顺序以及它们来自哪里,这意味着跟踪提交来自哪里变得更加容易。然而,问题是,如果你有许多分支,你想合并,那么这可能会变得凌乱和难看。这意味着清楚地理解历史会变得困难,这也是为什么许多开发人员更喜欢使用 rebase 而不是 merge。
重置基础
git 中的 Rebasing 将接受指定的提交,并将它们放在另一个分支中的提交之上。这样做的好处是,一旦您删除了分支,提交流将呈现线性,这将很容易导航和跟踪。然而,这样做的缺点是,它不能像合并那样直接保存工作流的历史。
为了了解这是如何工作的,我们可以基本上重复我们为合并工作流所采取的大部分步骤。我们可以使用mkdir git_test_rebase
、cd
创建一个名为git_test_rebase
的新文件夹,然后使用git_init
初始化 git 存储库。然后,我们可以创建一个包含两行代码和两次提交的新文件:
echo "This is a new file in the rebase folder" >> rebase_file.txtgit add rebase_file.txtgit commit -m "Create a new rebase text file"echo "This is the second line in the rebase file" >> rebase_file.txtgit add rebase_file.txtgit commit -m "Add second line to rebase_file"
由此,我们可以创建一个新的分支,稍后我们将使用以下内容将该分支重置回master
分支:
git checkout -b "rebase_branch"
在这个新分支中,和以前一样,我们可以创建另一个新文件并添加两个提交,这样新分支就有了一些历史:
echo "This is a new file in the rebase branch" >> new_file.txtgit add new_file.txtgit commit -m "Create new file"echo "This is the second line in the new file" >> new_file.txtgit add new_file.txtgit commit -m "Add second line to the new file"
然后我们可以进行checkout master
分支,以便像以前一样,我们可以向rebase_file
添加一个新行,并在主分支上创建第三个提交:
echo "Third line to the rebase_file" >> rebase_file.txtgit add rebase_file.txtgit commit -m "Add third line to the rebase_file"
在这一点上,我们回到了在最后一个存储库中执行merge
时的位置,在master
分支中执行了三次提交,在rebase_branch
中执行了两次提交,在第二次提交后 rebase 分支被分支。
这里的不同之处在于,我们现在必须再次对checkout
和rebase_branch
进行调整,因为是从中您想要合并的分支开始执行的,而不是从您想要合并的分支开始。一旦我们完成了这些,我们可以使用git rebase master
来告诉分支,我们想要将这个分支的所有提交重新放入master
分支。我们可以再次使用gitk
来检查发生了什么:
这里的主要区别是这个rebase
命令只是将来自rebase_branch
的提交放在了master
分支提交之上,而不需要创建新的提交。它也没有将master
分支指针移动到最近提交的位置。为此,我们可以使用以下命令强制master
分支执行rebase_branch
上的最新提交:
git branch -f master rebase_branch
这将显示:
当我们做gitk
时。为此,你必须确保没有检出master
分支,否则你将无法移动指针。
我们可以看到,rebase 已经将rebase_branch
的提交放在了master
分支的顶部,我们必须将master
分支的指针向上移动到这些新的提交。虽然这保留了提交的行结构,并且看起来很清晰,但是它没有按照提交的顺序保留提交的历史,这对于某些人来说是不理想的。
结论
现在,您知道了如何在一个特性分支上执行合并和重置,以合并提交。要记住的一个关键点是,必须从您想要合并的分支执行 rebase,而合并必须从您想要合并的分支执行。您还需要记住,合并将保留提交的历史以及它们来自哪里,但它可能会创建一个混乱的历史,通常很难破译,您需要一个额外的提交。相比之下,rebase 会产生一个很好的线性历史,易于阅读,但它不会保留它们的直接历史,而是将来自签出分支的提交放在指定分支的现有提交之上。您更喜欢哪种类型将是个人偏好之一,取决于他们各自的权衡,但在项目中,通常最好在开始时指定您将使用哪种约定。否则,您可能会以 rebases 和 merges 结束,这会混淆存储库的历史。
如果你喜欢你所读的,并且还不是 medium 会员,请使用下面我的推荐链接注册 Medium,来支持我和这个平台上其他了不起的作家!提前感谢。
https://philip-wilkinson.medium.com/membership
或者随意查看我在 Medium 上的其他文章:
https://python.plainenglish.io/a-practical-introduction-to-random-forest-classifiers-from-scikit-learn-536e305d8d87
掌握 Git:重置 v 还原
原文:https://towardsdatascience.com/mastering-git-reset-v-revert-12701108a451
你应该使用哪一个,为什么?
照片由 Praveen Thirumurugan 在 Unsplash 上拍摄
Git 是所有数据科学家和软件工程师都应该知道如何使用的工具。无论您是独自从事一个项目,还是作为一个大型分布式团队的一部分,了解如何使用 Git 从长远来看都可以为您节省大量时间。这是因为 git,以及更广泛的版本控制,允许您跟踪对代码库所做的更改,并与其他人有效地协作。你想逆转你所做的改变,用 git 很容易做到,你想从现有的代码中分离出一个新的特性,git,你想在你做改变的同时运行一个有效的产品,git,你想和其他人在同一段代码上工作,git 和 github!这真的很神奇。
Git 是所有数据科学家和软件工程师都应该知道如何使用的工具,所以我整理了一系列有助于掌握 git 的关键工具和功能。这篇文章是这个系列的第二篇文章,将介绍 git 重置和 git 恢复,它们可以用来撤销提交历史中的更改。如果您错过了本系列的第一篇文章,关于 git merge 和 git rebase,可以在下面找到链接:
git 用户会遇到的两个常用工具是git reset
和git revert
的工具。这两个命令的好处是,您可以使用它们来删除或编辑您在之前提交的代码中所做的更改。理解它们是如何工作的将会为你节省大量的时间,让你编写更干净的代码,并且当你这样做的时候对提交有更多的信心。
重置
在提交级别(我们改变整个提交),重置是将分支的当前尖端移动到先前提交的一种方式。这样做是为了从当前分支中删除我们不再需要的提交,或者撤销已经做出的任何更改。一个例子是使用下面的命令将分支new-feature
从当前的HEAD
向后移动两次提交:
git checkout new-feature reset HEAD~2
看起来像这样:
作者图片
朝向new-feature
分支头部的两个提交变得悬空或孤立。这意味着它们将在下次 Git 执行垃圾收集时被删除,实质上是从提交历史中删除它们。
但是,其效果取决于附加到命令的标志:
- 标志意味着尽管我们回复了两次提交,但这些提交中的更改仍然是工作目录的一部分,并被准备提交。如果此时运行
git commit
,那么属于提交C
和B
一部分的所有更改都将被提交,您将处于与重置前相同的阶段。 --mixed
是默认标志,在没有指定其他标志时使用。这样,虽然工作目录没有更改,也就是说没有文件被更改,但这些更改都不会被提交。这意味着如果你运行git status
,你会看到所有被修改的文件都是红色的,等待提交。--hard
标志将更改暂存快照和工作目录,以删除提交B
和C
中的所有更改。这意味着你根本看不到B
或C
的变化,就像它们从未存在过一样。这是最极端的重置,意味着它将把所有东西都变回提交A
后的状态。
git reset
的这种用法是一种简单的方法,可以撤销还没有与其他人共享的更改。这是因为git reset
改变了存储库的历史,所以应该只在改变没有公开时使用,比如当推送到远程存储库时。否则,当其他开发人员在您使用git reset
更改的提交基础上构建时,可能会出现导致合并冲突的问题,这肯定不会使您成为团队中最受欢迎的开发人员!
这意味着当提交主要在您的本地存储库时,可以使用git reset
。用例包括当在当前分支上开发的新特性应该是它们自己的分支的一部分时,当在先前的提交中引入了错误并且您想要移除它们时,或者当您想要改变提交历史时,例如不同提交中的不同文件或者不同提交名称。
归还
虽然git reset
应该主要用于存储库的非公共分支,但是有时您想要撤销对公共存储库所做的更改。这可能是因为所做的更改引入了意外的错误,或者更改本身并不需要。在这种情况下,应该使用git revert
而不是git reset
。
这是因为恢复会通过创建新的提交来撤消提交。这使它成为撤销公共提交历史中的更改的安全方式,因为它不会覆盖任何历史,而是在新的提交中撤销所有这些更改。例如,当您想要撤销提交B
中所做的更改时,您可以使用git revert B
创建一个新的提交来撤销所有这些更改。这样做的时候,git 会找出在B
中所做的更改,如果可能的话撤销所有这些更改,然后将一个新的提交添加到现有的项目中,如下所示:
git revert B
作者图片
如果可以进行更改,并且没有合并冲突,那么将打开一个编辑器,要求您命名将被添加到当前头文件末尾的新提交,就像B*
一样。
这保存了提交历史,并潜在地减少了您的团队可能必须处理的合并冲突的数量。但是在你这么做之前,因为git revert
有可能覆盖文件,你需要提交或者隐藏在恢复操作中可能丢失的更改
结论
git reset
和git revert
都是撤销先前提交中所做更改的有用命令。虽然git reset
通过将分支的当前头移回指定提交来实现这一点,从而改变提交历史,但是git revert
通过创建新提交来实现这一点,该新提交撤销指定提交中的改变,因此不改变历史。这意味着,当您想要在多次提交中撤销更改时,git reset
可能更有用,但通常应该仅在更改尚未公开时使用,而git revert
可以在仅需要撤销特定提交时使用,甚至可以在更改已经公开时使用。知道如何使用这两个命令会让你在提交时更有信心,因为你知道它们是可以撤销的,并且有希望让你和你的团队更有效率!
如果你喜欢你所读的,并且还不是 medium 会员,请使用下面我的推荐链接注册 Medium,来支持我和这个平台上其他了不起的作家!提前感谢。
https://philip-wilkinson.medium.com/membership
或者随意查看我在 Medium 上的其他文章:
https://python.plainenglish.io/a-practical-introduction-to-random-forest-classifiers-from-scikit-learn-536e305d8d87
在 Matplotlib 中控制插入轴
原文:https://towardsdatascience.com/mastering-inset-axes-in-matplotlib-458d2fdfd0c0
由 geralt 在 Pixabay 上拍摄封面图像。
嵌入轴是 Matplotlib 中一个奇妙的(且经常未被充分利用的)工具。它们可用于:
- 放大图的特定部分以更详细地显示它们。
- 用附加信息替换图中的空白区域。
- 给你的数字一点额外的活力!
在这篇文章中,我将介绍使用嵌入轴的基本知识,然后给出如何定制和改进你的绘图的额外细节。本文末尾的 Python 笔记本中包含了用于生成所有图的代码。
基础知识
创建嵌入轴有三个主要组成部分。首先,您需要一个现有的环绕轴来添加插入轴。然后,您需要创建插入轴,并定义它在环绕轴中的位置。最后,您需要将插入数据绘制到插入轴上。一个可选步骤是添加线条来指示插入缩放。这可以通过以下方式实现:
# Code snippet to create inset axes.
# axis is the surrounding axis into which the inset axis will be added# Create an inset axis in the bottom right corner
axin = axis.inset_axes([0.55, 0.02, 0.43, 0.43])# Plot the data on the inset axis and zoom in on the important part
plot_data(axin)
axin.set_xlim(-10, 12)
axin.set_ylim(0.75, 1.4)# Add the lines to indicate where the inset axis is coming from
axis.indicate_inset_zoom(axin)
在上面的例子中,axis.inset_axes
创建了插入轴axin
,并定义了它在原始axis
中的位置(稍后将详细介绍这个定位)。插入轴中显示的区域是通过设置轴限制来控制的,即axin.set_xlim
和axin.set_ylim
——把这想象成裁剪原始图,这样我们可以放大重要的部分。只需使用indicate_inset_zoom
即可添加缩放线和边框。
这将产生类似如下的结果:
使用单个插入轴放大部分绘图的示例。作者创造的形象。
嵌入轴的定位是轴相对于轴的定位。这意味着位置与轴限制无关。嵌入轴的坐标应在0,0和1,1之间。axis.inset_axes
需要四个参数来定义边界:[x0,y0,width,height]。因此,例如,[0.4,0.4,0.2,0.2]给出了一个位于图中心的插入轴,,而不管轴限制是什么:
当外部轴限制改变时,插入轴不会移动,因为(默认情况下)位置是相对于轴的。作者创造的形象。
越来越花哨
我们可以通过使用嵌入轴让更有创意:
- 没有理由为什么我们不能在我们的地块中有多个插入轴。也许我们想放大两个区域?
- 有时,嵌入轴中的内容可能实际上比整体情节更有趣。我们可以交换内容,以便周围的绘图包含放大的版本,并且插入轴显示更宽的视图。
- 插入轴甚至不需要显示与周围绘图相同的数据。放大只是一个用例。显示附加信息也很好。
下面我们用上面的图给出了这些不同用例的例子。看看带插入轴的图比无聊的原始图好看多了!
使用嵌入轴制作更有趣的图的一系列选项。作者创造的形象。
更多提示
创建插入轴时,还有一些需要注意的事项。
纵横比 —考虑插入轴的纵横比与其显示的原始区域的纵横比非常重要。这会导致插入轴中原始数据的拉伸或挤压,这可能会导致数据失真。例如:
使用嵌入轴时要注意不同的纵横比。作者创造的形象。
轴定位 —上面,我提到了轴相对于坐标定位嵌入轴。或者,可以使用相对于坐标的数据。创建插入轴时设置transform=axis.transData
。不过要小心!这使得嵌入轴的位置对轴限制敏感:
轴与数据相对定位。当轴限制改变时,使用数据相对定位时,插入轴会移动。作者创造的形象。
Z 排序— 默认情况下,插入轴应该出现在您已经绘制的任何内容的顶部。然而,对于手动控制,您可以调节zorder
来控制顶部的内容:
更改插入轴的 z 顺序值允许您将它们放置在现有数据的顶部,甚至是彼此之上。作者创造的形象。
下面是我用来生成本文中的情节的 Python 笔记本:
这就是我使用 Matplotlib 嵌入轴的指南。如果您有任何问题,请发表评论或联系我。感谢阅读,密谋愉快!
在 dbt 中掌握数据透视表
原文:https://towardsdatascience.com/mastering-pivot-tables-in-dbt-832560a1a1c5
这个函数将节省您编写 SQL 的时间。
照片由恩库鲁列科·乔纳斯在 Unsplash 上拍摄
有多少次你不得不把一个过于复杂的解决方案放在一起来解决一个简单的问题?如果你以前曾经编写过代码,你可能至少可以列出一些例子。对我来说,有几次是围绕着数据透视表进行的。它们听起来很简单,但是用 SQL 写起来很复杂。
幸运的是,我最近发现 dbt 在 dbt utils 中有一个函数,可以快速简单地创建数据透视表。不需要复杂的聚合或连接!
什么是数据透视表?
首先,让我们从什么是数据透视表开始。不,这不是 Excel 数据透视表。它是一个函数,将一列中的唯一值转换成它们自己的列,用一个布尔值填充该行的相应单元格。
例如,透视如下所示的数据…
作者图片
…会产生如下所示的数据…
作者图片
数据透视表用例
这是聚合和计算数据中是否存在某些特征的好方法。当我发现这个技巧时,我需要旋转一个充满不同葡萄酒特征的列。我希望每个特征都有一列,并有一个值告诉我该葡萄酒是否存在该特征。
使用数据透视表也是确保表中有唯一主键的理想方法。通常,当您的列包含多个 id 可能存在的不同属性时,您的主键不再是唯一的。由于一个列会产生重复项,因此最终会有多个 id 相同的行。
还不理解?
让我们看看这一款酒。看到除了wine_features
列,每一列的值都是一样的吗?因为这种酒是干的、有机的、生物动态的,所以创建了一个新的行来代表这些特征。
作者图片
这通常是因为您需要将数据库中的表连接在一起。不幸的是,数据透视表是避免这个问题并保持数据模型整洁的唯一方法。
你如何使用 dbt 数据透视表?
dbt 包含一个名为dbt _ utils . pivot的函数,它可以帮助您使用一行简单的代码透视表格。
看起来是这样的:
{{ dbt_utils.pivot(‘<column name>’, dbt_utils.get_column_values(ref(‘<table name>’), ‘<column name’)) }}
您只需指定要透视的列名。确保用单引号括起来。
您还可以指定该列所在的表的名称。同样,确保你用单引号括起来。
不幸的是,这需要是数据库中的一个永久表。你不能引用一个由 CTE 创建的表格,这是一个错误。我必须创建一个永久表,其中包含我需要连接在一起的表,然后在这个 pivot 函数中引用这些表。让它成为自己的中介模型就行了。
在实际查询中使用 pivot 函数时,它应该是这样的:
select
wine_name,
{{ dbt_utils.pivot('wine_features', dbt_utils.get_column_values(ref('wine_features_joined'), 'wine_features')) }}
from {{ ref('wine_features_joined') }}
group by wine_name
您可以看到,该函数替换了 SQL 查询中列名通常所在的位置。只有作为分组依据的列才是应该选择的其他列。
还要注意,我使用了一个{{ ref() }}
函数,因为我从一个永久表中进行选择,该表已经作为数据模型存在于我的 dbt 环境中。不要从 cte 中选择!
如果我们从一张葡萄酒及其不同特征的表格开始,看起来像这样…
作者图片
使用 pivot 函数运行我在上面展示的查询将导致该表如下所示:
作者图片
注意曾经是wine_features
列中的值的特性现在是列标题。这些列中的布尔值显示该功能是否存在于原始列中。
另一件需要记住的事情是…
通常,旋转列的数据类型很难处理。请确保将它们转换为适当的数据类型,并根据需要重命名它们。在这里,您可以看到透视列的数据类型是一个数字。显然,这是错误的。
作者图片
我不得不为我的每一个专栏重新编写和重命名我的专栏:
wine_features."organic" AS is_organic
引号允许您选择雪花中的列,尽管数据类型与预期的不同。
结论
现在,您已经准备好使用 dbt utils pivot 函数来透视表了!这将使你的生活变得更加简单,你的代码也更加整洁。请继续关注更多关于 dbt 在其 utils 包中为您提供的便利函数的文章。
在那之前,通过订阅我的时事通讯来了解更多关于 dbt 和现代数据栈的知识。
通过讲故事掌握分类模型的评估
原文:https://towardsdatascience.com/mastering-the-evaluation-of-classification-models-with-storytelling-f8a9f63f3723
数据科学家必备的知识
照片由 Unsplash 上的 Aditya Romansa 拍摄
今天,我们可以从世界各地获得源源不断的数据。分类模型是最流行的机器学习工具之一,用于在数据中发现模式并理解它,以便我们可以揭示决策的相关见解。它们是一种监督学习的形式,在这种学习中,我们训练一个模型,根据预先确定的特征对数据点进行分组。反过来,该模型输出数据点属于特定类别的可能性或概率。
用例层出不穷,广泛分布于各个行业— 语音识别、垃圾邮件检测、异常/欺诈检测、客户流失预测、客户细分和信用评估。
因此,作为一名数据科学家,掌握分类模型的艺术至关重要。
在本文中,我们将关注在数据科学中创建模型的最后一个步骤:评估模型性能,或者换句话说,评估分类的好坏。
有什么比一个好故事更能解释这些指标以及如何使用它们呢?
假设你是所在城市医院产前部的负责人。你的目标是为未来的父母提供最积极的体验。在这方面,你们雇佣了最好的医生,并组建了一支由护士和助产士组成的梦之队来支持他。
乌斯曼·优素福在 Unsplash 上拍摄的照片
医生忙得不可开交,没有时间检查所有的病人以确定他们是否怀孕。所以他使用分析和不同的血液标记进行验证。护士的角色是探访病人以确保预见性。这里我们有 4 种可能的情况:
- 医生说病人怀孕了,护士确认
→ 真阳性(TP) - 医生说病人怀孕了,但护士宣布无效
→ 假阳性【FP】 - 医生说病人没有怀孕,护士确认
→ 真阴性(TN) - 医生说病人没有怀孕,但护士宣布无效
→ 假阴性(FN)
作为部门主管,你专注于提供最佳质量的服务,因此你想评估医生在识别早孕方面的能力。为此,您可以使用 5 个关键指标:
1.准确(性)
准确性可能是最常见的度量标准,因为它相对容易理解。它是正确预测数除以预测总数的比值。
(TP+TN) / (TP + FP + TN + FN)
换句话说,准确性会告诉我们医生在给病人分类方面有多好。
50%的准确率意味着这个模型和掷硬币一样好。一般来说,根据应用领域的不同,我们的目标是达到 90%、95%或 99%以上的精度。
记住:有人说,如果我们有高精度,我们就有一个好模型。只有当数据集平衡时才是这样,也就是说,类的大小相对均匀。
假设在患者群中有更多未怀孕的患者(,即怀孕患者占少数)。在这种情况下,我们说样本是不平衡的,精确度不是评估性能的最佳指标。
2.精确
精度是正确预测的正元素数除以预测的正元素总数。所以这是一个准确性和质量的衡量标准——它告诉我们医生在预测怀孕方面有多好。
TP / (TP + FP)
根据模型应用,具有高精度可能是至关重要的。始终评估出错的风险,以决定精度值是否足够好。
如果医生宣布怀孕(但他错了),这可能会影响患者,因为他们可能会就这个重大消息做出改变人生的决定(例如,买新房子或换车)。
3.回忆
回忆——又名敏感度或真阳性率或命中率,是与实际阳性数相比,正确预测的阳性元素数。它告诉我们医生在检测怀孕方面有多好。
TP / (TP + FN)
与 precision 类似,根据模型应用,拥有高召回率可能是至关重要的。有时,我们不能错过一个预测(欺诈,癌症检测)。
假设医生错过了一个病例,没有预测到怀孕。患者在怀孕期间可能会保持一些不健康的习惯,如吸烟或饮酒。
4.特征
特异性—也称为选择性或真阴性率,总结了当结果为阴性时预测阳性类别的频率。可以理解为误报警指示灯。
TN / (TN + FP)
理想情况下,一个模型应该具有高特异性和高召回率,但这是一个权衡。每个模型都需要选择一个阈值。
基于上面提到的原因,我们不想错过一个怀孕案例。同时,如果我们没有可靠的血液标志物来确认怀孕,我们也不想提醒患者。作为部门主管,你需要决定医生的预后不够好的转变点是什么,我们需要护士进行体检。
5。F-measure | F1 得分
F1 分数是精确度和召回率的加权调和平均值。它反映了一个模型的有效性——当错过一个病例或错误宣布怀孕同样有风险时,医生的表现如何。
2 x (Precision x Recall) / (Precision + Recall)
当样本不平衡,准确性变得不合适时,我们可以使用 F1 分数来评估模型的性能。
如果没有怀孕的患者比怀孕的患者多得多,我们认为样本是不平衡的,并将使用 F1 分数来评估医生的表现。
既然我们已经设置了评估分类模型的关键指标,我们可以更仔细地研究如何以一种令人信服的方式可视化它们。
混淆矩阵
混淆矩阵或误差矩阵是一个简单的表格,汇集了来自分类模型的预测结果:*真阳性、真阴性、假阳性&假阴性。*它通过分解每个类别的正确和错误预测的数量,帮助可视化分类器产生的错误类型。
来源:蛋白质折叠过程中早期折叠残基可解释分类模型的应用(Sebastian Bittrich)
混淆矩阵突出显示了模型在进行预测时混淆的地方。
因此,与单独使用准确性相比,这是一种有用的可视化方法,因为它显示了模型的薄弱之处,并提供了改进它的可能性。
ROC 曲线和精确回忆曲线
ROC 曲线是假阳性率(也称为反向特异性)对真阳性率(也称为灵敏度)的曲线图。如果每个类别的患者数量大致相等(即平衡数据集),则应使用精确-召回曲线,而非平衡病例应使用精确-召回曲线(来源)。
一个好的模型用一条从 0 到 1 快速增长的曲线来表示。该公司表示,该模型不会牺牲太多的精确度来获得高召回率。
一个糟糕的模型——又名无技能分类器——不能区分类别,因此,在所有情况下都预测一个随机或恒定的类别。这些模型由从图的左下角到右上角的对角线表示。
来源:作者
所以你现在可能知道曲线的形状给了我们调试模型的宝贵信息:
- 如果曲线更接近左下方的随机线,则表明假阳性较低,真阴性较高。另一方面,如果图的 y 轴上有更大的值,这意味着更高的真阳性和更低的假阴性。
- 如果曲线不平滑,则意味着模型不稳定。
这些曲线也是比较模型和选择最佳模型的重要工具。
曲线下面积(AUC 或 AUROC)通常用于总结模型技巧。它可以取从 0.5(最差模型)到 1(完美模型)的值。
AUROC 值越高,模型越好。
现在,您已经掌握了开始评估您的分类模型所需的所有基本知识,对于您的数据科学家角色来说,同样重要的是,要有一个好的故事来向大量观众传达您的表现。
像往常一样,给我留下你的评论和消息,我会很高兴收到你的来信,让这篇文章变得更好。在 Medium 上关注我,了解更多关于数据科学的内容。
https://agiraud.medium.com/membership
用 Go 掌握 WebSockets
原文:https://towardsdatascience.com/mastering-websockets-with-go-c30d0ac48081
关于如何在 Go 中使用 WebSockets 构建实时 API 的教程
图片由珀西·博尔梅尔提供。Gopher 由拓也·上田提供,原始 Go Gopher 由勒内·弗伦奇提供(CC BY 3.0)
如果我们仔细想想,常规的 HTTP APIs 是愚蠢的,就像,真的愚蠢。我们可以通过发送数据请求来获取数据。如果我们必须在网站上保持数据新鲜,我们将不得不不断地请求数据,即所谓的轮询。
本文中的所有图片均由珀西·博尔梅尔制作。Gopher 由上田拓也制作,Go Gopher 由蕾妮·弗伦奇原创(CC BY 3.0)
这就像让一个孩子坐在后座上问“我们到了吗”,而不是让司机说“我们到了”。这是我们在设计网站时开始使用的方式,很傻是不是?
令人欣慰的是,开发人员已经通过诸如 WebSockets 、 WebRTC 、 gRPC 、 HTTP2 Stream 、 ServerSent Events 以及其他双向通信等技术解决了这个问题。
WebSockets 是最古老的双向通信方式之一,如今被广泛使用。它被大多数浏览器支持,并且相对容易使用。
在本教程中,我们将介绍什么是 WebSockets 以及它们如何工作,如何在 Go 中使用它们在服务器和客户端之间进行通信。我们还将探讨我在 WebSocket APIs 中看到的一些常见缺陷,以及如何解决它们。
在本教程中,我们将建立一个聊天应用程序,您可以进入不同的聊天室。WebSocket 服务器将使用 Go 构建,客户端使用普通 JavaScript 连接。当使用用 Go、Java、React 或任何其他语言编写的 Websocket 客户端进行连接时,我们学习和应用的模式可以很容易地进行调整。
这篇文章也有录音,可以在我的 YouTube 频道上看。
在 YouTube 上掌握 Go 中的 WebSockets
什么是 WebSockets &为什么您应该关注它
如何用简单的术语初始化 WebSocket
WebSocket 标准在 RFC 645 中定义。
WebSockets 使用 HTTP 向服务器发送初始请求。这是一个普通的 HTTP 请求,但是它包含一个特殊的 HTTP 头Connection: Upgrade
。这告诉服务器,客户机正试图将 HTTP 请求 TCP 连接升级到一个长期运行的 WebSocket。如果服务器用一个HTTP 101 Switching Protocols
响应,那么连接将保持活动,使得客户端和服务器能够双向、全双工地发送消息。
一旦这个连接被同意,我们就可以发送和接收来自双方的数据。WebSockets 没有更多的内容,这可能是你开始使用它们所需要了解的。
如果你想更多地了解在安装过程中到底发生了什么,我可以推荐 RFC。
您可能想知道是否需要实时解决方案。所以这里有几个经常使用 WebSockets 的领域。
- 聊天应用 —需要接收消息并转发给其他客户端的应用,这是 WebSockets 的完美匹配。
- 游戏— 如果你开发一款多人游戏,并且是基于网络的,那么 WebSockets 就是真正的天作之合。您可以从客户端推送数据,并将其广播给所有其他玩家。
- 提要— 对于需要数据提要的应用程序,可以使用 WebSockets 轻松地将更新的数据推送到任何客户端。
- 实时数据— 基本上,只要你有实时数据,WebSockets 就是一个很好的解决方案。
开始应用程序的基础
项目设置—一个 Go 后端和一个 JavaSript 客户端
我们将首先设置一个简单的 HTTP 服务器,它也使用文件服务器托管我们的 web 应用程序。我想避免使用 React 等任何 web 框架,所以我们将使用原生 JavaScript。通常,当连接到 WebSocket 时,步骤非常相似,所以将它移植到您使用的任何框架中应该没有问题。
首先初始化一个新模块
go mod init programmingpercy.tech/websockets-go
然后我们创建一个新文件main.go
,这将是我们的应用程序的起点。
我们将首先设置应用程序来服务 API 并托管 HTML/JS 代码。一旦我们完成了这些,我们将开始实际的 WebSocket 实现,这样更容易理解。
让我们用一个简单的代码填充main.go
来托管我们即将构建的网站。我们将只服务于frontend
目录,我们将创建和存储前端代码。
main . go——第一个简单托管前端的版本
现在让我们添加前端,这将是一个简单的原始 HTML/JS/CSS 文件,显示我们惊人的期待聊天应用程序。它由一个表单chatroom-selection
和另一个表单chatroom-message
组成,前者供用户进入某个聊天室,后者用于通过 WebSocket 发送消息。
这只是简单的 HTML 和 JavaScript,但是还没有实现 WebSocket 实现。唯一值得一提的是window["WebSocket"]
,这是一个全局变量,你可以用它来检查客户端的浏览器是否支持 WebSocket。如果没有定义,我们会提醒用户他们的浏览器不受支持。
创建一个名为frontend
的文件夹和一个名为index.html
的文件。然后用下列要点填充 index.html。我不会涉及 HTML 和 JS 部分,我希望你熟悉它们。
frontend/index.html —还没有 WebSockets 的简单网站
如果您在终端中运行带有go run main.go
的应用程序,并访问 localhost:8080 ,您应该会看到一个令人惊叹的网站,它拥有我们开始实现 WebSockets 所需的一切。
localhost:8080 —令人惊叹的聊天应用程序
现在,发送消息和改变聊天室除了打印到控制台之外什么都不做,但这是我们将要实现的。
在客户端和服务器之间连接 WebSocket
连接客户端和服务器
为了开始,我们将添加到前端,以便它尝试连接到我们的 WebSocket API。这在 JavaScript 中很容易,只需一行代码就可以完成。
在 JavaScript 中,有一个内置的 WebSocket 库,无需导入任何东西就可以使用。我们可以用new WebSocket(URL)
创建客户端,但是首先我们需要创建 URL。URL 由协议组成,就像常规的 HTTP URL 一样,后跟路径。将 WebSockets 放在一个/ws
端点上是一个标准。
我们使用 WebSockets 时有两种协议,一种是ws
,另一种是wss
。它的工作原理就像 HTTP 和 HTTPS,额外的 S 代表安全,并将对流量应用 SSL 加密。
强烈建议使用它,但需要证书,我们稍后会应用它。
让我们在 windows.onload 函数中添加一行连接到ws://localhost/ws
的代码。
index.html—添加了到后端的连接
现在,您可以继续运行程序,当您访问网站时,您应该会看到控制台上打印出一个错误,我们无法连接。这仅仅是因为我们的后端还不接受连接。
让我们更新后端代码以接受 WebSocket 连接。
我们将从构建我们的Manager
开始,它用于服务连接并将常规 HTTP 请求升级到 WebSocket 连接,管理器还将负责跟踪所有客户端。
我们将使用 Gorillas WebSocket 库来处理连接,这是通过创建一个Upgrader
来完成的,它接收 HTTP 请求并升级 TCP 连接。我们将为升级程序分配缓冲区大小,该大小将应用于所有新客户端。
管理器将公开一个名为serveWS
的常规 HTTP HandlerFunc,我们将在/ws
端点上托管它。此时,我们将升级连接,然后简单地再次关闭它,但我们可以验证我们可以通过这种方式连接。
创建一个名为manager.go
的文件,将要点中的代码填入其中。
manager.go —管理器起点,可以接受和升级 HTTP 请求
我们还需要将serveWS
添加到/ws
端点,以便前端可以连接。我们将启动一个新的管理器,并在main.go
内的setupAPI
函数中添加 HTTP 处理程序。
main.go —将管理器的服务功能作为端点公开
您可以通过运行以下命令来运行该软件
go run *.go
如果您继续访问该网站,您应该会注意到控制台中不再显示错误,现在连接已被接受。
客户和管理
负责所有客户的经理
我们可以将所有客户端逻辑添加到serveWS
函数中,但是它可能会变得非常大。我建议创建一个用于处理单个连接的客户端结构,该结构负责与客户端相关的所有逻辑,并由管理器管理。
客户端还将负责以同时安全的方式读/写消息。Go 中的 WebSocket 连接只允许一个并发的 writer,所以我们可以通过使用无缓冲通道来处理这个问题。这是大猩猩库的开发者自己推荐的技术。
在我们实现消息之前,让我们确保创建了客户机结构,并赋予管理器添加和删除客户机的能力。
我已经创建了一个名为client.go
的新文件,它现在很小,可以保存任何与我们的客户相关的逻辑。
我将创建一个名为ClientList
的新类型,它只是一个可以用来查找客户端的地图。我还喜欢让每个客户端持有一个对管理器的引用,这允许我们更容易地管理状态,甚至从客户端。
client.go —客户端的第一份草稿
是时候更新manager
来保存新创建的ClientList
了。由于许多人可以并发连接,我们也希望管理器实现sync.RWMutex
,这样我们可以在添加客户端之前锁定它。
我们还将更新NewManager
函数来初始化一个客户端列表。
函数serveWS
将被更新以创建一个带有连接的新客户端,并将其添加到管理器中。
我们还将使用插入客户端的addClient
函数和删除客户端的removeClient
函数来更新管理器。删除将确保优雅地关闭连接。
manager.go —能够添加和删除客户端的管理器
现在,我们已经准备好接受新客户并添加他们。我们还不能正确删除客户端,但我们很快就会这样做。
我们必须实现,以便我们的客户端可以读取和写入消息。
阅读和撰写邮件
以安全的方式同时编写消息
阅读和书写信息看起来似乎是一件简单的事情,事实也的确如此。然而,有一个许多人忽略的小陷阱。WebSocket 连接只允许有一个并发的 writer,我们可以通过让一个无缓冲的通道充当锁来解决这个问题。
我们将更新manager.go
中的serveWS
函数,以便在创建之后为每个客户端启动两个 goroutines。现在,我们将注释掉这段代码,直到完全实现。
manager.go —更新服务程序,为每个客户机启动读/写 goroutine
我们将从添加阅读过程开始,因为它稍微容易一些。
从套接字读取消息是通过使用ReadMessage
完成的,它返回一个messagetype
、有效载荷和一个错误。
消息类型用于解释发送的是什么类型的消息,是 Ping、pong、数据还是二进制消息等。所有类型都可以在 RFC 中读到。
如果出现问题,就会返回错误,一旦连接关闭,也会返回一个错误。因此,我们将希望检查某些关闭消息来打印它们,但对于常规关闭,我们不会记录。
client.go —添加了阅读消息功能
我们可以更新前端代码,并尝试发送一些消息来验证它是否按预期工作。
在index.html
中,我们有一个名为sendMessage
的函数,它现在在控制台中打印出消息。我们可以简单地更新它,将消息推送到 WebSocket 上。用 JavaScript 发送消息就像使用conn.send
函数一样简单。
index.html——传递信息
重新启动程序,在 UI 中输入一条消息,然后按 Send Message 按钮,您应该会看到在 stdout 中发送的消息类型和有效负载。
现在我们只能发送消息,但对消息什么也不做,是时候让我们增加写消息的能力了。
还记得我说过我们只能用一个并发进程来写 WebSocket 吗?这可以通过多种方式解决,Gorilla 自己推荐的一种方式是使用无缓冲通道来阻止并发写入。当任何进程想要在客户端的连接上写入时,它将改为将消息写入无缓冲通道,如果当前有任何其他进程正在写入,该通道将会阻塞。这使我们能够避免任何并发问题。
我们将更新client
结构来保存这个通道,并更新构造函数来初始化它。
client.go —添加充当网关的出口通道
writeMessages
功能与readMessages
非常相似。然而,在这种情况下,我们不会收到一个错误,告诉我们连接被关闭。一旦egress
通道关闭,我们将向前端客户端发送CloseMessage
。
在 go 中,我们可以通过接受两个输出参数来查看通道是否关闭,第二个输出参数是一个布尔值,表示通道关闭。
我们将使用 connections WriteMessage
函数,它接受 messagetype 作为第一个输入参数,接受 payload 作为第二个输入参数。
client.go —处理任何应该发送的消息的函数
如果你熟悉 Go,你可能已经注意到我们在这里使用了一个for select
,它现在是多余的。在本教程的后面,我们将向选择中添加更多的案例。
确保在
serveWS
函数中取消go client.writeMessages
的注释。
现在,在egress
上推送的任何消息都将被发送到客户端。目前没有进程向出口写入消息,但是我们可以快速破解以测试它是否按预期工作。
我们会将在readMessages
中收到的每条消息广播给所有其他客户端。我们将通过简单地将所有输入消息输出到每个客户端的出口来实现这一点。
client.go —在收到的每条消息中添加了一个小广播
我们只在第 29 行添加了 for 循环,稍后我们将删除它。这只是为了测试整个读写过程是否按预期进行。
是时候更新前端来处理传入的消息了。JavaScript 通过触发一些我们可以应用监听器的事件来处理 WebSocket 事件。
这些事件都在文档中进行了解释。我们可以很快覆盖它们。
- 关闭 —当 WebSocket 关闭时触发。
- 错误 —当 WebSocket 由于错误而关闭时触发。
- 消息 —当 WebSocket 收到新消息时触发
- 打开 —当 WebSocket 连接打开时触发。
根据您想要在前端做什么,您可以分配这些事件处理程序。我们对message
事件感兴趣,所以我们将添加一个监听器,暂时只将它们打印到控制台。
一旦连接打开,我们将添加一个简单的函数来打印发送的事件。这个事件对象包含一堆数据,比如发送的时间戳和消息类型。我们将需要包含在data
字段中的有效载荷。
index.html—添加 onmessage 侦听器来处理传入的消息
你现在可以尝试重启软件,访问网站并发送一些信息。您应该在控制台中看到事件正在被发送和接收。
这意味着现在读和写都有效。
使用事件方法进行缩放
构建在 WebSocket 上发送的消息
我们可以连接,我们现在可以发送和接收信息。这一切都很棒,我们有了一个基本的设置。
现在,如果你只想传递一种信息,这可能行得通。我通常发现创建一个基于事件/类型的方法使得扩展 WebSocket 更加容易。
这意味着我们创建了一个发送每条消息的默认格式。在这种格式中,我们有一个特定的字段来解释它是哪种消息类型,然后是一个有效载荷。
怎么,这听起来耳熟吗?
这是应该的,因为这基本上就是 WebSockets 现在正在做的事情。例外情况是,我们将消息作为 JSON 对象发送,我们的应用程序可以使用该对象来路由到要执行的正确操作/功能。
我发现这是一种易于使用、易于扩展并使 WebSocket 在许多用例中得到利用的方法。这是一种 RPC 解决方案。
我们首先在 JavaScript 文件中添加Event
类,这样我们就可以解析传入的消息。然后,我们将这些事件传递给一个routeEvent
函数,该函数检查字段type
的值,并将事件传递给真正的处理程序。
在onmessage
监听器中,我们希望 JSON 格式的数据适合事件类。
我们还将创建一个名为sendEvent
的函数,它将接受一个事件名称和有效载荷。它根据输入创建事件,并将其作为 JSON 发送。
每当用户使用sendMessage
发送消息时,它将调用sendEvent
。
下面的要点展示了处理这个问题的 JavaScript 部分。
index.html—JavaScript 标签被更新以处理事件。
既然网站已经有了接受Events
并发送它们的逻辑,我们还需要让后端处理它们。
首先创建一个名为event.go
的文件,它将包含事件的所有逻辑。
我们希望在后端有一个Event
结构,它应该是 JavaScript 事件类的一个副本。
Event . go—web socket 事件结构
注意,有效载荷的数据类型是一个json.RawMessage
,因为我们希望用户能够发送他们想要的任何有效载荷。由事件处理程序来决定有效负载数据的结构。
当在后端接收到消息时,我们将使用type
字段将其路由到适当的EventHandler
,eventhandler 是一个函数签名。因此,通过创建满足签名模式的新函数来添加新功能是很容易的。
EventHandler 签名将接受消息来自的Event
和Client
。它也会返回一个错误。我们接受客户端,因为一些处理程序可能希望在完成后返回响应或向客户端发送其他事件。
我们还将添加一个SendMessageEvent
,这是事件有效载荷中预期的格式。
event.go —添加了 EventHandler 签名和 SendMessageEvent
我喜欢让Manager
存储EventHandlers
的地图。这允许我很容易地添加东西,在一个真正的应用程序中,管理器可以包含一个数据库存储库,等等。我们将添加它,并添加一个名为setupEventHandlers
的新函数,用于添加所需的函数。
拥有一堆处理程序的一个好方法是将这些EventHandlers
存储在一个映射中,并使用Type
作为键。因此,我们将保留一个包含所有处理程序的映射,而不是使用开关来路由事件。
我们添加了一个routeEvent
函数,它接收传入的事件并从映射中选择正确的处理程序。
如果您有敏锐的眼光,您可能已经注意到 routeEvent 本身是一个 EventHandler,如果需要的话可以这样使用。
manager.go 向管理器添加了事件处理程序
在我们准备好整个事件基础设施之前,最后一件事是更改Client
。客户端的readMessages
应该将传入的 JSON 编组到一个Event
中,然后使用Manager
对其进行路由。
我们还将修改Clients
出口通道,使其不发送原始字节,而是发送Event
。这也意味着我们需要更改writeMessages
来在发送数据之前整理数据。
client.go 添加了事件而不是原始字节的用法
您可以尝试使用go run *.go
重启后端并发送消息。您应该会看到类似于{send_message [34 49 50 51 34]}
的东西正在被打印。由于当前处理程序不解析原始字节,因此有效负载应该打印为字节。
在我们实现它之前,我想确保我们覆盖了一些与 WebSocket 相关的主题。
心跳——乒乓球
通过心跳保持连接
WebSockets 允许服务器和客户端发送一个Ping
帧。Ping 用于检查连接的另一部分是否仍然存在。
但是我们不仅检查我们的其他连接是否还活着,而且我们还保持它活着。一个空闲的 WebSocket 将/可以关闭,因为它已经空闲了太长时间,Ping & Pong 允许我们轻松地保持通道活动,避免低流量的长时间运行的连接意外关闭。
每当发送了一个Ping
时,另一方必须用一个Pong
来响应。如果没有发送响应,可以假设对方已经不在人世。
这是合理的,你不能一直和没有回应的人说话。
为了实现它,我们将从Server
代码开始。这意味着我们的 API 服务器将频繁地向每个客户端发送 ping,并等待 Pong,如果没有,我们将删除该客户端。
让我们从定义要使用的定时器开始。在client.go
中,我们将创建一个pongWait
和一个pingInterval
变量。PongWait 是我们允许的 pong 之间的秒数,它将被来自客户端的每个 pong 重置。如果超过这个时间,我们将关闭连接,假设 10 秒钟的等待是合理的。
是我们向客户发送 pings 的频率。请注意,这必须低于 pongWait。如果我们有一个发送速度比 pongWait 慢的 PingInterval,pongWait 就会取消。
等等,如果我们每 15 秒发送一次 Ping,但只允许服务器在两次 Ping 之间等待 10 秒,那么连接将在 10 秒后关闭。
client.go —添加了计时变量,pingInterval 算法借用了 Gorilla
现在我们需要让服务器向每个客户端发送 ping 消息。这将在客户端的writeMessages
函数中完成。我们将创建一个基于pingInterval
触发的计时器,一旦触发,我们将发送一个类型为PingMessage
的消息,其中包含一个空的有效负载。
我们在同一个函数中这样做的原因是,我们希望记住连接不允许并发写入。我们可以让另一个进程在出口发送一个 Ping,并在事件结构上添加一个 messageType 字段,但是我发现这个解决方案有点复杂。
因为我们在同一个函数中运行它,所以我们防止它并发写入,因为它要么从出口读取,要么从定时器读取,而不是同时从两者读取。
client . go—write messages 现在频繁发出 Pings
我们正在发送 Pings,我们不需要更新前端代码来响应。这是因为 RFC 规范说任何PingMessage
都应该触发一个PongMessage
来响应。支持 WebSocket 的浏览器已经自动内置,以便客户端能够响应 ping 消息。
所以Server
正在向客户端发送 Pings。客户端用 Pong 消息响应,但是现在呢?
我们需要在服务器上配置一个PongHandler
。PongHandler 是一个将在PongMessage
上触发的功能。我们将更新readMessages
来设置一个初始的PongWait
计时器,一旦启动,它将开始倒计时连接保持活动的时间。
gorilla 软件包允许我们使用SetReadDeadLine
功能轻松设置。我们将获取当前时间,添加 PongWait,并将其设置为连接。
我们还将创建一个名为pongHandler
的新函数,每当客户端接收到一个PongMessage
时,该函数将使用SetReadDeadLine
重置计时器。
client.go 添加 PongHandler 以重置 pongs 之间的时间
太好了,现在我们保持连接活跃,使网站能够长期运行而不会断开连接。
尝试重新启动您的软件,并看到服务器上的日志打印 Pong 和 Pong。
我们已经实现了大部分的东西,现在是一些安全的时候了。
限制邮件大小
限制邮件大小很重要
安全的一个规则是总是预期恶意使用。如果人们可以,他们会的。因此,有一件事是一定要做的,那就是限制服务器上允许处理的最大消息大小。
这是为了避免恶意用户向 DDOS 发送大量帧或在您的服务器上做其他坏事。
Gorilla 使用接受所允许的字节数的SetReadLimit
使得在后端配置变得非常容易。如果消息大于限制,它将关闭连接。
您必须知道邮件的大小,这样您就不会限制正确使用应用程序的用户。
在聊天中,我们可以对前端施加字符限制,然后指定与最大消息匹配的最大大小。
我将设置一个限制,每条消息最多可以有 512 个字节。
client.go 设置最大读取限制可防止产生大量帧
如果您重新启动并尝试发送长消息,连接将关闭。
原产地检查
检查始发位置很重要
目前,我们允许来自任何地方的连接连接到我们的 API。这不是很好,除非那是你想要的。
通常,你有一个托管在某个服务器上的前端,并且那个域是唯一允许连接的源。这样做是为了防止跨站点请求伪造。
为了处理来源检查,我们可以构建一个接受 HTTP 请求的函数,并使用一个简单的字符串检查来查看来源是否被允许。
这个函数必须跟在签名func(r *http.Request) bool
之后,因为将常规 HTTP 请求升级为 HTTP 连接的upgrader
有一个字段接受这样的函数。在允许连接升级之前,它将根据请求执行我们的函数来验证来源。
manager.go 向 HTTP 升级程序添加了原点检查
如果您想测试它,您可以将 switch 语句中的端口改为除了8080
之外的端口,并尝试访问 UI。你应该会看到它崩溃了,并显示一条消息request origin not allowed
。
证明
验证 WebSocket 连接
API 的一个重要部分是,我们应该只允许能够进行身份验证的用户。
WebSocket 没有内置任何身份验证工具。不过这不是问题。
在允许用户建立 WebSocket 连接之前,我们将在serveWS
函数中对用户进行身份验证。
有两种常见的方法。它们都有一定的复杂性,但不会破坏交易。很久以前,您可以通过在 Websocket 连接 URL 中添加user:password
来通过常规的基本认证。这已经被否决了一段时间了。
有两种推荐的解决方案
- 进行身份验证的常规 HTTP 请求返回一次性密码(OTP ),该密码可用于连接到 WebSocket 连接。
- 连接 WebSocket,但是不要接受任何消息,直到发送了一个带有凭据的特殊身份验证消息。
我更喜欢第一个选项,主要是因为我们不希望机器人发送垃圾连接。
所以流量会是
- 用户使用常规 HTTP 进行身份验证,OTP 票证返回给用户。
- 用户使用 URL 中的 OTP 连接到 WebSocket。
为了解决这个问题,我们将创建一个简单的 OTP 解决方案。注意,这个解决方案非常简单,使用官方 OTP 包等可以做得更好,这只是为了展示这个想法。
我们将制作一个RetentionMap
,它是一个保存 OTP 的简单地图。任何超过 5 秒的 OTP 都将被丢弃。
我们还必须创建一个新的login
端点,接受常规的 HTTP 请求并验证用户。在我们的例子中,认证将是一个简单的字符串检查。在实际的生产应用程序中,您应该用实际的解决方案来代替身份验证。涵盖认证本身就是一篇完整的文章。
需要更新serveWS
,以便一旦用户调用它,它就验证 OTP,并且我们还需要确保前端沿着连接请求发送 OTP。
让我们从改变前端开始。
我们想创建一个简单的登录表单,并呈现它,以及显示我们是否连接的文本。所以我们将从更新index.html
主体开始。
index.html—在正文中添加了登录表单
接下来,我们将在 document onload 事件中删除 WebSocket 连接。因为我们不想在用户登录之前尝试连接。
我们将创建一个接受 OTP 输入的connectWebsocket
函数,它被附加为 GET 参数。我们不将其作为 HTTP 头或 POST 参数添加的原因是,浏览器中可用的 WebSocket 客户端不支持它。
我们还将更新onload
事件,为 loginform 分配一个处理程序。该处理程序将向/login
发送一个请求,并等待 OTP 返回,一旦返回,它将触发一个 WebSocket 连接。认证失败只会发送一个警报。
使用onopen
和onclose
我们可以向用户打印出正确的连接状态。更新index.html
中的脚本部分,使其具有以下功能。
index.html—在脚本部分添加支持 OTP 的 websocket
您现在可以尝试前端,当您尝试登录时应该会看到一个警告。
在将这些更改应用到前端之后,是时候让后端验证 OTP 了。有许多方法可以创建 OTP,有一些库可以帮助您。为了使本教程简单,我编写了一个非常基本的助手类,它为我们生成 OTP,一旦它们过期就删除它们,并帮助我们验证它们。有更好的方法来处理 OTP。
我创建了一个名为otp.go
的新文件,其中包含以下要点。
otp.go —删除过期密钥的保留映射
我们需要更新管理器来维护一个RetentionMap
,我们可以用它来验证serveWS
中的 OTP,并在用户使用/login
登录时创建新的 OTP。我们将保持期设置为 5 秒,我们还需要接受一个context
,这样我们就可以取消底层的 goroutine。
manager.go —更新了结构以具有 RetentionMap
接下来,我们需要实现在/login
上运行的处理程序,这将是一个简单的处理程序。您应该用一个真正的登录验证系统来替换身份验证部分。我们的处理程序将接受带有username
和password
的 JSON 格式的有效载荷。
如果用户名是percy
而密码是123
,我们将生成一个新的 OTP 并返回,如果不匹配,我们将返回一个未授权的 HTTP 状态。
我们还更新了serveWS
来接受一个otp
GET 参数。
manager.go —更新了 ServeWS 和登录以处理 OTP
最后,我们需要更新main.go
来托管login
端点,并向管理器传递一个上下文。
main.go —向管理器添加登录端点和取消上下文
一旦您完成了所有这些工作,您现在应该能够使用前端了,但是只有在您成功地使用了登录表单之后。
试试看,按Send Message
什么都不会。但是在你登录之后,你可以查看它正在获取消息的 WebSocket。
我们将只把事件打印到控制台,但是我们会到达那里。还有最后一个安全问题。
使用 HTTPS 和 WSS 加密流量
加密流量
让我们明确一件重要的事情,现在我们正在使用明文流量,投入生产的一个非常重要的部分是使用 HTTPS。
为了让 WebSockets 使用 HTTPS,我们可以简单地将协议从ws
升级到wss
。WSS 是 WebSockets Secure 的首字母缩写。
打开index.html
,更换connectWebsocket
中的连接部分,使用 WSS。
index.html 将 wss 协议添加到连接字符串中
如果你现在试用 UI,它将无法连接,因为后端不支持 HTTPS。我们可以通过向后端添加证书和密钥来解决这个问题。
如果您没有自己的证书,请不要担心,我们将自行签署一个证书,以便在本教程中使用。
我已经创建了一个使用 OpenSSL 创建自签名证书的小脚本。你可以在他们的 Github 上看到安装说明。
创建一个名为gencert.bash
的文件,如果你使用的是 Windows,你可以手动运行命令。
gencert.bash —创建自签名证书
执行命令或运行 bash 脚本。
bash gencert.bash
你会看到两个新文件,server.key
和server.crt
。你不应该分享这些文件。把它们存储在一个更好的地方,这样你的开发者就不会不小心把它们推到 GitHub 上(相信我,这种情况会发生,人们有发现这些错误的机器人)
我们创建的证书只能用于开发目的
一旦有了这些,我们将不得不更新main.go
文件来托管 HTTP 服务器,使用证书来加密流量。这是通过使用ListenAndServeTLS
而不是ListenAndServe
来完成的。它的工作原理是一样的,但是也接受一个证书文件和一个密钥文件的路径。
main.go —使用 ListenAndServeTLS 而不是使用 HTTPs
不要忘记更新originChecker
以允许 HTTPS 域。
manager.go —必须更新源以支持 https
使用go run *.go
重启服务器,这一次,改为访问 https 站点。
根据您的浏览器,您可能不得不接受该域是不安全的
您可能会看到如下所示的错误
2022/09/25 16:52:57 http: TLS handshake error from [::1]:51544: remote error: tls: unknown certificate
这是一个远程错误,这意味着它正从客户端发送到服务器。这说明浏览器无法识别您的证书提供商(您),因为它是自签名的。不用担心,因为这是一个自签名证书,只在开发中使用。
如果您使用的是真实的证书,您将不会看到该错误。
恭喜你,你现在使用的是 HTTPS,你的 Websocket 使用的是 WSS。
实现一些事件处理程序
在我们结束本教程之前,我希望我们实现实际的事件处理程序,以使聊天正常工作。
我们只实现了关于 WebSockets 的框架。是时候根据处理程序实现一些业务逻辑了。
我不会涵盖更多的架构原则或关于 WebSockets 的信息,我们只会通过最终确定获得一些实际操作,不会太多。希望您将看到使用这种事件方法向 WebSocket API 添加更多的处理程序和逻辑是多么容易。
让我们从更新manager.go
开始,在setupEventHandlers
中接受一个实函数。
manager.go 改为将 SendMessageHandler 作为输入添加到事件处理程序中。
我们希望实现SendMessageHandler
,它应该在传入事件中接受一个有效载荷,对其进行编组,然后将其输出到所有其他客户端。
在event.go
里面我们可以添加以下内容。
event.go —添加用于广播消息的真实处理程序
这就是我们在后端需要做的一切。我们必须清理前端,以便 javascript 以想要的格式发送有效载荷。所以让我们用 JavaScript 添加相同的类,并在事件中发送它。
在index.html
的脚本部分的顶部,为两种事件类型添加类实例。它们必须匹配event.go
中的结构,以便 JSON 格式是相同的。
index.html——在脚本标签中,我们定义了两个类
然后我们必须更新当有人发送新消息时触发的sendMessage
函数。我们必须让它发送正确的有效载荷类型。
这应该是一个SendMessageEvent
有效载荷,因为这是服务器中的处理程序所期望的。
index.html——sendMessage 现在发送正确的有效载荷
最后,一旦客户端收到消息,我们应该将它打印到文本区域,而不是控制台。让我们更新routeEvent
以期待一个NewMessageEvent
并将其传递给一个函数,该函数将消息追加到文本区域。
index.html—已添加,以便客户端在收到消息后打印该消息
您现在应该能够在客户端之间发送消息了,您可以轻松地尝试一下。打开两个浏览器选项卡上的 UI,登录开始和自己聊天,但是不要熬夜!
我们可以很容易地修复它,这样我们就可以管理不同的聊天室,这样我们就不会把所有的消息都发给每个人。
让我们从在index.html
中添加一个新的ChangeRoomEvent
开始,并更新用户已经切换聊天室的聊天。
index.html—增加了更衣室事件和逻辑
将manager.go
中的新 ChangeEvent 添加到setupEventHandlers
中,以处理这个新事件。
manager.go —添加了 chattroomevent & chattroomhandler
我们可以在客户端结构中添加一个chatroom
字段,这样我们就可以知道用户选择了哪个聊天室。
client.go 添加聊天室字段
在event.go
中,我们将添加ChatRoomHandler
,它将简单地覆盖客户端中新的chatroom
字段。
我们还将确保SendMessageHandler
在发送事件之前检查其他客户端是否在同一房间。
event.go 添加了 ChatRoomHandler
太好了,我们知道这是一个多么优秀的聊天应用程序,它允许用户切换聊天室。
您应该访问 UI 并尝试一下!
结论
在本教程中,我们为 Websocket 服务器构建了一个完整的框架。
我们有一个以安全、可伸缩和可管理的方式接受 WebSockets 的服务器。
我们涵盖了以下几个方面
- 如何连接 WebSockets
- 如何有效地向 WebSockets 读写消息?
- 如何用 WebSockets 构建 go 后端 API
- 如何为易于管理的 WebSocket API 使用基于事件的设计?
- 如何使用名为乒乓的心跳技术保持联系
- 如何通过限制消息大小来避免巨型帧,从而避免用户利用 WebSocket。
- 如何限制 WebSocket 允许的源
- 如何通过实现 OTP 票务系统在使用 WebSockets 时进行身份验证
- 如何将 HTTPS 和 WSS 添加到 WebSockets?
我坚信本教程涵盖了在开始使用 WebSocket API 之前需要学习的所有内容。
如果您有任何问题、想法或反馈,我强烈建议您联系我们。
我希望你喜欢这篇文章,我知道我喜欢。
匹配,加权,还是回归?
原文:https://towardsdatascience.com/matching-weighting-or-regression-99bf5cffa0d9
因果数据科学
理解和比较条件因果推理分析的不同方法
封面图片,由我的好朋友 Chiara 绘制
A/B 检验或随机对照试验是因果推断中的金标准。通过随机将单位暴露于治疗,我们确保平均而言,治疗和未治疗的个体是可比较的,并且我们观察到的任何结果差异都可以单独归因于治疗效果。
然而,通常治疗组和对照组并不完全可比。这可能是由于随机化不完善或不可用的事实。出于伦理或实践的原因,随机处理并不总是可能的。即使我们可以,有时我们没有足够的个人或单位,所以群体之间的差异是可以捕捉的。这种情况经常发生,例如,当随机化不是在个体水平上进行,而是在更高的聚合水平上进行,如邮政编码、县甚至州。
在这些情况下,如果我们有足够的关于个体的信息,我们仍然可以恢复治疗效果的因果估计。在这篇博文中,我们将介绍并比较不同的程序,以评估治疗组和对照组之间存在的完全可观察到的不平衡的因果关系。特别是,我们将分析加权、匹配和回归过程。
例子
假设我们有一个关于统计和因果推理的博客。为了改善用户体验,我们正在考虑发布一个黑暗模式,我们想了解这个新功能是否会增加用户在我们博客上花费的时间。
对照组(左)和治疗组(右)的网站视图,图片由作者提供
我们不是一家成熟的公司,因此我们不进行 A/B 测试,而是简单地释放黑暗模式,我们观察用户是否选择它以及他们在博客上花费的时间。我们知道可能会有选择:偏好黑暗模式的用户可能会有不同的阅读偏好,这可能会使我们的因果分析变得复杂。
我们可以用下面的 有向无环图(DAG) 来表示数据生成过程。
数据生成过程的 DAG,按作者排序的图像
我们使用来自[src.dgp](https://github.com/matteocourthoud/Blog-Posts/blob/main/notebooks/src/dgp.py)
的数据生成过程dgp_darkmode()
生成模拟数据。我还从[src.utils](https://github.com/matteocourthoud/Blog-Posts/blob/main/notebooks/src/utils.py)
引进了一些绘图函数和库。
from src.utils import *
from src.dgp import dgp_darkmodedf = dgp_darkmode().generate_data()
df.head()
数据快照,图片由作者提供
我们有 300 名用户的信息,我们观察他们是否选择了dark_mode
(治疗)、他们每周的read_time
(感兴趣的结果)以及一些特征,如gender
、age
和以前在博客上花费的总hours
。
我们想估计一下新的dark_mode
对用户read_time
的影响。如果我们运行一个 A/B 测试 或随机对照试验,我们可以比较使用和不使用黑暗模式的用户,我们可以将平均阅读时间的差异归因于dark_mode
。让我们看看我们会得到什么数字。
np.mean(df.loc[df.dark_mode==True, 'read_time']) - np.mean(df.loc[df.dark_mode==False, 'read_time'])-0.4446330948042103
选择dark_mode
的人平均每周花在博客上的时间少了 0.44 小时。我们应该断定dark_mode
是一个坏主意吗?这是因果关系吗?
我们没有随机化dark_mode
,因此选择它的用户可能无法与没有选择它的用户直接比较。我们能证实这种担忧吗?部分地。在我们的设置中,我们只能检查我们观察到的特征、gender
、age
和总计hours
。我们无法检查用户是否在我们没有观察到的其他维度上存在差异。
让我们使用优步[causalml](https://causalml.readthedocs.io/)
包中的create_table_one
函数来生成一个协变量平衡表,包含我们在治疗组和对照组中可观察特征的平均值。顾名思义,这应该永远是你在因果推断分析中呈现的第一张表。
from causalml.match import create_table_one
X = ['male', 'age', 'hours']
table1 = create_table_one(df, 'dark_mode', X)
table1
平衡表,作者图片
治疗组(dark_mode
)和对照组之间似乎存在一些差异。特别是,选择dark_mode
的用户更年轻,花在博客上的时间更少,他们更有可能是男性。
另一种同时观察所有差异的方法是使用成对的小提琴手。成对紫线图的优点是它允许我们观察变量的完整分布(通过核密度估计近似)。
变量分布,按作者分类的图像
violinplot 的洞察力非常相似:似乎选择了dark_mode
的用户和没有选择的用户是不同的。
我们为什么关心?
如果我们不控制可观察的特征,我们就无法估计真实的治疗效果。简而言之,我们不能确定结果的差异(T3)可以归因于治疗(T4),而不是其他特征。例如,可能男性阅读较少,也更喜欢dark_mode
,因此我们观察到负相关,即使dark_mode
对read_time
没有影响(甚至是正相关)。
就有向无环图而言,这意味着我们有一个 后门路径 ,我们需要阻塞以便我们的分析是因果。
无条件分析的 DAG,图片由作者提供
我们如何阻止后门路径?通过对这些中间变量进行分析,在我们的例子中是gender
。条件分析允许我们恢复dark_mode
对read_time
的因果关系。进一步调整age
和hours
的分析,可以提高我们估计的精度,但不会影响结果的因果解释。
条件分析的 DAG,按作者排序的图像
我们如何在gender
、age
和hours
上对进行分析?我们有一些选择:
- 匹配
- 倾向评分权重
- 带控制变量的回归
让我们一起来探索和比较一下吧!
条件分析
我们假设对于一组受试者 i = 1,…,n ,我们观察到一个元组 (Dᵢ,Yᵢ,Xᵢ) 包括
- 一个治疗分配 Dᵢ ∈ {0,1} (
dark_mode
) - 一个回应 Yᵢ ∈ ℝ (
read_time
) - 一个特征向量 Xᵢ ∈ ℝⁿ (
gender
、age
和hours
)
假设 1:不可发现性(或可忽略性,或对可观察性的选择)
无根据假设,作者的图像
即以可观察特征 X 为条件,治疗分配 D 几乎是随机的。我们实际假设的是,没有我们没有观察到的其他特征会影响用户是否选择dark_mode
和他们的read_time
。这是一个强假设,我们观察到的个人特征越多,这个假设就越有可能得到满足。
假设 2:重叠(或共同支撑)
重叠假设,作者图片
即没有观察结果被确定地分配给治疗组或对照组。这是一个更技术性的假设,基本上意味着对于任何级别的gender
、age
或hours
,可能存在选择dark_mode
的个体和不选择dark_mode
的个体。与未发现假设相反,重叠假设是可检验的。
相称的
执行条件分析的第一个也是最直观的方法是匹配。
搭配的思路很简单。例如,由于我们不确定男性和女性用户是否可以直接比较,所以我们在性别范围内进行分析。我们不是在整个样本中比较read_time
和dark_mode
,而是针对男性和女性用户分别进行比较。
df_gender = pd.pivot_table(df, values='read_time', index='male', columns='dark_mode', aggfunc=np.mean)
df_gender['diff'] = df_gender[1] - df_gender[0]
df_gender
不同性别、不同作者的平均阅读时间差异
现在dark_mode
的效果似乎颠倒了:对男性用户来说是负的(-0.03),但对女性用户来说是正的且更大(+1.93),这表明了正的聚合效应,1.93 - 0.03 = 1.90(假设性别比例相等)!这种符号颠倒是辛普森悖论的一个非常经典的例子。
对于gender
,这个比较很容易执行,因为它是一个二进制变量。由于多个变量可能是连续的,匹配变得更加困难。一个常见的策略是使用某种最近邻算法将治疗组中的用户与对照组中最相似的用户进行匹配。我不会在这里深入算法细节,但是我们可以用causalml
包中的NearestNeighborMatch
函数来执行匹配。
NearestNeighborMatch
功能生成一个新的数据集,其中治疗组中的用户已与对照组中的用户 1:1 匹配(选项ratio=1
)。
from causalml.match import NearestNeighborMatch
psm = NearestNeighborMatch(replace=True, ratio=1, random_state=1)
df_matched = psm.match(data=df, treatment_col="dark_mode", score_cols=X)
现在两个群体是不是更有可比性了?我们可以生产新版本的平衡表。
table1_matched = create_table_one(df_matched, "dark_mode", X)
table1_matched
匹配后的平衡表,图片由作者提供
现在两组之间的平均差异已经缩小了至少几个数量级。但是,请注意样本大小略有减少(300 → 208 ),因为(1)我们只匹配经过治疗的用户,而(2)我们无法为所有用户找到良好的匹配。
我们可以用成对的小提琴手来观察分布的差异。
plot_distributions(df_matched, X, "dark_mode")
匹配后控制变量的分布,按作者排列的图像
可视化匹配前后协变量平衡的一种流行方法是平衡图,它基本上显示了每个控制变量匹配前后的标准化平均差异。
平衡情节,作者图像
正如我们所看到的,现在两组之间所有可观察特征的差异基本上为零。我们也可以使用其他度量或测试统计来比较这些分布,例如 Kolmogorov-Smirnov 测试统计。
我们如何估计治疗效果?我们可以简单地在手段上有所不同。一种自动提供标准误差的等效方法是对治疗结果dark_mode
进行线性回归read_time
。
请注意,由于我们已经为每个接受治疗的用户执行了匹配*,我们估计的治疗效果是接受治疗者(ATT)的**平均治疗效果,*如果接受治疗的样本不同于总体样本(很可能是这种情况,因为我们首先在进行匹配),则平均治疗效果可能不同。
smf.ols("read_time ~ dark_mode", data=df_matched).fit().summary().tables[1]
匹配后回归结果,按作者排序的图像
这种影响现在是积极的,在 5%的水平上有统计学意义。
注意我们可能将多个治疗过的用户与同一个未治疗的用户进行了匹配,这违反了跨观察的独立性假设,进而扭曲了推论。我们有两种解决方案:
- 聚类预匹配个体水平的标准误差
- 通过引导程序计算标准误差(首选)
我们通过原始的个体标识符(数据帧索引)来实现第一和集群标准误差。
smf.ols("read_time ~ dark_mode", data=df_matched)\
.fit(cov_type='cluster', cov_kwds={'groups': df_matched.index})\
.summary().tables[1]
聚类 SEs 的匹配后回归结果,按作者排序的图像
现在这种影响在统计学上不那么显著了。
倾向得分
Rosenbaum 和 Rubin (1983) 证明了一个非常有力的结果:如果强可忽略性假设成立,则足以使分析以治疗的概率倾向得分为条件,以便具有条件独立性。
罗森鲍姆和鲁宾(1983)的结果,由作者图像
其中 e(Xᵢ) 是个体 i 接受治疗的概率,给定可观察特征 Xᵢ 。
倾向得分,按作者分类的图像
注意在 A/B 测试中,个人的倾向得分是不变的。
Rosenbaum 和 Rubin (1983 年)的结果令人难以置信地强大而实用,因为倾向得分是一个一维变量,而控制变量 X 可能是非常高维的。
在上面介绍的不成立假设下,我们可以将平均治疗效果改写为
平均治疗效果,图片由作者提供
注意,这个平均治疗效果的公式并不取决于潜在的结果yᵢ⁽⁾yᵢ⁽⁰⁾,而仅仅取决于观察到的结果 Yᵢ 。
这个平均治疗效果的公式意味着反向倾向加权(IPW) 估计量,它是平均治疗效果 τ 的无偏估计量。
平均治疗效果的 IPW 估计值,图片由作者提供
这个估计量不可行,因为我们没有观察到倾向得分e(xᵢ】。但是,我们可以估计它们。实际上, Imbens,Hirano,Ridder (2003) 表明,即使你知道真实值(例如,因为你知道抽样程序),你也应该使用估计的倾向得分。这个想法是,如果估计的倾向分数与真实的不同,这可以在估计中提供信息。
估计概率有几种可能的方法,最简单最常见的是 逻辑回归 。
from sklearn.linear_model import LogisticRegressionCVdf["pscore"] = LogisticRegressionCV().fit(y=df["dark_mode"], X=df[X]).predict_proba(df[X])[:,1]
最佳实践是,每当我们拟合一个预测模型时,在一个不同的样本上拟合该模型,相对于我们用于推断的样本。这种做法通常被称为交叉验证或交叉拟合。最好的(但计算代价昂贵的)交叉验证程序之一是留一法(LOO) 交叉拟合:当预测观察值 *i、*时,我们使用除了 i 之外的所有观察值。我们使用[skearn](https://scikit-learn.org/)
包中的cross_val_predict
和LeaveOneOut
函数实现 LOO 交叉拟合过程。
from sklearn.model_selection import cross_val_predict, LeaveOneOutdf['pscore'] = cross_val_predict(estimator=LogisticRegressionCV(),
X=df[X],
y=df["dark_mode"],
cv=LeaveOneOut(),
method='predict_proba',
n_jobs=-1)[:,1]
在估计倾向分数后,一个重要的检查是在治疗组和对照组之间绘制倾向分数。首先,我们然后可以观察两组是否平衡,这取决于两个分布有多接近。此外,我们还可以检查满足重叠假设的可能性。理想情况下,两种分布应该跨越相同的时间间隔。
sns.histplot(data=df, x='pscore', hue='dark_mode', bins=30, stat='density', common_norm=False).\
set(ylabel="", title="Distribution of Propensity Scores");
倾向得分分布,按作者分类的图像
正如预期的那样,治疗组和对照组之间的倾向得分分布显著不同,这表明两组几乎没有可比性。然而,这两个分布跨越了相似的区间,表明重叠假设可能得到满足。
我们如何估计平均治疗效果?
一旦我们计算出倾向得分,我们只需要用各自的倾向得分对观察值进行加权。然后,我们可以计算加权的read_time
平均值之间的差值,或者使用wls
函数(加权最小二乘法)对dark_mode
进行加权回归read_time
。
w = 1 / (df["pscore"] * df["dark_mode"] + (1-df["pscore"]) * (1-df["dark_mode"]))
smf.wls("read_time ~ dark_mode", weights=w, data=df).fit().summary().tables[1]
IPW 回归结果,图片由作者提供
在 5%的水平上,dark_mode
的影响现在是积极的,几乎具有统计学意义。
请注意wls
函数会自动对权重进行归一化,使其总和为 1,这大大提高了估计器的稳定性。事实上,当倾向分数接近 0 或 1 时,非标准化的 IPW 估计量可能非常不稳定。
控制变量回归
我们今天要复习的最后一种方法是带控制变量的线性回归。这个估计器非常容易实现,因为我们只需要将用户特征— gender
、age
和hours
—添加到dark_mode
上的read_time
的回归中。
smf.ols("read_time ~ dark_mode + male + age + hours", data=df).fit().summary().tables[1]
条件回归结果,按作者排序的图像
平均治疗效果再次为阳性,在 1%的水平上具有统计学显著性!
比较
不同的方法如何与相互关联?
IPW 和回归
IPW 估计量和有协变量的线性回归之间有一个紧密联系。当我们有一个一维的离散协变量 X 时,这一点尤其明显。
在这种情况下,IPW 的估计量(即 IPW 估计的数量)由下式给出
IPW 估算要求的等效公式,图片由作者提供
IPW 估计值是治疗效果 τₓ 的加权平均值,其中权重由治疗概率给出。
有控制变量的线性回归的要求是
OLS 需求的等效公式,作者图片
OLS 估计量是治疗效果 τₓ 的加权平均值,其中权重由治疗概率的方差给出。这意味着线性回归是一个加权的估计量,它对具有我们观察到的治疗可变性更大的特征的观察值给予更大的权重。由于二进制随机变量在其期望值为 0.5 时具有最高的方差, OLS 对具有我们观察到的治疗组和对照组之间 50/50 分割的特征的观察值给予最大的权重。另一方面,如果对于某些特征,我们只观察经过治疗或未经治疗的个体,那么这些观察结果的权重将为零。我推荐安格里斯特和皮施克(2009) 的第三章了解更多细节。
IPW 和匹配
正如我们在 IPW 一节中所看到的,Rosenbaum 和 Rubin (1983) 的结果告诉我们,我们不需要对所有协变量 X 进行条件分析,但对倾向得分 e(X) 进行条件分析就足够了。
我们已经看到了这个结果是如何暗示一个加权估计量的,但它也延伸到匹配:我们不需要匹配所有协变量 X 上的观察值,但在倾向得分 e(X) 上匹配它们就足够了。这种方法被称为倾向得分匹配**。**
psm = NearestNeighborMatch(replace=False, random_state=1)
df_ipwmatched = psm.match(data=df, treatment_col="dark_mode", score_cols=['pscore'])
和以前一样,在匹配之后,我们可以简单地将估计值计算为均值差,记住观察值不是独立的,因此我们在进行推断时需要谨慎。
smf.ols("read_time ~ dark_mode", data=df_ipwmatched)\
.fit(cov_type='cluster', cov_kwds={'groups': df_ipwmatched.index})\
.summary().tables[1]
匹配回归结果的倾向得分,按作者排序的图片
dark_mode
的估计效果为正,在 1%水平显著,非常接近真实值 2!
结论
在这篇博文中,我们已经看到了如何使用不同的方法执行条件分析。匹配直接匹配治疗组和对照组中最相似的单位。加权只是根据接受治疗的概率,对不同的观察值赋予不同的权重。相反,回归根据条件处理方差对观察值进行加权,对具有处理组和对照组共有特征的观察值给予更大的权重。
这些程序非常有帮助,因为它们可以让我们从(非常丰富的)观察数据中估计因果关系,或者在随机化不完美或我们只有少量样本时纠正实验估计。
最后但同样重要的是,如果你想了解更多,我强烈推荐这个来自保罗·戈德史密斯-平克姆的关于倾向分数的视频讲座,它可以在网上免费获得。
整个课程是的瑰宝,能够在网上免费获得如此高质量的资料是一种难以置信的特权!
参考
[1] P. Rosenbaum,D. Rubin,因果效应观察研究中倾向评分的核心作用 (1983 年),生物计量学。
[2] G. Imbens,K. Hirano,G. Ridder,利用估计倾向得分对平均治疗效果的有效估计 (2003),计量经济学。
[3] J .安格里斯特,J. S .皮施克,大多无害的计量经济学:一个经验主义者的伴侣 (2009),普林斯顿大学出版社。
相关文章
- 理解弗里希-沃-洛弗尔定理
- 如何比较两个或多个分布
- Dag 和控制变量
密码
你可以在这里找到 Jupyter 的原始笔记本:
https://github.com/matteocourthoud/Blog-Posts/blob/main/notebooks/ipw.ipynb
感谢您的阅读!
我真的很感激!🤗如果你喜欢这个帖子并且想看更多,可以考虑 关注我 。我每周发布一次与因果推断和数据分析相关的主题。我尽量让我的帖子简单而精确,总是提供代码、例子和模拟。
还有,一个小小的 免责声明 :我写作是为了学习所以出错是家常便饭,尽管我尽力了。当你发现他们的时候,请告诉我。也很欣赏新话题的建议!
物化视图:提取洞察力的经济有效的方法
原文:https://towardsdatascience.com/materialized-views-the-cost-effective-way-to-extract-insights-b4a066f85fb
为什么不应该对每个任务都使用常规视图
照片由 Karolina Grabowska 拍摄:https://www . pexels . com/photo/style-round-glasses-with-optical-lenses-4226865/
很容易习惯于将 SQL 中的视图作为一种方便的工具。视图是通用的,可以省去用户重复编写复杂查询的麻烦。
也就是说,它们并不总是这项工作的最佳工具。
事实上,根据用例的不同,视图可能不如被低估的替代品:物化视图。
在这里,我们提供了物化视图的简要概述,讨论了物化视图胜过视图的方法,并解释了为什么拥抱物化视图对您最有利,即使到目前为止没有物化视图您也能管理得很好。
物化视图
物化视图类似于视图,但是它们之间有明显的区别。
与存储查询的视图不同,物化视图存储查询的输出。
这似乎是一个微不足道的区别。毕竟,如果返回相同的信息,存储查询输出而不是查询会有什么不同呢?
利益
当计算是决定性因素时,物化视图可以胜过视图。物化视图的关键好处是它们不需要太多的计算,因为查询结果已经预先确定了。
图像处理对大量数据执行分析,其中通过连接和聚合定期获得相同的信息。这种方法是有效的,但是重复运行相同的查询来获取相同的信息只是浪费计算。
使用物化视图,用户可以提取相同的信息,而不需要重复运行计算密集型查询。
限制
当然,物化视图也有自己的缺点。
因为它们只存储查询输出,所以不会根据底层数据库的变化进行调整。这可能是一个问题,因为物化视图可能会报告可能过时的值。
为了使物化视图保持可用,无论何时数据被以多种方式修改,都必须手动更新它们。
此外,与视图不同,物化视图需要内存使用,因为它们包含的查询结果存储在磁盘上。
个案研究
在 SQL 中创建和维护物化视图的过程与视图非常相似,但是有一些区别值得一提。因此,让我们进行一个简单的案例研究来演示物化视图。
我们有一个名为Animals
的简单表格,显示了动物的记录和它们对应的年龄。
代码输出(由作者创建)
假设我们计划在整个研究中经常检查每种动物的数量。我们可以编写生成该计数的查询,并将其存储在名为animal_count
的物化视图中。
有了这个物化视图,我们可以随时检查动物数量。
代码输出(由作者创建)
因为现在查询输出是预先计算的,所以现在通过物化视图访问它将需要更少的时间和计算。
不幸的是,与运行存储查询并根据底层数据的任何变化进行调整的视图不同,物化视图需要手动刷新。
为了展示刷新物化视图的重要性,我们可以删除表中兔子的所有记录,并查看修改后物化视图显示的内容。
代码输出(由作者创建)
如输出所示,尽管已经删除了兔子的所有记录,但是兔子的数量仍然存在。这意味着物化视图现在正在报告过时的信息。
为了确保物化视图跟上对数据库所做的更改,用户必须使用REFRESH
命令。
现在,物化视图与底层数据相匹配。
代码输出(由作者创建)
为什么要使用物化视图
物化视图在语法和功能上都类似于视图。但是,它们需要额外的维护才能保持可用。那么,当用户可以坚持使用常规视图时,为什么还要依赖物化视图呢?
对于习惯于使用免费 RDBMS 系统(例如 PostgreSQL)编写查询的用户来说,这可能是一个有效的论据,在这些系统中,查询的计算需求和执行时间是无关紧要的。
这种软件的用户可以免费编写查询,并且可以养成为了方便而单独使用视图的习惯。他们可能不加选择地使用视图来运行和重新运行查询,而不考虑他们的计算需求。
不幸的是,这种方法不能很好地转化为在数据仓库中进行的分析。数据仓库(本地或云)提供了巨大的存储空间和强大的计算能力。作为回报,这些仓库根据自己的定价模式向用户收费。
由于需要更高计算量的查询会导致更高的价格,这些平台的用户需要对他们如何进行分析保持警惕,因为他们可能会因查询而收费。因此,在用数据仓库规划分析时,必须考虑价格优化。
物化视图作为从数据库中提取信息的视图的一种更经济的替代方式(假设数据不经常修改)。
此外,根据所选择的数据仓库,它们的主要缺点(即需要手动维护它们)甚至可能不是问题。许多云数据仓库(如 Snowflake)都包含在数据发生变化后自动更新物化视图的特性,而不需要用户进行任何维护工作。
结论
照片由 Unsplash 上的 Prateek Katyal 拍摄
物化视图是分析很少或没有变化的数据的最佳选择,因为它们使用户无需重新运行相同的查询就能获得信息。
为数据仓库服务付费的企业自然会采用价格友好的分析方法。
因此,即使您习惯于使用免费的 RDMBS 软件来分析数据,您也可能不得不在未来迁移到大数据解决方案,因此熟悉物化视图并接受成本优化的思维方式并没有什么坏处。
希望您已经开始理解物化视图,并将它们更多地结合到您的脚本中。但是,如果您认为需要后退一步,从总体上探索常规视图,请查看下面的文章:
我祝你在数据科学的努力中好运!
基于显微图像处理的材料数据挖掘
原文:https://towardsdatascience.com/materials-data-mining-via-image-processing-of-micrographs-4814e7112e40
基于显微图像的特征提取的基本处理步骤
照片由 Joyita Bhattacharya 在 Unsplash 上拍摄
背景
在我的文章“使用 Matminer 和 Pymatgen 发现材料数据的潜力”中,我讨论了材料四面体的概念——为各种技术用途开发材料的基本框架。占据四面体顶点的重要参数是过程、结构、属性和性能。
结构的特征基于特征长度尺度,该尺度可以从几埃( 1 埃= 10 ⁻ ⁰ *米)*到几百微米 (1 微米= 10 ⁻ *⁶米)*变化。长度刻度用于在显微镜下辨别特征。使用非常强大的高分辨率显微镜,如这台,人们可以识别小到原子甚至更小的特征。
微米和亚微米尺寸的特征,称为微结构,使用不同类型的显微镜捕获为图像或显微照片。这些微观结构存储了大量信息,这些信息对于理解材料的性质和性能至关重要。我们可以使用机器学习/深度学习从这些图像中挖掘重要的特征。典型的微结构包含基于像素强度或对比度可区分的特征。一些重要的特征是分隔不同域(也称为颗粒或晶粒)的边界或界面。这些域在形状、大小、取向、大小分布和空间排列(统称为形态学)上可以变化。这些特征影响材料的性质。
作者图片:微观结构示意图
尽管可以使用其他材料表征技术来研究这些特征,但显微镜的优势在于可视化。自'*'眼见为实'*以来,显微照片为材料的行为提供了更可信的解释。显微照片的处理揭示了定性和定量特征。比方说,如果我们想要进行颗粒尺寸和形态的定量测量,就需要对边缘进行精确的识别。此外,边缘检测也有助于研究颗粒之间的界面结构。在某些材料(尤其是合金)中,某些元素可能会在界面上偏析,从而影响强度、导电性等性能。
通过人工观察进行边缘检测是一项单调乏味的任务,并且涉及各种人为误差。为了最小化这样的错误,需要自动化这样的过程。该过程的自动化需要实施稳健的数字图像处理和数据挖掘算法。既然我已经强调了显微照片的数字图像分析的重要性,让我带你看一些基本的处理步骤。我使用了基于 python 的开源库 scikit-image 进行演示。出于同样的目的,您也可以探索 PIL 的 OpenCV。
查看图像并获得形状
下面的代码片段显示了如何读取图像并找到它的尺寸或“形状”。shape 属性以元组的形式产生图像的维度。在这张当前的显微照片中,它是(1800,1500)-高度和宽度分别是 1800 和 1500 像素。请注意,这是一个灰度图像,因为元组中的第三项没有被提及,而是采用默认值 1。
p = io.imread("Particles.jpg")
p.shape
一般处理步骤:图像的去噪、锐化和亮度调整
由 Vadim Bogulov 在 Unsplash 上拍摄的照片
用于去噪、锐化、边缘检测的图像矩阵的细化主要涉及与滤波器/核矩阵的卷积运算。卷积操作包括首先水平和垂直翻转 2D 滤波器(内核),随后是图像矩阵**的逐元素乘法和加法。**注意,在对称核矩阵的情况下,翻转是不必要的。
作者图片
这里,我将考虑两个滤波器- 高斯和中值,分别用于显示与卷积相关和不相关的图像中的噪声降低。
线性高斯滤波器通过卷积对图像矩阵进行操作。属性“sigma”是高斯滤波器中的标准偏差。sigma 值越高,越模糊。
高斯模糊随着西格玛而增加
中值滤波是一个非线性过程。当滤波器窗口沿图像矩阵滑动时,所述矩阵的中值像素值作为输出信号。为了更好地理解,示出了图形表示。
作者图片:中值过滤器的图形表示
在移除诸如盐和胡椒类型的电子噪声时,该滤波器优于高斯滤波器。虽然高斯平滑会导致边缘模糊,但中值平滑会保留边缘。
与去噪相反,我们可以使用 skimage 的*“滤镜模块”*的“*反锐化掩模”*功能锐化散焦或模糊的图像以准确识别其中的特征。下面显示了代码片段以及输入(散焦图像)和输出(锐化)图像,以供比较。
Sharpimg = filters.unsharp_mask(p, radius = 20.0, amount = 1.0)fig, ax = plt.subplots(nrows =1, ncols =2, sharex = True, figsize =(15,15))ax[0].imshow(p, cmap = 'gray')
ax[0].set_title("Original", fontsize = 10)
ax[1].imshow(Sharpimg, cmap ='gray')
ax[1].set_title("Sharpened",fontsize = 10)
代码输出
有时,调节亮度也会使图像变得清晰,以便进行特征检测,这可以使用 skimage 的*“曝光模块”的功能“adjust _ gamma”*来完成。通过应用幂律变换获得伽马校正的输出图像。
伽马校正显微照片
图像分割阈值:生成二值图像
阈值处理当我们需要根据像素强度将图像背景从前景中分离出来时,就需要用到阈值处理。例如,在超合金(用于飞机喷气发动机、燃气轮机的材料)微观结构的情况下,背景是基底金属,前景由赋予这类材料超高强度的沉淀物组成。有时,背景可能只是用于装载样品以便在显微镜下进行研究的样品架。在这种情况下,用于阈值处理的图像显示了透射电子显微镜(TEM)网格/支架上的四角颗粒。应用不同类型的阈值方法来区分背景(TEM 网格)和粒子。对于给定的显微照片,我们发现均值阈值处理比其他方法做得更好,它清晰地形成了二值图像,如代码输出片段所示。
显示不同阈值方法的代码输出
上述方法是在对图像进行进一步处理以提取有意义的定性和定量特征之前,获得无噪声图像的一些初步步骤。正如本文开头提到的,粒度是控制材料性能的重要参数/特征之一。为了从图像中估计颗粒尺寸,我们需要生动地检测颗粒的边缘。并且使用各种边缘检测滤波器,该任务变得容易。在这里,我将讨论其中的几个。
使用 Roberts、Sobel、Canny 滤波器进行边缘检测
罗伯茨 和 索贝尔 滤波器分别是 2×2 和 3×3 卷积核。这两种滤波器都具有用于水平和垂直边缘检测的 x 和 y 分量。Sobel 内核操作的代码片段以及相应的输出如下所示。
from skimage.filters import sobel,sobel_v, sobel_h
p_sobel = sobel(p_g, mode='reflect')
p_sobel_v=sobel_v(p_g)
p_sobel_h=sobel_h(p_g)
Sobel 过滤器:代码输出片段
Canny 滤波器通过平滑、Sobel 算子的梯度近似、使用滞后阈值检测边缘来执行多任务工作。函数' canny '具有用于高斯平滑和高低阈值的 sigma 等参数。
用于检测边缘方向的 Gabor 滤波器
该线性过滤器捕捉微结构中特征的纹理或取向分布。取向分布决定了材料性能的均匀性。颗粒/晶粒的随机取向导致均匀/各向同性性质,而特定方向的取向,技术上称为优选取向,导致各向异性性质。Gabor 滤波器的复正弦分量提供了与方向相关的信息。所述滤波器的输出具有实部和虚部。想了解更多关于 Gabor 滤镜的知识,请点击 这里 。
让我们尝试生成一组具有 0°、60°、90°和 120°取向的 Gabor 滤波器,并将它们应用于显示不同取向粒子的原始透射电子显微照片。我正在分享代码片段以及下面的输出过滤图像。我们可以清楚地看到各个过滤图像中的方向*(用与粒子边缘一致的黄色虚线突出显示)*。请注意,已过滤图像的实部会显示出来。
p_gabor =[]for degree in (0, 60, 90, 120):
real,imag = filters.gabor(p, frequency=0.05, theta =(degree* (np.pi)/180))
p_gabor.append(real)
Gabor 滤波图像捕捉粒子的方向
我们也可以应用 Gabor 核来提取方向信息。生成一组不同方向的 Gabor 核,然后允许它们与图像矩阵交互,以解析相应方向的边缘,如下面的快照所示。
作者对原始图像进行了 Gabor 核处理
点击 这里 从我的 GitHub 库中获取基本图像处理的完整代码。
总结
挖掘定性和定量特征是理解材料微观结构信息属性的关键。此外,来自生物成像的数据是医疗保健行业的支柱。因此,明智地处理这些显微照片是至关重要的。
尽管有几个商业图像处理软件,为同样的目的生成你自己的算法是有成本效益和灵活性的。最重要的是,它打开了通过机器学习实现自动化的大门。
在这篇文章中,我解释了一些定性提取信息的基本处理步骤,例如检测粒子边缘和方向。然而,从显微照片中提取完整的数据还需要对相关特征进行定量评估,这将在我的下一篇文章中讨论。
直到那时,快乐的图像处理!!
通过显微照片的图像处理挖掘材料数据:第二部分
原文:https://towardsdatascience.com/materials-data-mining-via-image-processing-of-micrographs-part-ii-87e4807723fb
从图像测量材料特性
斯文·米克在 Unsplash 上的照片
“一图胜千言。”—阿尔伯特·爱因斯坦
背景
我的上一篇文章“通过显微照片的图像处理进行材料数据挖掘”解释了显微照片处理的一些基本和关键步骤。如前所述,显微照片捕捉材料的细微微米/亚微米尺寸特征,这些特征会显著影响材料的性能。在这里,我将讨论使用 scikit-image 对一个这样的特征——“颗粒大小”的定量评估。
参数测量
案例研究 1:应用分水岭算法从聚类中检索单个颗粒尺寸
鲍里斯·巴尔丁格在 Unsplash 上拍摄的照片
让我先展示一张不同形状和大小的颗粒重叠在一起的合成显微照片。我们首先将单个粒子从簇/聚集体中分离出来。然后我们测量这些分离出来的粒子的大小。借助分水岭算法——一种图像分割方法——实现颗粒的解聚。我正在分享上述活动的一步一步的指南。
第一步:导入必要的库并加载镜像
**# IMPORTING LIBRARIES**
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import ndimage as ndi
from skimage import io, color
from skimage.segmentation import watershed
from skimage.feature import peak_local_max**# LOADING IMAGE**im1 = io.imread("O3.jpg")
plt.imshow(im1, cmap ='gray')
作者图片:显示集群的合成显微照片
图像有几个粒子簇,它的维数用一个元组(1254,1313,3)表示。元组的前两个元素表示高度和宽度,第三个元素告诉我们通道的数量(颜色的数量)。该图像是具有三个通道/颜色的 RGB 图像。
第二步:转换成灰度图像
我已经应用了从 skimage 的颜色模块导入的函数 rgb2gray 来将 rgb 图像转换为灰度。
第三步:阈值化得到二值图像
在使用 try_all_threshold 函数对该图像进行其他阈值方法处理之后,发现平均阈值方法最适合于该图像。
第四步:计算欧氏距离变换
欧几里德距离是图像中任意两个像素之间最近距离的度量。图像的每个像素被分类为源/背景像素(具有零强度)或目标像素(具有非零强度)。我们观察到从任何物体的中心到背景的欧几里得距离有一个递减的梯度。从图像中获得的距离图很好地捕捉到了这一点:
距离地图
第五步:寻找局部峰值和分水岭线
从地理上来说,分水岭是分隔流域的高地。我们将相同的概念应用于粒子/对象集群的图像分割。在这种情况下,簇中单个对象边界的像素强度构成分水岭,将它们彼此分开。
我们首先通过应用函数 peak_local_max 来定位粒子的峰值(局部最大值)的坐标。我们使用该函数的输出作为分水岭函数中的标记。我们为图像中的每个粒子标记这些标记。这些标记构成了将单个颗粒从聚集体中分离出来的分水岭。该任务的代码片段如下所示。
coords = peak_local_max(distance, min_distance =100)
mask = np.zeros(distance.shape, dtype=bool)
mask[tuple(coords.T)] = True
markers,_= ndi.label(mask)
第六步:应用分水岭算法进行图像分割
最后,应用具有诸如反距离变换图像、标记和掩模(二值图像)的参数的分水岭算法,我们获得分离的粒子。通过应用 matplotlib 颜色图,簇中这些粒子的个性以不同的颜色显示。
individual_particles = watershed(-distance, markers, mask=binary)
显示原始图像、距离图和分段图像的代码输出(应用分水岭功能后)
案例研究 2:特性测量——粒度和粒度分布
让我们继续上面的图片,我们应用分水岭算法来分离粒子和簇。我们现在开始测量颗粒尺寸。
图像中比例因子的作用
在详细讨论特征/参数值的计算之前,我们需要理解图像中的“比例的概念。任何图像中的比例因子给出了对象/特征的度量的概念。数字图像由称为像素的小点组成。像素的大小决定了用来拍照的仪器的分辨率。
根据在图像中捕获的实际物理对象的尺寸,在任何图像上提供代表以千米/厘米/毫米/微米/纳米为单位的特定值的几个像素的刻度线或条。例如,谷歌地图有一个以英里或公里为单位的比例尺。相反,显微照片具有微米/纳米的比例尺,因为它们捕捉材料的小长度尺度特征。
没有比例尺是不可能测量图像的特征的。因此,具有可量化特征的图像应该总是包含比例尺。
尺寸测量使用region propsskimage . measure 模块 功能
将 regionprops 函数应用于标记图像区域的测量代码如下所示。
from skimage.measure import label, regionprops, regionprops_table
label_img = label(individual_particles, connectivity = im2.ndim)props=regionprops(label_img, im2)for p in props:
display('label: {} area: {}'.format(p.label, p.area))
props_t = regionprops_table(label_img, im2, properties='label','area', 'equivalent_diameter','solidity'])df = pd.DataFrame(props_t)
使用 regionprops_table 函数将面积、等效直径和实度等属性制成与 pandas 兼容的表格。考虑每像素 0.005 微米的标度来计算颗粒的尺寸。
显示颗粒大小的代码输出
当显微照片中有不同尺寸的颗粒时,参数“颗粒尺寸分布”变得很重要。所有现实世界的物质都包含无数的粒子(比如多晶金属)。显微照片只捕捉到材料的一小部分代表性区域。正如本文开头提到的,颗粒大小决定了材料的几个重要属性。
现在,问题是应该考虑平均颗粒尺寸还是整个颗粒尺寸分布。平均粒度是指粒度均匀的材料,而 PSD 是粒度在一定范围内变化的材料的重要参数。
显示 D10、D50 和 D90 百分位的粒度分布图提供了大量信息,并被用作理解材料性能的指南。此外,它通过计算如下定义的“跨度”告诉我们参数的一致性。
较小的跨度值表示颗粒尺寸更加一致。另一方面,较大的跨度值意味着分布的异质性。
我已经采取了下面的模拟图像,以显示颗粒大小分布的计算。
作者图片:模拟图片
这张图片显示了大量大小不同、分离良好的单个颗粒。因为在这个图像中没有粒子群,所以应用分水岭函数的步骤是不相关的。首先,我们将此图像转换为灰度,然后进行阈值处理。然后,我们将 label 函数应用于二进制图像,生成一个带标签的图像(如下所示),用不同的颜色显示不同大小的粒子。
标签图像
应用每像素 4 纳米的标度来计算代表颗粒尺寸的**当量直径。注意,等效直径是与区域面积相同的圆的直径。我们使用 regionprops 和 regionprops_table 直接计算并列出所需的属性。然后,该表格被导出为电子表格文件。使用 Numpy 的百分位数函数,我们获得了统计参数— D10、D50 和 D90 百分位数。因此,估计大约等于 1 的跨度,这表明模拟图像的粒子尺寸不均匀。注意,跨度越接近零表示 PSD 越一致。
粒度分布可视化
我使用了Seaborn——一个统计数据可视化库,使用 distplot 函数绘制颗粒尺寸分布。该函数结合了直方图和核密度估计(KDE)函数。下图显示了模拟图像的粒度分布图。
粒度分布图
**代码:**请点击 这里可以从我的 Github 库中获取代码。
总结
定量估计是图像数据处理的基本部分,因为它有助于决策。在图像中定义比例尺对于特征的量化是强制性的。此外,在定量分析之前,图像需要经历一组细化步骤。
在这篇文章中,我通过拍摄两张图像来说明上述观点,目的是挖掘颗粒尺寸数据。我演示了一个额外的预处理步骤的必要性,即应用分水岭函数来分离第一幅图像**中有重叠颗粒的颗粒,以进行尺寸计算。**否则我们会得到大小不同的粒子群。这将完全误导对材料属性的相同解释。
这篇文章的另一个亮点是描述了颗粒尺寸分布的意义,而不是包含不同尺寸颗粒的系统(图片)的单一平均值。PSD 是具有结构异质性的材料**(例如多晶材料)的重要量化指标。**
我希望这篇文章将有助于从图像中计算可测量的参数,这些参数可以作为预测算法的输入。
感谢阅读!
每个数据科学家都应该知道的数学优化试探法
原文:https://towardsdatascience.com/mathematical-optimization-heuristics-every-data-scientist-should-know-b26de0bd43e6
寻找最佳解决方案(在最高的顶端)。图片由 Dall-E 2 提供。
本地搜索、遗传算法等等
解决数学优化问题有许多不同的方法。您可以使用贪婪算法、约束编程、混合整数编程、遗传算法、局部搜索等等。根据问题的大小和类型,以及期望的解决方案质量,一种技术可能比另一种更好。
这篇文章概述了解决离散优化问题的不同启发式方法。首先,我解释了用数学方法描述优化问题所需的三个要素。然后,我将给出一些常见的和性能良好的搜索试探法的解释。
最优化问题
这里有一个简短的数学优化复习。为了从数学上定义一个优化问题,你需要以下三个组成部分:决策变量、约束和目标。
让我们看一个简单的例子。你是一个小邮局,你每投递一个包裹就赚不同的钱。送货车的空间有限。投递部门希望在每一轮投递中投递尽可能高的总价值。你应该递送哪些包裹?
图片作者。
决策变量
决策变量可以取不同的值。目标是找到决策变量的最佳值。最好的价值观是什么?这取决于目标和约束条件。在投递后的例子中,每个包裹都有一个二元决策变量。如果不传递包,则变量为 0,如果传递包,则变量为 1。
限制
约束是不允许的事情或界限,通过正确设置它们,你肯定会找到一个在现实生活中可以实际使用的解决方案。投递后的例子中的一个约束:你不能投递所有的包裹,因为投递车的空间有限。如果货车的最大空间等于 600,您应该添加一个约束,以确保所选的包不会超过此限制。
目标
目标是你在最优化问题中的目标,这是你想要最大化或最小化的。投递局的目标是选择最有价值的包裹进行投递。在目标中,您希望最大化所选包的总价值。
以下是该示例的完整描述:
问题的数学描述。图片作者。
如果一个问题是明确定义的(即存在一个可行解),那么对于优化问题总是存在至少一个最优解。很难找到这些最优解决方案中的一个,尤其是当问题很大很复杂的时候。并不是这篇文章中讨论的所有技术都能保证找到最佳解决方案。但是,如果您将它们正确地应用于大型问题,它们会比使用约束或混合整数编程技术的解决方案更快。
优化技术
有不同的试探法可以用来解决优化问题。在这里,我将解释其中的一些。
我假设你已经熟悉蛮力,即尝试所有可能的解决方案并跟踪最佳方案。另一个你可能知道的技术是动态编程,在这里问题被分解成更小的子问题。如果你不熟悉动态编程和暴力破解,这篇文章解释了它们。当你的问题很小时,蛮力和动态编程完全可以使用。当问题越来越大时,他们将花费太多的时间并且效率低下。蛮力和动态编程不是启发式的,因为它们不会减少搜索空间。您可以通过系统地测试一个较小的解决方案子集的所有可能的解决方案(蛮力),决定将蛮力与局部搜索或遗传算法结合起来。
贪婪算法
要解决投递局问题,一个简单的方法是从贪婪算法开始。他们给出了一个基准,并很快提供解决方案。greedy 背后的想法是,你选择一个包裹,直到送货车装满。你不选择任何包,但你可以从最有价值的包开始。你继续这样做,直到货车装满。假设货车的最大载客量为 60,以下是我们将选择的包装:
贪婪选择:最高值。图片作者。
还有其他方法来决定下一个套餐。通过将每个包装的价值和尺寸相除,可以得到每个包装的每个尺寸单位的价值。你可以把它描述为价值密度。通过选择每尺寸单位价值最高的包装,有可能提出更好的解决方案。
贪婪选择:每尺寸单位的最高值。图片作者。
贪婪的一个优点是速度快。但是对于更复杂的问题,在大多数情况下,解决方案远非最优。
本地搜索
下一个技巧很有趣。本地搜索非常直观。它是这样运作的:你从一个解决方案开始,你要通过局部行动来改进这个解决方案。这意味着你对当前的解决方案做了一点小小的改动,从而提高了目标。您继续应用局部移动,直到没有更多的移动可以提高目标。
让我们再来看看交货的例子。我们可以从一个解决方案开始:
左边是本地搜索开始解决方案,右边是其他包。图片作者。
局部移动可以是将选定的包与未选定的包交换。我们密切关注容量限制,并努力满足每次本地搬迁的需求。移动的一个例子可能是:
局部移动:交换两个包来增加目标。图片作者。
移动后,新的目标值为 115。我们从选择中删除具有较低值的包,并添加具有较高值的包,同时仍然有一个可行的解决方案。
通过应用一个局部移动,你可以达到的所有可能的解决方案被称为当前解决方案的邻域。
你不能超过货车的容量限制。所以在这种情况下,如果一个包比其他任何包都大,即使价值很高,我们也绝不会选择这个包!
陷入局部最优。图片作者。
这是本地搜索的一个缺点。你可能会陷入局部最优:
局部最优。图片作者。
有办法克服这个问题。你可以选择一次交换多个包,使之等同于一次移动。通过这样做,你的邻居增加了,你可以找到更多的解决方案。
你也可以决定从多个解决方案开始,而不是一个。然后对每个解重复交换过程,这被称为迭代局部搜索。
另一种方法是选择以一定概率使目标变得更糟的行动:
降低目标值时选择包的概率。图片作者。
如果温度参数很大,接受降级移动的可能性很高。如果温度小,这个几率就低。模拟退火就是利用了这个概率。它从高温开始,然后逐渐降低。这意味着,在开始的时候,你是在执行一个随机的解决方案。当温度降低时,搜索变成局部的。模拟退火在硬基准测试中表现出色。
禁忌搜索是我想提到的最后一种避免局部最优的技术。禁忌搜索的想法是跟踪你已经访问过的解决方案。不允许再回到他们身边。将所有以前的解决方案保存在内存中的成本会很高。相反,您可以决定存储转换或保持固定大小的禁忌列表。
可以结合迭代局部搜索、模拟退火和禁忌搜索等技术。
遗传算法
你也可以决定使用遗传算法。遗传算法的核心思想是反映自然选择的过程。解决问题的方法叫做个体。首先,从生成初始种群开始。这个群体是由个体组成的。然后,你计算每一个人的适应度。您可以将适应度函数与目标进行比较。最适合的个体(他们有最好的客观价值)被选择出来进行繁殖,以便产生后代。
对于我们的递送示例,每个包裹都是一个基因,其值可以是 0 或 1。我们有四个人的初始群体看起来像这样:
遗传算法的初始种群(4 个个体)。图片作者。
现在我们选择最适合的个体(最高的客观价值)来产生后代。在我们的例子中,个体 2 和 4 具有最高的适合度值。有不同的方法可以产生后代,一种常见的方法是随机选择一个交叉点,交换基因直到这个交叉点。
交叉:交换最佳个体的基因,直到交叉点。图片作者。
下一步是突变。我们以较低的随机概率翻转一些基因,在我们的情况下,让我们取 0.14 (1 除以 7,7 是包装的数量)。这是重要的一步,因为我们希望保持多样性,防止过早趋同。我们将变异后代 1:
突变:将一个基因按一定概率从 0 → 1 或 1 → 0 进行切换。图片作者。
现在,我们将计算新个体的适应度。人口数量是固定的。具有最低适应值的个体死亡。
加上后代,适应值最低的个体死亡。图片作者。
下面是完整的算法:
- 创建初始种群。
- 重复直到收敛:
- 选择中最适者的个体。
-选择一个交叉点创建后代。 - 突变基因。
-计算适应度,一些个体死亡。
- 选择中最适者的个体。
使用遗传算法,也有可能陷入局部最优。有多种方法可以克服这一点。您可以创建初始群体的子集,并在选择阶段进行随机化。这可以防止在选择过程中反复使用相同的群体。另一种避免局部极小值的方法是给存活时间更长的个体和/或比其他个体更独特的个体额外的奖励,因为他们可以帮助找到一个通用的解决方案。
混合技术
这里讨论的快速找到高质量解决方案的最后一种方法是通过组合不同的技术。
一个例子是大邻域搜索,其中局部搜索与约束规划(CP)或混合整数规划(MIP)相结合。CP 和 MIP 的缺点是它们在处理大型问题时会遇到困难,并且需要大量时间来获得最优解。通过将本地搜索与 CP 或 MIP 相结合,你就拥有了两个世界的精华。你可以用 CP 或 MIP 解决小的子问题,用局部搜索选择一个新的子问题。
大型小区搜索的步骤是:
- 从问题的可行解决方案开始。你可以使用任何你喜欢的技术找到解决方法。
- 重复直到满足一个标准:
-选择一个邻域(问题的一部分)。
-优化这个子问题(用 CP 或 MIP)。
在求解过程中,你会跟踪最佳解。例如,可以通过固定一组变量来定义邻域。
混合方法的另一个例子是 memetic 算法。模因算法结合了遗传算法和局部搜索。
以混合方式结合多种“工具”。由猎人哈利在 Unsplash 上拍摄的照片
结论
在这篇文章中,你已经看到了不同的启发式方法来解决数学优化问题。希望你能通过局部搜索、遗传算法或混合方法快速找到优化问题的解决方案!还有其他有趣的和性能良好的搜索试探法,如粒子群优化,蚁群优化和随机隧道。
有关系的
Matplotlib vs. Plotly:让我们决定一劳永逸
原文:https://towardsdatascience.com/matplotlib-vs-plotly-lets-decide-once-and-for-all-dc3eca9aa011
从 7 个关键方面进行深入快速的比较
作者的愚蠢形象
足球迷有一个令人讨厌的习惯。每当一名年轻但公认杰出的球员出现时,他们都会将他与梅西或罗纳尔多等传奇人物相提并论。他们选择忘记那些传奇人物在新手长出牙齿之前就已经统治了这项运动。将 Plotly 与 Matplotlib 进行比较,从某种意义上来说,类似于开始时的情况。
Matplotlib 从 2003 年开始被大量使用,而 Plotly 在 2014 年才刚刚问世。
此时许多人已经厌倦了 Matplotlib,所以 Plotly 因其新鲜感和交互性而受到热烈欢迎。尽管如此,该库不能指望从 Matplotlib 那里夺取 Python 绘图包之王的头把交椅。
2019 年,当 Plotly 在 7 月发布其 Express API 时,事情发生了巨大的变化。这激发了人们对图书馆的兴趣,人们开始到处使用它。
随着另一个主要版本(5.0.0)于去年 6 月发布,我认为 Plotly 已经足够成熟,可以与 Matplotlib 相提并论。
说完这些,我们开始吧:
自定义函数来绘制分数。完整的功能体可以在我创建的GitHub gist上看到。
https://ibexorigin.medium.com/membership
获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:
https://alphasignal.ai/?referrer=Bex
1.API 可用性
让我们从比较它们的 API 的易用性开始。两者都提供高级和低级接口来与核心功能交互。
1.1 高级 API 的一致性(Pyplot 与 Express)
一方面,Plotly Express 在一致性方面表现出色。它只包含访问内置图的高级功能。它没有引入执行现有功能的新方法,而是一个包装器。所有对 Express 的 plot 调用都返回核心人物对象。
另一方面,PyPlot 接口将所有绘图功能和定制打包到一个新的 API 中。即使绘图调用具有相同的签名,定制功能也不同于 Matplotlib 的面向对象 API 中的功能。
这意味着如果你想切换界面,你必须花时间去了解它们的区别。
此外,在 Matplotlib 中创建 plots 会返回不同的对象。例如,plt.scatter
返回一个PathCollection
对象,而plt.boxplot
返回一个字典。这是因为 Matplotlib 为每个绘图类型实现了不同的基类。对许多人来说,这确实令人困惑。
plot_scores(mpl=0, px=1)
1.2 在 API 之间切换所需的代码量
要从 PyPlot 切换到 Matplotlib 的 OOP API,只需改变与核心数据结构的交互方式,比如figure
和axes
对象。对图的调用具有相似的签名,并且参数名称不会改变。
从 Plotly Express 切换到 Plotly Graph Objects (GO)需要一个陡峭的学习曲线。创建所有绘图的函数签名发生了变化,GO 为每个绘图调用添加了更多的参数。尽管这样做是为了引入更多的定制,但我认为情节最终会异常复杂。
另一个缺点是 GO 将一些核心参数移到了情节调用之外。例如,可以在 Plotly Express 中的绘图内直接创建对数轴。在 GO 中,您可以使用update_layout
或update_axes
功能来实现这一点。而 Matplotlib 的 PyPlot 或者 OOP API 就不是这样(参数不移动也不改名字)。
plot_scores(mpl=1, px=1)
1.3 定制 API
尽管有一个单独的关于定制的章节,但是我们必须从 API 的角度来讨论它。
Matplotlib 中的所有定制都有独立的功能。这允许你以代码块的形式改变情节,并使用循环或其他程序。
相比之下,Plotly 广泛使用字典。虽然这为您与图和数据的交互提供了一定的一致性,但它以代码可读性和长度为代价。由于许多人更喜欢update_layout
函数,它的参数经常以一大堆嵌套字典结束。
您可能会停下来思考这些 API 之间的差异,但是 Matplotlib 的 API 更 Pythonic 化,可读性更好。
plot_scores(2, 1)
2.速度
要了解速度之间的真正差异,我们必须使用更大的数据集。我将从 Seaborn 导入钻石数据集,并比较创建简单散点图所需的时间。
我将使用%%timeit
magic 命令,该命令多次运行相同的代码块来查看标准偏差误差。
测量 Matplotlib:
绘图测量:
Matplotlib 几乎比 Plotly 快 80 倍,SD 误差更低。可能这是因为 Plotly 渲染了交互剧情。让我们再次检查速度,这次关闭交互性:
不幸的是,关闭交互性并没有多大帮助。Matplotlib 在速度上碾压 Plotly:
plot_scores(3, 1)
3.支持的绘图类型数量
在这一领域,Plotly 处于领先地位。从 Plotly API 参考中,我数出了近 50 个独特的图。尤其是,Plotly 是一流的,当谈到某些类型的图表。
例如,它专门支持财务绘图,并提供了figure_factory
子包来创建更复杂的自定义图表。
另一方面,Matplotlib 有适度的情节选择。我不认为他们会与 Plotly 提供的丰富选择相匹配,即使我们添加了 Seaborn 的情节:
plot_scores(3, 2)
4.交互性
嗯,如果只有 Plotly 有这个功能,怎么比较交互性呢?
没有多少人知道这一点,但在 Jupyter 笔记本之外,默认情况下,Matplotlib 绘图以交互模式呈现。
作者截图
不幸的是,这种程度的交互性与 Plotly 相比根本不算什么。所以,让我们把普洛特利的分数提高一分:
plot_scores(3, 3)
现在,对于决胜局——除了一般的交互性,Plotly 还提供了自定义按钮:
滑块:
以及更多将整体用户体验提升到更高水平的功能。这值得再提一点:
plot_scores(3, 4)
5.自定义
对于许多数据科学家来说,定制就是一切。您可能希望根据您的项目数据创建自定义主题并使用品牌颜色(一个很好的例子可以在这里的看到用于可视化网飞数据)。
也就是说,让我们看看您可以优化的最重要的组件以及它们在包之间的差异。
5.1 颜色和调色板
Matplotlib 和 Plotly 都有专门的颜色和调色板子模块。
Matplotlib 允许用户使用颜色标签、十六进制代码、RGB 和 RGBA 系统来更改绘图元素的颜色。最值得注意的是,在mpl.colors.CSS4_COLORS
下,可以通过 100 多个 CSS 颜色标签。
Plotly 确实实现了相同的功能,但是 Matplotlib 提供了来自其他绘图软件如 Tableau 的颜色。此外,在 Matplotlib 中传递颜色和调色板不会造成混乱。
在 Plotly 中,至少有六个参数处理不同的调色板。相比之下,MPL 只有两个灵活的参数color
和cmap
,可以适应您传递的任何颜色系统或调色板。
plot_scores(4, 4)
5.2 默认样式
在临时分析期间,不需要超出默认设置。这些类型的分析通常会持续很长时间,因此这些默认设置必须能够即时生成尽可能高质量的图表。
我想我们所有人都同意 Matplotlib 默认,嗯,真糟糕。看看这两个图书馆创建的奥运会运动员身高体重散点图:
普洛特利的看起来更好。
此外,我喜欢 Plotly 坚持数据可视化最佳实践的方式,比如只在必要时使用额外的颜色。
例如,当创建条形图或箱线图时,Plotly 使用统一的颜色而不是调色板。Matplotlib 做的正好相反,它给每个条形图或箱线图着色,即使这种颜色没有给图添加新的信息。
plot_scores(4, 5)
5.3 主题
Plotly 采取这一部分仅仅是因为它有神奇的“黑暗模式”(叫我主观,我不在乎)。它看起来更舒服,给情节一种奢华的感觉(尤其是和我最喜欢的红色搭配的时候):
在朱庇特实验室看起来太光滑了!
plot_scores(4, 6)
5.4 全局设置
我花了这么长时间来集成 Plotly 的原因是它缺乏控制全局设置的功能。
Matplotlib 有rcParams
字典,您可以很容易地调整它来设置全局绘图选项。你可能会认为一个非常依赖字典的图书馆会有一个类似的字典,但是不!
Plotly 在这方面真的让我很失望。
plot_scores(5, 6)
5.5 轴
轴最重要的组成部分是刻度线和刻度标签。
老实说,直到今天,我还没有完全掌握如何在 Matplotlib 中控制 ticks。这是因为 Matplotlib 没有一致的 API 来控制轴。
你可能会责怪我不够努力,但我通过查看一次文档,就明白了我需要学习的关于控制虱子的一切。
Plotly 只有一种处理 ticks 的方式(通过update_xaxes/update_yaxes
或update_layout
)。当您在 Express 和 GO 之间切换时,这些函数不会改变,而在 Matplotlib 中,情况并非如此。
plot_scores(5, 7)
5.6 控制图表的个别方面如何?
我们必须把这个给 Matplotlib。我这样做不是因为普洛特利已经领先了,我必须保持这种阴谋。
Matplotlib 将各个组件实现为单独的类,使其 API 非常精细。更精细意味着有更多的选项来控制图中可见的对象。
以箱线图为例:
即使情节看起来空白,它允许几乎无限的定制。例如,在返回的字典的boxes
键下,您可以将每个箱线图作为面片对象进行访问:
这些对象打开了所有发生在 Matplotlib 下的神奇事物的大门。它们也不局限于箱线图,你可以从许多其他图中访问面片对象。使用这些面片对象,您可以自定义绘图中形状周围的每条线、每个角和每个点。
plot_scores(6, 7)
6.散点图
散点图在统计分析中起着举足轻重的作用。
它们用于了解相关性和因果关系,检测异常值,并在线性回归中绘制最佳拟合线。所以,我决定用一整节来比较两个图书馆的散点图。
为此,我将选择前面章节中的身高与体重散点图:
Matplotlib 的默认散点图。
我知道,看起来很恶心。然而,看着我做一些定制,把情节变成一件(好吧,我不会说艺术):
减小分笔成交点大小并降低其不透明度后的相同散点图
在应用最后一步之前,我们可以看到这些点围绕不同的行和列进行分组。让我们添加抖动,看看会发生什么:
抖动后相同的散点图。
现在比较最初的图和最后的图:
左图——初始图。右—转换后。
我们甚至不知道这些点在 Plotly 中被分组为行和列。它不允许将标记大小更改为小于默认值。
这意味着我们不能抖动分布来考虑体重和身高在离散值上四舍五入的事实。
plot_scores(7, 7)
哇!到目前为止一直是不分上下。
7.证明文件
作为最后一个组成部分和决胜局,让我们比较文档。
当我还是初学者的时候,Matplotlib 文档是我期望找到问题答案的最后一个地方*。*
首先,如果不打开其他几个链接页面,你就无法阅读文档的任何一页。文件是一个混乱的怪物。
第二,它的教程和示例脚本似乎是为学术研究人员编写的,几乎就像 Matplotlib 故意恐吓初学者一样。
相比之下,Plotly 要有条理得多。它有完整的 API 参考,并且它的教程大多是单机版的。
它并不完美,但至少看起来不错——感觉不像你在读 90 年代的报纸。
*plot_scores(7, 8)*
> 2022 年更新:Matplotlib 完全修改了它的文档,看起来非常令人愉快。我们可以称这部分为平局。
摘要
老实说,我在写这个比较的时候,完全相信 Matplotlib 最终会胜出。
当我完成一半的时候,我知道会发生相反的情况,我是对的。
Plotly 确实是一个非凡的图书馆。尽管我让我的个人偏好和偏见影响了分数,但没有人能否认这个软件包已经实现了许多里程碑,并且仍在快速发展。
这篇文章并不是要说服你放弃一个包而使用另一个。相反,我想强调每个包擅长的领域,并展示如果将这两个库都添加到您的工具箱中,您可以创建什么。
感谢您的阅读!
*https://ibexorigin.medium.com/membership https://ibexorigin.medium.com/subscribe
我的更多故事:
https://ibexorigin.medium.com/yes-these-unbelievable-masterpieces-are-created-with-matplotlib-b62e0ff2d1a8 https://ibexorigin.medium.com/how-to-use-matplotlib-annotations-like-you-know-what-you-are-doing-da61e397cce3 *
注意事项:什么是注意,以及如何在 Transformer 模型中计算注意
原文:https://towardsdatascience.com/matters-of-attention-what-is-attention-and-how-to-compute-attention-in-a-transformer-model-4cbbd3250307
变压器型号注意事项综合简易指南(附示例代码)
图 1:变压器模型中的注意力(来源:作者)
在这篇文章中,你将学习注意力,它的计算和在一个转换网络中的角色。您还将了解文本转换器中的矢量嵌入、位置嵌入和注意力实现。这将利用概念,“变形金刚”和“自动编码器”,所以,如果你想了解更多关于这些话题,然后随时检查我的早期职位。
注意力和视觉注意力
如果我们分析人类的认知,我们会注意到人类所有的认知能力都归结为一个重要的特征,叫做‘注意力’。注意力不仅对资源的有效利用很重要,而且也是原始感觉数据语义理解的主要成分。那么,什么是注意力呢?在最简单的形式中,注意力是通过处理并保存在记忆中以备将来回忆来记录环境刺激的能力。由于人类拥有有限的处理能力,因此,注意力就成了选择什么应该被处理和保存在记忆中的标准。通过这种方式,视觉注意力是识别场景中显著的物体/区域并将其记录为有助于实现目标的潜在有意义刺激的过程。来自眼睛的视觉输入在大脑的不同阶段进行处理,每个阶段在大脑的不同区域进行处理,称为视觉皮层。在早期阶段,处理低级特征(例如,方向、运动、颜色和强度)。在稍后阶段,获得高级特征,并且形成聚集的抽象表示,其被称为显著图* [1]。*
图 2:以显著性图形式的人类视觉注意力处理的框图[1]
这些显著图形成了视觉注意力的基础。几乎所有的知识都是通过视觉或听觉获得的,包括语言学习和理解,因此,文本处理也采用了与视觉处理相同的注意机制。因此,在文本处理的情况下,这种注意力是识别序列中各种显著单词的机制,并且构建显著图将意味着构建最能代表给定文本序列的显著单词序列。
变压器型号中的注意事项
序列建模是以连续表示来表示输入序列(例如,单词)并恢复语义上映射到输入序列的另一序列的过程。这种建模的一个示例应用是语言建模和翻译任务。序列建模任务长期以来被认为是作为递归神经网络(RNN)架构来建模的,因为它们能够捕获输入序列中各种元素之间的语义关系。这种模型通常使用基于编码器-解码器的架构,该架构结合了递归层,由此编码器将输入序列压缩成潜在向量,而解码器将其扩展成输出序列。递归层确保在输入和输出序列之间存在位置映射。
这些 rnn 试图捕捉我们阅读文本时人类使用的注意力机制。然而,这种方法使问题过于复杂,因此需要很长的训练时间,而表现平平。转换器作为一种补救措施被提出,由于其相对高效和准确,它们很快成为构建语言模型的标准方法。在对文本序列建模时,表示序列中各种元素位置的内部关系也很重要。这种建模通常被称为自我注意或内部注意。变形金刚单独使用自我注意机制来表示和映射输入和输出序列,从而更好地模仿人类语言理解。
矢量和位置嵌入
transformer 模型使用几个预处理步骤来为核心模块(例如,注意力和前馈网络)准备好输入文本。首先,通过将长文本分割成一组单独的单词来对输入文本进行标记化。在处理这些输入单词标记之前,它们应该以不变的形式表示。这种表示通过使用某种字典模型(例如,单词袋模型)来获得。这些模型通常已经在大量的单词集合上进行了预训练,并为将单个单词表征表示为多维空间中的一组嵌入向量提供了良好的基础。这种多维空间中的单词基于它们与其他单词的语义相似性形成聚类。正如您在图 3 中看到的,在这样的嵌入之后,具有相似含义的单词聚集在一起。
图 3:示例序列中单词的向量嵌入(来源:作者)
在向量嵌入之后,嵌入单词的位置也是有用的,因为它暗示了该单词在给定序列中的上下文。结合这种位置嵌入的一种方式是将序列中单词的索引与其向量嵌入相加/连接。但是,如果您注意的话,您会发现当一个文本文档中有数百万个单词时,这种技术并不适用。随着序列长度的变化,规范化这种基于索引的定位也不起作用。因此,使用了一种嵌入这种定位的巧妙方法,该方法借用了正弦波建模的概念。产生具有变化频率的大的连续信号,然后在离散时间步长获得样本。这提供了一种简单而有效的方法,可以很好地适应大文本。
图 4:位置向量计算(图片来源:作者)
正如你在图 4 中注意到的,向量的元素是从正弦和余弦波函数中采样的。(余弦波只是正弦波的相移形式)。对于偶数指数,使用正弦函数,对于奇数指数,使用余弦函数。注意正弦函数的波长从 2pi 变化到 2pi * C 。其中' C' 是用于控制信号中振荡总数的常数。在[2]中,它的值是一个大整数(如 10000)。**
在获得每个标记的位置向量之后,通过将向量嵌入与位置嵌入相加来获得组合表示。输出嵌入向量然后被馈送到变换器的注意网络中。图 5 显示了这种嵌入的结果。
图 5:示例序列中单词的组合嵌入(向量嵌入+位置嵌入)(来源:作者)
变压器模型架构中的注意事项
注意力在变压器模型架构中起着关键的作用。事实上,这就是变形金刚的语义力量所在。注意力允许确定一个序列中最突出的单词及其相互关系。这样就有可能从一大篇文章中提取要点,并从中得出有意义的抽象概念。注意力取决于三个术语:查询、键和值。顾名思义,查询是一个搜索词,目的是在序列中找到相关的词。这是人们想要引起变压器注意的地方。例如,在前面使用的单词序列中,如果我们选择“作者”作为查询,那么我们实际上是在寻找序列中与“作者”有密切关系的所有其他单词。
图 6:变压器模型中的注意力(图片来源:作者,概念图覆盖:作者,图片摘录:[2])
如果您有数据库方面的背景,这听起来会很相似。因此,就像对数据库的查询一样,对转换器的查询需要某种字典/数据库(即键、值对)。您将查询作为一个键插入,并获得所需的值。然而,由于我们仍在尝试学习关联,因此需要执行查询与关键字的相似性。
以这种方式,键实质上是转换器模型的输入向量,而值是模型的输出。然后,查询是在给定时间从这些关键字中提取的任何样本。然而,在训练时不存在的新单词的情况下,查询也可以不同于关键字。
自我关注
自我关注是一种关注形式,其中查询、键和值是从输入到转换器模型的相同原始单词序列中采样的。直觉告诉我们,转换器应该能够在训练阶段学习输入序列中的单词关联。这允许 transformer 构建语义单词关联,并能够在给定一个特定单词作为来自序列的查询时推断出其他单词。
如果我们从另一个角度来看,自我关注类似于协方差分析,它旨在通过构建基于协方差的权重/相似性矩阵来发现序列中各个单词的相似性。事实上,[2]中描述的注意函数只是一个协方差矩阵乘以单词向量的矩阵。
因此,在自我注意的情况下, QK^T 是位置嵌入词向量之间的成对相似性。并且 softmax 根据相似性得分生成概率分布。当这个相似性权重矩阵与嵌入的单词向量 V 相乘时,它将作为一个掩码,并且只突出那些与相应查询向量具有最高相似性的单词。
可学习的自我关注
**当在神经网络中使用自注意时,这种查询、键和值的参数是可学习的。更具体地,查询、关键字和值具有专用层,由此相应层的输出是查询矩阵 **Q、**关键字矩阵 **K、和值矩阵 V 。这些层在没有偏差项的情况下被训练,这意味着它们只乘以各自的权重( WQ 、 WK 、 WV )。这些重量是可学习的参数,因此,自我关注通过训练得到改善。
编解码器注意
除了自我注意之外,作者在[2]中还利用了另一种称为“编码器-解码器注意”的注意机制。在这样的关注层中,查询来自先前的解码器层,而键、值对来自编码器的输出。这允许使用编码器-解码器架构进行序列建模的能力。这种应用的一个很好的例子是语言翻译任务,其中输入和输出不是一一相关的。
多头关注
单一的自我注意机制提供了一种对输入和输出序列之间的单词关联进行建模的方式。然而,在一个 transformer 架构中使用多个注意模块(称为头)是有益的。这意味着拥有多层注意力矩阵束( Q , K , V )以及各自的训练权重( WQ , WK , WV ),这允许更好地处理大型文本,并为不同的头部提供不同的注意力输出。这些注意力头的输出然后连接在一起,最终形成线性输出。
结束语
在本文中,您了解了变压器网络中的注意力以及如何计算注意力。为了进一步阐明概念和实验方法,您可以运行各自的 python 笔记本,该笔记本以清晰简单的方式详细描述了每个步骤。
代码请点击链接:
**https://github.com/azad-academy/transformer-attention
在 Patreon 上支持我:
https://patreon.com/azadlab
在子栈上找到我:
https://azadwolf.substack.com
订阅更多:
https://azad-wolf.medium.com/subscribe
关注推特更新:
https://www.twitter.com/@azaditech
参考文献:
[1] L. Itti , C. Koch,“视觉注意力的计算模型”,自然评论神经科学第 2 卷,第 194-203 页,2001 年
[2] 阿希什·瓦斯瓦尼、诺姆·沙泽尔、尼基·帕尔马、雅各布·乌兹科雷特、利翁·琼斯、艾丹·戈麦斯、卢卡斯·凯泽、伊利亚·波洛舒欣《注意力是你所需要的一切》,arXiv:1706.03762【cs。CL],2017**