TowardsDataScience-2023-博客中文翻译-二十五-
TowardsDataScience 2023 博客中文翻译(二十五)
如何在 Python 中进行单变量离群点检测
离群点检测系列——第二部分
·发表于 Towards Data Science ·阅读时间 9 分钟·2023 年 2 月 6 日
--

介绍
在开始离群点检测之前,第一个要问的问题是,“我的数据集是否存在离群点?”虽然通常答案是“是的”,但在投入大量精力(如使用机器学习模型)来隔离它们之前,建议先探测离群点的存在。
因此,我们将通过查看数据可视化等检测离群点存在的一般方法来开始本系列离群点检测教程的第二部分。然后,我们将继续讨论检测单变量和多变量离群点的方法。
让我们开始吧!
地球是一个离群点——理论
[towardsdatascience.com
要使用的数据集
在整个教程中,我们将使用 Diamonds 数据集。它足够大,不是一个玩具数据集,并且具有良好的数值和类别特征组合。
import seaborn as sns
diamonds = sns.load_dataset("diamonds")
diamonds.head()

图片由作者提供
我们在离群点检测后的最终任务是根据钻石的物理特征和价格预测其克拉数:
diamonds.columns
Index(['carat', 'cut', 'color', 'clarity', 'depth', 'table', 'price', 'x',
'y', 'z'],
dtype='object')
在进入那部分之前,我们希望检测并隔离离群值,以使我们的数据集尽可能干净(目前相对较干净)。
因此,让我们提取特征和目标数组,并使用 Sklearn 的 OrdinalEncoder 类对类别列进行编码:
import numpy as np
from sklearn.preprocessing import OrdinalEncoder
# Extract feature and target arrays
X, y = diamonds.drop("carat", axis=1), diamonds["carat"]
# Select categorical column names
cats = X.select_dtypes(exclude=np.number).columns.tolist()
# Encode categoricals
X.loc[:, cats] = OrdinalEncoder().fit_transform(X[cats])
现在,我们准备好了…
离群值真的存在吗?
…要回答“离群值真的存在吗?”这个问题,第一步是查看目标变量(克拉)的总结统计数据。我们在寻找目标的最小值、均值和最大值之间是否存在可疑的间隙。
y.describe()
count 53940.000000
mean 0.797940
std 0.474011
min 0.200000
25% 0.400000
50% 0.700000
75% 1.040000
max 5.010000
Name: carat, dtype: float64
最小值、均值和最大值分别为 0.2、0.8 和 5.01。最小值和最大值之间、均值和最大值之间的显著差距已经表明可能存在离群值(记住,离群值通常是分布中的极端数据点)。
让我们通过绘制目标变量的直方图来确认这一怀疑:
import matplotlib.pyplot as plt
sns.histplot(y)
plt.xlabel("Carat");

正如我们所看到的,直方图的大部分数据集中在 0.2 到 3 克拉之间。但直方图的右尾延伸到 5 克拉,但我们没有看到任何数据箱。如果你看到一个具有长左尾或右尾但尾部没有数据箱的直方图,那么分布中存在少量极端值(离群值)。 (离群值——检查!)
现在,让我们查看特征数组:
X.drop(cats, axis=1).describe()

图片由作者提供
在排除类别特征后,我们可以大致猜测价格是最重要的特征。像克拉一样,我们可以看到均价与最便宜和最昂贵的钻石之间的显著差异。让我们再次绘制直方图:
sns.histplot(X["price"])
plt.title("Histogram of Diamond Carats")
plt.xlabel("Carat");

图片由作者提供
我们可以看到大多数数据点低于 5000 美元;其余的箱体高度非常低。这变得棘手——现在我们是否将所有 5000 美元以上的点视为离群值?
好吧,不——我们不能仅凭一个图来判断样本是否为离群值。记住,第一印象往往是骗人的。
让我们更仔细地看一下,通过绘制钻石切工质量与价格的箱线图。切工质量从左到右按降序排列。
sns.boxplot(x="cut", y="price", data=diamonds, whis=2)
plt.xlabel("")
plt.ylabel("Price");

图片由作者提供。钻石切工质量与价格。须上方的黑暗斑点表示离群值——有很多,因此它们被混杂在一起。
尽管我们在图中看到许多离群值,它们被标记在箱体的须上方,但我们不能安全地断定它们是离群值。
如果你注意到,最高质量的理想钻石的价格约为 2000 美元(查看中位数),然而,最低质量的普通钻石的价格却高得多,约为 3000 美元。这表明数据集的特征之间存在一些隐藏的多变量关系,我们不能仅从单变量和双变量图中得出结论。
实际上,这几乎适用于所有数据集。纯描述性统计和简单图表只能暗示异常值的存在。由于我们无法通过这些方法看到所有特征之间的大致关系,我们必须转向更复杂的技术。
单变量异常值检测
首先,让我们看看允许我们在单一分布中检测异常值的技术,比如我们钻石克拉的目标数组。
在单变量数据集上进行异常值检测的最流行方法之一是使用普通的 z 分数。回想一下,一个数据点的 z 分数告诉我们它离均值有多少标准差。
例如,如果分布 A 的均值为 5,标准差为 3.2,则样本 17.2 的 z 分数为 (17.2–5) / 3.2 = 3.8125。
我们可以使用 scipy 中的 zscore 函数计算数组中所有元素的 z 分数:
from scipy.stats import zscore
zscore(y).abs()[:5]
0 1.198168
1 1.240361
2 1.198168
3 1.071587
4 1.029394
Name: carat, dtype: float64
在统计学中,z 分数 3 通常用作内点和异常值之间的阈值。因此,通过数组过滤,我们可以将任何样本标记为异常值,如果其 z 分数的绝对值高于 3:
from scipy.stats import zscore
# Calculate the abs of zscore and filter above 3
is_outlier = zscore(y).abs() > 3
outliers_y = y[is_outlier]
num_outliers = len(outliers_y)
print(f"The number of outliers: {num_outliers}")
print(f"Percentage of outliers: {num_outliers / len(y):.3f}")
The number of outliers: 439
Percentage of outliers: 0.008
该方法将 0.8% 的样本标记为异常值,这并非巧合。使用 z 分数三作为异常值阈值的想法源于 经验法则。

经验法则 表示,数据在一个、两个和三个标准差或 z 分数内大致代表 68%、95% 和 99.7% 的分布。
所以,如果样本的 z 分数高于 3,我们可以安全地得出结论,这是一种极端值,因为它与大约 99.7% 的数据集不同。
我们使用 z 分数方法找到了 439 个异常值,但我们真的可以相信这个结果吗?实际上,我们不能。
经验法则,进而,z 分数仅适用于正态分布数据。从我们之前看到的钻石克拉直方图中,我们可以确定我们的目标 y 并不符合正态分布:

作者提供的图像。钻石克拉的直方图。该图显示了分布不是正常的。
所以,我们现在必须采取另一种方法。
修改的 z 分数
当你的分布不正常时,z 分数无法用于检测异常值。相反,你可以使用 z 分数的“兄弟”——中位绝对偏差 (MAD),也称为修改的 z 分数。
这个想法很简单——由于异常值对分布的均值和标准差影响最大,我们将停止使用这些度量标准,而用中位数来替代它们。
为什么用中位数替换均值?中位数表示第 50 百分位,即分布排序时的中间值。这意味着无论离群点多大或多小,它们都不会对中位数的值产生丝毫影响。
当我们在标准差公式中用中位数替换均值时,我们得到中位数绝对偏差。它通过从分布中的每个样本中减去中位数,取绝对值,然后计算差值的中位数来计算。以下是在 NumPy 中对我们的目标 y 进行此操作的方法:
# Find the median
median_y = y.median()
# Find the abs value of differences between the median
abs_diffs = (y - median_y).abs()
# Take the median of differences
mad = abs_diffs.median()
mad
0.32000000000000006
我们可以使用scipy中的median_abs_deviation函数来确认我们的计算是正确的:
from scipy.stats import median_abs_deviation
median_abs_deviation(y)
0.32000000000000006
我们得到了相同的结果。现在,如果我们将 MAD 值插入以下公式中,我们得到修改后的 z 分数:

图片由作者提供。酷炫的 M 是中位数,x_i 是分布中的第 i 个样本。
在修改后的 z 分数中,MAD 分数 3.5 被用作离群点的阈值。公式已作为 PyOD 库中的MAD估算器实现。我们来看看如何使用它:
from pyod.models.mad import MAD
# Reshape the target to make it 2D
y_2d = y.values.reshape(-1, 1)
# Fit to the target
mad = MAD().fit(y_2d)
# Extract the inlier/outlier labels
labels = mad.labels_
labels
array([0, 0, 0, ..., 0, 1, 0])
一旦你从models.mad导入估算器,你可以将其拟合到目标数组y。默认情况下,所有 PyOD 估算器都需要 2D 数组,因此我们在重新调整形状后喂入y。
在拟合 MAD 后,我们可以使用labels_属性获取每个样本的内点/外点标签。它返回 0 表示内点,返回 1 表示外点。
让我们来统计 1 的数量:
outliers_y_mad = y[labels == 1]
num_outliers = len(outliers_y_mad)
print(f"The number of outliers: {num_outliers}")
print(f"Percentage of outliers: {num_outliers / len(y):.3f}")
The number of outliers: 222
Percentage of outliers: 0.004
如你所见,MAD估算器发现了 222 个离群点,而我们用 z 分数发现了 439 个。由于MAD使用了中位数等鲁棒度量,我们可以更信任这个结果。
在去除离群点后,让我们再次绘制钻石克拉数的直方图:
# Filter for inliers, marked as 0s
inliers = y[labels == 0]
sns.histplot(inliers)
plt.title("Histogram of Diamond Carats After Outliers")
plt.xlabel("Carat");

如我们所见,直方图的尾部消失了。
结论
这是在给定新数据集时执行单变量离群点检测的步骤总结:
-
首先检查是否存在离群点,使用基本方法如汇总统计、直方图和箱线图。
-
一旦你对离群点的存在有了合理的假设,你可以开始对目标数组进行单变量离群点检测。
-
如果数组是正态分布的,你可以使用 z 分数。
-
如果你不知道分布类型或确认它不是正态分布,你可以使用修改后的 z 分数,阈值为 3.5。
感谢阅读!
查看我在DataCamp 上的离群点检测综合课程以深入了解该主题(第一章免费!)。
[## 使用我的推荐链接加入 Medium - Bex T.
获取对我所有⚡高级⚡内容的独家访问权限,并在 Medium 上无限制地访问。通过购买我的……
ibexorigin.medium.com [## 每当 Bex T. 发布新内容时,你将收到电子邮件。
每当 Bex T. 发布新内容时,你将收到电子邮件。通过注册,如果你还没有 Medium 账户,你将创建一个账户…
ibexorigin.medium.com ## 如何在 Python 中进行异常值检测:机器学习第一部分
地球是一个异常值 —— 这一理论
[towardsdatascience.com
更多来自我的故事…
[## 5 个 Python 开发者只能羡慕的优秀 Julia 特性
Julia 与 Python 辩论的延续
medium.com ## 5 个最佳 Python 合成数据生成器及其在数据不足时的使用方法
让我们获取更多数据
[towardsdatascience.com [## Bentoml 与 Fastapi:最佳 ML 模型部署框架及其为什么是 Bentoml
BentoML 和 FastAPI 在模型部署方面的详细比较。
如何在 Landsat 卫星图像上绘制坐标,使用 Python
原文:
towardsdatascience.com/how-to-plot-coordinates-on-landsat-satellite-images-with-python-5671613887aa
使用 Landsat 元数据和 Rasterio 将像素位置映射到地理坐标
·发表于 Towards Data Science ·阅读时间 8 分钟·2023 年 5 月 16 日
--

图片来源于 GeoJango Maps 在 Unsplash
位置,位置,位置!这不仅仅是房地产市场的陈词滥调,而且对于遥感也极其重要。无论是监测环境变化、分析城市发展,还是跟踪作物健康,精确的地理定位至关重要。我们需要知道卫星图像中物体的确切坐标,以确保分析和解释的准确性。
因此,我们将探讨如何使用两种方法将坐标直接绘制到 Landsat 场景上:
-
Landsat 元数据文件 (MLT)
-
Rasterio — 一个用于访问地理空间栅格数据的包
我们还将使用 Rasterio 来 重新投影 卫星图像的坐标。特别是,我们将从原始坐标参考系统 (UTM) 转换到 Google Maps 使用的坐标系统 (EPSG:4326)。在此过程中,我们将讨论代码,你可以在 GitHub 上找到完整的项目。
下载 Landsat 场景
我们需要从下载一个 Landsat 场景开始。你可以使用 EarthExplorer 门户完成此操作。或者,如果你想使用 Python,下面的文章会引导你完成整个过程:
使用 landsatxplore Python 包简化 Landsat 场景下载
towardsdatascience.com
最终,你应该有一个类似于图 1的文件夹。这些是所有可用的Landsat 二级科学产品文件。我们将使用可见红光带(_B4.TIF)和 JSON 元数据文件(_MTL.json)。这些文件在下面已被突出显示。

图 1:Landsat 二级科学产品文件(来源:作者)
这个特定场景拍摄于爱尔兰东海岸上方。为了查看这个,我们可视化红光带。我们加载 tiff 文件(第 9 行)。我们缩放光带(第 12 行)并裁剪以增强对比度(第 15 行)。你可以在图 2中看到结果图像。
import tifffile as tiff
import numpy as np
import matplotlib.pyplot as plt
# Landsat scene ID
ID = 'LC08_L2SP_206023_20221118_20221128_02_T1'
# Load Red (B4) band
B4 = tiff.imread('./data/{}/{}_SR_B4.TIF'.format(ID, ID))
# Scale band
B4 = np.clip(B4*0.0000275-0.2, 0, 1)
# Clip to enhance contrast
B4 = np.clip(B4,0,0.2)/0.2
# Display grayscale image
fig, ax = plt.subplots(figsize=(10, 10))
plt.imshow(B4, cmap='gray')
ax.set_axis_off()

图 2:爱尔兰东海岸 Landsat 场景的可见红光带(来源:作者)
我们希望将一个点直接绘制到这个图像上,指向爱尔兰村庄 Howth。看着图 2,你可以看到它大约在沿海的 1/3 处突出。我们要绘制的具体经纬度点在图 3中给出。

图 3:Howth, Dublin 的纬度和经度坐标(来源:谷歌地图)
使用 Landsat 元数据(MLT)绘制点
好的,因此使用 MLT 文件绘制点不像使用 Rasterio 那样简单。不过,探索这种方法是值得的。你会发现我们不必依赖于任何包。我们还将了解 Landsat 使用的坐标参考系统(CRS)。
Landsat 元数据
MLT 文件包含大量关于 Landsat 场景的元数据。你可以在数据格式控制手册中找到所有细节。我们对投影属性感兴趣。这些是关于场景 CRS 的详细信息,并将允许我们对图 2中的像素进行地理定位。我们加载这些属性(第 4–5 行)。
import json
# Load metadata file
MTL = json.load(open('./data/{}/{}_MTL.json'.format(ID, ID)))
projection = MTL['LANDSAT_METADATA_FILE']['PROJECTION_ATTRIBUTES']
projection
图 4提供了投影属性的列表。第一个项目告诉我们[UTM](https://www.usgs.gov/faqs/how-are-utm-coordinates-measured-usgs-topographic-maps#:~:text=The UTM (Universal Transverse Mercator,Zone 19%2C which includes Maine.) 是该场景的 CRS。这是一个投影坐标系统(PCS)。它用于将地球表面(球体)上的地理位置投影到场景(二维表面)上。稍后我们将看到它与地理坐标系统(GCS)的不同之处。

图 4:Landsat 场景的投影属性(来源:作者)
在将 UTM 转换为像素位置时,我们需要从图 4中获取另外两条信息。这些是:
-
GRID_CELL_SIZE_REFLECTVE——这告诉我们每个像素覆盖 30 平方米的土地
-
投影坐标(例如 CORNER_UL_PROJECTION_X_PRODUCT)——这些给出了场景边界框的 UTM 坐标(以米为单位)
实际上,我们只需要前两个坐标。这些坐标是用于边界框的左上角(UL)。查看图 5,我们可以看到这些信息如何用于转换为像素位置。我们知道每个像素是 30 平方米。这意味着我们可以从(543000,6004200)开始,增加/减少 30 米来获取下一个像素的起始位置。如果 UTM 坐标落在该像素范围内,我们就选择这个像素。

图 5:像素起始和结束的 UTM 坐标
在 Landsat 波段上绘制坐标
我们使用这些信息创建了coords_to_pixels函数。作为参数,它接受 UL 角的 UTM 坐标(ul)和我们想要转换的坐标(utm)。
def coords_to_pixels(ul, utm, m=30):
""" Convert UTM coordinates to pixel coordinates"""
x = int((utm[0] - ul[0])/m)
y = int((ul[1] - utm[1])/m)
return x, y
我们现在可以使用这个函数来找到我们在 Howth 的点的像素。我们从 UL 角的 UTM 坐标开始(第 4-5 行)。我们直接从谷歌地图上获取了我们的点,因此它们以纬度和经度的形式给出(第 8-9 行)。我们将这些转换为 UTM 坐标(第 12 行),得到(694624,5918089)。最后,我们将这些转换为像素(第 16 行)。这给了我们像素值(5054,2870)。
import utm
# Get UTM coordinates of upper left corner
ul = [float(projection['CORNER_UL_PROJECTION_X_PRODUCT']),
float(projection['CORNER_UL_PROJECTION_Y_PRODUCT'])]
# Lat/long coordinates of Howth
lat=53.3760
long=-6.0741
# Convert GPS to UTM coordinates
utmx,utmy,_,_ = utm.from_latlon(lat,long)
print(utmx,utmy)
# Convert UTM to pixel coordinates
x,y = coords_to_pixels(ul, [utmx,utmy])
print(x,y)
让我们看看这有多准确。我们在图 2中给定的像素值(第 5 行)上向图像中添加一个白色圆圈。为了看到我们的点,我们通过裁剪图像周围的圆圈来放大(第 8-9 行)。将图 6与图 3中的点进行比较,我们可以看到我们已经准确地将点绘制到 Landsat 场景中。一般而言,90%的 Landsat 点位置误差小于 12 米。
import cv2
# Add circle to image at pixel coordinates
image = B4.copy()
image = cv2.circle(image, (int(x), int(y)), 10, 1,5)
# Crop image around pixel coordinates
h = 100
crop_image = image[y-h:y+h, x-h:x+h]

图 6:使用 Landsat 元数据文件绘制的 Howth 坐标(来源:作者)
使用 rasterio 绘制坐标
上述过程有效!但是,我们可以使用 Rasterio 包让我们的生活更轻松。我们打开红色可见光波段的 tiff 文件(第 4 行)。场景的元数据嵌入在文件中。例如,我们打印 CRS(第 7 行),输出“EPSG:32629”。这是EPSG 代码,表示 UTM 区域 29N。换句话说,与 MLT 文件中给出的 CRS 相同。
import rasterio as rio
# Open red channel with rasterio
B4 = rio.open('./data/{}/{}_SR_B4.TIF'.format(ID, ID))
# Display image map projection
print(B4.crs)
我们可以使用index函数将 UTM 转换为像素坐标(第 9 行)。这输出(5054,2870)。和之前一样!
# Lat/Long coordinates of Howth
lat=53.3760
long=-6.0741
# Get UTM coordinates
utmx,utmy,_,_ = utm.from_latlon(lat,long)
# Convert UTM to pixel coordinates
y,x = B4.index(utmx,utmy)
print(x,y)
所以,使用 Rasterio,我们不再需要担心边界框甚至 CRS。然而,我们仍然需要将纬度和经度转换为 UTM。如果我们想直接绘制纬度和经度,我们将不得不将 Landsat 场景重新投影到 Google Maps CRS。
重投影 Landsat 场景
我们使用下面的代码来完成这一任务。总的来说,它将红色光谱带图像重投影到 EPSG:4326(Google Maps CRS),并将重投影图像保存到新文件中。有几点需要注意:
-
我们计算一个新的转换函数(第 12 行)。这是 Rasterio 用来将坐标转换为像素的内容。
-
当我们重投影文件时,我们使用最近邻重采样方法(第 42 行)。这意味着我们使用最近的单元格的值来填充其他单元格的值。
我们需要一种重采样方法,因为重投影过程会“扭曲”图像以适应新的 CRS。我们将在输出重投影图像时看到这一点。
from rasterio.warp import reproject, Resampling, calculate_default_transform
dst_crs = "EPSG:4326" # google maps CRS
filename = './data/{}/{}_SR_B4.TIF'.format(ID, ID)
new_filename = './data/{}/{}_SR_B4_EPSG4326.tif'.format(ID, ID)
# Open file
with rio.open(filename) as src:
src_transform = src.transform
# calculate the transform matrix for the output
dst_transform, width, height = calculate_default_transform(
src.crs,
dst_crs,
src.width,
src.height,
*src.bounds, # unpacks outer boundaries
)
# set properties for output
dst_kwargs = src.meta.copy()
dst_kwargs.update(
{
"crs": dst_crs,
"transform": dst_transform,
"width": width,
"height": height,
"nodata": 0,
}
)
# write to disk
with rio.open(new_filename, "w", **dst_kwargs) as dst:
# reproject to new CRS
reproject(
source=rio.band(src, 1),
destination=rio.band(dst, 1),
src_transform=src.transform,
src_crs=src.crs,
dst_transform=dst_transform,
dst_crs=dst_crs,
resampling=Resampling.nearest)
为了确认过程已成功完成,我们加载投影图像(第 2 行)并输出其 CRS(第 3 行)。这给我们 “EPSG:4326”。成功!
# Open red channel
B4 = rio.open('./data/{}/{}_SR_B4_EPSG4326.TIF'.format(ID, ID))
print(B4.crs)
现在我们可以直接从经纬度坐标获取像素坐标。这给出了(6336,2227)的值。这些值与之前不同,因为如前所述,投影图像已经被扭曲。
# GPS coordinates of Howth
lat=53.3760
long=-6.0741
# Convert UTM to pixel coordinates
y,x = B4.index(long,lat)
print(x,y)
当我们在图 7中绘制点在投影图像上时,我们可以看到这一点。与图 6相比,纵横比发生了变化。实际上,许多像素已经被更改。这是因为,与 UTM 不同,EPSG:4326 是一个地理坐标系统(GCS)。它用于在球体上(即地球)绘制点。在二维平面上显示具有此 CRS 的图像会导致纵横比的扭曲。

图 6:霍斯坐标在投影图像上绘制(来源:作者)
本文重点讨论了在单个光谱带上绘制坐标。如果你正在处理多个光谱带或 RGB 可视化,底层方法将是相同的。这是因为 Landsat 场景中的所有光谱带将具有相同的 CRS。有关 RGB 通道可视化的详细信息,请参见下面的文章。
如何处理可视化卫星图像时的多个光谱带、大像素值和扭曲的 RGB 通道
[towardsdatascience.com
希望你喜欢这篇文章!你可以在 Mastodon | Twitter | YouTube | Newsletter 上找到我 — 免费注册以获取 Python SHAP 课程
[## 使用我的推荐链接加入 Medium — Conor O’Sullivan
作为 Medium 会员,你的会员费的一部分将用于支持你阅读的作者,并且你可以全面访问每个故事……
参考资料
Maurício Cordeiro Python 在地球科学中的应用:使用 Rasterio 进行栅格合并、裁剪和重投影 medium.com/analytics-vidhya/python-for-geosciences-raster-merging-clipping-and-reprojection-with-rasterio-9f05f012b88a
BGU 栅格 (rasterio) geobgu.xyz/py/rasterio.html#raster-file-connection
gerdoo 地理参考 Landsat 图像的索引 medium.com/@gerdoo/georeferencing-landsat-images-in-python-92d5d5aafc67
PyGIS 栅格坐标参考系统 (CRS) pygis.io/docs/d_raster_crs_intro.html
如何使用 DiagrammeR 包在 R 中绘制图表
原文:
towardsdatascience.com/how-to-plot-graphs-using-the-diagrammer-package-in-r-3fe4642091cc
DiagrammeR , grViz()
·发布在 Towards Data Science ·6 分钟阅读·2023 年 2 月 15 日
--

图片由 Lukas 提供: www.pexels.com/photo/person-holding-blue-pen-590014/
DiagrammeR 是一个 R 包,用于使用 graphviz 和 mermaid 创建图表和流程图。
在本文中,我们将探讨如何使用 DiagrammeR 包中的 grViz() 创建图表。
-
什么是 DiagrammeR?
-
什么是 grViz()?
-
如何使用 grViz() 创建节点、边、标签及其替代和连接
-
如何编织到 HTML
什么是 DiagrammeR
DiagrammeR 是一个在 htmlwidgets 中的 R 包,用于生成使用 graphviz 和 mermaid 库的图表。
1. 创建一个 RMarkdown 文档。
在 RStudio -> 转到 File → New File → R Markdown,然后输入标题并选择输出格式为 HTML。

R Markdown 文档将会在顶部显示文档标题和其他信息。

下面会有 r chunks,带有定界符 py` py` and ```` py ````
当你渲染 R Markdown 文档时,它会运行每个代码块,并将结果嵌入在代码块下方,或者你可以运行每个代码块,结果将显示在该代码块下。
我们可以将 R Markdown 文档编织成 HTML 输出。让我们看看如何在本文中做到这一点。
2. 安装 DiagrammeR 包
在 RStudio 右侧点击 Packages → Install,输入包名 DiagrammeR 并点击 Install。

2. 什么是 grViz?
DiagrammeR 包使用 grViz() 函数生成 Graphviz 图表。我们来看看如何使用 grViz() 函数创建图表。
3. 如何使用 grViz() 创建节点、边、标签及其替代和连接?
在创建图时,我们必须提及布局、节点属性、边属性和连接。

作者提供的图片
让我们来看一下如何创建上述显示的图。在上面的图中,* * 绿色圆圈被称为节点。A=15,B=10,等等-> 是节点的标签。箭头标记称为边。
1. 提及布局
默认布局是 dot。
layout = dot
2. 提及节点属性:
我们可以提到节点的形状、颜色、样式、宽度等。
node [shape = circle,color=green,style=filled,fixedsize=True,width=0.6]
3. 提及边的属性:
边的属性包括颜色、箭头头部、箭头尾部、笔宽、方向等。
edge[color=grey,arrowhead=vee]
4. 提及标签和替代项:
我们必须提及节点的标签和替代项。
A[label = 'A= @@1']
B[label = 'B=@@2']
C[label = 'C=@@3']
D[label = 'D=@@4']
E[label = 'E=@@5']
这里 A -> 节点名称
A=@@1 → 这是标签。
[1]:15
[2]:10
[3]:20
[4]:2
[5]:2
@@1 -> 是替代。这里 1 是脚注编号。当图被渲染时,值 15 将替代 1。
5. 提及连接细节
在我的图中,A 连接到 B 和 C。B 连接到 D,C 连接到 E。
A->B
A->C
B->D
C->E
完整 R 代码:
```{r , echo=FALSE}
library(DiagrammeR)
grViz("
digraph {
layout = dot
节点 [shape = circle,color=green,style=filled,fixedsize=True,width=0.6]
edge[color=grey,arrowhead=vee]
A[label = 'A= @@1']
B[label = 'B=@@2']
C[label = 'C=@@3']
D[label = 'D=@@4']
E[label = 'E=@@5']
A->B
A->C
B->D
C->E
}
[1]:15
[2]:10
[3]:20
[4]:2
[5]:2
")
```py
**Output Graph:**

Image by Author
Let’s create another graph where nodes A and C are in the same rank and B, and E are in the same rank.
If we mention rank=same {A->C}. A and C nodes will be placed in the same line.
rank=same {A->C}
rank=same {B->E}
**Full code**
library(DiagrammeR)
grViz("
digraph {
layout = dot
node [shape = circle,color=green,style=filled,fixedsize=True]
edge[color=grey,arrowhead=vee,minlen = 1]
A[label = 'A=@@1']
B[label = 'B=@@2']
C[label = 'C=@@3']
D[label = 'D=@@4']
E[label = 'E=@@5']
A->B
B->D
edge [minlen = 2]
rank=same {A->C}
rank=same {B->E}
}
[1]:15
[2]:10
[3]:20
[4]:2
[5]:2
")输出图:

作者提供的图片
让我们从另一个节点的下箭头创建一个节点。

作者提供的图片
让我们看看如何创建上述提到的图。
首先,我们需要创建一个空节点。
A -> blank1;
blank1 -> B[minlen=10];
{{ rank = same; blank1 B }}
blank1 -> C
C -> blank2;
blank2 -> D[minlen=1];
{{ rank = same; blank2 E }}
blank2 -> E [minlen=10]
输出:

作者提供的图片
之后,我们可以将空节点的标签设为“ ”,并将宽度和高度设置为非常小(0.01)
blank1[label = '', width = 0.01, height = 0.01]
blank2[label = '', width = 0.01, height = 0.01]
完整代码
```{r , echo=FALSE}
library(DiagrammeR)
grViz("
digraph {
layout = dot
节点 [shape = circle,color=green,style=filled,fontsize=45,fixedsize=True,width=4.0]
edge[color=grey,arrowhead=vee,penwidth=5,arrowsize=5]
A[label = 'A= @@1']
B[label = 'B=@@2']
C[label = 'C=@@3']
D[label = 'D=@@4']
E[label = 'E=@@5']
blank1[label = '', width = 0.01, height = 0.01]
A -> blank1;
blank1 -> B[minlen=10];
{{ rank = same; blank1 B }}
blank1 -> C
```py
blank2[label = '', width = 0.01, height = 0.01]
C -> blank2;
blank2 -> D[minlen=1];
{{ rank = same; blank2 E }}
blank2 -> E [minlen=10]
}
")```py
**输出:**

作者提供的图片
下一步是去除箭头标记。可以通过提及边的属性“dir=none”来去除。
```py
A -> blank1[dir=none];
C -> blank2[dir=none];
完整代码:
```{r , echo=FALSE}
library(DiagrammeR)
grViz("
digraph {
layout = dot
节点 [shape = circle,color=green,style=filled,fontsize=45,fixedsize=True,width=4.0]
edge[color=grey,arrowhead=vee,penwidth=5,arrowsize=5]
A[label = 'A= @@1']
B[label = 'B=@@2']
C[label = 'C=@@3']
D[label = 'D=@@4']
E[label = 'E=@@5']
blank1[label = '', width = 0.01, height = 0.01]
A -> blank1[dir=none];
blank1 -> B[minlen=10];
{{ rank = same; blank1 B }}
blank1 -> C
```py
blank2[label = '', width = 0.01, height = 0.01]
C -> blank2[dir=none];
blank2 -> D[minlen=1];
{{ rank = same; blank2 E }}
blank2 -> E [minlen=10]
}
")```py
## 输出:

图片由作者提供
# 4\. 编织到 HTML
选择选项编织 -> 编织到 HTML

整个 R-Markdown 文档将被渲染为 HTML 文档。
如果不希望在 HTML 输出中显示 R 代码,请在 R-chunk 代码中设置 echo =False。
```py
```{r , echo=FALSE}```py
这个 HTML 页面可以从我的 GitHub 链接下载。
github.com/IndhumathyChelliah/R-Projects
结论:
DiagrammeR 是一个 R 包,允许使用 Graphviz 和 mermaid 样式创建图形。在本文中,我们介绍了如何使用 GraphViz 样式。我们介绍了如何使用 DiagrammeR 包中的 grViz() 函数创建节点、标签、边、连接和图形布局。我们还介绍了节点属性、边属性、标签及其替代。本文仅涵盖了 dot 布局,这是默认布局。
感谢阅读,希望大家喜欢!
参考文献:
DiagrammeR 是一个 R 包,允许你使用类似 Markdown 的文本创建流程图、图示和图形。
rich-iannone.github.io [## DiagrammeR — 文档
Graphviz 支持是 DiagrammeR 包的重要组成部分。Graphviz 包含图形描述语言…
rich-iannone.github.io [## dot
Graphviz dot 是处理有方向边的默认工具。布局算法将边对齐在同一…
如何准备机器学习数据
原文:
towardsdatascience.com/how-to-prepare-data-for-machine-learning-eb9d9973832f
准备数据以进行建模是数据科学流程中最基本的第一步之一
·发表于 Towards Data Science ·8 分钟阅读·2023 年 4 月 19 日
--

图片由 John Moeses Bauan 提供 / Unsplash
训练预测模型要求我们的数据格式正确。我们不能将 .csv 文件直接输入模型,并期望它能够正确地进行泛化。
在本文中,我们将探讨如何准备机器学习数据,从数据准备流程开始,到将数据划分为训练集、验证集和测试集。
数据准备流程
流程是为了准备数据以供机器学习模型处理而执行的一系列步骤。流程可以根据你拥有的数据类型有所不同,但通常包括以下步骤:
数据收集:第一步是收集你想用来训练机器学习模型的数据。这些数据可以来自不同的来源,如 CSV 文件、数据库、API、物联网传感器等。
数据清理:一旦数据被收集,重要的是要通过删除缺失数据、纠正错误以及删除重复或无关的数据来清理数据。数据清理有助于提高用于训练机器学习模型的数据质量。
数据预处理:数据预处理包括数据规范化、数据标准化、分类变量编码和异常值处理。这些步骤有助于准备数据,使其能够被机器学习模型处理。
数据转换:数据转换包括降维、特征选择和新特征的创建。这些步骤有助于减少数据噪声,提高机器学习模型做出准确预测的能力。
管道一词也用于 Python 的 Scikit-learn 库中的一个编程对象。这允许我们指定数据处理步骤,以便于编码并减少错误的发生。
如果你想了解 Scikit-Learn 管道,可以阅读下面的文章
在数据科学和机器学习中,管道(pipeline)是一组顺序步骤,允许我们控制数据流动的…
数据收集
从可靠且与您试图解决的问题相关的数据源中收集数据是很重要的。
例如,如果你想创建一个用于预测房价的机器学习模型,你需要收集包括房屋位置、房间数量、是否有花园、靠近公共交通等特征的数据。这些信息就像它们向我们人类提供房产价值一样,也会对预测模型提供帮助。
选择最适合你的问题和数据可用性的来源非常重要。例如,如果你想为物联网系统中的异常检测构建机器学习模型,你需要从系统中的传感器收集数据。
其他数据来源可以是
-
公共和私人 API
-
直接供应商
-
调查
此外,评估收集的数据质量是很重要的。数据可能包含错误、重复或遗漏,这些问题可能会对机器学习模型的质量产生负面影响。
因此,你应该进行数据清理,并检查是否有缺失、重复或不良数据。数据清理可以帮助提高机器学习模型的质量,并实现更准确的预测。
这里有一篇文章专注于数据集创建的过程
自建数据集而不是使用预建解决方案的重要性
towardsdatascience.com
数据收集阶段的实际例子
一个实际的数据收集例子可能是构建一个用于分类花卉的模型。在这种情况下,我们可以收集不同花卉的数据,比如花瓣和萼片,并记录它们的长度和宽度。
为了收集这些数据,我们可以使用一个移动应用程序,该程序允许我们拍摄花卉照片并记录它们的特征。或者,我们可以使用电子表格或数据库手动收集数据。
无论你使用什么数据收集方法,确保数据能代表要解决的问题是很重要的。在这种情况下,我们可以收集来自不同物种和不同地理区域的花卉数据,以确保数据的多样性。
数据预处理
一般来说,数据预处理包括数据的归一化或标准化、类别变量的编码和异常值处理。
数据归一化 / 标准化用于减少数据的尺度,以便它们之间可以相互比较。许多机器学习模型,例如 K-近邻和神经网络,要求数据进行归一化或标准化才能表现良好。
标准化和归一化这两个术语通常可以互换使用。但实际上,它们并不完全相同。
我邀请有兴趣的读者阅读这个资源解释标准化和归一化之间差异的文章以了解更多信息。
类别变量编码用于处理那些不是数字格式的变量,例如性别或眼睛颜色。类别变量编码将这些变量转换为数字,以供机器学习模型使用。
异常值处理用于处理那些与其余数据差异很大的数据。这些数据可能对机器学习模型产生负面影响,因此必须妥善管理。
数据预处理阶段的实际示例
让我们思考文本。大量的文本组织成语料库(文档列表)。在将这些数据输入机器学习模型之前,应该首先对其进行处理。
一个假设的流程可能是:
-
文本标记化
-
停用词移除
-
词干提取 / 词形还原
-
向量化
这些是文本数据预处理中的一些最常见步骤。
数据转换
数据转换可以包括维度减少、特征选择和新特征的创建。
维度减少用于减少数据中的特征数量。当你拥有大量数据但资源受限,例如机器学习模型的处理时间,这一步骤会非常有用。最常用的技术之一是PCA(主成分分析)。
特征选择用于选择数据中最重要的特征。当你拥有许多特征但希望只使用其中一部分来训练机器学习模型时,这一步骤会非常有用。
阅读如何在 Python 中使用一种叫做Boruta的技术进行特征选择
了解 Boruta 算法如何进行特征选择。解释 + 模板
[towardsdatascience.com
创建新特征用于从现有数据中创建新特征。这个步骤在你想要创建在原始数据中不存在但对训练机器学习模型有用的特征时特别有用。
数据转换阶段的实际示例
PCA 主要用于减少机器学习模型中的可用特征数量。
在 Sklearn 中实现 PCA 非常简单 —— 这段代码片段捕捉了训练和应用 PCA 的一般思路。
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit(X_train)
X_train_pca = pca.transform(X_train)
# we have successfully reduced the entire feature set to just two variables
x0 = X_train_pca[:, 0]
x1 = X_train_pca[:, 1]
将数据划分为训练集、验证集和测试集
将数据划分为训练集、验证集和测试集是准备机器学习数据的重要步骤。
训练集:训练集用于训练机器学习模型。它包含模型将用来学习对预测有用的关系的数据。
验证集:验证集用于在训练过程中评估机器学习模型的性能,并测试其超参数。
测试集:测试集用于在训练后评估机器学习模型的性能。
将数据划分为训练集、验证集和测试集很重要,因为它可以准确评估机器学习模型的性能。
如果你使用所有数据来训练机器学习模型,然后在数据本身上评估模型性能,你可能会得到模型性能的扭曲图景。
正确评估模型是否学习到知识的最重要技术之一叫做交叉验证。
它包括将训练集划分为折叠,以便迭代地评估模型。
如果你想了解交叉验证及其如何在你的代码中应用,阅读下面的文章。
了解什么是交叉验证 —— 构建可泛化模型的基本技术。
[towardsdatascience.com
使用 Sklearn 的 train_test_split 的实际示例
Sklearn 提供了一个很棒的 API 来管理数据拆分。看看这段代码。
from sklearn.model_selection import train_test_split
# X is out feature set, y is the target column
X_trainval, X_test, y_trainval, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
X_train, X_val, y_train, y_val = train_test_split(X_trainval, y_trainval, test_size=0.25, random_state=42)
print(f"Training set shape: {X_train.shape}")
print(f"Validation set shape: {X_val.shape}")
print(f"Test set shape: {X_test.shape}")
在这个示例中,我们将数据按 60/20/20 的比例划分为训练集、验证集和测试集。换句话说,我们使用 60% 的数据来训练模型,20% 来验证模型在训练过程中的表现,其余的 20% 用于在训练后测试模型。
random_state 参数允许我们设置随机数生成种子,以便每次运行代码时都能获得相同的数据划分。
最终,我们打印了训练集、验证集和测试集的大小,以便验证数据划分是否正确。
使用 sklearn,将数据分为训练集、验证集和测试集变得快速而简单,使我们能够专注于设计和训练模型。
结论
在这篇文章中,我们查看了为机器学习准备数据所需的步骤。
我们已经看到,从可靠且与问题相关的来源收集数据以及数据清洗是确保用于训练机器学习模型的数据质量的关键。
我们还研究了数据预处理,包括数据归一化和标准化、分类变量编码和异常值处理,以及数据转换,这可能包括降维、特征选择和新特征的创建。
最终,我们了解了将数据分为训练集、验证集和测试集以准确评估机器学习模型性能的重要性。
为机器学习准备数据需要密切关注细节,但正确执行数据准备步骤可能是机器学习模型有效与否的关键。
如果你想支持我的内容创作活动,请随时通过下面的推荐链接加入 Medium 会员计划。我将获得您投资的一部分,您将能够以无缝的方式访问 Medium 上大量关于数据科学及更多内容的文章。
[## 使用我的推荐链接加入 Medium - Andrea D'Agostino
阅读 Andrea D'Agostino 的所有故事(以及 Medium 上成千上万的其他作者的故事)。您的会员费用直接……
medium.com](https://medium.com/@theDrewDag/membership?source=post_page-----eb9d9973832f--------------------------------)
推荐阅读
对有兴趣的人,这里列出了我推荐的每个与机器学习相关的主题的书籍。这些书籍在我看来是必读的,并且对我的职业生涯产生了很大影响。
免责声明:这些是亚马逊会员链接。我会因推荐这些商品而从亚马逊获得少量佣金。您的体验不会改变,也不会额外收费,但这将帮助我扩大业务并制作更多关于人工智能的内容。
-
机器学习入门: 自信的数据技能:掌握数据工作基础,提升你的职业生涯 作者:Kirill Eremenko
-
Sklearn / TensorFlow: 动手实践机器学习:Scikit-Learn、Keras 和 TensorFlow 作者:Aurelien Géron
-
NLP: 文本作为数据:机器学习与社会科学的新框架 作者:Justin Grimmer
-
Sklearn / PyTorch: 使用 PyTorch 和 Scikit-Learn 进行机器学习:用 Python 开发机器学习和深度学习模型 由 Sebastian Raschka 著
-
数据可视化: 用数据讲故事:商业专业人士的数据可视化指南 由 Cole Knaflic 著
有用的链接(由我编写)
-
学习如何在 Python 中进行顶级的探索性数据分析: Python 中的探索性数据分析 — 一步一步的过程
-
学习 TensorFlow 基础知识: 开始使用 TensorFlow 2.0 — 深度学习简介
-
使用 TF-IDF 在 Python 中进行文本聚类: Python 中的 TF-IDF 文本聚类
如何准备你的数据以进行可视化
Python
无需使用 Tableau Prep 或 Alteryx
·
关注 发布于 Towards Data Science ·8 分钟阅读·2023 年 6 月 26 日
--
图片由 Robert Katzki 拍摄,来源于 Unsplash
想开始你的下一个数据可视化项目吗?首先从熟悉数据清洗开始。数据清洗是任何数据管道中至关重要的一步,将原始的“脏”数据输入转变为更可靠、更相关、更简洁的数据。诸如 Tableau Prep 或 Alteryx 之类的数据准备工具是为此目的而创建的,但为什么要花钱使用这些服务,而你可以通过像 Python 这样的开源编程语言完成这项任务呢?本文将指导你如何使用 Python 脚本为可视化准备数据,提供一种比数据准备工具更具成本效益的替代方案。
注:在本文中,我们将重点关注使数据为 Tableau 的数据可视化做好准备,但主要概念同样适用于其他商业智能工具。
我明白了。数据清洗似乎只是将可视化或仪表板变为现实这一漫长过程中的另一个步骤。但它至关重要,并且可以是令人愉快的。这是你通过深入了解你拥有和没有的数据,以及你必须做出的相应决策,来使自己对数据集感到舒适的过程。
尽管 Tableau 是一个多功能的数据可视化工具,但有时获得答案的路线并不明确。这时,在将数据加载到 Tableau 之前处理数据集可能是你最大的秘密帮手。让我们探讨一些在将数据集成到 Tableau 之前,数据清洗为何有益的关键原因。
-
消除无关信息: 原始数据通常包含不必要或重复的信息,这可能会使你的分析变得杂乱。通过清洗数据,你可以去除这些垃圾,并将你的可视化集中在最相关的数据特征上。
-
简化数据转换: 如果你对打算生成的可视化有清晰的愿景,在将数据加载到 Tableau 之前进行这些预转换可以简化过程。
-
团队内部的更容易转移: 当数据源定期更新时,新增加的内容可能会引入不一致性,并可能破坏 Tableau。通过 Python 脚本和代码描述(更正式地称为 Markdown 文档),你可以有效地分享并帮助他人理解你的代码,以及排除可能出现的任何编程问题。
-
节省数据刷新时间: 需要定期刷新的数据可以从利用Hyper API中受益——这是一个生成特定于 Tableau 的 Hyper 文件格式的应用程序,并允许自动数据提取上传,同时使数据刷新过程更高效。
现在我们已经介绍了一些准备数据的优势,让我们通过创建一个简单的数据管道将其付诸实践。我们将探讨数据清洗和处理如何集成到工作流程中,并帮助使你的可视化更易于管理。
使用 Python 脚本创建数据管道

图片由作者提供
我们的数据旅程相当简单:数据清洗、数据处理以生成可视化,并将其转换为 Tableau 准备好的 Hyper 文件,以实现无缝集成。
在深入我们的工作示例之前,有一点需要注意的是,对于 Hyper 文件转换,你需要下载 pantab 库。这个库简化了 Pandas 数据框到 Tableau .hyper 提取文件的转换。你可以通过在你选择的环境的终端中使用以下代码轻松完成这个任务(对于那些对环境不太熟悉的人,这是一个很好的入门文章,介绍了环境是什么以及如何安装某些库):
#run the following line of code to install the pantab library in your environment
pip install pantab
教程:使用 Python 探索加拿大电动汽车许可证的数据准备
我们将致力于制作的数据可视化专注于根据来自加拿大统计局的政府数据,展示不同电动汽车制造商和车型的受欢迎程度。
需要注意的是,这建立在我之前文章中探讨的数据集基础上:使用 R 进行电动汽车分析。如果你对数据集的初步探索和决策背后的理由感兴趣,请参考该文章以获取更多细节。本教程专注于构建 Python 脚本,在每一步执行初始输入后,我们将把每个 Python 脚本的输出保存到其相应的文件夹中,如下所示:

图片由作者提供
文件夹流程确保管道组织良好,并且我们能够保留项目中每个输出的记录。让我们开始构建第一个 Python 脚本吧!
数据清洗
我们管道中的初始脚本遵循数据清洗的基本步骤,对于这个数据集包括:保留/重命名相关列,删除空值和/或重复项,以及使数据值一致。
我们可以通过指定输入文件的位置和输出文件的目的地来开始。这一步很重要,因为它使我们能够在相同位置组织文件的不同版本,在这种情况下,我们每月修改文件输出,因此每个文件输出按月份分隔,如文件名末尾所示 2023_04:
以下代码读取原始 .csv 输入文件,并定义我们希望保留的列。在这种情况下,我们感兴趣的是保留与购买车型相关的信息,并忽略与汽车经销商或其他无关列相关的信息。
现在我们可以缩短列名,去除前导或尾随的空白,并添加下划线以便更易于理解。
接下来,在检查数据集中只有少量空值之后,我们将使用.dropna函数删除这些空值。此时,你还可以删除重复项,但对于这个特定的数据集,我们将不这样做。这是因为存在大量重复信息,并且在缺乏行标识符的情况下,删除重复项会导致数据丢失。
最后一步是将我们的数据保存为 .csv 文件到适当的文件夹位置,该位置将放在我们共享目录的clean_data文件夹中。
注意我们如何使用__file__引用文件,并通过使用 bash 命令指定文件目录,其中../表示上一级文件夹。这结束了我们的数据清理脚本。现在让我们进入数据处理阶段吧!
完整的工作代码和汇总脚本可以在我的 GitHub 仓库 这里 找到。
可视化的数据处理
让我们重新审视一下我们试图实现的可视化目标,这些目标旨在突出电动车注册受欢迎程度的变化。为了有效展示这一点,我们希望最终的 Tableau 准备数据集包含以下功能,我们将对此进行编码:
-
按年份的车辆绝对计数
-
按年份的车辆比例计数
-
注册车辆的最大增加和减少
-
注册车辆的排名
-
车辆注册的先前排名以便进行比较
根据你希望生成的可视化,创建理想列可能是一个迭代过程。在我的情况下,在构建了可视化之后我才包含了最后一列,因为我知道我想提供一个排名差异的视觉对比,因此 Python 脚本做了相应的调整。
对于以下代码,我们将重点关注模型聚合数据集,因为品牌的其他数据集非常相似。让我们首先定义我们的 inputfile 和 outputfile:
注意我们如何引用来自 clean_data 文件夹的 inputfile,这是我们数据清理脚本的输出。
以下代码读取数据,并创建一个按 Vehicle_Make_and_Model 和 Calendar_Year 聚合计数的数据框:
pivot函数的功能类似于 Excel 中的数据透视表功能,它将 Calendar_Year 中的每个值作为列输入。
然后,脚本使用 For Loop 创建 per_1K 输入。这计算每个模型的比例,以便在相同的尺度上比较每个模型,并为每一年创建一列:
通过按年份计算比例,我们可以从数据集开始的 2019 年到最后一个完整数据年的 2022 年,计算每个模型的最大增加和减少。
在这里,melt函数用于将按年份分离的 per_1K 列重新透视为行,以便我们只保留一个 per_1K 列及其相关值。
以下代码允许我们将绝对计数和刚刚创建的其他计算进行连接。
我们现在可以使用许可计数创建rank列,并按Vehicle_Make_and_Model和Calendar_Year对这些值进行排序。
最后一列是通过使用shift函数创建的previous_rank列。
最后,我们可以将输出保存到管道中的clean_model文件夹路径,提供一个准备好可视化的数据集。
作为友好的提醒,包括
clean_brand处理数据集的完整 Python 脚本代码可以在我的 GitHub 仓库这里找到。
将最终数据文件转换为.hyper 文件格式
我们管道中的最后一步相对简单,因为我们只需将处理后的.csv 文件转换为.hyper 文件格式。只要你已经下载了前面提到的pantab库,这应该是相对容易的。
值得一提的是,在 Tableau 中,连接的数据可以是实时连接或提取的。实时连接确保数据有持续流动,源的更新几乎立即在 Tableau 中反映出来。提取的数据涉及 Tableau 创建一个带有.hyper 文件扩展名的本地文件,其中包含数据的副本(数据源的详细描述可以在这里找到)。它的主要优点是加载速度快,Tableau 可以更高效地访问和呈现信息,这对于大型数据集特别有益。
hyper 文件转换脚本的代码从加载pandas和pantab包开始,然后读取你需要的cleaned_model数据集以供 Tableau 使用。
最后一行代码使用frame_to_hyper函数生成.hyper 文件并将其保存到hyper文件夹中。
最后一步,我们可以通过打开一个新工作簿将.hyper 文件格式轻松加载到 Tableau 中,在select a file部分你可以选择要加载的文件,选择more。当我们加载ev_vehicle_models.hyper文件时,它应显示为 Tableau 提取,如下图所示,你的数据已准备好进行可视化构建!

结束语
通过将深思熟虑的规划融入到你的可视化中,你可以通过创建简单的数据管道来简化仪表板的维护。如果你缺乏资源也不要担心;像 Python 这样的开源编程程序提供了强大的功能。作为最终的友好提醒,要获取 Python 脚本,请查看我的 GitHub 仓库这里。
除非另有说明,所有图片均由作者提供。
参考文献
-
Salesforce, Tableau Hyper API, 2023
-
R.Vickery, 数据科学家 Python 虚拟环境指南,2021 年 1 月
-
K.Flerlage, Tableau 数据源第一部分:数据源类型,2022 年 7 月
如何在软件工程师职位面试中展示你的项目
软件工程师面试的实用技巧
·
关注 发表在Towards Data Science · 7 分钟阅读·2023 年 6 月 16 日
--
展示技术项目是软件工程师职位面试中的常见部分,这用于测试你的技能和经验。
今天我想分享一些帮助我提升项目展示效果的技巧,希望对你有所帮助。*
开篇部分应当清晰明了
你的展示的第一段应非常直接,易于理解,并清楚表明要点。面试官应理解你的项目目的,以及你希望他们在深入具体细节之前记住的一个重要细节(或成就)。
为实现这一点,你的展示第一部分应包括以下内容:
-
一个清楚总结你项目的“标题”句子。
从核心内容开始。例如,“我将展示一个我在最近工作中进行的 3 个月项目,其中我从零开始构建了公司的主要数据增强系统。”
-
对公司的产品和/或团队的领域的介绍。
在深入项目之前,帮助面试官理解公司或团队领域的背景。
-
解释需求/痛点。
在开始讨论解决方案——即你的项目——之前,解释其必要性。项目之前的状况是什么?你的项目旨在解决哪些痛点?为什么你的项目很重要?
架构图
拥有一个包括项目组件、所用技术以及团队/公司周边架构的架构图,将使面试官更容易跟随,同时方便你引用特定组件或技术。
最好提前准备好图表,以免在面试过程中浪费时间。
在制作你的图表时请记住以下几点:
-
保持你的图表相对高层次且易于理解,避免过于详细的内容。记住你是在向一个不具备所有背景的外部听众解释。
-
确保你的图表包含足够的信息来传达相关细节,例如项目中使用的主要组件和技术。
-
考虑包括一些你们团队在日常工作中使用的组件和技术,即使它们在这个项目中没有被具体使用。这为你展示项目之后提供了潜在的讨论话题,展示额外的经验和知识。
此外,我倾向于为项目展示准备两个图表:一个提供公司产品或团队领域的高层次概述,另一个深入项目本身的解释。
从高层次图表开始可以减少面试官的负担,因为他们已经具备背景信息和对系统及产品的总体理解。
以下是我所提供的架构图示例:
高层次产品概述

作者草图
深入项目概述

作者草图
注意颜色——我所参与的系统部分在两个图表中均标记为蓝色。
通过在图表中包含比实际讨论更多的组件并突出我们将重点关注的部分,我实现了几个目标:
-
面试官一眼就能获得比我在展示中明确提到的更多信息,从而洞悉我所使用的技术范围。
-
我隐性地邀请面试官询问我关于项目架构的问题。
-
我让面试官很容易识别我在项目中工作的系统部分,并理解我的展示重点。
请记住,你的图表不仅限于远程面试。你还可以打印图表并带到办公室面试中。我个人可以证明,每次在面试中拿出我的图表时,面试官都对这种准备程度印象深刻。这是一种展示你专注和职业素养的非同寻常且有影响力的方法。
如果你无法携带这些视觉辅助工具,练习快速绘制重要部分,以保持绘图时间简短并让面试官保持关注。
成就 — 推而非拉
以下是一些值得提及的成就示例:
-
可衡量的成就: 将错误率降低 X%,将系统延迟提高 Y 毫秒或 Z%,减少每季度 W 天的手工工作。
-
克服障碍(这个项目为何复杂?) 例如,处理高度复杂的代码库或遗留代码,在低测试覆盖率或有限可见性的环境中调试,独立学习复杂的新技术和系统。
-
侦察规则: 展示你如何使环境变得比收到时更好,例如进行知识分享会议或实施重要的指标和警报。
-
人际事务 例如,与来自不同团队和部门的同事合作,有效地表达你的信息以吸引和引导他们。
记住,不同的面试官可能对你工作的不同方面有不同的兴趣,这取决于职位的需求或他们想了解的技能。
以 WOW 效果结束
从你之前列出的成就中,选择最令人印象深刻的几项纳入你的结尾段落。突出最令人印象深刻的数字或指标,展示你努力工作的回报。这些元素很可能会给面试官留下深刻的印象。
进行模拟面试并获取反馈
模拟面试是评估你的项目对外部听众的感觉、练习解释的连贯性和提升整体展示效果的好方法。它们提供了从客观和专业人士那里获取具体反馈的机会。
如果可能的话,尝试与熟悉你要展示项目的人员(前同事)和不熟悉的人员进行模拟面试。前者可以帮助发现任何不准确之处或提出更好的展示方式,而后者则可以从面试官的角度提供宝贵的反馈。
无论哪种情况,确保安排与在你领域工作的人员进行模拟面试——有经验的开发人员,最好是经验丰富的面试官,这样他们的反馈对你最为相关。
从一次面试中改进

图片由 Karolina Grabowska 提供,来源于 Pexels
将每次面试看作是一次小的回顾。面试后反思:
-
如果面试官误解了你的某个解释,考虑下次如何更清晰地表达。
-
如果在你结束展示后面试官反复问一些问题——考虑在下次面试中作为展示的一部分主动提供答案。
-
如果对某些回答不满意,找出改进这些回答的方法,并为未来的面试准备类似或相关的问题。
每一次面试都是学习和成长的机会。花时间分析和反思面试经历。如果你投入一些时间进行自我反思,可能会发现宝贵的见解。
总结
总结本文中提到的关键要点,以下是我对在软件工程师职位面试中展示项目的建议:
-
确保你的开场部分清晰明确。从一个概述项目目的的底线句子开始。然后,提供公司产品或团队领域的简短概述。最后,解释你的项目旨在解决的痛点,以结束介绍。
-
准备一个架构图,最好是两个:一个是产品的高层概览,另一个是项目的深入图。如果有面对面的面试,考虑打印这些图表或学习如何快速勾勒主要组件。
-
了解你在面试中想提及哪些成就,并以“主动推送而非被动等待”的方式分享。这意味着在项目展示过程中主动提及这些成就,而不是等到被问及时才说。
-
以 WOW 效果收尾,在你的展示结束时突出你最令人印象深刻的成就,特别是那些可以量化的成就,以在面试官心中留下持久的印象。
-
进行模拟面试并获取反馈。安排这些模拟面试与经验丰富的开发人员和面试官进行,以学习他们的见解和建议。
-
将每次面试视为学习机会,并在面试后花时间进行反思。反思这次经历,并将学到的经验应用到你未来的面试中。
就这些!希望你喜欢阅读这篇博客文章。你是否有任何额外的技巧来展示面试中的项目?在评论中分享你的想法吧!
2023 年如何为 AI 项目定价
我在 2017 年 TDS 文章发布 6 年后,新的变化和保持不变的内容
·
关注 发表在 数据科学的前沿 ·11 分钟阅读·2023 年 1 月 12 日
--
图片由 愚木混株 Cdd20 提供,来源于 Pixabay
这是一篇为从事 AI/ML 咨询领域的其他人提供难得见解的文章。几乎没有人愿意谈论定价或招聘实践。我 2017 年的文章“如何为 AI 项目定价”提供了如何为客户定价机器学习(ML)项目的见解。但是自 2017 年以来已经过去很久了!在提供 2023 年关于如何为 AI/ML 项目定价的更新之前,我将简要总结一下我之前文章的一些关键点。由于需求变更、集成问题、数据问题和用户接受等因素,为大型 ML 项目提供固定价格估算可能会很棘手。执行 ML 项目期间,管理层和项目经理应该预期需求和范围会发生变化。未知或需求风险越大,你应该越倾向于使用按小时计费而非固定价格。尽量在尽早的阶段消除风险。科学优先,工程其次。截止到 2017 年 8 月,我们的 ML 咨询标准费率是每小时 250 美元,项目必须至少为 5000 美元(20 小时)。我们在 2017 年的目标是在固定价格项目中也能达到我们的小时费率……但现在是 2023 年了。让我们看看发生了什么变化,什么保持不变。
一样的地方是什么?
令人惊讶的是,我们的展望与 2017 年时基本相同。自那篇文章发布以来已经过去了几年,这篇文章被各种来源引用,例如这篇和这篇。自 2017 年以来,人工智能咨询行业快速发展,我注意到一些事情保持不变。其中一个主要的稳定点是,疫情定价对我们来说并不存在。自 2017 年以来,我们保持了每小时 250 美元的定价。我们的固定定价基于这一小时费率,如 2017 年文章中所述,我们发现这种定价模式对客户和我们的业务都有效。此外,我们继续保持作为分包商的模式,而不是成为主要承包商,因为这使我们能够根据需要进行扩展或缩减,并且与政府客户的互动变得更加容易。然而,我们发现拥有一个行业特定的销售团队(如国防、产品、政府和金融科技)有助于我们为服务建立长期定价。

图片由Clker-Free-Vector-Images提供,来源于Pixabay
在定价方面,我们发现未知因素,即我们所称的需求风险,仍然是决定我们倾向于小时计费还是固定价格的主要因素。当需求不明确或可能发生变化时,我们更倾向于小时计费。另一方面,当需求非常紧密,或我们称之为可执行规格时,我们更倾向于固定价格。AI/ML 项目的一个大风险是数据的访问,这已成为我们在大大小小项目中看到的许多范围变化和延迟的源头。
我们发现,为项目定义和解决方案架构提供专家建议已成为我们咨询服务中越来越重要的一个方面。许多客户带着定义不明确的需求来找我们,我们必须在讨论的最早阶段识别任何证明无法实现的需求或需要新科学的需求。
此外,正如我们在 2017 年所做的,我们继续倾向于将尽可能多的低端工作委托给客户的员工,以减少完成项目所需的时间。
我们在 AI/ML 咨询领域的招聘情况
显然,给 AI 项目定价与薪酬密切相关。在疫情期间,AI/ML 咨询领域发生了几次变化,主要与人员配置有关。首先,大公司纷纷抢聘新手,支付不合理的薪水。这使得人才库枯竭,并为统计学和生物学等领域的新毕业生创造了一些扭曲的激励,促使他们转行进入数据科学和 ML 领域。这也导致了人员流动,因为员工在公司间跳槽。去年,情况再次发生变化。随着经济放缓和裁员的到来,我采访了许多被大型组织解雇并希望转向咨询的人员。客观地说,离开六位数薪水工作的人员,其资格比期待五位数薪水的新毕业生要低。不幸的是,这些所谓的经验丰富的候选人更不愿意付出必要的努力来展示他们的能力。我惊讶地发现,申请人的薪资预期与工作经验年限高度相关,但那些有约 3 到 5 年经验的申请人的测试分数显著下降。我的理论是,在大团队中从事狭窄任务几年后,许多经验丰富的申请人并不习惯我们每天所做的疯狂咨询工作。我的方法是忽略市场的波动,专注于个人为团队带来的价值。我尽量将这一点与候选人将带来的可计费工作紧密匹配。因此,定价与薪酬相关,通过确保薪酬和利润率让我们保持我们的定价结构。
让我告诉你一些关于我们招聘过程的事情。我们在这里的做法有些不同。我们询问每位候选人的薪资期望,然后在技术面试评分后,我们将薪资期望与技能测试得分绘制成图表。我们根据图表显示的最佳技能价值来选择雇用对象。我们从经过初筛的申请者那里请求薪资期望(技术、地域和人际评分),然后尽量回答他们可能对角色或公司有的任何问题,这些问题在招聘广告中没有说明。我的目标是在技术面试之前解决职责和薪资讨论,以便技术面试可以完全专注于技术能力。每个人的时间对我来说都很宝贵,因此我创建了一个在技术面试之前的每一步,人们可以以很少的时间投入申请,然后决定是否进入下一步或退出的情况。
有什么新鲜事?
首先,我们依然存在!这是一个大消息。在一个充满了要么退出要么消失的初创企业的领域中,我们保持了独立。我们根据我在 2017 年《Towards Data Science》文章中描述的基本计划,逐步建立了我们的业务和声誉。我们的 CEO Matt 于 2016 年 6 月 22 日成立了公司,而七年后我们依然在坚持我们的事业。
销售渠道随着时间的推移也发生了变化。我们一直提供详细的 RFQ,但值得一提的变化是我们现在申请了一些 RFI 和 RFP。我们曾经非常讨厌 RFP,以至于根本不申请它们。现在,随着时间的推移,我们已经在 供应商名单 上安下了我们的旗帜,当我们认为某个机会对我们来说足够有吸引力时,我们就会参与竞争。

致谢:作者以及那些通过坚定的意志熬夜填写表格使我们进入这些名单的工作人员。
就项目的规模而言,我们现在的目标是建立预期能够带来至少 100K(400 小时)规模的项目的关系。微小的客户不再那么适合。然而,如果我们看到一个初始范围为 25K,但有后续机会达到 150K 的项目,我们就会全力投入并建立起关系。请注意我们将重点从项目规模转变为客户规模。从一个客户那里获得的小交易逐渐累积,因此我们已将概念转变为代表客户而非具体交易。如果我们与一个合作伙伴合作处理各种小交易,我们可以汇总他们带来的项目的价值,这样这些较小的交易从“大局”角度来看就会变得有意义。至于我们如何建模业务,我们使用一个名为 floatapp.com 的工具,将我们的 P&L 数据直接输入到我们的收入预测中。我们还使用 HubSpot 作为 CRM 来跟踪交易和机会。
对于项目管理,我们使用一个 自托管的 GitLab 实例来进行大多数票据跟踪,但我们最终使用各种工具,因为我们使用客户希望我们在其项目中使用的工具(从 JIRA 到 GitHub)。我们现在明确收取项目管理费用,这是件好事。
另一个变化是我们现在提供给客户的产品种类更多了。我们的服务被组织为 SKU,包括 AI 战略、差距分析和 AI 路线图,交付物是报告或顾问服务、解决方案架构,交付物是需求文档或范围文档、虚拟 CTO,服务包括与高管团队和投资者的更深入互动,以及我们最初的技术产品,包括 数据科学、AI/ML/DL 模型训练 和 部署与 MLOps。这里的变化是我们在过程早期就介入(例如,路线图开发),并且我们会坚持更长时间(例如,MLOps 和带有服务水平协议的支持)。

多汁的本地硬件。感谢:Lemay.ai 和对在服务器室中增加额外电力线非常理解的电工。
我们还转向使用比以往更多的本地硬件。我们的硬件现在是 4U 机架式的,包括 Titan RTX(24 GB,4608 CUDA 核心,Turing 架构,CC 7.5)、5 个 RTX 3090(24 GB,10496 CUDA 核心,Ampere 架构,CC 8.6)配备 NVLINK、5 个 GeForce GTX 1050 Ti,以及大量 Xeon/i9 CPU,当然,还有大量的 RAM。当在本地进行研发活动时,我们可以节省很多费用。如果涉及到磁盘,我们一直使用 unraid 进行二级备份以及离线三级备份。顺便提一下,当你订购 45drives storinator server 时,可以将 unraid 安装为基础操作系统。另一个变化是,我们看到一些转向云计算的客户又转回了本地 GPU,并存储在托管设施中。我们还升级到了 10 千兆以太网交换机和网络卡。处理大型数据集时非常快速。
关于硬件定价,我们很久以前决定不对硬件成本加价。这不是我们的商业模式。相反,我们让客户准确知道硬件和保修的成本,如果需要,我们只收取我们的时间费。客户尊重我们的定价透明度,我们也收到了非常积极的反馈。客户支付托管费用,我们只有在需要时才亲自前往现场。

图片由 Gerd Altmann 提供,来自 Pixabay
另一个变化是,近年来对 MLOps(机器学习操作)的需求显著增加。在人工智能咨询领域,Kafka、Terraform 和其他工具的使用急剧上升。
MLOps 涉及使用基础设施即代码(IAC)来自动化构建、测试、部署和监控机器学习模型的过程。MLOps 工具如 Kubernetes 和 Docker 因其能够轻松管理和扩展机器学习工作负载而在人工智能咨询行业中已相当流行。但现在像 mlflow、DVC、Airflow 和 Ansible 这样的工具也逐渐成为我们工作声明中的标准组件。我们还在要求中看到许多 CI/CD 工作流和容器注册表。像 Kafka 和 Terraform 这样的组件因为速度(Kafka 很快——使用事件驱动的实时数据流——因此基本上具有高吞吐量和低延迟,只要计算资源足够)和扩展性(Terraform 使用代码而不是网页上的按钮来配置和管理资源)而获得了关注。Terraform 作为创建、管理和扩展机器学习工作负载的基础设施工具(连同 bicep 和其他工具)非常受欢迎。曾经一个 jupyter notebook 和 Keras 就足以完成项目的时代已经过去了。
合作是一个销售渠道
我们也了解到,很多机器学习(ML)团队存在,因此当合作有意义时,我们会与他们合作。这使我们能够向客户提供更广泛的服务,并帮助我们专注于机器学习而不涉及其他领域。此外,我们发现有时现成的解决方案更为合理,因此在这些情况下,我们开始接受作为工具集成商的报酬。我们发现,尤其是对于位于加拿大和美国的团队,我们能够有效合作。我特别提到这一点是因为航空航天和国防部门使得与这两个国家以外的第三方合作变得困难。目前看来,最好的合作实际上是与那些遇到他们自己无法解决的机器学习需求的前端团队。我们专注于机器学习而不涉及前端,他们则专注于前端而不涉及机器学习,因此这种合作非常契合。另一个模式是数字化转型团队,这些团队提供 IT 服务但在人工智能/机器学习方面没有深入的专业知识。我们作为他们与客户之间的增值服务进入合作关系。我们单独开具发票,但以同步的方式进行,这样合作伙伴在与最终客户的关系中与我们步调一致。
这到底意味着什么?
尽管人工智能咨询行业经历了巨大的增长,我们在定价上的方法和考虑因素保持得相当一致。我们现在提供更多的端到端服务,并继续提供透明、诚实的定价,力求在实现自身目标与满足客户需求之间找到平衡。展望今年,我预计定价将保持稳定,而且我认为企业客户将会有更多的工作转移给咨询团队。随着领域内(是的,我要提到ChatGPT)的进展速度惊人,高管们强烈意识到人工智能/机器学习必须成为他们议程的一部分,以保持竞争力。对我来说,最大的收获是我们在 2017 年时只是一个位于我车库的小创业公司,而现在我们在一个更好的办公室里,有着更大的玩具和更大的项目。我们以增加价值为定价重点,不会对硬件采购或前端开发加价,因为我们在这些方面没有增加价值。
给人工智能项目定价很复杂,但公式有效,所以我们仍然坚持使用它。
如果你喜欢这篇文章,可以看看我一些阅读量较高的旧文章,比如“大型语言模型的道德”。另外,加入我们的新闻通讯吧!
下次见!
丹尼尔·夏皮罗,博士
首席技术官,Lemay.ai
daniel@lemay.ai
如何编程一个神经网络
原文:
towardsdatascience.com/how-to-program-a-neural-network-f28e3f38e811
从头实现神经网络的逐步指南
·发表于 Towards Data Science ·阅读时间 14 分钟·2023 年 9 月 23 日
--

一个有三个隐藏层的神经网络
在本文中,我们将从头开始构建一个神经网络,并使用它来分类手写数字。
为什么要重新发明轮子/神经网络,我听到你说?难道我不能直接使用我最喜欢的机器学习框架来解决问题吗?是的,你可以使用许多现成的框架来构建神经网络(比如 Keras、PyTorch 和 TensorFlow)。使用这些框架的问题在于,它们让我们很容易将神经网络当作黑箱处理。
这并不总是坏事。我们通常需要这种程度的抽象,以便能够处理手头的问题,但如果我们要在工作中使用神经网络,仍然应该努力至少对其内部运作有一个基本了解。
从头开始构建神经网络在我看来是培养深刻理解其工作原理的最佳方式。
到本文结束时,你将了解前馈和反向传播算法,激活函数是什么,epoch 和 batch 之间的区别是什么,以及如何训练神经网络。我们将通过训练一个神经网络来识别手写数字来完成本例。
什么是神经网络?
神经网络,或人工神经网络,是一种机器学习算法。它们是许多深度学习和人工智能系统的核心,例如计算机视觉、预测和语音识别。
人工神经网络的结构有时与大脑中的生物神经网络的结构进行比较。我总是建议对这种比较保持谨慎。确实,人工神经网络看起来有点像生物神经网络,但将它们与像人脑这样复杂的东西进行比较是一个很大的飞跃。
神经网络由几层神经元组成。每一层神经元的激活基于前一层的激活、连接前一层与当前层的权重集合,以及施加在当前层神经元上的偏置集合。

包含两个隐藏层的神经网络的一般结构。神经元根据其激活程度着色(激活量越大,神经元越暗)。正权重用红色表示,负权重用蓝色表示。线宽表示权重大小。
第一层是输入层。输入层的激活来自神经网络的输入。最后一层是输出层。输出层的激活是神经网络的输出。中间的层称为隐藏层。
神经网络是对函数的广义近似。像其他任何函数一样,当我们给它一个输入时,它会返回一个输出。
神经网络的新颖之处在于如何从输入到输出。这个过程由网络权重和偏置如何影响神经元激活以及这些激活如何在网络中传播,最终到达输出层来驱动。神经网络使用前馈算法将输入转换为输出。
为了使神经网络提供有用的输出,我们必须首先对其进行训练。当我们训练神经网络时,我们所做的就是通过反向传播和梯度下降迭代调整权重和偏置,以提高输出的准确性。我们计算需要将权重和偏置向哪个方向和调整多少。
前馈算法
前馈算法将我们的神经网络输入转化为有意义的输出。顾名思义,该算法将信息“向前传递”从一层到下一层。
为了理解它是如何实现的,让我们先放大来看信息是如何从一层传递到下一层的一个神经元的。

连接层 0 中神经元与层 1 中第一个神经元的权重
第二层中第一个神经元的激活a₀⁽¹⁾是通过对前一层的激活进行加权求和,再加上偏置,并通过激活函数σ(x)来计算的:
计算a₀⁽¹⁾的方程
带圆括号的上标表示层索引,从 0 开始,表示输入层。激活(a)和偏置(b)下标表示神经元索引。权重(w)下标中的前两个数字表示权重连接的神经元的索引(当前层中的)和从(前一层中的)索引。
激活函数决定一个神经元是否应根据接收到的输入而被激活。常见的激活函数包括 sigmoid、tanh、修正线性单元(ReLU)和 softmax。为了简单起见,在我们的实现中,我们将始终使用 sigmoid 激活函数。

Sigmoid、tanh 和 ReLU 激活函数
我们用来计算 a₀⁽¹⁾ 的方程可以向量化,以便我们可以计算第二层中的所有激活值:
计算 a⁽¹⁾ 的向量化方程
现在我们有了第二层的神经元激活值 a⁽¹⁾,我们可以使用相同的计算来找到 a⁽²⁾,然后是 a⁽³⁾,以此类推……
让我们看看如何在 Python 中实现这一点:
import numpy as np
import math
class Network:
def __init__(self, layers):
self.layers = layers
self.activations = self.__init_activations_zero()
self.weights = self.__init_weights_random()
self.biases = self.__init_biases_zero()
def __init_activations_zero(self):
activations = []
for layer in self.layers:
activations.append(np.zeros(layer))
return activations
def __init_weights_random(self):
weights = []
for i in range(0, len(self.layers) - 1):
weights.append(np.random.uniform(-1, 1, (self.layers[i+1], self.layers[i])))
return weights
def __init_biases_zero(self):
biases = []
for i in range(1, len(self.layers)):
biases.append(np.zeros(self.layers[i]))
return biases
def sigmoid(self, x):
return 1 / (1 + np.exp(-x))
def feedforward(self, input_layer):
self.activations[0] = input_layer
for i in range(0, len(self.layers) - 1):
self.activations[i+1] = self.sigmoid(np.dot(self.weights[i], self.activations[i]) + self.biases[i])
Network 类包含有关我们神经网络的所有信息。我们通过传递一个整数列表来初始化它,该列表与每层的神经元数量相关。例如,network = Network([10, 3, 3, 2]) 将创建一个输入层有十个神经元、两个隐藏层每个包含三个神经元,以及一个输出层有两个神经元的网络。
__init_* 方法初始化激活值、权重和偏置。激活值和偏置最初都是零。权重被赋予一个在 -1 和 1 之间的随机值。
feedforward 方法循环遍历各层,计算每个后续层的激活值。
下面是一个示例,使用feedforward计算我们 [10, 3, 3, 2] 网络在给定随机输入时的输出。通过打印最终层的激活值来检查输出:
network = Network([10, 3, 3, 2])
input_layer = np.random.uniform(-1, 1, (10))
network.feedforward(input_layer)
print(network.activations[-1])
...
[0.29059666 0.5261155 ]
就这样!我们已经成功实现了前向传播算法!让我们将注意力转向反向传播。
反向传播算法
反向传播算法是神经网络从错误中学习的过程。
在上述前向传播算法的实现中,我们将网络权重初始化为 -1 和 1 之间的随机数,并将所有偏置设置为 0。使用这种初始设置,网络对任何给定输入生成的输出本质上是随机的。
我们需要一种方法来更新权重和偏置,使网络的输出变得更有意义。为此,我们使用梯度下降:
梯度下降更新步骤
其中 aₙ 是一个输入参数的向量。下标 n 表示迭代次数。f(aₙ) 是一个多变量代价函数,∇f(a) 是该代价函数的梯度。𝛾 是学习率,它决定了每次迭代中 aₙ 应调整的幅度。我之前写过一篇关于梯度下降的 文章 2,其中详细讨论了梯度下降。
使用机器学习解决工程优化问题
towardsdatascience.com
在神经网络的情况下,aₙ 包含了所有网络的权重和偏置,f(aₙ) 是网络代价(注意 f(aₙ) ≡ C)。这里我们使用 L2-范数代价函数来定义网络代价,该函数是根据期望网络输出 ŷ 和实际网络输出 y 计算的。ŷ 和 y 都是包含 n 个值的向量,其中 n 是输出层中的神经元数量。
L2-范数代价函数
现在我们知道如何计算代价函数,但还不知道如何计算代价函数的梯度。
计算代价函数的梯度是反向传播的核心。代价函数的梯度告诉我们需要将网络中的权重和偏置向哪个方向以及调整多少,以提高输出的准确性。
代价函数的梯度
为了找到这些偏导数,神经网络采用了 链式法则。这段高中微积分的内容是神经网络工作的关键。
为了演示链式法则在反向传播算法中的应用,我们将考虑一个每层包含一个神经元的网络:

每层包含一个神经元的神经网络
我引入了一个简化版本的符号表示,因为在这个例子中我们不需要为每层中的神经元编号。下面,我还引入了一个新变量 z,它封装了激活函数的输入。
我们从输出层 L 开始反向传播,并迭代地向后通过各层。
对于输出层,我们有:
单层神经元网络输出层的 C、a⁽ᴸ⁾ 和 z⁽ᴸ⁾
现在我们已经为输出层定义了 C、a⁽ᴸ⁾ 和 z⁽ᴸ⁾,我们可以计算它们的导数并应用链式法则来找到 ∂C/∂w⁽ᴸ⁾:
应用链式法则来计算 ∂C/∂w⁽ᴸ⁾
对于 ∂C/∂b⁽ᴸ⁾ 也是类似的:
应用链式法则来计算 ∂C/∂b⁽ᴸ⁾
和 ∂C/∂a⁽ᴸ⁻¹⁾:
应用链式法则计算 ∂C/∂a⁽ᴸ⁻¹⁾
现在我们有了 ∂C/∂a⁽ᴸ⁻¹⁾ 的表达式,我们可以反向迭代通过网络找到成本函数对之前的权重和偏置的敏感性:
成本函数对 w⁽ᴸ⁻¹⁾ 和 b⁽ᴸ⁻¹⁾ 的敏感性
我们为计算这种简化的每层一个神经元网络中对权重和偏置的敏感性定义的方程,在每层有多个神经元时基本保持不变。
变化的是关于 L-1ᵗʰ 层激活值的成本函数的导数。这是因为成本函数通过网络的多个路径受到这些激活值的影响。
我们将成本函数对 L-1ᵗʰ 层激活值的导数定义为:
成本函数关于 L-1ᵗʰ 层中第 kᵗʰ 激活的导数
其中下标 j 和 k 分别表示在 Lᵗʰ 和 L-1ᵗʰ 层的激活值。
反向传播算法在 Network 类中的 backpropagation 方法中实现:
def backpropagation(self, expected_output):
# Calculate dcost_dactivations for the output layer
dcost_dactivations = 2 * (self.activations[-1] - expected_output)
# Loop backward through the layers to calculate dcost_dweights and dcost_dbiases
for i in range(-1, -len(self.layers), -1):
dactivations_dz = self.dsigmoid(np.dot(self.weights[i], self.activations[i-1]) + self.biases[i]) # Sigmoid output layer
dz_dweights = self.activations[i-1]
dz_dbiases = 1
self.dcost_dweights[i] += dz_dweights[np.newaxis,:] * (dactivations_dz * dcost_dactivations)[:,np.newaxis]
self.dcost_dbiases[i] += dz_dbiases * dactivations_dz * dcost_dactivations
# Calculate dcost_dactivations for hidden layer
dz_dactivations = self.weights[i]
dcost_dactivations = np.sum(dz_dactivations * (dactivations_dz * dcost_dactivations)[:,np.newaxis], axis=0)
注意,这个方法进行了矢量化处理,以适应每层多个神经元。dcost_dweights 和 dcost_dbiases 存储在与之前定义的权重和偏置数组相同形状的数组中。这使得使用这些偏导数进行梯度下降变得非常简单。我还认为这使得代码更具可读性。
当我们向后遍历网络时,我们对每一层应用链式法则,并使用本节中介绍的方程计算成本函数对每层权重和偏置的敏感性。
训练一个神经网络来分类手写数字
实现了前向传播和反向传播算法之后,是时候将所有内容整合起来,训练一个用于识别手写数字的神经网络了。
为此,我们需要一个标注了相应值的手写数字数据集。自己生成这个数据集将会非常费力。幸运的是,已经存在可以用于这个数字识别问题的数据库。我们将使用修改版国家标准与技术研究所(MNIST)数据库* 3,这是一个大型的标注手写数字数据库,用于训练我们的神经网络。

从 MNIST 数据库中随机选择的样本
MNIST 数据库包含 70000 个标注的灰度图像,大小为 28 x 28 像素(总共 784)。数据库中的每个标注图像称为 样本。MNIST 数据库被划分为 训练 和 测试 子集,其中包含 60000 和 10000 个样本,分别用于训练和测试。
正如它们的名字所示,训练子集用于训练网络,测试子集用于测试网络的准确性。这样我们可以使用网络从未见过的样本来测试其准确性。
接下来,我们将训练子集分割成批次。在这个例子中,我决定每个批次包含 100 个样本。总共有 600 个批次。我们将训练子集分成批次的原因是我们不会在每个样本后更新网络的权重和偏置。相反,我们是在每个批次后更新的。这样,当我们应用梯度下降时,我们使用的是基于一个批次所有样本计算的平均梯度,而不是基于单个样本的梯度来调整权重和偏置。
一个周期包含训练子集中的所有批次。一个周期会遍历所有这些批次。选择用一个周期训练我们的网络意味着网络只会“看到”训练子集中的每个样本一次。增加周期数意味着网络将对每个样本进行多次训练,从而“看到”每个样本多次。

训练工作流程图
下方显示了Network类的完整定义,包括在训练工作流程中使用的所有方法。train_network方法负责协调训练工作流程。
import numpy as np
class Network:
def __init__(self, layers, learning_rate):
self.layers = layers
self.learning_rate = learning_rate
self.activations = self.__init_activations_zero()
self.weights = self.__init_weights_random()
self.biases = self.__init_biases_zero()
self.G_weights = self.__init_G_weights()
self.G_biases = self.__init_G_biases()
self.dcost_dweights = self.__init_weights_zero()
self.dcost_dbiases = self.__init_biases_zero()
self.cost = 0
self.costs = []
def __init_activations_zero(self):
activations = []
for layer in self.layers:
activations.append(np.zeros(layer))
return activations
def __init_weights_random(self):
weights = []
for i in range(0, len(self.layers) - 1):
weights.append(np.random.uniform(-1, 1, (self.layers[i+1], self.layers[i])))
return weights
def __init_weights_zero(self):
weights = []
for i in range(0, len(self.layers) - 1):
weights.append(np.zeros((self.layers[i+1], self.layers[i])))
return weights
def __init_biases_zero(self):
biases = []
for i in range(1, len(self.layers)):
biases.append(np.zeros(self.layers[i]))
return biases
def __init_G_weights(self):
G_weights = []
for i in range(0, len(self.layers) - 1):
G_weights.append(np.zeros([len(self.weights[i]), len(self.weights[i][0]), len(self.weights[i][0])]))
return G_weights
def __init_G_biases(self):
G_biases = []
for i in range(0, len(self.layers) - 1):
G_biases.append(np.zeros([len(self.biases[i]), len(self.biases[i])]))
return G_biases
def sigmoid(self, x):
return 1 / (1 + np.exp(-x))
def dsigmoid(self, x):
sig = self.sigmoid(x)
return sig * (1 - sig)
def calculate_cost(self, expected_output):
cost = np.sum((self.activations[-1] - expected_output)**2) # L2 cost function
return cost
def feedforward(self, input_layer):
self.activations[0] = input_layer
for i in range(0, len(self.layers) - 1):
self.activations[i+1] = self.sigmoid(np.dot(self.weights[i], self.activations[i]) + self.biases[i])
def backpropagation(self, expected_output):
# Calculate dcost_dactivations for the output layer
dcost_dactivations = 2 * (self.activations[-1] - expected_output)
# Loop backward through the layers to calculate dcost_dweights and dcost_dbiases
for i in range(-1, -len(self.layers), -1):
dactivations_dz = self.dsigmoid(np.dot(self.weights[i], self.activations[i-1]) + self.biases[i]) # Sigmoid output layer
dz_dweights = self.activations[i-1]
dz_dbiases = 1
self.dcost_dweights[i] += dz_dweights[np.newaxis,:] * (dactivations_dz * dcost_dactivations)[:,np.newaxis]
self.dcost_dbiases[i] += dz_dbiases * dactivations_dz * dcost_dactivations
# Calculate dcost_dactivations for hidden layer
dz_dactivations = self.weights[i]
dcost_dactivations = np.sum(dz_dactivations * (dactivations_dz * dcost_dactivations)[:,np.newaxis], axis=0)
def average_gradients(self, n):
# Calculate the average gradients for a batch containing n samples
for i in range(0, len(self.layers) - 1):
self.dcost_dweights[i] = self.dcost_dweights[i] / n
self.dcost_dbiases[i] = self.dcost_dbiases[i] / n
def reset_gradients(self):
# Reset gradients before starting a new batch
self.dcost_dweights = self.__init_weights_zero()
self.dcost_dbiases = self.__init_biases_zero()
def reset_cost(self):
self.cost = 0
def update_G(self):
for i in range(0, len(self.layers) - 1):
self.G_biases[i] += np.outer(self.dcost_dbiases[i], self.dcost_dbiases[i].T)
for j in range(0, len(self.weights[i])):
self.G_weights[i][j] += np.outer(self.dcost_dweights[i][j], self.dcost_dweights[i][j].T)
def update_weights_and_biases(self):
# Perform gradient descent step to update weights and biases
# Vanilla Gradient Descent
# for i in range(0, len(self.layers) - 1):
# self.weights[i] -= (self.learning_rate * self.dcost_dweights[i])
# self.biases[i] -= (self.learning_rate * self.dcost_dbiases[i])
# AdaGrad Gradient Desecent
self.update_G()
for i in range(0, len(self.layers) - 1):
self.biases[i] -= (self.learning_rate * (np.diag(self.G_biases[i]) + 0.00000001)**(-0.5)) * self.dcost_dbiases[i]
for j in range(0, len(self.weights[i])):
self.weights[i][j] -= (self.learning_rate * (np.diag(self.G_weights[i][j]) + 0.00000001)**(-0.5)) * self.dcost_dweights[i][j]
def process_batch(self, batch):
for sample in batch:
self.feedforward(sample['input_layer'])
self.backpropagation(sample['expected_output'])
self.cost += self.calculate_cost(sample['expected_output'])
def train_network(self, n_epochs, batches):
for epoch in range(0, n_epochs):
print(f"Epoch: {epoch}\n")
for batch in batches:
self.process_batch(batch)
self.costs.append(self.cost / len(batch))
self.reset_cost()
self.average_gradients(len(batch))
self.update_weights_and_biases()
self.reset_gradients()
print(f"Cost: {self.costs[-1]}")
注意,我们使用 AdaGrad 梯度下降算法来更新网络的权重和偏置。Adagrad 比传统梯度下降算法复杂一些,但在这个应用中表现更好。
接下来,我们需要定义网络的形状。每个样本中的 784 个像素值构成输入层的激活,因此输入层的大小设置为 784。由于我们正在对 0 到 9 的数字进行分类,我们也知道输出层必须包含 10 个神经元。对于隐藏层,我发现两个每层 32 个神经元的隐藏层对这个问题效果很好。
总的来说,这个网络中有 26432 个权重和 74 个偏置。这意味着在训练网络时,我们是在一个 26506 维的参数空间中进行优化!
不要被这项优化任务的规模吓倒。我们已经通过实现前馈和反向传播算法以及定义训练工作流程完成了艰巨的工作。
在训练网络之前,需要对训练数据进行一些准备工作,以将其分割成批次。然后,我们可以调用train_network来训练网络。最后,一旦网络训练完成,我们通过检查网络对测试子集的输出,来计算网络准确率,以查看网络正确分类了多少样本。
import numpy as np
import matplotlib.pyplot as plt
from keras.datasets import mnist
from network import Network
def calculate_accuracy(network, x, y):
# Calculate network accuracy
correct = 0
for i in range(0, len(x)):
network.feedforward(x[i].flatten() / 255.0)
if np.where(network.activations[-1] == max(network.activations[-1]))[0][0] == y[i]:
correct += 1
print(f"Accuracy: {correct / len(x)}")
# Prepare training data
(train_X, train_y), (test_X, test_y) = mnist.load_data()
# Define n_epochs and set up batches
n_epochs = 5
n_batches = 600
batches = []
input_layer = np.array_split(train_X, n_batches)
expected_output = np.array_split(np.eye(10)[train_y], n_batches)
for i in range(0, n_batches):
batch = []
for j in range(0, len(input_layer[i])):
batch.append({'input_layer' : input_layer[i][j].flatten() / 255.0, 'expected_output' : expected_output[i][j]})
batches.append(batch)
# Setup and train network
network = Network([784,32,32,10], 0.1)
network.train_network(n_epochs, batches)
# Calculate accuracy of the network
calculate_accuracy(network, test_X, test_y)
...
Accuracy: 0.942
训练完成后,网络的准确率为 94.2%。对于一个从零开始构建的神经网络来说,这并不算差!
总结
在这篇文章中,我展示了如何使用 Python 从零开始构建一个简单的神经网络。
我们详细讲解了前馈和反向传播算法的实现,介绍了训练工作流程,并用 26432 个权重和 74 个偏置训练了一个神经网络来识别 MNIST 数据库中的手写数字,达到了 94.2%的网络准确率。
通过改进我们的实现可以获得更好的准确性。例如,使用 ReLU 激活函数用于隐藏层,softmax 用于输出层,已被证明能提高网络的准确性 4。
类似地,我们可以选择不同形式的梯度下降来调整权重和偏差,这可能使我们在 26506 维参数空间中找到更优的最小值。
将每个样本展平成一维数组的过程也会丢弃大量重要信息。更先进的卷积神经网络保留了图像中邻近像素的信息,通常比这里实现的基本网络类型表现更好。
当我开始写这篇文章时,我的目标是制作一个简明的资源,让刚接触神经网络的人能够阅读并获得对其工作原理的基本理解。我希望我达到了这个目标,并在某种程度上鼓励你继续学习这个极具趣味的主题。
这篇文章是否帮助你更深入地理解了神经网络的工作原理?请在评论中告诉我。
喜欢这篇文章吗?
关注 和 订阅 获取更多类似内容 — 与你的网络分享 — 尝试开发你自己的神经网络或尝试更先进的卷积神经网络。
除非另有说明,所有图片均由作者提供。
Yann LeCun 和 Corinna Cortes 拥有 MNIST 数据库的版权。MNIST 数据库根据 Creative Commons Attribution-Share Alike 3.0 license的条款提供。
参考文献
1 GitHub (2023), artificial_neural_network
2 Bruce, C. (2023). PID Controller Optimization: A Gradient Descent Approach. Medium
3 Deng, L. (2012). The MNIST database of handwritten digit images for machine learning research. IEEE Signal Processing Magazine, 29(6), 141–142
4 Nwankpa, C., Ijomah, W.L., Gachagan, A., & Marshall, S. (2018). Activation Functions: Comparison of trends in Practice and Research for Deep Learning. ArXiv, abs/1811.03378
如何在 Amazon ECS 上将 ML 模型正确部署为 Flask APIs
原文:
towardsdatascience.com/how-to-properly-deploy-ml-models-as-flask-apis-on-amazon-ecs-98428f9a0ecf
在 Amazon ECS 上部署 XGBoost 模型以推荐完美的小狗
·发布在 Towards Data Science ·阅读时间 8 分钟·2023 年 3 月 16 日
--

图片由 Carissa Weiser 提供,来自 Unsplash
随着 ChatGPT 的大获成功,AI 技术对我们生活的影响变得越来越明显。然而,除非这些出色的 ML 模型向所有人开放并且正确部署以应对高用户需求,否则它们将无法对世界产生任何积极影响。因此,能够不仅开发 AI 解决方案,还要了解如何正确部署它们是如此重要。更不用说,这项技能将使你在市场上变得更有价值,并开启有利可图的 ML 工程师职业机会。
在这篇文章中,我们将把 XGBoost 模型部署为 Flask API,并使用 Gunicorn 应用服务器在 Amazon Elastic Container Service 上进行部署。该模型将根据某人家的大小推荐一只腊肠犬或德国牧羊犬小狗。

图片由作者提供,来源: [1–2]
👉 游戏计划
-
训练一个 XGBoost 模型
-
构建一个简单的 Gunicorn-Flask API 进行推荐
-
为 API 构建 Docker 镜像
-
在 Amazon ECS 上部署 Docker 容器
完整源码 GitHub 仓库: 链接🧑💻
flask-on-ecs - repo structure
.
├── Dockerfile
├── README.md
├── myapp.py
├── requirements.txt
└── train_xgb.ipynb
在云上部署 ML 模型
我们通常需要将本地训练的 ML 模型部署到生产环境中供互联网用户使用。这种方法需要首先将 ML 模型封装成 API,然后进行 Docker 化。AWS 提供了一个名为 Elastic Container Service(ECS)的专用工具,它消除了管理计算环境(如 EC2)的麻烦,使我们可以使用名为 Fargate 的无服务器工具简单地部署 Docker 容器。
关于 Web 服务器与应用服务器的说明
在传统的 web 开发世界中,通常的做法是让 Web 服务器(如 NGINX)处理来自客户端的大量流量,并与提供动态内容的后台应用程序(API)进行交互。可以将 Web 服务器视为餐厅中的服务员,服务员接收并处理顾客的订单。类似地,Web 服务器接收并处理来自 Web 客户端(如 web 浏览器)的请求。服务员随后与厨房沟通以准备订单,并将完成的餐点送到顾客那里。同样,Web 服务器与后台应用程序沟通以处理请求,并将响应发送回 Web 客户端。设置通常如下:

作者提供的图片
Web 服务器非常棒,因为它们能够将客户端请求分发到多个后台应用程序,从而提高性能、安全性和可扩展性。然而,在我们的案例中,由于我们将在 AWS 上部署 Flask API,因此有云原生的负载均衡器可以处理流量路由到后台 API,并且还可以强制执行 SSL 加密。因此,包含 NGINX 会显得有些多余。仅使用 Gunicorn 应用服务器对于大多数 ML 模型部署情况而言是足够的,前提是你打算使用 AWS 负载均衡器。好吧,但……
什么是 WSGI 和 Gunicorn?
WSGI(Web 服务器网关接口)只是一个约定或 规则集,在 web 服务器与 web 应用程序通信时需要遵循。Gunicorn(绿色独角兽)是一个遵循 WSGI 规则并处理客户端请求的 应用服务器,将请求发送到 Python Flask 应用程序。Flask 本身提供了 WSGI Werkzeug 的开发服务器用于初步开发,但如果我们要在生产中部署 API,那么我们的 Flask 应用程序可能需要同时处理多个请求,因此我们需要 Gunicorn。
实时 API 的典型云架构
在云中运行 API 得到了应用负载均衡器(ALBs)的极大提升,因为它们通常可以代替 NGINX 的功能并将流量路由到我们的后台应用程序。这个教程将仅专注于在 ECS 上部署 Flask API,我们可以在未来的帖子中讨论 ALBs。

作者提供的图片
好了,背景介绍完了,我们开始构建和部署一些 API 吧!
👉 第一步:训练 XGBoost 模型
训练一个 XGBoost 模型,根据房屋面积预测 Dachshund(腊肠犬)或 German Shepherd(德国牧羊犬),并将模型保存为 pickle 文件。
要在 VS Code 中运行它,我们来创建一个单独的 Python 3.8 环境:
conda create --name py38demo python=3.8
conda activate py38demo
pip install ipykernel pandas flask gunicorn numpy xgboost scikit-learn
然后重新启动 VS Code 并在 Jupyter Notebook 中 -> 选择 'py38demo' 作为内核。
训练并 pickle XGBoost 模型:

如您所见,我们成功训练了模型,在 300 和 600 平方英尺的房屋上进行了测试,并将 XGBoost 模型保存为 pickle (.pkl) 文件。
👉 步骤 2:构建一个简单的 Gunicorn-Flask API
让我们构建一个非常简单的 Flask API 来提供我们的 XGBoost 模型预测。我们有一个简单的助手函数,将 0/1 模型预测转换为 ‘wiener dog’/‘german shepherd’ 输出:
要运行 API,请在终端中:
python myapp.py
在另一个终端中,测试一下发送 POST 请求:
curl -X POST http://0.0.0.0:80/recms -H 'Content-Type: application/json' -d '{"area":"350"}'
我是如何在我的 Mac 上本地运行它的:

我们的 API 工作得很好,但你可以看到我们收到一个警告,说明这是一个开发服务器:

让我们停止 API,改用 Gunicorn 生产级服务器:
gunicorn --workers 4 --bind 0.0.0.0:80 myapp:flask_app_obj
回到我们的 VS Code:

现在我们准备好将 API Docker 化了!📦
👉 3. 为 API 构建 Docker 镜像
以下是一个 Dockerfile,它使用 python3.8 基础镜像。我们需要 3.8 版本,因为我们在本地使用该版本训练了 XGBoost 模型。
注意:由于我在 Mac 上构建镜像,我需要指定
- -platform linux/amd64
使其与 ECS Fargate Linux 环境兼容。
这是我们构建和运行镜像的方法。
注意:我们将主机(即笔记本电脑)的 80 端口绑定到 Docker 容器的 80 端口:
docker build --platform linux/amd64 -t flaskapi .
docker run -dp 80:80 flaskapi
让我们快速再测试一下:

现在我们知道我们的 API 在 Docker 容器内运行良好,是时候将其推送到 AWS 了!🌤️
👉 步骤 4:在 Amazon ECS 上部署 Docker 容器
这个部分看起来可能很复杂,但实际上,如果我们将过程分解为 6 个简单步骤,就会很简单。

图片由作者提供
i) 将 Docker 镜像推送到 ECR
让我们创建一个名为 demo 的 ECR 仓库,在这里我们可以推送 Docker 镜像。

然后我们可以使用 ECR 提供的推送命令:
# autheticate
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <Your-aws-acc-no>.dkr.ecr.us-east-1.amazonaws.com
#tag the image
docker tag <Your-local-docker-image-name>:latest <Your-aws-acc-no>.dkr.ecr.us-east-1.amazonaws.com/<Your-ECR-repo-name>:latest
#push the image to ECR
docker push <Your-aws-acc-no>.dkr.ecr.us-east-1.amazonaws.com/<Your-ECR-repo-name>:latest
假设:你已经在本地计算机上配置了 AWS CLI,并设置了具有正确权限的 IAM 用户来与 ECR 进行交互。你可以在此 链接 中找到更多信息。
运行上述 3 个命令后,我们可以看到我们的镜像已经在 ECR 上 🎉

复制并粘贴图像 URI 到某处,因为我们在接下来的步骤中需要它。
ii) 创建一个 IAM 执行角色
我们需要创建一个执行角色,以便我们的 ECS 任务能够从 ECR 拉取镜像。我们将其命名为:simpleRole

iii) 创建一个安全组
安全组是必需的,以允许互联网中的任何人向我们的 API 发送请求。在现实中,你可能希望将其限制为特定 IP 集合,但我们将其开放给所有人,并称之为:simpleSG

iv) 创建一个 ECS 集群
这一步很简单,只需几秒钟。我们称之为:flaskCluster

在我们的集群正在配置的同时,让我们创建一个任务定义。
v) 创建任务定义
任务定义,顾名思义,是一组与要运行的镜像、要打开的端口以及我们希望分配的虚拟 CPU 和内存相关的指令。我们称之为:demoTask

vi) 运行任务
让我们在flaskCluster上运行我们的demoTask,使用来自步骤 iii)的simpleSG。

该测试我们部署的 API 从公共 IP地址开始! 🥁
curl -X POST http://<PUBLIC-IP>:80/recms -H 'Content-Type: application/json' -d '{"area":"200"}'

它运行正常! 🥳
正如你所见,我们通过向 ECS 提供的公共 IP发送 POST 请求,成功获得了完美的狗狗推荐。 🔥
感谢阅读,希望你发现这对入门 Flask、Gunicorn、Docker 和 ECS 有所帮助!
想要更多关于机器学习工程的实用文章吗?
免费订阅 以便在我发布新故事时收到通知。
成为 Medium 会员以阅读我和其他成千上万位作者的更多故事。你可以通过使用我的 推荐链接 来支持我。你不会额外支付费用,但我会获得佣金。
参考资料
如何使用 Quip Python APIs 从/到相同的 Quip 电子表格读取和写入数据
我们分析师经常被要求提供一种解决方案,使最终用户能够提供其输入,这些输入随后可以用作最终分析解决方案中的覆盖/附加上下文。
·
跟进 发表在 Towards Data Science ·7 min read·Nov 17, 2023
--
Chris Ried的照片在Unsplash上发布
让我们以一个电子商务购物应用程序为例。系统中有一个逻辑,会在供应商收到 100 个负面客户评分后将其列入黑名单。现在,可能会出现负面评分是由于应用内体验或配送/退货体验引起的情况。因此,为了保持公平,供应商可以选择每六个月对黑名单提出异议。为了方便本文讨论,我们假设审批/拒绝会记录在离线电子表格中。
所有新的撤销黑名单申请在一周内会被导出到一个电子表格,并发送给团队进行审查。团队会审查数据并批准或拒绝这些申请。然后,他们会将数据返回系统进行更新。这是每周进行的工作。
现在,这些手动干预的数据需要被添加到系统中。可以有多种方法来实现。用户可以将他们的数据上传到一个 s3 桶中,然后可以安排将其读取到数据库中。或者,我们可以使用 quip,这样所有个人可以实时更新同一个电子表格,然后可以按照固定的节奏将其上传到数据库中。
Quip 是一款协作软件,允许多人实时编辑文档和电子表格,用户可以自由选择任何终端客户端——桌面/移动设备。
在这篇文章中,我将展示如何将一个 quip 电子表格自动化,以读取用户输入的数据,上传到数据库表中,然后将新的数据写回到同一个电子表格。我将使用 Redshift 作为这次练习的数据库。
这个任务可以分为两个独立的部分。首先,从 Quip 电子表格读取数据并将其存储在数据库中的表中。其次,我们将对这些数据进行一些操作或检查,并与数据库中现有的数据进行联接,然后将处理后的数据写入到已经存在的 quip 电子表格中。我们将分别讨论这两个情况,以便如果您只想读取或只想写入,这篇文章也能帮助您完成这项工作。让我们来看第一个部分。
第一部分 — 从 Quip 电子表格读取数据并将其写入数据库中的表。
步骤 1: 获取访问令牌以通过 Quip API 连接到 Quip。
我们需要生成一个访问令牌,以提供对个人 Quip 账户的 API 访问权限。要生成个人访问令牌,请访问此页面:quip.com/dev/token。如果您有企业 SSO 启用的 Quip 账户,那么 URL 会有所不同,例如 — quip-corporate.com/dev/token

一旦您点击上方的获取个人访问令牌按钮,您将获得一个令牌,我们将在后续部分使用该令牌通过 API 访问 quip 电子表格。
步骤 2: 导入库
首先,让我们导入所需的库。在这一部分,我们主要使用 quipclient 和 pandas_redshift 库。
import quipclient as quip
import pandas as pd
import numpy as np
import pandas_redshift as pr
import boto3
from datetime import datetime as dt
import psycopg2
import warnings
warnings.filterwarnings('ignore')
import socket
步骤 3: 使用令牌 ID 连接到 Quip
QuipClient API 需要基本 URL、线程 ID 和访问令牌来访问任何文件。基本 URL 是你尝试读取(或写入)的 quip 服务器的 URL。在公司账户的情况下,这通常会在 URL 中包含公司名称。线程 ID 是 Quip 服务器上所有文件的唯一标识符。它是目标文件的基本 URL 之后的字母数字值,在这个例子中是一个电子表格。
如果文件的 URL 看起来像 — https://platform.quip-XXXXXXXXX.com/abcdefgh1234/,则基本 URL 将是 — https://platform.quip-XXXXXXXXX.com,thread_id 将是 — abcdefgh1234。
访问令牌是我们在第 1 步中刚刚生成的。
现在,使用 QuipClient API,我们通过访问令牌和 thread_id 连接到 URL。
#####################################
# declaring Quip variables
#####################################
baseurl = 'https://platform.quip-XXXXXXXXX.com'
access_token = "************************************************************************"
thread_id = 'abcdefgh1234'
##########################################
# connecting to Quip
##########################################
client = quip.QuipClient(access_token = access_token, base_url=baseurl)
rawdictionary = client.get_thread(thread_id)
第 4 步:从 quip 中读取数据到数据框
第 3 步中的 rawdictionary 输出返回一个 HTML 列表。Pandas 函数 read_html 将帮助将 HTML 部分读取到数据框 dfs 中。因此,dfs 是一个数据框的列表。这个列表中的每个数据框包含来自 quip 电子表格中每个选项卡的数据。在这个例子中,我们只考虑最后一个选项卡的数据。因此,使用索引 -1 来获取 raw_df 中的最后一个数据框。
##########################################
# cleaning the data and creating a dataframe
##########################################
dfs=pd.read_html(rawdictionary['html'])
raw_df = dfs[-1]
raw_df.columns=raw_df.iloc[0] #Make first row as column header
raw_df=raw_df.iloc[1:,1:] #After the above step, the 1st two rows become duplicate. Delete the 1st row.
raw_df=raw_df.replace('\u200b', np.nan) #Replacing empty cells with nan
第 5 步:连接到数据库以将数据写入表中
要访问 Redshift 实例,我们需要 Redshift Endpoint URL。例如,实例将如下所示:
datawarehouse.some_chars_here.region_name.redshift.amazonaws.com.
我们连接到数据库,并将数据框(在第 4 步中创建)写入新表或现有表中。pandas_to_redshift 函数允许你将数据附加到现有表中或完全覆盖它。请注意,如果选择 append = False,那么每次执行此操作时,表将被删除并重新创建。如果你希望在覆盖数据时保留某些列的数据类型或字符长度,或者用户权限,最好在执行此操作之前先截断表。你可以通过发出直接的 Truncate 命令来截断表。SQLAlchemy 和 psycopg2 是更简单的选项。截断表后,你可以使用 append = True 执行操作。我通常对需要保留历史数据的类型 2 表使用 append=True。
#### Truncating the Table ####
##########################################
# Connecting to DataBase
##########################################
user1="user"
pswd1="password"
connection_db=psycopg2.connect(dbname = 'test_db',
host='test_host.com',
port= '1234',
user= user1,
password= pswd1)
##########################################
# Connection Established
##########################################
df = pd.read_sql_query("""Select distinct
from test_table
order by 1 asc
""",connection_db)
result = df.to_markdown(index=False)
cur = connection_db.cursor()
cur.execute('TRUNCATE TABLE test_table')
#### Writing to the table ####
##########################################
# connecting to redshift and s3
##########################################
pr.connect_to_redshift(dbname = 'db',
host = 'server.com',
port = 1234,
user = 'user',
password = 'password')
pr.connect_to_s3(aws_access_key_id = '*************',
aws_secret_access_key = '*************************',
bucket = 'test',
subdirectory = 'subtest')
##########################################
# Write the DataFrame to S3 and then to redshift
##########################################
pr.pandas_to_redshift(data_frame = raw_df,
redshift_table_name = 'test_table',append = True,
region = 'xxxxxxx')
这完成了第一部分,即从 quip 电子表格中读取数据并写入 Redshift 表。现在,让我们看一下第二部分。
第二部分:将数据写入现有的 Quip 电子表格。
对于这一部分,前三个步骤与第一部分相同。所以,请遵循上述第 1、2 和 3 步。我们将从这里的第 4 步开始。
第 4 步:连接到数据库以读取数据
我们将在这里使用 psycopg2 连接到 Redshift 实例,并从 Redshift 表中读取需要写入 Quip 电子表格的数据。在这里,我将数据框转换为 Markdown 以获得一个干净的表格,这也是 QuipClient 库的一个先决条件。
##########################################
# Connecting to DataBase
##########################################
user1="user"
pswd1="password"
connection_db=psycopg2.connect(dbname = 'test_db',
host='test_host.com',
port= '1234',
user= user1,
password= pswd1)
##########################################
# Connection Established
##########################################
df = pd.read_sql_query("""Select distinct
from test_table
order by 1 asc
""",connection_db)
result = df.to_markdown(index=False)
第 5 步:将数据写入 Quip 文件
要将数据写入 Quip 电子表格,可以使用QuipClient库中的edit_document函数。此函数具有多个参数。格式可以是 HTML 或 markdown。默认为 HTML,这就是我们在第 4 步将数据框转换为 markdown 的原因。您需要指定section_id和location来指定要添加数据的位置——追加、前置、在特定部分之后/之前等。针对这种特定情况,我只想将数据追加到现有电子表格的新标签页中。您可以在这里了解更多信息。
有时,操作已执行,但脚本仍因 API 响应延迟而失败。try-except 错误块用于捕获任何超时错误。
##########################################
# Inserting the data to Quip
##########################################
try:
client.edit_document(thread_id=thread_id,
content = result,
format='markdown',
operation=client.APPEND)
print("Test DB is updated.")
except socket.timeout:
print("Error Excepted!")
我们完成了!
希望您找到本文有帮助。如果您有任何额外的问题,请随时联系。
感谢您的阅读!
下次再见……
如何减少你的主数据管理费用
原文:
towardsdatascience.com/how-to-reduce-your-master-data-management-bill-a95bf45b2353
利用开源工具抓住“低垂的果实”
·发表于Towards Data Science ·阅读时间 9 分钟·2023 年 4 月 28 日
--
主数据管理(MDM)是商业供应商用来描述实体解析框架的流行词。我与几家供应商进行了交谈,大多数供应商提供 SaaS,并按从来源中摄取的记录总数定价。这对于大型企业来说,每年的费用在 6 到 7 位数美元之间。
这篇文章的目标读者
你计划很快实施 MDM 吗?你是否向供应商询问了报价?或者你的公司是否已经投资了 MDM SaaS?这无疑不是一项小投资。
如果你能通过几天的工程工作显著减少年度订阅费用呢?用一句话描述这个想法:
利用开源工具抓住“低垂的果实”,让 MDM 完成艰巨的工作。
这种做法可以轻松转化为每年节省两位数百分比或 5 到 7 位数美元。
为什么实体解析很重要
一般规模较大的企业使用多个数据源。这些数据源用于其运营(ERP 系统)、客户关系管理(CRM)、分析(数据湖、数据仓库)等(文件系统、外部来源)。

几乎每个系统中都存在冗余记录。一些交易正式链接到记录 AB InBev,另一些则链接到 AB INBEV NV。如果重复记录未被发现,我们将失去对单一客户实体的完整视图。客户重复记录的示例取自我写的一篇文章。图片由作者提供。
相同现实世界客户实体的记录隐藏在不同的来源中;并非所有记录都通过外键链接或所有属性都同步,每个来源中都存在重复记录。这是一个重要的数据质量问题。并且不仅仅是客户实体,还包括供应商、产品、人员和其他实体类型。
我知道一家通过许多并购实现增长的公司。业务随着时间的推移整合了许多新的产品线和地理区域。但 IT 整合迅速滞后,操作了 100 多个 ERP 系统,团队继续在孤岛中工作。这导致了错失协同机会。举几个例子:
-
跨产品线和地区的交叉销售机会被错过了。
-
由于地区和产品线的遗留界限,现场工作的团队利用不够理想。
-
与供应商的谈判不够理想,因为各团队独立采购相同的产品。
-
因为制造/采购和销售之间缺乏透明度,订单积压失控。

大型企业在职能、地区和业务线之间维护许多 IT 系统。箭头表示已实施的过程、数据流或只是手动文书工作。我们能否追踪一个产品从关闭机会到制造、分销、安装/销售和服务的全过程?图片来源于作者。
但它不需要永远保持这样。我在下面概述了一个简化的架构。MDM 平台处理从头到尾的实体解析过程。结果是一组客户、产品、人员和供应商的交叉引用——所有源的联接键查找表。我们将这些与其他信息(订单、报价、交易等)的综合视图结合起来,以克服上述挑战。

我们从源头提取并加载(EL)数据到湖中的原始层。一部分数据流入我们的主数据管理(MDM)平台,MDM 平台处理从头到尾的实体解析。转换(T)补充了 MDM 工作,形成一个完整的数据集市。分析和反向 ETL 提供可操作的洞察。图片来源于作者。
实体解析的端到端工作原理
Christophides 和合著者的文章 Big Data 的端到端实体解析:调查 提供了深入的概述——这是一个关于实体解析方法论的精彩综述。不要错过我们在这里不会涉及的许多主题。
下一张图代表了实现实体解析的众多方式之一。

实体解析可以是一个迭代过程。我们摄取和预处理记录,工程化相似性特征,选择(和拟合)分类模型,并聚类匹配。我们可以设置规则(相似性阈值),在这些规则下高度相似的对会被自动视为真实匹配(红色),并将一批可能但不确定的案例分配给人工审核(绿色)。解决的例子帮助我们学习和改进。图片来源于作者。
从高层来看,这些是典型的端到端过程的步骤:
-
预处理/规范化时仅保留语义。
-
构建记录块,限制比较的数量。
-
工程化特征以衡量属性的相似性。
-
选择(并调整)一个模型来预测成对匹配的可能性。
-
将成对匹配转换为实体集群。
-
与人工审核(批量)可能但不确定的例子。
通常,你会将一批不确定的案例分发给人工审核。标注的结果可以用来重新调整你的分类模型,甚至让你重新考虑任何一个初步步骤(预处理、阻塞、特征工程)。更强大的模型可以检测到更多值得审核的有趣案例,使得这个过程具有迭代性。
为什么不在内部构建你的实体解析框架?
通常高昂的计算成本加上人工参与的审核过程增加了问题的另一个维度:预算。你不希望你的云账单或劳动成本飙升。
因此,一个快速且便宜的解决方案在操作上可能过于昂贵。你决定选择更复杂的版本。还有更多你希望包含的组件,超过了我们在前面部分讨论的内容。例如,使用弱监督根据领域专家提供的启发式规则对成对数据进行标注。或者主动机器学习根据估计的不确定性优先考虑样本以进行人工审核。
每个组件单独来看都像是一个可管理的任务。真正的挑战在于构建和管理所有内容所需的多样技能:处理基础设施和安全,构建后台、分类模型,以及为审核员构建前端。
你也可以构建一些你团队更有信心的组件,然后让供应商处理其余的繁重工作。我与两个提供强大匹配引擎作为产品的供应商进行了交流——你必须在自主管理的基础设施上安装的软件。我还与提供 SaaS 注释服务的供应商进行了交流,用于管理审核任务。
听起来像是很多讨论。但这也是一个快速学习的机会。我还建议先用开源框架进行实验,再与供应商交谈。个人经验中的一些好处:
-
避免营销噪音电话,因为你已经知道你想要什么。
-
挑战供应商解决你在使用开源软件时发现的边缘案例。让他们找到解决方案。
-
确定每个供应商的弱点——他们都有!
-
更加自信地进行谈判。告诉他们你了解他们的弱点,并且你不是一个容易对付的对象。毫无疑问,这将大大影响他们产品的定价。
如何显著降低你的 MDM 成本
我与大多数 MDM 供应商交流过,他们的定价基于导入到平台上的总记录数。但这还不是全部。他们还会试图向你推销与外部 API 的集成,例如地址验证。
下图详细展示了数据准备步骤。我用绿色标出了节省资金的机会。

每个绿色框都是一个节省资金的机会。预处理(例如 SQL)帮助我们筛选出相关记录。开源实体解析处理简单案例,再次减少输入到 MDM 的记录数量。最后,只有在无法用便宜的替代品替换时,才调用昂贵的第三方 API。图片由作者提供。
你必须投资以抓住每一个节省资金的机会。让我们从投资较低的机会开始:
通过简单的预处理来节省资金
你的源系统中的客户记录并非全部同等重要。许多记录可能对业务没有任何价值或不符合你的 MDM 业务案例。

我们将相关数据提取+加载到数据湖中。有些记录对 MDM 没有益处。我们可以通过规则(转换为 SQL 并在数据湖中执行)来识别这些记录。这些规则可以随时更改。图片由作者提供。
-
僵尸记录没有关联到单一订单、交易、合同、开放机会或其他与操作相关的实体。因此,你可能不会从解决这些无效记录中受益。
-
解决你的 B2C 客户的可能性有多大?MDM 的卖点是提供跨地区、产品线等的客户 360 度视图。如果在你的业务中,这对 B2C 很少有用,那么为何要投资解决这些实体?
一般思路是收集具有显著价值的业务案例。然后,通过诸如“我们是否需要处理没有任何收入的客户记录来满足你的需求?”之类的问题来挑战业务。所有的答案汇总将识别值得纳入 MDM 的子集。
排除记录不是一个永久的决定。新的业务案例是否足以证明重新纳入先前排除的记录?提交代码更改;数据将被包含在下一个 MDM 批次中。
通过更便宜的第三方 API 替代品来节省资金
如果不将 MDM 与第三方 API 集成,你将无法释放 MDM 的全部潜力。两个显著的例子是:
-
地理编码和验证地址。
-
为 B2B 客户提供行业分类、层级(母公司、子公司)及其他关键绩效指标(年收入、员工人数)。
典型的 MDM 供应商会尝试向你销售内部解决方案或市场领导者以确保安全——没人因为购买 IBM 而被解雇。但这真的是你为业务获得的最佳价值吗?

使用友好的服务对地址进行地理编码。一些服务不仅提供搜索结果,还会给出置信度评分。如有必要,对于低于阈值的评分,可以调用第二种更昂贵的服务。图片由作者提供。
以地理编码服务为例。Google Maps和Mapbox是两个突出的市场领先者,还有很多其他供应商提供闭源专有解决方案。另一方面,像 Geoapify 和 Opencage 这样的供应商依赖开源和开放数据,特别是 OpenStreetMap 生态系统。这些开放的替代方案提供了远低于闭源竞争对手的价格。更重要的是,它们带有友好的许可,允许你在没有限制的情况下存储和分享数据。
你说 Google Maps 在你的数据上比 OpenStreetMap 更准确?没问题。如果首选服务的信心较低,你可以使用其他服务作为后备。
使用开源实体解析节省开支
许多 MDM 供应商提供的功能在流行的开源替代品中很难找到——专有的发音算法、集体匹配、以实体为中心的匹配等。这些将帮助你捕捉到你可能会错过的边缘案例。

使用开源工具来衡量实体记录对的相似度。让复杂的专有 MDM 解决方案为你做繁重的工作。提高你的投资价值。图片由作者提供。
那大部分情况呢?根据我的经验,大多数检测到的重复项都是低悬的果实——如果你问我,大约 80 比 20 的比例。我们可以通过简单的开源实体解析步骤快速抓取 80%。选择一个相对保守的匹配相似度阈值并自动解析。假设你的数据包含 20%的冗余记录(根据个人经验估算),我们可以在将数据导入 MDM 之前减少 16%的样本量。
从架构上讲,我们可以将这样的步骤作为脚本部署在我们的数据湖上,并在提取和加载源数据后执行。我们可以将协调开销保持在最低限度。可能一次性作业会清理大部分数据,而每隔一段时间执行一次就能完成其余部分。
我们可以将输出的检测到的低悬果实对存储在交叉引用表中,并将其与 MDM 的结果结合使用,以获得完整的视图。
证明概念并自信谈判
MDM 是一项昂贵的长期投资。一个合理的方式来证明其价值是通过几天/几周的工作——在公司内部数据上的概念验证(POC)。
我们最关键的数据源中有多少冗余的客户/产品/供应商记录?跨源的参考差距有多大?这些如何转化为低效?利益相关者需要在投资昂贵解决方案之前有一些粗略的估计。
你可以在几天内对数据的关键子集进行 POC 测试。检查一下开源实体解析框架。但不仅仅报告你能自信检测到的重复项数量。通过随机抽样和手动努力调查可能但不确定的情况。

你使用开源工具和几行代码自信地检测到了许多重复项(绿色)。你的算法在哪些方面需要赶上?通过随机分层抽样和一些老式的手动调查工作来了解。图片由作者提供。
你的内部解决方案在哪些方面需要赶上?拼写错误是否合理?对同义词或缩写不了解?在非拉丁语言中表现差劲?挑战 MDM 供应商,看看他们是否能更有信心地解决这些问题。如果你最喜欢的供应商在任何方面落后,用你的证据谈判价格——这是降低 MDM 费用的另一种方式。
结论
MDM 平台在绝对金额上是昂贵的。供应商通过这些平台产生的价值来证明其价格标签。我同意这一点。然而,我看到显著增加投资回报的潜力。
那为什么不完全在内部构建呢?你可以保持复杂度低和架构简单。例如,一个简单的脚本加上保守的阈值将比什么都没有要好。真正的问题是,除了这些,你是否能从实体解析中获得更多收益?一些考虑因素:
-
将实体解析视为业务问题,而非 IT 问题。收集具有显著估计价值的业务案例。展示内部解决方案与购买解决方案相比,你能做什么和何时能做。
-
你的团队是否具备构建内部解决方案的专业知识?你不希望仅仅为了实体解析而雇佣一整支工程师团队。
-
最后,MDM 价格存在显著差异。如果预算是一个问题,避免市场领导者。许多供应商在这个领域竞争。有些供应商的价格范围非常低,远低于一整支内部工程师团队的薪资。
如何在 5 个简单示例中将 SQL 查询重写和优化为 Pandas
编程
从 SQL 过渡到 Pandas 以改善你的数据分析工作流程
·发布于 Towards Data Science ·9 分钟阅读·2023 年 6 月 1 日
--

作者提供的图片
数据分析师、工程师和科学家都熟悉 SQL。该查询语言仍广泛用于处理任何类型的关系数据库。
然而,现在越来越多的情况,尤其是对于数据分析师,技术要求在上升,人们预计至少要了解一种编程语言的基础知识。在处理数据时,Python 和 Pandas 特别是常见的工作要求之一。
尽管 Pandas 对于熟悉 SQL 的数据人员来说可能是新的,但在 SQL 中选择、过滤和聚合数据的概念可以很容易地转移到 Pandas 中。在这篇文章中,让我们看看一些常见的 SQL 查询以及如何在 Pandas 中编写和优化它们。
随意在你自己的笔记本或 IDE 中跟随操作。你可以从 Kaggle 这里 下载数据集,该数据集在 CC0 1.0 Universal (CC0 1.0) 公共领域献身许可证下免费使用。
只需导入并运行以下代码,开始吧!
import pandas as pd
from functools import reduce
df = pd.read_csv("/Users/byrondolon/Desktop/Updated_sales.csv")
df.columns = [i.replace(" ", "_") for i in df.columns]py
SQL 的简单示例及其 Pandas 等效方法
查询整个表
我们可以直接开始,看看经典的 SELECT ALL from 一个表。
这是 SQL:
SELECT * FROM df
这是 Pandas 代码
df

Pandas 代码输出 — 作者提供的图片
你需要做的就是调用 Pandas 中的 DataFrame 来返回整个表及其所有列。
你可能还希望在编写更复杂的查询之前快速检查表中的一个小子集。在 SQL 中,你可以使用 LIMIT 10 或类似的方式仅获取选定数量的行。在 Pandas 中,你可以调用 df.head(10) 或 df.tail(10) 来获取表的前 10 行或最后 10 行。
查询没有空值的表
要在最初的选择查询中添加条件,除了限制行数之外,你还可以在 SQL 的 WHERE 子句中放入过滤表的条件。例如,如果你想要所有 Order_ID 列中没有任何空值的行,SQL 查询将如下所示:
SELECT * FROM df WHERE Order_ID IS NOT NULL
在 Pandas 中,你有两个选项:
# Option 1
df.dropna(subset="Order_ID")
# Option 2
df.loc[df["Order_ID"].notna()]

Pandas 代码输出 — 作者提供的图片
现在,我们得到的表没有 Order_ID 列中的任何空值(你可以与上面的第一个输出进行比较)。这两种方法都会返回没有空值的表,但它们的工作方式略有不同。
你可以使用 Pandas 中的 dropna 方法返回没有任何空行的 DataFrame,指定 subset 参数中要从中删除空值的列。
另外,loc 方法允许你传递一个掩码或布尔标签来过滤 DataFrame。在这里,我们传递 df["Order_ID"].notna(),如果你单独调用它,将返回一个 True 和 False 值的系列,这些值可以映射到原始 DataFrame 行,以确定 Order_ID 是否为空。当我们将它传递给 loc 方法时,它会返回 df["Order_ID"].notna() 评估为 True 的 DataFrame(即所有 Order_ID 列不是空的行)。
查询表中的特定列
接下来,我们将选择表中的几个特定列,而不是选择所有列。在 SQL 中,你可以在查询的 SELECT 部分中写入列名,如下所示:
SELECT Order_ID, Product, Quantity_Ordered FROM df
在 Pandas 中,我们将代码写成这样:
df[["Order_ID", "Product", "Quantity_Ordered"]]

Pandas 代码输出 — 作者提供的图片
要选择特定的列子集,你可以将列名列表传递给 Pandas 中的 DataFrame。你也可以像这样单独定义列表以便清晰:
target_cols = ["Order_ID", "Product", "Quantity_Ordered"]
df[target_cols]
分配一个目标列列表,然后将其传递到 DataFrame 中,可以使在需要对代码进行更改时处理表变得更加容易。例如,你可以让一个函数返回你需要的列作为列表,或者根据用户需要的输出来追加和删除列表中的列。
SQL 和 Pandas 中的 GROUP BY
现在我们可以开始聚合数据。在 SQL 中,我们通过将要分组的列传递给 SELECT 和 GROUP BY 子句来实现这一点,然后在 SELECT 子句中将该列添加到像 COUNT 这样的聚合度量中。例如,这样做将允许我们对原始表中每个 Product 的所有单独的 Order_ID 行进行分组,并计算它们的数量。查询可能如下所示:
SELECT
Product,
COUNT(Order_ID)
FROM df
WHERE Order_ID IS NOT NULL
GROUP BY Product
在 Pandas 中,它会像这样显示:
df[df["Order_ID"].notna()].groupby(["Product"])["Order_ID"].count()

Pandas 代码输出 — 图片由作者提供
输出是一个 Pandas Series,其中表格按产品进行分组,并显示每个产品的Order_ID计数。除了我们之前在 Pandas 中包含过滤条件的查询外,我们现在要做三件事:
-
添加
groupby并传递你想要对 DataFrame 进行分组的列(或列列表); -
在原始分组的 DataFrame 上用方括号传递列名;
-
调用
count(或任何其他聚合)方法对 DataFrame 中的目标列进行聚合。
为了更好地阅读,我们可以将条件赋值给一个变量(这在后面会派上用场),并格式化查询,使其更易读。
condition = df["Order_ID"].notna()
grouped_df = (
df.loc[condition]
.groupby("Product")
["Order_ID"] # select column to count
.count()
)
grouped_df
一个完整的 SQL 查询在 Pandas 中翻译并高效编写
现在我们已经具备了完整 SQL 查询的大部分组件,让我们看看一个更复杂的查询在 Pandas 中的样子。
SELECT
Product,
COUNT(Order_ID)
FROM df
WHERE Order_ID IS NOT NULL
AND Purchase_Address LIKE "%Los Angeles%"
AND Quantity_Ordered == 1
GROUP BY Product
ORDER BY COUNT(Order_ID) DESC
在这里,我们在之前的查询中添加了一些内容,包括多个过滤条件以及 ORDER BY,以便查询返回的表按照我们正在聚合的度量进行排序。由于查询中有更多组件,让我们逐步看看如何在 Pandas 中实现这个查询。
首先,我们不直接在调用loc方法时传递多个条件,而是定义一个条件列表,并将其赋值给变量FILTER_CONDITIONS。
FILTER_CONDITIONS = [
df["Order_ID"].notna(),
df["Purchase_Address"].str.contains("Los Angeles"),
df["Quantity_Ordered"] == "1",
]
如之前所述,传递给loc的条件应该是一个 Pandas 掩码,其结果为真或假。可以将多个条件传递给loc,但语法应如下所示:
df.loc[condition_1 & condition_2 & condition_3]
然而,像这样直接传递条件列表是不可行的:
df.loc[FILTER_CONDITIONS]
# doesn't work -> you can't just pass a list into loc
如果你尝试上述操作会出现错误,因为每个条件应通过&操作符(用于“且”条件)或|操作符(用于“或”条件)分隔。相反,我们可以编写一些快速代码来以正确的格式返回条件。我们将使用functools.reduce方法将条件组合在一起。
如果你想在笔记本中查看它的样子,并查看使用reduce函数组合字符串的效果,可以尝试这个:
reduce(lambda x, y: f"{x} & {y}", ["condition_1", "condition_2", "condition_3"])
这将输出如下字符串:
>>> 'condition_1 & condition_2 & condition_3'
回到我们实际的 Pandas 条件,我们可以改写为(不使用字符串格式化,仅使用我们在FILTER_CONDITIONS变量中定义的条件列表)。
reduce(lambda x, y: x & y, FILTER_CONDITIONS)
reduce的作用是将一个函数累积地应用于一个可迭代对象中的元素,或者在我们的例子中,对FILTER_CONDITIONS列表中的每一项运行lambda函数,并用&操作符将它们组合起来。这会一直运行,直到没有条件剩下,或者在这种情况下,对于所有三个条件,它将有效地返回:
df["Order_ID"].notna() & df["Purchase_Address"].str.contains("Los Angeles") & df["Quantity_Ordered"] == "1"
最后,让我们将条件列表添加到 Pandas 中以创建最终的分组查询:
final_df = (
df
.loc[reduce(lambda x, y: x & y, FILTER_CONDITIONS)]
.groupby("Product")
.size()
.sort_values(ascending=False)
)
你会注意到与之前的查询有两个额外的区别:
-
与其指定要计数的特定列,我们可以简单地调用
size方法,它会返回 DataFrame 中的行数(就像之前每个Order_ID值都是唯一的,并且意味着我们计数时的每一行一样); -
在 Pandas 中有几种不同的方法来进行排序,其中一种方法是简单地调用
sort_values并传递ascending=False以按降序排序。
如果你想使用之前的语法来聚合数据,它将如下所示:
final_df = (
df
.loc[reduce(lambda x, y: x & y, FILTER_CONDITIONS)]
.groupby("Product")
["Order_ID"].count()
.sort_values(ascending=False)
)

Pandas 代码输出 — 图片由作者提供
两种方法的输出将与之前相同,即一个 Series,其中包含你分组的列和每个产品的计数。
如果你想要输出 DataFrame,可以在系列上调用 reset_index 方法,以获取你分组的列和你聚合的列的原始列名(在这种情况下,我们按“Product”分组,并计数“Order_ID”)。
final_df.reset_index()

Pandas 代码输出 — 图片由作者提供
就这样!所有完整 SQL 查询的组件,但最终用 Pandas 编写。为了进一步优化处理数据的过程,我们可以做以下一些事情:
-
将不同的列列表(用于 SELECT 或 GROUP BY)放入它们自己的变量或函数中(这样你或用户可以随着时间的推移修改它们);
-
将合并列列表的逻辑移到自己的函数中,以便最终用户不会对
reduce逻辑感到困惑; -
在调用
reset_index之后,我们可以重命名输出列(如果我们在多个列上聚合,则重命名多个列),以便更清晰,例如命名为“Count_Order_ID”。
感谢你花时间阅读这篇文章!如果你还没有,请点击关注按钮,以便及时了解我更多的技术文章。此外,如果你喜欢我的内容,我会很高兴如果你通过下面的推荐链接注册 Medium。这样我可以获得你月订阅的一部分,而且你将可以访问一些只有 Medium 会员才能使用的独家功能。
[## 通过我的推荐链接加入 Medium — Byron Dolon
作为 Medium 会员,你的会员费的一部分将分配给你阅读的作者,你可以访问每一个故事…
byrondolon.medium.com](https://byrondolon.medium.com/membership?source=post_page-----6983cae8426e--------------------------------)
更多来自我: - 5 个实用技巧助你成为数据分析师 - 掌握电子商务数据分析 - 在 Pandas DataFrame 中检查子字符串 - 学习 Python 的 7 个最佳 GitHub 仓库 - 5(又半)行代码助你理解 Pandas 数据
机器学习实验的艺术
原文:
towardsdatascience.com/how-to-run-machine-learning-experiments-that-really-matter-2ba0a88bc579
5 个简单的策略帮助你从机器学习实验中获得最大收益
·发表于数据科学之路 ·阅读时间 7 分钟·2023 年 1 月 17 日
--

图片由Oudom Pravat提供,Unsplash上的照片
实验是机器学习行业的核心。我们之所以进步,是因为我们进行实验。
然而,并非所有实验都是同等有意义的。一些实验比其他实验产生更多的商业影响。然而,专注于影响的实验选择、执行和迭代的艺术,通常不会在标准的机器学习课程中涵盖。
这常常会引起很多混淆。新的机器学习从业者可能会觉得你应该把所有东西都投入到一个问题上,然后“看看哪些有效”。但事实并非如此。
需要明确的是,我不是在谈论离线和在线测试及其变体的统计数据,例如 A/B 测试。我谈论的是实验完成前后发生的事情。我们如何选择实验的对象?如果结果是负面的,我们该怎么办?我们如何尽可能高效地进行迭代?
更广泛地说,你如何从机器学习实验中获得最大的收益?这里有 5 个简单的策略供你采纳。
1 — 知道何时进行实验
作为机器学习从业者,你脑海中总会有无数个问题。比如,如果我们去掉这个特征会怎样?如果我们添加那层神经网络会怎样?如果我们使用这个声称更快的其他库会怎样?花时间的可能性是无穷的。
鉴于你的时间预算有限,你应该如何决定实验的内容?以下是一些实用的建议:
优先考虑预期收益最高的实验。 花时间充分了解现有模型并找出最大的差距:这就是你要集中精力的地方。例如,如果一个模型仅使用少量特征,那么最好的实验可能是围绕特征发现进行的。如果一个模型是简单的逻辑回归模型,那么在模型架构方面的工作可能更有前景。
不要为了了解已经知道的东西而进行实验。 在考虑启动任何实验之前,先进行研究。如果文献中对你尝试回答的问题有广泛的共识,那么你可能不需要围绕它设计实验。相信共识,除非你有强有力的理由不这样做。
在实验之前定义清晰的成功标准。 如果没有明确的成功标准,你永远不知道什么时候完成。就是这么简单。我见过太多因为实验后启动标准发生变化而未能部署的模型。通过在进行任何实验之前定义并沟通清晰的标准来避免这一陷阱。
2 — 总是从假设开始
科学实验总是从假设开始。我们首先提出假设,然后进行实验以确认或排除它。无论哪种结果,我们都获得了知识。这就是科学的运作方式。
科学假设必须是一个声明,通常含有“因为”这个词。它不能是一个问题。“哪个模型更好?”不是一个假设。
一个假设可以是:
-
“我假设 BERT 模型对于这个问题效果更好,因为单词的上下文很重要,而不仅仅是它们的频率”,
-
“我假设神经网络在这个问题上比逻辑回归效果更好,因为特征和目标之间的依赖关系是非线性的”,
-
“我假设添加这组特征会提高模型性能,因为这些特征在另一个相关的用例中也被使用”,
等等。
我常常看到人们进行大量实验并将结果呈现在长长的电子表格中,却没有明确的结论。当被问到“为什么这个数字比那个数字大?”时,答案通常是某种形式的临时猜测。这就是 HARKing,即在结果已知后进行假设。
HARKing 是科学的对立面,它是伪科学。它很危险,因为它可能导致统计偶然现象,这些结果看起来真实,但实际上只是偶然的结果(并且在实际应用中不会出现)。
科学方法 — 在实验之前制定假设 — 是防止偶然发现的最佳保护措施。
3 — 创建紧密的反馈循环
在你的机器学习流程中改变一件事应该像改变一行代码并在终端中执行提交脚本一样简单。如果复杂得多,最好先缩短反馈周期。紧凑的反馈循环意味着你可以快速测试想法,而不需要复杂的操作。
这里有一些方法来实现这一点:
-
自动化命名。 花在思考如何命名(模型、实验、数据集等)的时间,是未能实际进行实验的时间。与其试图起一些巧妙和深刻的名字,比如“BERT_lr0p05_batchsize64_morefeatures_bugfix_v2”,不如使用coolname等库来自动化命名,只需将参数直接写入日志文件中。
-
慷慨记录。 记录实验参数时,最好多记录一些。记录是便宜的,但由于记不清楚修改了哪些设置而重新运行实验是昂贵的。
-
避免笔记本。 笔记本难以版本控制、共享,并且将代码与日志混在一起,使得每次想要更改内容时都需要上下滚动。笔记本在探索性数据分析和可视化中有其用处,但在机器学习实验中,脚本通常更好:它们可以版本控制、共享,并且在代码与日志(即输入与输出)之间创建明确的界限。
-
从小做起,快速失败。 最好先在一个小的子样本数据集上进行实验。这可以让你快速获得反馈而不浪费太多时间,并且“快速失败”:如果这个想法不起作用,你会尽早知道。
-
一次只改变一件事。 如果你同时改变多个东西,你就无法知道这些改变中的哪一个导致了你看到的模型性能变化。通过一次只改变一件事来简化你的工作。
4 — 避免“新奇事物”偏见。
我经常看到人们对最新的机器学习研究论文过于兴奋,并试图将其强行应用于特定的用例。现实是,我们在机器学习生产中遇到的问题往往与机器学习研究中的问题大相径庭。
例如,像 BERT 这样的超大型语言模型在学术基准数据集如 GLUE 上取得了显著进展,GLUE 包含了许多语言学上棘手的问题。
“奖杯无法放进行李箱里,因为它太小了。是什么太小了,奖杯还是行李箱?”
然而,一个典型的商业问题可能仅仅是检测电子商务目录中包含电池的所有产品,对于这种问题,简单的词袋方法完全可以解决,而 BERT 可能是杀鸡用牛刀。
“新奇事物”偏见的解药,再次是严格遵循科学方法,并在进行任何实验之前制定清晰的假设。
“这是一种新模型”并不是一个假设。
5 — 避免实验炼狱
实验的结果可以是积极的(我们确认了假设)或消极的(我们否定了假设),这两种结果同样有价值。积极的结果改进我们的生产模型,从而提升我们的业务指标,而消极的结果则缩小了我们的搜索范围。
我看到过太多的同行陷入“实验炼狱”:实验结果是消极的(这个想法不起作用),但他们却没有结束并继续前进,而是不断尝试原始想法的不同修改,可能是因为组织压力,可能是因为“沉没成本”偏见,或者其他原因。
实验炼狱会阻碍你转向其他更有成果的想法。接受消极实验结果只是过程的一部分,并在需要时继续前进。这就是经验科学应有的进展方式。
结语:始终保持实验
总结一下,
-
知道何时实验:优先考虑预期收益最大的实验,
-
总是从假设开始:避免 HARKing 的伪科学,
-
创建紧密的反馈循环:尽可能让你快速测试想法,
-
避免“新奇事物偏见”:记住,学术问题上的成功不一定是业务问题成功的良好指标,
-
避免实验炼狱:接受消极结果是过程的一部分,并在需要时继续前进。
让我以一条建议作为结束,这条建议是我在亚马逊的科学经理曾经给我的:
最优秀的机器学习科学家的机器很少闲置。
他指的是最优秀的机器学习科学家总是有一系列他们想要进行的实验,这些实验对应于他们制定并希望测试的不同假设。每当他们的机器即将闲置(例如即将开始周末假期时),他们会在注销前从他们的待办事项中提交实验。
机器学习是一个经验性的领域。更多的实验带来更多的知识,最终带来更多的专业知识。掌握影响力巨大的机器学习实验的艺术,你就走上了成为机器学习专家的道路。
👉 关注 我,在 Medium 上查看更多我的内容。
📫* 订阅 以便直接将我的下一篇文章发送到你的收件箱中。
💪* 成为 Medium 会员 ,并解锁无限访问权限。
还在好奇如何最大化你作为机器学习专业人员的影响力吗?请查看下面链接的文章 👇
忘掉“安静退出”:3 种策略,用更少的时间创造更多的商业影响
[towardsdatascience.com
如何在 Python 中保存和加载神经网络
原文:
towardsdatascience.com/how-to-save-and-load-your-neural-networks-in-python-cb2063c4a7bd
保存和加载 PyTorch 和 TensorFlow/Keras 中的检查点和整个深度学习模型的完整指南
·发布在 Towards Data Science ·8 分钟阅读·2023 年 4 月 5 日
--

如何在 PyTorch 和 TensorFlow/Keras 中保存和加载神经网络(图片由作者绘制)
训练神经网络通常需要大量时间和计算资源。如果在投入了这些时间和计算后丢失模型,那将是非常遗憾的。
这就是为什么你应该能够根据用例在不同阶段(训练中或训练完成后)保存和加载深度学习模型的原因:
本文介绍了如何为两个主要深度学习框架保存和加载检查点和整个模型:
- PyTorch
import torch
- TensorFlow/Keras
import tensorflow as tf
from tensorflow import keras
一般来说,当加载保存的模型时,确保使用的框架版本与保存模型时使用的版本相匹配是很重要的。
print('PyTorch version:', torch.__version__)
print('TensorFlow version:', tf.__version__)
print('Keras version:', keras.__version__)
这在将模型迁移到不同的机器或环境时尤为重要。因此,在保存和版本化模型时,存储框架的版本作为元数据是很重要的。
为什么机器学习中的版本控制必须超越源代码,涵盖数据集和机器学习模型以确保可追溯性……
如何保存和加载模型检查点
检查点功能对于在训练期间指定时间保存模型非常有用。这类似于在视频游戏中保存进度。它确保你无需从头开始,可以在出现问题时从检查点恢复。

在 PyTorch 中保存和加载模型检查点
PyTorch 检查点包含以下组件2:
-
模型状态(权重和偏差)
-
优化器状态
-
训练步骤或纪元
-
你选择保存的任何额外信息(例如,训练配置,如优化器、指标或当前训练损失)
PyTorch 模型通常以 PyTorch 二进制格式(.pt或.pth)保存。虽然这两种文件扩展名之间没有区别,但开发者社区3 建议使用 **.pt** 文件扩展名而不是.pth文件扩展名,因为后者与 Python 路径配置文件的文件扩展名冲突。
你可以使用以下代码片段在 PyTorch 中保存训练检查点(checkpoint_1.pt、checkpoint_2.pt等):
# Define your model
model = ...
optimizer = ...
criterion = ...
# Train the model
for epoch in range(num_epochs):
# Train the model for one epoch
...
# Save a checkpoint after each epoch
PATH = f'checkpoint_{epoch}.pt'
torch.save({
'epoch': epoch,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'train_loss': train_loss,
},
PATH)
你可以使用以下代码片段在 PyTorch 中加载训练检查点(例如checkpoint_3.pt)。确保:
-
在继续模型训练之前设置
model.train() -
继续仅在剩余的纪元中进行训练(
for epoch in range(epoch+1, num_epochs))。
# Define your model
model = ...
optimizer = ...
criterion = ...
# Load a saved checkpoint
checkpoint = torch.load('checkpoint_3.pt')
epoch = checkpoint['epoch']
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
# Set dropout and batch normalization layers to train mode
model.train()
# Resume training the model for the remaining epochs
for epoch in range(epoch + 1, num_epochs):
...
在 TensorFlow/Keras 中保存和加载模型检查点
与 PyTorch 相比,TensorFlow/Keras 中的检查点仅在检查点文件(.ckpt)中保存模型状态(权重和偏差)[6]。
你可以通过使用回调函数在 TensorFlow/Keras 中保存训练检查点(checkpoint_1.ckpt、checkpoint_2.ckpt等),如下所示:
# Define and compile your model
model = ...
...
model.compile(...)
# Define the checkpoint callback that saves the model's weights at every epoch
PATH = f'checkpoint_{epoch}.ckpt'
cp_callback = tf.keras.callbacks.ModelCheckpoint(
filepath = PATH,
save_weights_only = True, # If False, saves the full model
save_freq = 'epoch')
# Train the model with the checkpoint callback
model.fit(X_train,
y_train,
epochs = num_epochs,
validation_data = (X_val, y_val),
callbacks = [cp_callback])
如果你不想使用回调函数,也可以使用model.save_weights(PATH)方法来保存模型权重。
你可以使用以下代码片段在 TensorFlow/Keras 中加载训练检查点(例如checkpoint_3.pt)。确保仅在剩余的纪元中继续训练(num_epochs — epoch)。
# Define and compile your model
model = ...
...
model.compile(...)
# Load a saved checkpoint
epoch = 3
model = model.load_weights(PATH = f'checkpoint_{epoch}.ckpt')
# Define the checkpoint callback that saves the model's weights at every epoch
...
# Resume training the model
model.fit(X_train,
y_train,
epochs = (num_epochs-epoch),
validation_data = (X_val, y_val),
callbacks = [cp_callback])
如何保存和加载深度学习模型
你还可以在模型训练完成时保存模型。这在你想要部署模型或推理发生在训练代码之外时非常有用。

在 PyTorch 中保存和加载整个模型
与检查点相比,PyTorch 只在模型训练完成后保存模型状态(权重和偏差)2。
PyTorch 模型也以 PyTorch 二进制格式( .pt 优于.pth 3)保存。
你可以使用以下代码片段在 PyTorch 中保存训练好的模型(model.pt)。确保保存 model.state_dict() 而不是单独保存 model(请参见下面的替代方案)。
PATH = "model.pt"
# Define your model
model = ...
# Train the model
...
# Save the model
torch.save(model.state_dict(), PATH)
你可以使用以下代码片段在 PyTorch 中加载训练好的模型(model.pt)。确保:
-
在加载权重之前,创建一个相同的模型实例
-
在使用模型进行推理之前,设置
model.eval()
# Define your model architecture
model = ...
# Load the saved model parameters into your model
model.load_state_dict(torch.load(PATH))
# Set dropout and batch normalization layers to evaluation mode before running inference
model.eval()
# Use the model for inference
# ...
或者,你可以如下所示保存整个模型:
PATH = "model.pt"
# Define your model
model = ...
# Train the model
...
# Save the model
torch.save(model, PATH)
使用这种方法,你不需要在加载权重之前定义模型。
# Load the saved model parameters into your model
model = torch.load(PATH)
# Set dropout and batch normalization layers to evaluation mode before running inference
model.eval()
# Use the model for inference
# ...
然而,不推荐这种方法: 这种方法不保存模型类本身。相反,它保存了包含类的文件的路径。因此,使用这种方法保存模型可能会导致在其他项目或不同源代码中重用模型时出现问题。
在 TensorFlow/Keras 中保存和加载整个模型
Keras 模型包含以下组件 [5, 6]:
-
模型架构包括优化器及其状态、损失和
saved_model.pb中的指标 -
模型的状态(权重和偏置)在
variables/目录中。 -
模型的编译信息
模型可以保存为以下文件格式 5:
-
TensorFlow SavedModel 格式 — 推荐和默认格式,当没有指定其他文件扩展名时使用。
-
HDF5 格式(
.h5)——一种较旧且轻量的替代方案,不保存外部损失和指标
你可以使用以下代码片段在 TensorFlow/Keras 中保存训练好的模型(model):
PATH = "model" # Will save the model in TensorFlow SavedModel format
# Define and compile your model
model = ...
...
# Train the model
...
# Save the model
model.save(PATH)
你可以使用以下代码片段在 TensorFlow/Keras 中加载训练好的模型(model)。
# Load the saved model
model = keras.models.load_model(PATH)
# Use the model for inference
# ...
最佳检查点选择
最佳检查点选择是一种深度学习技术,它在训练期间监控验证指标(没有提前停止),并使用具有最佳验证指标的检查点进行推理。
在最近的 关于中级深度学习技术的文章 中,我们回顾了目前似乎没有关于最佳检查点选择的最佳实践的普遍理解。虽然 深度学习调优手册 1 推荐使用最佳检查点选择,但 Kaggle 大师们不推荐此方法,因为这种技术往往使模型过度拟合验证集 4。
针对计算机视觉和自然语言处理的深度学习模型微调的实用指南
towardsdatascience.com
尽管如此,我们将介绍如何将最佳检查点选择应用于你的深度学习管道。
PyTorch 中的最佳检查点选择
保存 类似于 在 PyTorch 中保存模型检查点,但有一些变化:
-
我们 仅保存模型,但不保存训练信息,如 epoch、优化器状态等,因为我们不打算继续训练此模型。
-
在训练过程中手动添加验证指标的监控
# Define your model
model = ...
optimizer = ...
criterion = ...
# Train the model
for epoch in range(num_epochs):
# Train the model for one epoch
...
if (best_metric < current_metric):
best_metric = current_metric
# Save a checkpoint after each epoch
PATH = f'checkpoint_{epoch}.pt'
# Save the model
torch.save(model.state_dict(), PATH)
加载 等同于 在 PyTorch 中加载整个模型。
TensorFlow/Keras 中的最佳检查点选择
保存 类似于 在 TensorFlow/Keras 中保存模型检查点,但有一些变化:
-
使用
save_weights_only = True保存整个模型 -
从
PATH中移除.ckpt以将模型保存为 SavedModel 格式 -
添加
monitor和save_best_only参数
# Define and compile your model
model = ...
...
model.compile(...)
# Define the checkpoint callback that saves the model's weights at every epoch
PATH = f'./checkpoints/checkpoint_{epoch}' # Remove .ckpt to save as SavedModel format
cp_callback = tf.keras.callbacks.ModelCheckpoint(
filepath = PATH,
monitor = "val_acc", # Metric to monitor for best checkpoint picking
save_best_only = True,
save_weights_only = True,
save_freq = 'epoch')
# Train the model with the checkpoint callback
model.fit(X_train,
y_train,
epochs = num_epochs,
validation_data = (X_val, y_val),
callbacks = [cp_callback])
加载 等同于 在 TensorFlow/Keras 中加载整个模型。
总结
本文回顾了在深度学习框架 PyTorch 和 TensorFlow/Keras 中保存和加载神经网络的不同用例。下文展示了 PyTorch 和 Keras 之间的比较概述。

PyTorch 与 TensorFlow/Keras 中保存深度学习检查点或模型时的概述(图像由作者提供)
-
模型架构: 在 PyTorch 中,模型架构从未保存,因此还必须通过某些源代码版本控制进行保存。在 TensorFlow/Keras 中,当你保存整个模型时,模型架构会被保存。
-
模型权重: PyTorch 和 TensorFlow/Keras 都可以仅保存模型权重。然而,在 PyTorch 中,这是在保存最终训练模型时完成的,而在 TensorFlow/Keras 中,这适用于检查点保存。
喜欢这个故事吗?
免费订阅 以便在我发布新故事时收到通知。
[## 每当 Leoni Monigatti 发布时接收电子邮件。
每当 Leoni Monigatti 发布时接收电子邮件。通过注册,如果你还没有,你将创建一个 Medium 账户……
medium.com](https://medium.com/@iamleonie/subscribe?source=post_page-----cb2063c4a7bd--------------------------------)
在 LinkedIn,Twitter* 和* Kaggle上找到我!
参考资料
1 V. Godbole, G. E. Dahl, J. Gilmer, C. J. Shallue 和 Z. Nado (2023). 深度学习调优手册 (第 1.0 版) (访问日期:2023 年 2 月 3 日)
2 M. Inkawhich for PyTorch (2023). 保存和加载模型 (访问日期:2023 年 3 月 27 日)。
3 kmario23 在 Stackoverflow (2019)。PyTorch 中 .pt、.pth 和 .pwf 扩展名之间有什么区别?(访问日期:2023 年 3 月 27 日)。
4 P. Singer 和 Y. Babakhin (2022)。深度迁移学习实用技巧 于 2022 年 11 月在 Kaggle Days Paris 上介绍。
5 TensorFlow (2023)。指南:保存和加载 Keras 模型(访问日期:2023 年 3 月 27 日)。
[6] TensorFlow (2023)。教程:保存和加载模型(访问日期:2023 年 3 月 27 日)。
完整指南:如何通过合同测试和 dbt 有效地扩展你的数据管道和数据产品
开始使用 dbt 实施合同测试所需了解的一切
·
关注 发表在 Towards Data Science ·11 min read·2023 年 10 月 25 日
--
图片由 Jonas Gerg 提供,来源于 Unsplash
让我讲一个关于数据管理系统和规模的故事,这个故事如果你是一名数据或分析工程师,可能会引起你的共鸣,尤其是在 2023 年尽力做到最好时。
不久之前,几乎所有的数据架构和数据团队结构都遵循了集中式方法。作为数据或分析工程师,你知道在哪里找到所有的转换逻辑和模型,因为它们都在同一个代码库中。你可能与你的同事紧密合作,那个同事负责构建你所使用的数据管道。数据团队只有一个,最多两个。
这种方法对于数据源和用例有限的小型组织和初创公司是有效的。它也适用于那些没有完全专注于从数据中提取价值的大型企业。然而,随着组织优先考虑数据驱动,有了更多的机器学习、分析和商业智能数据用例的需求。

由一个团队开发和维护的集中式数据架构
用例和数据源的激增增加了管理数据的复杂性和创建与维护数据系统所需的人数。为了满足这些需求,你公司最新版本的数据策略可能已经转向去中心化。这包括组建去中心化的数据团队和采用像数据网格这样的去中心化数据架构。
去中心化允许组织扩展数据管理,但带来了确保不同组件协调的新挑战,例如由各个团队开发和管理的数据产品和管道。
在这种架构和组织结构中,往往不清楚每个组件的负责人,导致问题和责任推诿。团队之间的集成点数量也增加,维护不同组件之间的工作接口变得更加困难。

多团队和多个组件的去中心化数据架构
如果你能认同这种情况,你并不孤单。你的组织可能正在经历数据去中心化的过程。 为了应对这一过渡,我们可以借鉴在运营世界中成功实施去中心化和分布式架构(如微服务)的经验。他们是如何做到的?他们如何在那个规模下提供可靠的系统?好吧,他们利用了现代测试技术。
“多年来,软件工程成功地采纳了由‘两块披萨团队’完成的小工作单元的概念。每个团队负责自己在较大系统中的组件。团队通过明确、版本化的接口进行集成。遗憾的是,数据还未跟上软件的步伐。单体数据架构仍然是常态——尽管存在明显的缺陷。” — dbt labs
在本文中,我将介绍其中的一种技术:合同测试。我将展示 如何使用 dbt 来为你的上游源和 dbt 模型的公开接口创建简单的合同测试。这种类型的测试将帮助你保持理智,因为你的 dbt 应用程序变得越来越复杂和分散。
但是……什么是合同测试?
当一个分布式系统开始成长为由多个团队开发的多个组件时,团队可能首先尝试的验证系统行为是否符合预期的方法是 实施端到端测试来测试整个系统。

端到端测试范围侧重于验证系统整体
端到端测试由于其复杂性、反馈缓慢以及维护和协调的难度,往往变得非常难以使用。
这在大规模实施微服务的操作世界中也是如此。当作为整体测试系统不是一个选项时,工程团队开始实施不同的方法,例如合同测试。
“集成合同测试是在外部服务的边界进行的测试,以验证其是否符合消费者服务所期望的合同。” — 托比·克莱姆森
团队仍然可以保留一小部分端到端测试,但他们通过使用更快速、更可靠的测试如合同测试、组件测试和单元测试来向下移动测试金字塔。
不同测试类型的权衡通常通过测试金字塔可视化。我在之前的文章中提到过这个概念,关于 为 dbt 模型实施单元测试。

如果我们将相同的概念应用于数据管理系统,可以为 dbt 应用程序实施合同测试,以验证两种类型接口的行为:
-
上游源。
-
公开接口,例如 marts 和输出端口。

合同测试范围
数据系统的合同测试的好处
正如我们所见,数据架构变得越来越复杂和分散,就像之前的操作服务一样。随着这种类型的系统不断扩展,运行可维护和有效的端到端测试套件的能力逐渐减弱。
合同测试通过提供多个优势,成为管理不同情况的强大盟友:
-
减少验证系统行为所需的端到端测试数量。这将导致更快的反馈和更低的维护成本。
-
通过为团队公共接口提供明确的期望,管理在同一代码库中工作的不同数据团队的复杂性。
-
在低环境中发现组件之间的集成问题,确保它们在生产环境中出现之前得到解决。
-
在不同的数据管道或数据产品之间定义和记录更明确的接口。
合同测试与数据质量测试
你可能在想,但……合同测试的概念听起来像我们已经在数据管道中运行的质量测试。
这是一个合理的观察,因为合同测试与数据质量测试之间的界限模糊。我喜欢将合同测试视为现代数据测试策略中的质量测试的一个子集。

合同测试可以视为质量测试的一个子集
区别在于合同测试关注于模式和约束,而数据质量测试关注于实际数据及其特征。让我们看一些例子。
合同测试范围:
-
检查列类型。
-
检查模式级别的预期约束,如主键和外键、非空列。
-
检查给定列的接受值。
-
检查给定列的有效范围。
质量测试范围:
-
评估完整性,例如列中非空值的百分比。
-
评估唯一性,例如非唯一行的数量。
-
评估一致性,例如源中的所有用户标识符是否都包含在输出中。
实施我们的第一个合同测试
好了,理论够多了,让我们通过一个简单的示例开始实际操作。我们有一个叫做health-insights的 dbt 应用程序,它从上游数据源获取体重和身高数据,并计算体质指数指标。
我们来自出色的后台团队的同事负责生成我们需要的体重和身高数据,以构建我们的 health-insights 应用程序。他们在一个有点忙碌和压力的不同团队工作。有时他们未能通知我们模式的更改。为了测试这些上游接口中的变化,我们决定创建第一个源合同测试。

我们示例的系统架构
首先,我们需要添加两个新的 dbt 包,dbt-expectations和 dbt-utils,这将允许我们对源的模式和接受的值进行断言。
# packages.yml
packages:
- package: dbt-labs/dbt_utils
version: 1.1.1
- package: calogica/dbt_expectations
version: 0.8.5
测试数据源
让我们开始为我们的第一个源定义合同测试。我们从raw_height表中提取数据,该表包含来自健身应用程序用户的身高信息。
我们与数据生产者达成一致,我们将接收身高测量值、测量单位和用户 ID。我们同意数据类型,并且仅支持‘cm’和‘inches’作为单位。基于这些信息,我们可以在 dbt 源 YAML 文件中定义我们的第一个合同。
基础构件
查看前面的测试,我们可以看到几个正在使用的 dbt-unit-testing 宏:
-
dbt_expectations.expect_column_values_to_be_of_type: 这个断言允许我们定义预期的列数据类型。
-
accepted_values: 这个断言允许我们定义特定列的接受值列表。
-
dbt_utils.accepted_range: 这个断言允许我们为给定列定义一个数值范围。在示例中,我们期望该列的值不小于 0。
-
not null: 最后,像‘not null’这样的内置断言允许我们定义列约束。
使用这些构建块,我们添加了几个测试来定义上述合同期望。注意我们如何将测试标记为“contract-test-source”。这个标签允许我们在本地和之后在 CI/CD 管道中隔离地运行所有合同测试:
dbt test --select tag:contract-test-source
为 marts 和输出端口实现合同测试
我们已经看到如何快速创建 dbt 应用的源的合同测试,但我们的数据管道或数据产品的公共接口呢?
作为数据生产者,我们希望确保我们按照数据消费者的期望生产数据,以便满足我们与他们的合同,并且使我们的数据管道或数据产品可信赖和可靠。
确保我们履行对数据消费者义务的简单方法是为我们的公共接口添加合同测试。
Dbt 最近发布了一个新特性,model contracts,它允许为 dbt 模型定义合同。在构建模型时,dbt 将验证模型的转换是否生成与其合同匹配的数据集,否则将构建失败。
让我们看看实际操作。我们的 mart,body_mass_indexes,从我们从源头获取的体重和身高数据中生成一个 BMI 指标。我们与供应商的合同规定了以下内容:
-
每列的数据类型。
-
用户 ID 不能为 null
-
用户 ID 总是大于 0
让我们使用 dbt 模型合同定义 body_mass_indexes 模型的合同:
构建块
查看前面的模型规范文件,我们可以看到几个允许我们定义合同的元数据。
-
contract.enforced: 这个配置告诉 dbt 我们希望在每次运行模型时强制执行合同。
-
data_type: 这个断言允许我们定义模型运行后预期生成的列类型。
-
约束: 最后,约束块给了我们定义有用约束的机会,比如某列不能为 null,设置主键和自定义表达式。在上面的示例中,我们定义了一个约束,告诉 dbt 用户 ID 必须始终大于 0。你可以在这里查看所有可用的约束。
源合同测试与 dbt 模型合同
我们为源定义的合同测试与为 marts 或输出端口定义的合同测试之间的区别在于合同何时被验证和强制执行。
Dbt 在‘dbt run’生成模型时强制执行模型合同,而基于dbt 测试的合同在 dbt 测试运行时被强制执行。
如果某个模型合同没有得到满足,当你执行‘dbt run’时会看到一个错误,并且会提供失败的具体细节。你可以在以下 dbt 运行控制台输出中查看一个示例。
1 of 4 START sql table model dbt_testing_example.stg_gym_app__height ........... [RUN]
2 of 4 START sql table model dbt_testing_example.stg_gym_app__weight ........... [RUN]
2 of 4 OK created sql table model dbt_testing_example.stg_gym_app__weight ...... [SELECT 4 in 0.88s]
1 of 4 OK created sql table model dbt_testing_example.stg_gym_app__height ...... [SELECT 4 in 0.92s]
3 of 4 START sql table model dbt_testing_example.int_weight_measurements_with_latest_height [RUN]
3 of 4 OK created sql table model dbt_testing_example.int_weight_measurements_with_latest_height [SELECT 4 in 0.96s]
4 of 4 START sql table model dbt_testing_example.body_mass_indexes ............. [RUN]
4 of 4 ERROR creating sql table model dbt_testing_example.body_mass_indexes .... [ERROR in 0.77s]
Finished running 4 table models in 0 hours 0 minutes and 6.28 seconds (6.28s).
Completed with 1 error and 0 warnings:
Database Error in model body_mass_indexes (models/marts/body_mass_indexes.sql)
new row for relation "body_mass_indexes__dbt_tmp" violates check constraint
"body_mass_indexes__dbt_tmp_user_id_check1"
DETAIL: Failing row contains (1, 2009-07-01, 82.5, null, null).
compiled Code at target/run/dbt_testing_example/models/marts/body_mass_indexes.sql
在管道中运行合同测试
到目前为止,我们有了一套强大的合同测试套件,但我们如何以及何时运行它们?
我们可以在两种类型的管道中运行合同测试。
-
CI/CD 管道
-
数据管道
例如,你可以在CI/CD 管道中按照计划执行源合同测试,目标是像测试或预生产这样的低环境中的数据源。你可以设置管道,以便每次合同不符合时管道会失败。
这些失败提供了关于其他团队在这些变化到达生产环境之前引入的违反合同的变化的宝贵信息。

Github Actions 中的 dbt CI/CD 管道示例
每次通过 CI/CD 管道部署新变化时,你也可以运行你的输出端口/mart 合同测试。由于 dbt 模型合同在每次模型构建时都会被检查,因此你可以告诉 dbt 强制执行合同,以便如果新模型变化引入了合同中的破坏性变化,你的团队会在数据消费者受到影响之前得到通知。
最后,你还可以在生产环境中的数据管道中运行源和输出端口/mart 合同测试。在生产环境中运行合同测试可以帮助你的团队了解数据管道是否因上游依赖项破坏了合同而失败,或是因为你生产的数据没有满足与下游消费者的合同。
开始的附加提示
-
从小开始,测试那些更脆弱、更容易失败的集成点。
-
在实施合同测试时应用宽容读者模式。仅对你需要的数据进行断言。
-
根据你的需求调整合同测试的行为,你可以配置严重性属性以使其产生响亮的失败或仅发出警告。
-
将这些类型的测试与现代数据可观测性工具如 Montecarlo集成,使其成为你事件管理过程的一部分。
-
即使你的数据系统不是使用 dbt 开发的,也要利用 dbt 合同测试。你仍然可以在 dbt 中定义源合同测试,并在使用其他框架或纯 SQL 创建的表或文件上执行这些测试。
-
考虑更高级的合同测试技术如消费者驱动的合同可能会让在特定背景下实施合同测试变得更容易。
结论
我们已经看到数据系统的测试策略如何通过合同测试等测试技术受益,随着这些系统变得更加去中心化和复杂。
我们还看到了如何利用 dbt 内置功能和额外的 dbt 包开始实施合同测试。我们将这种测试应用于两个集成点:上游数据源和数据集市/输出端口。
我希望这篇文章能为你和你的团队提供所有工具和技巧,以在数据系统扩展以满足新的数据用例时开始实施合同测试。如果你感兴趣,可以查看这个 Github 仓库中的示例 dbt 应用程序的源代码。
你准备好开始你的合同测试之旅了吗?我很想听听你的想法和经验。
这篇文章是我在写的一系列关于 测试数据管道和数据产品 的文章中的一部分。
我总是期待认识新的人。如果你想联系我,你可以在 Linkedin、 Github、 Instagram、Substack或 我的个人网站上找到我。
感谢我的 Thoughtworks 同事 Arne、Manisha 和 David 抽时间审阅这篇文章的早期版本。感谢 dbt-expectations 包 的维护者们的伟大工作。
除非另有说明,否则所有图片均由作者提供。
如何安全地将 Synapse Pipelines 连接到 Azure Functions
原文:
towardsdatascience.com/how-to-securely-connect-synapse-pipelines-to-azure-functions-9bcdb176e150
学会使用 Synapse 外泄保护、私有端点和 Azure AD 认证来创建安全连接
·发布在 Towards Data Science ·4 分钟阅读·2023 年 1 月 4 日
--

照片由 Alina Grubnyak 在 Unsplash 上提供
1. 介绍
Azure Functions 是一个流行的工具,用于创建 REST API。团队可以使用 API 暴露他们的应用程序,这些应用程序可以被其他团队使用。一个常见的模式是将 Synapse 管道连接到 Azure Functions,例如运行其他团队提供的小型计算、创建元数据或发送通知。在本博客中,连接 Synapse 到 Azure Functions 的安全性方面讨论如下:
-
Synapse 数据外泄保护以防止内部攻击
-
私有端点 限制 Azure Function 仅对内部可见
-
Azure AD 认证使用身份访问 Azure Function
-
将 Synapse 托管身份 白名单设置为唯一允许访问 Azure Function 的身份
在本博客文章和 Git 仓库 [securely-connect-synapse-azure-function](https://github.com/rebremer/securely-connect-synapse-to-azure-functions) 中,讨论了如何安全地将 Synapse 连接到 Azure Functions,另见下文概述。

- 安全地在 Synapse Pipelines 中使用 Azure Functions
在本博客的其余部分中,将部署一个项目,在其中将 Synapse 管道连接到 Azure Function。在下一章中,将部署该项目
2. 部署项目:安全地将 Synapse 连接到 Azure Function
在本章中,执行以下步骤:
-
2.0 前提条件
-
2.1 部署 Synapse 和 Azure Functions
-
2.2 设置私有端点连接
-
2.3 设置 Azure AD 认证
-
2.4 部署 Synapse 管道
2.0 前提条件
本教程中需要以下资源:
-
Azure PowerShell(仅 2.3 需要 Powershell,其他段落的 CLI 命令可以轻松修改为 bash)
最后,将下面的 Git 仓库克隆到本地计算机。如果您尚未安装 git,您可以从网页下载一个 zip 文件。
https://github.com/rebremer/securely-connect-synapse-to-azure-functions
2.1 部署 Synapse 和 Azure Functions
在这一部分,创建了一个 Synapse 工作区和 Azure Functions,具有以下属性:
-
Synapse 工作区已部署具有管理的 VNET,允许团队创建指向 Azure 中其他 PaaS 服务(例如存储、SQL 以及 Azure Functions)的私有端点
-
Synapse 工作区已部署并启用了数据外流保护。这意味着数据只能通过事先批准的私有端点流动(例如,连接到 Synapse 部署所在的同一 Azure AD 租户中的服务的私有端点)
-
Azure Function 使用 Python 创建,并在基本 SKU 上部署
请参见 [Scripts/1_deploy_resources.ps1](https://github.com/rebremer/securely-connect-synapse-to-azure-functions/blob/main/Scripts/1_deploy_resources.ps1) 以获取此部分的 Azure CLI 脚本
2.2 设置私有端点连接
在这一部分,设置了一个 Synapse 工作区与 Azure Function 之间的私有链接连接,具有以下属性:
-
从 Synapse 托管 VNET 启动到 Azure Function 的私有端点
-
批准 Azure Function 中的私有端点。批准私有端点后,Azure Function 不再暴露于公共互联网。部署 scm 接口仍然对互联网开放,也可以通过添加此链接来决定是否限制此 fqdn 的暴露,见 这里
请参见 [Scripts/2_Setup_private_endpoint_Synapse_FunctionApp.ps1](https://github.com/rebremer/securely-connect-synapse-to-azure-functions/blob/main/Scripts/2_setup_private_endpoint_Synapse_FunctionApp.ps1) 以获取此部分的 Azure PowerShell 脚本。部署后,您将在 Synapse 中找到一个已批准的私有端点,请参见下方。

2.2 批准 Synapse 到 Azure Function 的管理私有端点
2.3 设置 Azure AD 认证白名单 Synapse 托管身份
在这一部分,设置了 Synapse 和 Azure Function 之间的认证,具有以下属性:
-
Azure AD 认证已设置用于 Azure Function
-
Synapse 托管身份被列入白名单,只有 Azure AD 对象 ID 被允许触发 Azure Function
参见 [Scripts/3_Setup_AzureAD_auth_Synapse_FunctionApp.ps1](https://github.com/rebremer/securely-connect-synapse-to-azure-functions/blob/main/Scripts/3_setup_AzureAD_auth_Synapse_FunctionApp.ps1) 获取 Azure CLI 脚本。部署后,你会发现 Synapse 管理的身份被允许访问功能,详见下文。

2.3 Azure Function 的 Azure AD 认证,仅允许 Synapse 管理的身份访问
2.4 部署 Synapse 管道
在这一部分,部署了具有以下属性的 Synapse 管道:
-
Synapse 管道使用网络活动访问 Azure Function。
-
在网络活动中,使用私有端点连接功能,因此调用不会被 Synapse 数据泄露保护阻止
-
在网络活动中,使用系统分配的管理身份进行 Azure Function 的身份验证
参见 [Scripts/4_deploy_synapse_pipeline.ps1](https://github.com/rebremer/securely-connect-synapse-to-azure-functions/blob/main/Scripts/4_deploy_synapse_pipeline.ps1) 获取 Azure CLI 脚本。部署后,确保 Azure Function URL 和 Azure AD 资源 ID 正确填写,详见下文。

2.4 Synapse 管道通过 Azure AD 认证连接到 Azure Function
3. 结论
Azure Functions 是创建 REST API 以暴露服务的热门工具,无论是内部还是外部。Synapse 工作区是一个可以利用其他团队 API 的示例。数据工程师可以使用 Synapse 管道来获取元数据、发送通知和/或运行其他团队暴露的小型计算。在本文中,连接 Synapse 到 Functions 的安全方面讨论如下:
-
Synapse 数据泄露保护 以防止内部攻击
-
私有端点 限制 Azure Function 的暴露
-
Azure AD 认证提供者 限制凭证管理
-
Synapse 管理的身份 被白名单列为访问 Azure Function 的权限
参见这个 Git 仓库 [securely-connect-synapse-azure-function](https://github.com/rebremer/securely-connect-synapse-to-azure-functions) 和下文的架构。

3. 安全地在 Synapse 管道中使用 Azure Functions
如何将 SLURM 作业发送到集群
原文:
towardsdatascience.com/how-to-send-a-slurm-job-to-a-cluster-dd1cf021c7ac
一个关于如何将 SLURM 作业发送到集群的教程,特别适用于深度学习和数据科学
·发布于Towards Data Science ·阅读时间 8 分钟·2023 年 8 月 21 日
--

所以你习惯于使用 Google Colab 的免费 GPU 来训练深度学习模型,但你准备好提升到集群的强大能力,并且不知道如何做到这一点?你来对地方了!🚀
在剑桥大学的神经科学研究实习期间,我训练了用于计算机视觉任务的大型模型,而谷歌提供的免费 GPU 不足够用,因此我决定使用本地集群。
然而,文档非常少,我不得不向其他人请求脚本以尝试理解它们,并或多或少地整理了对我有用的内容。现在我已经整理了运行基本 Python 脚本所需的一切。这个指南就是我希望当时能拥有的。
一个典型的机器学习使用案例
比如说,你想训练一个鸟类分类器,包含 500 个不同的类别和高分辨率的图片。这是 Google Colab 永远无法运行的。
首先,你需要确保你的深度学习模型训练脚本已经准备好。这个脚本应该包含加载数据集、定义神经网络架构和设置训练循环的必要代码。
你应该能够从终端运行这个脚本。
例如,假设你有一个叫做train_bird_classifier.py的脚本,你应该能够使用以下命令运行它:
python train_bird_classifier.py
这个脚本可能看起来像这样:
# train_bird_classifier.py
import torch
from torch.utils.data import DataLoader
# Assuming necessary functions, models, and transformations are defined in various files.
from utils import build_model, BirdDataset, collate_fn, train_model
from transformations import train_transforms, test_transforms
def main():
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# Dataset and DataLoader setup
train_dataset = BirdDataset('data/train/', transform=train_transforms)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, collate_fn=collate_fn)
test_dataset = BirdDataset('data/test/', transform=test_transforms)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False, collate_fn=collate_fn)
# Model setup
model = build_model().to(device)
# Training
model = train_model(model, train_loader, device)
# Save the trained model
torch.save(model.state_dict(), 'bird_classifier_model.pth')
if __name__ == '__main__':
main()
而不是在你的本地计算机或 Google Colab 上运行这个脚本,我们将在一个强大的集群上运行。这样你可以训练更大的模型,速度更快!
什么是 SLURM?
SLURM 代表简单 Linux 资源管理实用工具。
它是一个开源的 作业调度器,用于超级计算机和计算集群中分配计算资源给作业。它是一个工作负载管理器,负责管理、调度和监督集群中作业的执行。

照片由 Michał Parzuchowski 提供,来自 Unsplash
想象一个繁忙的火车站,有多个轨道和进出的火车。每列火车(代表一个作业)需要特定的资源:一个站台、一个出发时间、一定数量的车厢和一条路线。SLURM 就像是车站的总控制器,确保每列火车获得所需的资源而不会发生碰撞、延迟或浪费宝贵的轨道空间。
它有效地管理和调度列车,确保它们以最优方式出发和到达,最大化车站容量的使用。在超级计算机和集群的世界中,SLURM 执行类似的协调,确保每个计算作业顺利而高效地执行。
SLURM 是如何工作的?
-
提交:用户编写指定作业需求和要执行任务的脚本。这些脚本通过
sbatch命令提交。 -
排队:提交的作业被放入队列中。SLURM 使用其调度算法来优先处理这些作业。
-
资源分配:SLURM 检查集群中可用的资源(如节点、CPU、内存),并根据作业的要求和在队列中的优先级将其分配给作业。
-
执行:一旦资源分配完成,作业开始运行。在执行期间,分配给作业的资源不能被其他作业使用。
-
完成:作业完成后,资源被释放并返回到资源池中,准备分配给其他作业。
现在你知道了 SLURM 的定义,以下是我们将要进行的步骤概述:
0. 准备你想在集群上执行的脚本
-
编写调用你脚本的 bash 文件
-
将你的作业提交到队列中
-
(可选):监控你的作业
1. 编写你的 bash 脚本
你的脚本需要是一个 bash 文件,所以文件名末尾需要有 .sh 扩展名。例如 train_classifier.sh。
你的脚本应始终以 shebang (#!) 开头,后跟解释器的路径,通常为 /bin/bash 用于 SLURM 脚本:
#!/bin/bash
这一行指示系统使用 Bash shell 执行脚本。
添加 SLURM 指令
#SBATCH:在 SLURM 脚本中,#SBATCH是 SLURM 特定指令的前缀,这些指令提供给 SLURM 调度器指令。这些指令不会像常规 shell 命令那样执行,但 SLURM 调度器会读取和解释这些指令,以了解如何处理作业。
以下是编写脚本时最有用的指令的详细介绍:
- 作业名称:(这将在作业队列中可见)
#SBATCH -J <your_job_name>
用我们的例子来说,它可能是:
#SBATCH -J train_bird_classifier
-
账户: 如果你有 GPU 积分,它们将从你指定的账户中使用。
-
-A指定作业关联的账户(或项目)。这对于作业记账非常有用。
#SBATCH -A <account_name>
- 重排设置: 该指令防止作业在失败后被重新排队。
#SBATCH --no-requeue
-
分区:
-
-p定义作业应该安排的分区(或队列)。一个集群可以有不同的分区。
#SBATCH -p <partition_name>
- 节点和任务:
#SBATCH --nodes=1
#SBATCH --ntasks=1
这些指令请求一个节点(--nodes=1)和一个任务或 CPU 核心(--ntasks=1)用于作业。
-
GPU:
-
--gres指定作业所需的通用资源。在这里,它请求一个 GPU。
#SBATCH --gres=gpu:1
- 电子邮件通知: 这定义了 SLURM 将发送与作业相关通知的电子邮件地址。
#SBATCH --mail-user=<your_email>
-
作业持续时间:
-
--time设置了作业的总运行时间限制。如果作业在 12 小时内未完成,SLURM 将终止它。
#SBATCH --time=12:00:00
加载环境
当你提交作业时,你可能需要加载一个虚拟环境。例如,要加载一个 conda 环境,你可以添加:
module load miniconda/3
conda init bash
conda activate <env_name>
运行脚本
快好了!现在你只需指定从哪个路径运行脚本,并运行脚本。我们用以下命令实现:
# Enter the path where your script is
cd <path_where_your_script_is>
# Run the script
python <name_of_the_script_you_want_to_run>
到目前为止,我们的.sh脚本总结
#!/bin/bash
# Name of the job:
#SBATCH -J <job_name>
#SBATCH -A <account_name>
#SBATCH --no-requeue
#SBATCH -p <partition_name>
#SBATCH --nodes=1
#SBATCH --ntasks=1
##SBATCH --exclusive
#SBATCH --gres=gpu:1
#SBATCH --mail-user=<your_email_adress>
#SBATCH --mail-type=END
#SBATCH --time=04:00:00
#source ~/.bashrc
echo '------------------'
date
echo '------------------'
module load miniconda/3
conda init bash
conda activate <env_name>
cd <path_where_the_script_to_execute_is>
# Use the FOLD variable here
python <name_of_script_to_execute>
echo '------------------'
date
echo '------------------'
以我们的鸟类分类器为例,它可能会是这样的:
#!/bin/bash
# Name of the job:
#SBATCH -J bird_classifier_job
#SBATCH -A <account_name>
#SBATCH --no-requeue
#SBATCH -p <partition_name>
#SBATCH --nodes=1
#SBATCH --ntasks=1
##SBATCH --exclusive
#SBATCH --gres=gpu:1
#SBATCH --mail-user=<your_email_adress>
#SBATCH --mail-type=END
#SBATCH --time=04:00:00
#source ~/.bashrc
echo '------------------'
date
echo '------------------'
module load miniconda/3
conda init bash
conda activate bird_classifier_env
cd path_to_train_bird_classifier_directory
# Use the FOLD variable here
python train_bird_classifier.py
echo '------------------'
date
echo '------------------'
2. 提交你的作业
当你完成脚本编写后,你已经准备好将作业提交到队列中。
使用sbatch(代表提交批处理)命令提交脚本:
$ sbatch train_classifier.sh
就这些!你的作业已经提交到队列中,如果没有错误,它将被执行!
3. 监控你的作业
一旦你提交了作业,它不会立即执行,而是首先被添加到队列中。你可以监控你提交的作业的几件事情(它在队列中的位置、发送了多少作业、它已经执行了多长时间)。
你可以使用squeue功能来监控此情况:
$ squeue
你还可以使用scontrol show job请求特定作业的详细信息。
$ scontrol show job [JOB_ID]
如果你提交了多个具有相同名称的作业(如果你提交了多次稍有变化的相同作业,这可能会发生),你可以使用grep。例如,如果你想在squeue输出中搜索所有名称为“my_job”的作业:
$ squeue | grep "my_job"
4. 取消作业
如果需要取消作业,请在 Shell 中使用scancel命令,然后跟上作业 ID(可以使用squeue获取):
$ scancel [JOB_ID]
请注意,如果你提交了作业,然后在作业开始执行前修改了源代码,它将在执行时使用最新版本的脚本,而不是提交作业时的版本。
结论
将 SLURM 作业提交到集群可能一开始会让人觉得令人望而却步,但一旦你掌握了它,它将成为 HPC 体验的重要组成部分。
始终记住尊重集群的指南和资源限制,保持耐心,并继续学习以优化你的工作流程。
希望这份指南对你有所帮助!
感谢阅读!在你离开之前:
-
确保你已经查看了Google colab 上的笔记本。
-
查看我在 GitHub 上的AI 教程汇编。
[## GitHub - FrancoisPorcher/awesome-ai-tutorials: 最佳 AI 教程汇编,助你成为数据科学大师…
最佳 AI 教程汇编,助你成为数据科学大师! - GitHub …
你应该将我的文章订阅到你的收件箱里。 在这里订阅。
如果你想访问 Medium 上的高级文章,你只需每月$5 的会员费。如果你通过 我的链接注册,你将以没有额外费用的方式支持我。
如果你觉得这篇文章有启发性且对你有帮助,请考虑关注我以获取更多深入内容!你的支持帮助我继续制作有助于我们集体理解的内容。
参考文献:
-
Yoo, A. B., Jette, M. A., & Grondona, M. (2003). SLURM: 简单的 Linux 资源管理工具。并行处理作业调度策略(第 44–60 页)。Springer,柏林,海德堡。 论文链接(如在线提供)
-
SLURM 工作负载管理器 — 官方文档。 SLURM 文档
-
Oak Ridge National Laboratory. (年份). SLURM 培训材料。从培训材料链接检索。
如何将表格时间序列数据发送到 Apache Kafka,使用 Python 和 Pandas
现在学习如何使用在线零售交易的示例日志在 Kafka 中生成和消费数据
·
关注 发布于 Towards Data Science ·15 分钟阅读·2023 年 1 月 24 日
--
图片来源于 Tech Daily 在 Unsplash
时间序列数据有各种形式,通常以高频率生成,例如传感器数据和交易日志。它也以巨大的数据量产生,其中记录是以毫秒而非小时或天来分隔的。
但什么样的系统可以处理如此持续的数据流?一种较旧的方法是将原始数据转储到数据湖中,并通过长期运行的过程进行大批量处理。如今,许多公司更喜欢实时处理原始数据,并将汇总结果写入数据库。
例如,在线零售商可以通过产品和日期不断汇总交易数据,而不是按需运行昂贵的数据库查询。但这在实际中是如何运作的?让我们找出答案吧!
在本教程中,我们将使用 Python 和 Apache Kafka 来处理来自真实在线零售商的大量时间序列数据。
你将学到的内容
到本教程结束时,你将了解:
-
初创公司和在线业务为何使用 Apache Kafka
-
时间序列数据的独特性质以及它如何与 Kafka 协作
-
如何在本地机器上安装和运行 Kafka
-
如何使用 Python 和 Pandas 库批量发送时间序列数据到 Kafka
你应该已经了解的内容
本文针对数据科学家和工程师,因此我假设你具备以下条件:
-
你熟悉 Python 并且使用过 Pandas 库,或者至少知道它在数据科学中的用途。
-
你听说过 Apache Kafka,并大致了解它的用途。
但如果你不符合这些标准,也不必担心。这个教程足够简单易懂,我们会简要解释这些技术。请注意,它不适合绝对初学者。
为什么使用 Apache Kafka 处理时间序列数据?
Apache Kafka 是实时数据处理的行业标准。你可以用它来处理大量的数据流(只要你有足够的计算资源)。
例如,一些一级方程式赛车队将 Kafka 与 Kubernetes 结合使用,以处理每毫秒传入的大量传感器数据。这些数据被实时分析,以预测比赛结果,并为车队提供驾驶员的洞察。
除了处理数据流的能力外,还有其他关键原因说明在线业务可能选择使用 Apache Kafka:
-
可扩展性:Kafka 设计用于处理高数据量和低延迟,非常适合那些预期快速增长并需要一个可以随之扩展的解决方案的初创公司。
-
解耦:你听说过 事件驱动架构 吗?Kafka 是一种经常被用来促进这种模式的工具。它允许系统的解耦,这意味着架构的不同部分可以独立开发和部署。这对于快速迭代并需要能够在不影响其他架构部分的情况下修改系统的初创公司尤为有用。
-
耐久性:Kafka 会将所有发布的消息存储在可配置的时间内,这意味着它可以作为系统中所有数据的持久日志。这对于需要保持所有数据记录以满足审计或合规要求的初创公司非常有用。
-
广泛采用:Apache Kafka 在行业中得到了广泛的采用,这意味着它拥有庞大的用户基础和强大的工具及资源生态系统。这对于希望利用 Kafka 社区集体经验的初创公司来说非常有帮助。
这一点尤其重要,因为 Apache Kafka 有着 notoriously steep 的学习曲线。没有大量的教程和演示,许多初学者将难以使其正常运行。
为什么要在 Apache Kafka 中使用 Python?
因为 Python 是数据和机器学习社区中最受欢迎的语言。这些社区可以从 Apache Kafka 中获益良多,但目前还没有足够吸引他们技能水平的 Kafka 教程。
如果你是数据团队的一部分,你更有可能知道 Python 和 Pandas,而不是 Java。然而,大多数旧版 Kafka 教程都是为使用 Java 的软件工程师编写的。这是因为软件工程师传统上构建了与 Kafka 交互的组件(而 Kafka 本身是用 Java/Scala 编写的)。
然而,Kafka 的用户基础正在发生变化。软件和数据团队的职责开始趋于融合。这是由于数据在现代组织中的重要性日益增长,以及数据管理和处理任务的复杂性增加。今天,数据专业人员也会参与与 Kafka 交互的软件组件的开发,但他们仍然会遇到很多阻力,因为他们通常不熟悉 Java 生态系统中的技术。这就是为什么我们在这些示例中使用 Python 和 Pandas 的原因。
好了,前言部分就到这里——让我们进入教程。
本教程的先决条件
你需要的第一件事是时间——大约 30 分钟(在你安装了所需的软件后)。
说到这一点,请确保在继续之前已经安装了以下软件。
软件
-
Windows 或基于 Unix 的操作系统 我们将提供 Windows 和基于 Unix 的操作系统(如 macOS 和 Ubuntu)的命令。
-
Python 3.0+ 和所需的库
—** 你可以从 Python 下载页面 下载安装程序。
— 可选地,你可能想要创建一个 虚拟环境 用于本教程,以避免依赖冲突。
**所需库
—** Pandas: 使用
pip3 install pandas安装— Kafka-python: 使用
pip3 install kafka-python安装 -
Java 8+ 这是 Apache Kafka 的前提条件。要安装它,请选择以下之一:
—Ubuntu:使用
sudo apt install default-jre安装— macOS:使用
brew install java安装- 如果你使用的是带有 M1 芯片的 Macbook Pro,你可能需要按照本指南中概述的步骤安装 Java:“如何在 Apple Mac M1 Pro 上设置 Java (Dev.to)” — 这大约需要 2 分钟。
— Windows:下载并运行 Windows 的 Java 可执行文件
-
Apache Kafka 你可以从 Apache Kafka 下载页面 下载 Apache Kafka 二进制文件。
将文件内容解压到一个方便的位置。例如,你可以为本教程创建一个项目文件夹并将其解压到那里。
主要步骤
在深入细节之前,让我们回顾一下我们将要覆盖的主要步骤。
-
设置 Kafka: 我们首先将熟悉 Kafka 的命令行工具,并使用它们来:
— 启动 Zookeeper 和 Kafka 服务器
— 创建一个主题(我们将向其发送数据)
-
数据分析: 使用 Python 和 Pandas 来:
— 仔细查看在线零售数据集的结构。
— 将其读入数据框并理解不同的数据类型。
我们还将探讨使用数据框和时间序列数据与 Kafka 的好处。
-
使用生产者向 Kafka 发送数据: 使用 kafka-python 库来:
— 将 csv 文件读取到数据框中并初始化 Kafka python 生产者。
— 遍历行并将其批量发送到 Kafka
-
从 Kafka 中读取数据: 我们将再次使用 kafka-python 库来:
— 从我们的 Kafka 主题中读取消息
— 将批处理消息转换回数据框。
— 对其进行一些简单的聚合操作。
你会在 ‘tabular-timeseries-kafka’ 子文件夹中找到代码。如果你想直接跳到代码部分,可以使用以下命令克隆我们的教程仓库:
git clone github.com/quixai/tuto...
设置 Apache Kafka
如果你还没这样做,从 Apache Kafka 下载页面 下载 Apache Kafka(例如,“kafka_2.12–3.3.1.tgz”)并将文件内容解压到一个方便的位置。
在你能对 Kafka 进行任何操作之前,你需要启动核心服务。每个服务都需要在单独的终端窗口中运行。这两个服务是:
-
Zookeeper 服务负责管理所有不同服务、代理和客户端之间的协调,这些组成了 Kafka 集群。
-
Kafka 服务器服务运行 Apache Kafka 的核心功能,如消息代理。
按照这些说明操作时,请在你解压 Kafka 的目录中启动每个终端窗口(例如,“*C:\Users\demo\Downloads\kafka_2.13–3.3.1*”)。
要启动核心 Kafka 服务,请按照以下步骤操作:
1. 在 Kafka 目录中,打开一个终端窗口,并使用以下命令启动 zookeeper 服务:
- Linux / macOS
bin/zookeeper-server-start.sh config/zookeeper.properties
- Windows
bin\windows\zookeeper-server-start.bat .\config\zookeeper.properties
-
你应该会看到一堆日志消息,表明 zookeeper 启动成功。
保持窗口打开。
2. 打开第二个终端窗口,并使用以下命令启动 Kafka 服务器:
- Linux / macOS
bin/kafka-server-start.sh config/server.properties
- Windows
bin\windows\kafka-server-start.bat .\config\server.properties
-
再次,你应该会看到类似的日志消息,表明服务器启动成功。
也保持这个窗口打开。
3. 接下来,你需要创建一个名为“transactions”的主题来存储数据。
- 如果“主题”这个术语不太熟悉,可以把它看作是一个不断更新日志文件的过程。有很多方式来 详细解释主题的作用,但目前可以简单理解为,它是一个记录特定类型数据(如:传入的交易)及数据本身的事件日志文件。
要在 Kafka 中创建一个主题:
打开第三个终端窗口并输入以下命令:
- Linux / macOS
bin/kafka-topics.sh --create --topic transactions --bootstrap-server localhost:9092
- Windows
bin\windows\kafka-topics.bat --create --topic transactions --bootstrap-server localhost:9092
- 你应该会看到确认消息 “创建了主题 transactions”。
Kafka 部分就这些了。希望你已经顺利设置好了。如果遇到任何问题,可能会有帮助的 故障排除指南。现在,让我们进入激动人心的部分。
分析数据
对于本练习,我们将使用一个跨国数据集,该数据集包含了 2010 年 01 月 12 日至 2011 年 09 月 12 日之间发生的所有交易,数据来自于一个位于英国的在线零售商。该数据集来源于加州大学主办的机器学习库。你可以在 UCI 机器学习库网站的 专页 上找到有关数据集的更多详细信息。
准备工作,按以下步骤操作:
-
为此项目创建一个文件夹(例如,“tabular-timeseries-kafka”)。
-
从 这个存储位置 下载压缩的 CSV 文件,并将其解压到项目文件夹中。
我们将提供 Python 命令作为说明,但你也可以使用 Pycharm Community Edition 等 IDE。
第一个任务是检查文件中的数据,并查看 Pandas 默认如何解释这些数据:
要检查数据,请按照以下步骤操作:
1. 在你的项目目录中打开一个终端窗口并输入 python 启动 Python 控制台。
2. 通过输入以下命令将 CSV 读入 DataFrame:
>>> import pandas as pd
>>> df = pd.read_csv("online_retail_II.csv", encoding="unicode_escape")
>>> print(df.info())
3. 检查 df.info 的输出:
RangeIndex: 1067371 entries, 0 to 1067370
Data columns (total 8 columns):
# Column Non-Null Count Dtype
- - - - - - - - - - - - - - -
0 Invoice 1067371 non-null object
1 StockCode 1067371 non-null object
2 Description 1062989 non-null object
3 Quantity 1067371 non-null int64
4 InvoiceDate 1067371 non-null object
5 Price 1067371 non-null float64
6 Customer ID 824364 non-null float64
7 Country 1067371 non-null object
dtypes: datetime64ns, float64(2), int64(1), object(4)
memory usage: 65.1+ MB
请注意,InvoiceDate 列已被读取为对象数据类型。
在 Pandas 中,对象数据类型用于表示字符串值或无法轻易转换为数值数据类型的数据。
为了使数据集成为真正的时间序列数据集,我们需要将记录创建的时间(在此例中为 InvoiceDate)转为合适的 DateTime 格式。我们稍后会解释原因。
将 InvoiceDate 列转换为日期格式:
- 在 Python 控制台中,输入以下命令:
>>> df['InvoiceDate'] = pd.to_datetime(df['InvoiceDate'])
>>> df.set_index('InvoiceDate', inplace=True)
-
这种转换允许你利用 pandas 的广泛 时间序列功能。
-
例如,将其设置为 DatetimeIndex 可以帮助你优化时间序列数据,包括预计算和缓存的日期范围;快速且轻松地选择日期、日期范围及其相关值;以及使用时间块(“年”、“月”)进行快速汇总。
Kafka 可以对时间序列数据做什么
所以我们知道 Pandas 有许多与时间序列相关的功能,但 Kafka 呢?确实,Kafka 也提供了许多功能来优化时间序列数据的处理。这些包括:
-
压缩:Kafka 支持多种压缩算法,这些算法可以利用时间数据来减少数据流的大小,提高数据处理效率。
-
数据分区:Kafka 允许你按键和时间对数据流进行分区,这对于将数据处理分布到多个消费者实例中很有用。
-
自定义序列化:Kafka 提供了一个可插拔的序列化框架,允许你为数据指定自定义序列化器和反序列化器,这对于优化数据处理性能可能非常有用。
所有这些功能部分依赖于时间的正确格式。
请注意,在这个基础教程中我们不会使用这些功能。但是,如果你打算在生产环境中使用 Kafka,了解时间数据在与 Kafka 交互时所扮演的角色是很重要的。
使用 DataFrames 与 Kafka 的优点
Pandas DataFrames 在以表格格式存储数据时特别有用,因为数据集中的每个特征都可以作为一维形状或 Series 进行操作,同时仍然形成一个多维数据集。DataFrames 还配备了一些方便的内置函数,允许你快速操作和处理数据。这在使用 Kafka 时非常宝贵。
例如,在下一主要步骤中,我们将以批量的形式发送数据。我们将记录分批处理,以模拟生产场景,你可能每秒接收到数百条记录。
如果你为每条单独的记录发送一条消息,你可能会遇到瓶颈或系统故障。这就是为什么你以较低频率发送小批量数据(而不是以高频率发送大量小消息)的原因。
正如你所见,当我们使用 Pandas 分块功能时,这个过程非常简单。这是 Pandas DataFrames 提供的众多 Kafka 友好功能之一。
创建一个 Kafka 生产者来发送数据
现在,让我们将所学内容放入一个合适的 Python 文件中,并开始向 Kafka 发送数据。我们将使用kafka-python库,这是多个用于将 Python 应用程序连接到 Kafka 的库之一(另一个是confluent-kafka-python库)。
要创建一个生产者,请遵循以下步骤:
- 在你的项目目录中,创建一个名为
producer.py的文件,并插入以下代码块:
# Import packages
import pandas as pd
import json
import datetime as dt
from time import sleep
from kafka import KafkaProducer
# Initialize Kafka Producer Client
producer = KafkaProducer(bootstrap_servers=['localhost:9092'])
print(f'Initialized Kafka producer at {dt.datetime.utcnow()}')
这段代码导入了所有必需的库并初始化 Kafka 生产者,告诉它连接到你(希望)仍然在计算机上运行的服务器下的‘localhost’。
2. 接下来,添加一个‘for’循环,它将遍历文件并以批量的形式发送数据:
# Set a basic message counter and define the file path
counter = 0
file = "online_retail_II.csv"
for chunk in pd.read_csv(file,encoding='unicode_escape',chunksize=10):
# For each chunk, convert the invoice date into the correct time format
chunk["InvoiceDate"] = pd.to_datetime(chunk["InvoiceDate"])
# Set a counter as the message key
key = str(counter).encode()
# Convert the data frame chunk into a dictionary
chunkd = chunk.to_dict()
# Encode the dictionary into a JSON Byte Array
data = json.dumps(chunkd, default=str).encode('utf-8')
# Send the data to Kafka
producer.send(topic="transactions", key=key, value=data)
# Sleep to simulate a real-world interval
sleep(0.5)
# Increment the message counter for the message key
counter = counter + 1
print(f'Sent record to topic at time {dt.datetime.utcnow()}')
代码包括解释性的注释,但基本上它做了以下几件事:
-
按每 10 行批量读取 CSV 文件
-
将每个批次序列化为 JSON 并将 JSON 编码为字节数组
-
将 JSON 作为消息发送到 Kafka 主题“transactions”
你可以在我们的 Github 仓库中查看完整的文件。 github.com/quixai/tutorials/timeseries/producer.py
3. 保存你的文件并使用以下命令运行代码:
python producer.py
-
在你的终端窗口中,你应该开始看到类似这样的确认信息:
“将记录发送到主题的时间是 2022–12–28 13:23:52.125664”为每条发送的消息。
-
如果你收到缺少模块的错误,请确保你已安装 kafka-python 库(
pip3 install kafka-python)。
一旦数据进入 Kafka 主题,它可以被多个消费者读取,并提取用于更多下游处理。
创建一个消费者来读取这些消息并对其进行基本操作。
创建一个 Kafka 消费者来读取数据
创建 Kafka 消费者的过程与前一步非常类似。在这种情况下,我们将读取每个批量的消息,并将其转换回 DataFrame。
我们将假设这个消费者是用于某种库存分析管道,它只关心每个库存项目的总销售额。因此,在数据中,我们只查看 StockCode、销售数量和价格。我们将计算每条记录的销售总值,以便按 StockCode 汇总销售数据。
- 在你的项目目录中,创建一个名为
consumer.py的文件,并插入以下代码块以初始化消费者。
from kafka import KafkaConsumer
import json
import pandas as pd
# Consume all the messages from the topic but do not mark them as 'read' (enable_auto_commit=False)
# so that we can re-read them as often as we like.
consumer = KafkaConsumer('transactions',
group_id='test-consumer-group',
bootstrap_servers=['localhost:9092'],
value_deserializer=lambda m: json.loads(m.decode('utf-8')),
auto_offset_reset='earliest',
enable_auto_commit=False)
-
我们正在用比我们为生产者设置的更多选项来初始化消费者。
-
首先,我们告诉它从哪个主题读取,然后 Kafka 服务器运行在哪里,最后,我们提供了一个 lambda 函数用于将消息值反序列化回 Python 字典。
- 接下来,添加‘for’循环,它将迭代消息并对其进行一些处理。
for message in consumer:
mframe = pd.DataFrame(message.value)
# Multiply the quantity by the price and store in a new "revenue" column
mframe['revenue'] = mframe.apply(lambda x: x['Quantity'] * x['Price'], axis=1)
# Aggregate the StockCodes in the individual batch by revenue
summary = mframe.groupby('StockCode')['revenue'].sum()
print(summary)
-
正如你在代码注释中看到的,我们正在进行一个简单的计算,输出按
StockCode的每个消息批次的收入汇总。 -
当然,最终目标是按
StockCode保持总体收入的持续统计。这将需要一些进一步的处理,将汇总数据写入数据库,从而为某种仪表盘提供数据。 -
然而,为了本教程的目的,这里是一个很好的停止点。如果一切正常,你应该能看到每条消息的汇总结果被记录下来。它应该类似于这样:
Name: revenue, dtype: float64
StockCode
16161P 10.50
16169N 10.50
21491 11.70
22065 17.40
22138 44.55
22139 44.55
22352 30.60
85014A 17.85
85014B 17.85
85183B 144.00
如果你看到记录的汇总,做得好!祝贺你完成了所有步骤。
总结一下
如果你只是把它做在本地机器上,这个过程可能显得平凡无奇,但当你考虑到这种模式如何扩展时,它会变得更加有趣。让我们稍微回顾一下你做了什么——你完成了两个关键任务:
1— 你生成了高频率的消息流,并将其流入一个 Kafka 主题
在这种情况下,你是在“重放”客户交易的历史日志,并人为地将每条消息延迟半秒。
它如何扩展:
-
在生产环境中,可能会有某种前端应用程序生成流数据,我们需要做一些额外的路由工作将其放入主题中(因为前端应用程序和 Kafka 集群将位于不同的服务器上)。
-
消息也可能以不规则的频率到达,而不是标准的半秒,并且序列化可能会以某种方式进行优化。
2 — 你从 Kafka 主题中消费了高频率的消息流,并对数据进行了汇总处理
对于这个教程,你只是通过同一台机器进行数据流处理和消费,这可能看起来没什么特别的——但实际上,会有许多不同机器的消费者。
它如何扩展:
-
你可以在不同的服务器上运行一整套应用程序,每个应用程序以不同的方式消费流数据。
-
一个消费者可能是一个欺诈检测应用程序,它会深入交易历史,寻找可疑的交易模式。
-
另一个消费者可能是一个订单履行管道,它只关心最新的未完成订单。它会读取新消息,并将订单发送去处理。
-
另一个消费者可能是一个数据聚合管道,它将数据与 CRM 中的数据结合,并将其放入数据仓库中供市场营销团队分析。
如你所见,当你使用 Apache Kafka 来利用高频时间序列数据时,它可以变得极其强大。表格时间序列数据在许多应用中都很常见,如金融分析、传感器数据分析和社交媒体分析。
按照本教程中概述的步骤,你现在应该具备了将表格时间序列数据发送到 Apache Kafka 并利用其能力进行实时数据处理和分析的坚实基础。无论你是在进行小规模还是大规模项目,Apache Kafka 都是你工具包中的一个重要工具,希望我们能让你更接近掌握它的终极目标。
-
你可以在我们的 教程 GitHub 仓库中找到本教程及其他教程的源代码。
-
数据来源:Daqing Chen, Sai Liang Sain, 和 Kun Guo, 数据挖掘在线零售行业:“基于 RFM 模型的客户细分案例研究”,《数据库营销与客户策略管理期刊》,第 19 卷,2012 年(在线出版日期:2012 年 8 月 27 日。doi: 10.1057/dbm.2012.17)
关于作者
Tomáš Neubauer 是 Quix 的首席技术官——一个实时数据工程堆栈,帮助工程师管理越来越庞大和快速的数据流。它使数据和机器学习团队更容易访问流数据,这些团队喜欢用 Python 进行工作。
在业余时间,Tomáš 喜欢在布拉格周围的山丘中骑山地自行车,他还喜欢品尝捷克提供的最优质啤酒。如果你对教程有任何问题,可以联系他 @Tomas Neubauer,或在 The Stream——一个针对实时数据爱好者的开放社区。
本文最初发表于: https://www.quix.io/blog/send-timeseries-data-to-kafka-python
如何使用 Keras API 和 Google Colab 开始使用 TensorFlow
逐步教程,使用神经网络分析人类活动
·
关注 发布于 Towards Data Science ·11 分钟阅读·2023 年 1 月 19 日
--
图片由 Serghei Trofimov 提供,来源于 Unsplash
本初学者教程旨在简要概述 TensorFlow 的基本知识,并指导你完成一个实践项目。如果你符合以下条件,本教程可能对你有价值:
-
你已经建立了第一个传统的机器学习模型,现在你对如何开始使用第一个 TensorFlow 模型感到好奇。
-
你已经探讨了 TensorFlow 的基本概念,但正在寻找一个实际挑战来提升你的技能。
如果你对数据科学或机器学习模型完全陌生,我建议你先集中关注其他教程,因为首先具备基本的理解是至关重要的。
本文结构分为三个主要部分:
1 TensorFlow 和 Keras API 的简要介绍
2 使用 Google Colab 设置 TensorFlow 的教程(免费)
3 实践项目:人体活动分类
#1 TensorFlow 和 Keras API 的简要介绍
如果你对 TensorFlow 完全陌生,我推荐以下视频,其中主要概念和不同的层结构以简短但有效的方式进行了解释。这不是一门全面的讲座,但它对主题本身提供了一个很好的介绍。
虽然 TensorFlow 是基础的机器学习平台,但 Keras 另一方面是一个 API,可以帮助你快速设置模型,并减少手动编码工作。
Keras 是一个用 Python 编写的深度学习 API,运行在机器学习平台 TensorFlow 之上。它的开发重点是实现快速实验。能够尽快从想法到结果是做出好研究的关键。[
keras.io/about]
开发团队表示 Keras 是:
-
简单 — 但不简单化。Keras 减少了开发者的认知负担,让你可以专注于问题中重要的部分。
-
灵活 — Keras 采用了逐步披露复杂性的原则:简单的工作流程应该快速且简单,而任意高级的工作流程应该可能通过一个清晰的路径构建在你已经学到的东西上。
-
强大 — Keras 提供了行业级的性能和可扩展性:包括 NASA、YouTube 和 Waymo 在内的组织和公司都在使用它。
[再次来源于 https://keras.io/about]
#2 使用 Google Colab 设置 TensorFlow 的教程(免费)
使用 TensorFlow 的一个好建议是将其运行在图形处理单元(GPU)或张量处理单元(TPU)上,而不是普通的中央处理单元(CPU)加速器。虽然简单的模型和计算可能仍能在 CPU 上运行,但你可能会注意到,TensorFlow 的全部功能只能在图形硬件上充分发挥。
使用 GPU 最简单直接的方法是使用 Google Colaboratory (“Colab”),这有点像“一个 无需设置且完全在云端运行的免费 Jupyter notebook 环境。”虽然这个教程更多地谈论了 Colab 的简单性和优势,但也有缺点,如 GPU 小时限制和计算能力降低,与真正的云环境相比。然而,我相信 Colab 可能不是一个不好的服务,可以用来迈出 TensorFlow 的第一步。
要在 Colab 中为 TensorFlow 设置基本环境,你可以按照以下几个步骤进行:
-
打开
colab.research.google.com/并注册一个免费账户 -
在 Colab 中创建一个新的notebook
-
从菜单中选择Runtime并更改运行时类型
-
从硬件加速器选项中选择GPU - 点击保存

这张截图由作者从Google Colab拍摄
现在,你可以导入 TensorFlow,并使用以下几行代码检查一切是否设置妥当:
现在你应该能看到输出中显示一个版本(例如“2.5.0”)以及一个指示 GPU 使用的物理设备消息。你的 Notebook 现在可以实际使用 TensorFlow 了!
#3 实践项目:人类活动分类
以下教程旨在分析一个关于人类活动分类的数据集。该数据集由携带不同活动的人的智能手机惯性传感器记录的时间序列组成。
背景与数据集信息
了解简单的智能手机测量是否可以用来分类人类正在进行的身体活动是很有趣的。六种活动类别如下:
-
走路
-
上楼
-
下楼
-
坐着
-
站立
-
躺着
描述测量和不同活动的短视频可以在这里找到:
这种基于传感器的分类模型的结果可以被健康应用程序或其他智能手机应用程序使用,以改善用户体验、根据当前记录建议活动或跟踪一天中的身体活动。
给定的数据集包含来自智能手机惯性传感器的测量,这些智能手机由执行不同活动的人携带。数据集总共有 10,299 条记录,分为 70%训练数据(7,352 条记录)和 30%测试数据(2,947 条记录)。传感器信号(加速度计和陀螺仪)经过噪声过滤器预处理,然后在固定宽度滑动窗口中采样。每个信号记录了三个坐标(x,y,z),可以视为具有 128 个时间戳及其对应值的时间序列记录。目标列包含活动标签:WALKING, WALKING_UPSTAIRS, WALKING_DOWNSTAIRS, SITTING, STANDING, LAYING。
训练和测试数据集的所有特征都是数字(浮点数),经过归一化处理,并限制在-1 和 1 之间。图 1 中展示了沿 128 个时间戳的两个示例测量。显示出每个人类活动在三个坐标和时间上具有其特征。因此,有可能识别时间序列中的模式和趋势,这些模式和趋势指示活动类别。
关于数据集和背景的更多信息可以在 readme 文件中找到,或在此网站上。
步骤 01:初始数据加载和所需库
要开始项目,需要将数据集加载到 Colab 环境中。不要过于担心下面的代码,它只是将所有必需的文件移动到你的工作空间:
不出所料,我们需要导入一些必需的库来简化工作。其中一个重要的库是 当然 TensorFlow 本身:
为了简化我们的教程,我们将仅使用来自惯性信号的可用身体数据。数据集已经分为训练集(7352 条数据)和测试集(2947 条数据),包含 128 个时间序列测量和 3 个坐标。为了获得格式为 (n, 128, 3) 的数据集,我们可以使用以下代码:
准备的最后一个重要步骤是将目标变量转换为 独热编码 测量。我们不再有表示类别的数值(例如 0 = WALKING),而是得到包含每个可用类别概率的数组(例如 [1, 0, 0, 0, 0, 0] = WALKING 或 [0, 0, 0, 1, 0, 0] = SITTING),在这种情况下,某个类别的概率为 100%。这很重要,因为 TensorFlow 会计算每个可能类别的概率。因此,我们需要相应地准备训练数据。Keras API 提供了一个简单的内置函数来满足这一要求。你会注意到,目标变量的形状从 (n,) 变为 (n, 6):
步骤 02:绘制一些示例时间序列
在本教程中,我们将忽略适当的探索性分析部分,因为我们想更多地关注 TensorFlow 的使用。然而,在这个阶段绘制一些示例时间序列可能是有意义的,因为这将帮助我们更好地理解我们希望用于分类的数据。
我们现在可以使用以下代码来绘制数据集中的一些随机测量。我选择了测量 992(走路)和 2481(躺着)来展示数据中的差异:
输出如下图所示。你可能已经可以识别出人身体测量中的一些差异,取决于所进行的活动。最后,我们希望并有机会在数据上运行一个神经网络模型,该模型可能很好地预测我们的活动类别。让我们看看吧!

截图由作者从 Google Colab 获取。

截图由作者从 Google Colab 获取。
步骤 03:使用 Keras API 构建和训练神经网络
要在 TensorFlow 中构建和训练神经网络,你需要将可用的训练数据拆分为训练和验证子集。然后,TensorFlow 将使用这两部分来训练模型,并使用验证损失和验证准确度来评估进展。你可以调整准确度数据集的大小,但我在这种情况下使用了原始训练数据的 20%。可以设置随机状态以在不同时间重现可识别的数据拆分。
监控模型在训练过程中的最佳实践是设置TensorBoard。你可以使用以下代码准备你的笔记本,其中加载扩展并创建一个日志写入器:
要设置神经网络,你首先需要决定你想构建哪种类型的网络。最简单的架构是一个线性层堆栈,称为顺序模型。你可以通过将层实例的列表传递给构造函数来创建顺序模型。开始时,你可以初始化你的模型:
model = tf.keras.Sequential()
从这一点开始,你可以根据需要添加任何你想使用的层。网上有很多优秀的教程描述了这些层的不同功能。在本教程中,我们将仅引导你完成一个简单的工作设置,以便你成功运行第一个模型。随意添加和修改架构以提高我的准确率!
首先,我们添加输入层,其维度为我们的数据集(128,3):
model.add(tf.keras.layers.InputLayer(input_shape=(128,3)))
其次,我们添加一个一维卷积层,在这里我们可以设置滤波器数量和卷积核大小。该层之后将跟随一个批量归一化层,它将转换输入,使其标准化,即均值为零,标准差为一。
model.add(tf.keras.layers.Conv1D(filters=256, kernel_size=10))model.add(tf.keras.layers.BatchNormalization())
其次,我们添加一个ReLu 层和一个全局平均池化层。最后,我们需要一个Dense 层,将我们的网络激活为六类输出。由于这是一个分类问题,我们使用softmax 激活函数,并设置六个单元(因为我们有六个类别)。
model.add(tf.keras.layers.ReLU())model.add(tf.keras.layers.GlobalAveragePooling1D())model.add(tf.keras.layers.Dense(units=6, activation=tf.nn.softmax))
要训练模型,我们首先需要用合适的优化器编译模型。在本教程中,我选择了 Adam 优化器,你可以调整学习率。
model.compile(optimizer=tf.keras.optimizers.Adam(0.001), loss='categorical_crossentropy', metrics=['accuracy'])
要训练模型,我们需要决定运行多少个周期以及每个批次的批量大小。一个周期是指每次通过所有训练集样本时的时间步长。批量大小是每个批次的数据条目数量。为了将我们的模型与 TensorBoard 监控相链接,我们添加一个回调并设置日志目录。
callbacks = [tf.keras.callbacks.TensorBoard(log_dir=logdir)] model.fit(x_train, y_train, epochs=100, batch_size=32, callbacks=callbacks, validation_data=(x_valid, y_valid))
当你运行代码时,你会看到单元格下方的一些输出,这些输出指示每个训练周期的情况。在 TensorBoard 中,你将能够看到训练和验证数据的准确率逐渐提高以及损失逐渐减少。训练完成所有周期后,它将自动停止。以下是模型拟合的完整代码:
现在让我们尝试理解我们所构建的模型以及在训练过程中模型如何发展。正如下面的截图所示,在前 20 到 30 个训练周期中有显著的提升,之后的提升则较为温和。蓝线表示训练数据集的准确性和损失,红线表示验证数据集。

截图由作者从Google Colab中获取

截图由作者从Google Colab中获取
总体而言,我们做得还不错,达到了 85.38%的验证准确性。训练损失和验证损失之间的可比发展表明训练过程没有过拟合。然而,仍有进一步改进模型的机会。请随意自行探索。以下是一些想法:
-
尝试不同的训练周期、批量大小和学习率
-
修改 Conv1D 层中的滤波器数量和内核大小
-
添加更多层并尝试不同的架构
-
将其他数据集添加到模型中(除了仅有的身体数据)
最后,评估模型在测试数据集上的准确性始终是最后一步。到目前为止,我们只使用训练数据和验证数据训练了模型。要将最终模型应用于测试数据集,可以使用下面的代码。我们使用模型计算每个类别的概率。之后,我们取每行的最大概率,从而识别该行的离散类别。
我的方法中测试准确性为 84.42%,因此稍低于验证准确性,但与训练准确性处于相似水平。
恭喜你:你使用 TensorFlow 应用了第一个神经网络!
请注意,你将会获得不同的准确性和损失值,因为 TensorFlow 不能以相同的方式重现。然而,我预期你的值会在 75%到 90%之间。
总结
上述教程的目的是为你提供一个实际的入门思路,开始使用 TensorFlow 和 Keras API。我知道这远不够详细或对特性的全面描述。然而,我希望这能帮助一些初学者独立运行他们的第一个模型,并理解 TensorFlow 周围的基本组成部分。
当然,还有更多的内容等待探索。试着超越我们最初的准确性成就,并在评论中分享你的成果!
阅读 Jonas Dieckmann 在 Medium 上的文章。他是菲利普斯的分析经理和产品负责人,对…充满热情并撰写文章。
希望你觉得有用。告诉我你的想法!也欢迎在 LinkedIn 上与我联系 www.linkedin.com/in/jonas-dieckmann/ 或在 Medium 上关注我。
另见我其他的一些文章:
供应链材料规划中的机器学习模型开发
towardsdatascience.com ## ICA 简介:独立成分分析
你是否曾经遇到过需要分析一个复杂且高度相关的数据集的情况……
towardsdatascience.com
参考文献
Reyes-Ortiz, Jorge, Anguita, Davide, Ghio, Alessandro, Oneto, Luca & Parra, Xavier. (2012). 使用智能手机进行人类活动识别。UCI 机器学习库
本数据集使用了知识共享署名 4.0 国际(CC BY 4.0)许可证。这允许出于任何目的共享和改编数据集,前提是给予适当的署名。
欲了解更多信息,请访问这个网站。
如何使用 AWS Lambda 为数据科学设置一个简单的 ETL 流水线
如何使用 AWS Lambda 设置一个简单的 ETL 流水线,该流水线可以通过 API 端点或计划触发,并将结果写入 S3 桶中进行摄取
·发表于 Towards Data Science ·10 分钟阅读·2023 年 2 月 4 日
--

AWS Lambda 的 ETL 简介
当你需要构建 ETL 流水线时,有许多选择。你可以使用像 Astronomer 或 Prefect 这样的工具进行编排,但你还需要有地方来运行计算。对此,你有几个选择:
-
类似 AWS EC2 的虚拟机(VM)
-
类似 AWS ECS 或 AWS Fargate 的容器服务
-
类似 AWS EMR(弹性映射减少)的 Apache Spark
-
无服务器计算,如 AWS Lambda
每种选择都有其优点。如果你追求设置、维护和成本上的简便,可以使用AWS Lambda或无服务器计算来运行简单的任务。
注意,我说的是简单。AWS Lambda 不适合计算密集型或长时间运行的任务。它们适合执行耗时几分钟而非几小时的小量代码。
什么是 AWS Lambda 和无服务器计算?
AWS 中的 Lambda 函数是一段响应事件的代码。事件可以是对 API 端点的请求、文件上传到 S3 桶中,或计划事件。代码被执行,结果被返回。以下是 AWS 对其工作原理的很好的描述:
Lambda 仅在需要时运行你的函数,并自动扩展,从每天几次请求到每秒几千次。你只需为你消耗的计算时间付费——当你的代码没有运行时不会收费。更多信息,请参见 AWS Lambda 定价。
Lambda 函数是一种很好的方式,用于考虑需要频繁运行的小型 ETL 作业。例如在触发时,如 API 调用,或按照计划每晚运行。它还允许你协调多个 Lambda 函数,以创建更复杂的 ETL 管道。
让我们深入创建第一个 Lambda 函数。
创建你的 Lambda 函数
从 AWS 控制台中,导航到 Lambda 服务。点击创建函数按钮开始。系统会提示你选择一个蓝图。对于这个例子,我们将选择从头开始创建。给你的名称一个合适的名字,并选择Python 3.9作为运行时。选择你偏好的架构或通常在本地开发的架构;这使得上传与 Lambda 函数兼容的新库更容易。

作者提供的图片
你可以创建一个新角色或选择一个现有的角色。我们将在下一节中讲解。
Lambda 角色
创建 Lambda 函数的一个关键部分是角色。角色允许函数访问其他 AWS 服务。对于这个例子,我们需要授予函数访问Lambda和S3的权限。我还授予了VPC访问权限,但这个设置中并不是必要的。
创建新角色是一个好习惯,这可以帮助你隔离仅需要的权限。如果你要为 ETL 用例创建多个 Lambda 函数,可以考虑更通用的名称,如Lambda-ETL-Role。

作者提供的图片
设置函数的超时时间
接下来是配置函数的超时时间。根据函数执行所需的时间,你可以将其增加到 15 分钟。对于这个例子,我们将其设置为1 分钟。你可以在 CloudWatch 日志中查看是否达到了超时。
点击配置选项卡,在常规配置下,将超时时间设置为 1 分钟。

作者提供的图片
使用参数和密钥扩展
接下来——这并不是 100% 必要的,但当你想确保安全地处理敏感数据而不在代码中暴露它时,这是一个很好的做法。过去,我写过关于如何使用环境变量来在本地实现这一点;然而,在 AWS 中,我们将使用参数和密钥扩展。
参数和密钥扩展允许你将敏感数据存储在 AWS Secrets Manager 中,并在 Lambda 函数中访问这些数据,这是一种存储 API 密钥、数据库凭证等的好方法。你也可以用它来存储非敏感数据,如配置设置。你可以在这里了解更多关于此功能的信息:配置 Secrets Manager
我们将通过添加一个层到我们的 Lambda 函数开始,这样我们就可以访问扩展。从代码标签页,滚动到最底部并点击添加层。选择AWS 层,然后选择AWS-Parameters-and-Secrets-Lambda-Extension-Arm64和该层的最新版本。

接下来,我们需要添加代码来帮助我们访问秘密。我将其添加到一个小函数中,该函数将查找我的API 密钥以获取 The Movie Database (TMDB) 并返回它。你可以在下面看到完整代码。TMDB 是一个获取电影和电视节目信息的绝佳 API,且对于非商业用途免费使用。
如下所示,我们将创建一个headers变量,其中包含 JSON 对象。然后将其传递到我们对secrets_extension_endpoint的 API 调用中。响应将是一个包含秘密字符串的 JSON 对象。我们将解析该字符串并返回 API 密钥。
在我的代码中显示<< your secrets ARN >>的地方,你需要将其替换为你的ARN。你可以在AWS Secrets Manager 控制台中找到它。
注意: 你需要回到并修改你的角色,以允许访问你创建的秘密。你可以在 AWS 文档中找到如何做的说明。请按照“示例读取一个秘密(附加到身份)”部分中的示例操作。
import requests
import json
def get_tmdb_api_key():
headers = {"X-Aws-Parameters-Secrets-Token": os.environ.get("AWS_SESSION_TOKEN")}
secrets_extension_endpoint = (
"http://localhost:"
+ "2773"
+ "/secretsmanager/get?secretId="
+ "<< your secrets arn >>"
)
r = requests.get(secrets_extension_endpoint, headers=headers)
secret = json.loads(r.text)["SecretString"]
secret = json.loads(secret)
TMDB_API_KEY = secret["TMDB_API"]
return TMDB_API_KEY
添加对 Pandas 的支持
接下来,我们的示例以及许多其他 ETL 用例将需要使用 Pandas。我们需要在 Lambda 函数中添加一个层来支持这一点。从代码标签页,滚动到最底部并点击添加层。选择自定义层,然后选择AWSSDKPandas-Python39-Arm64和最新的层版本。
启用此层后,你可以在代码中import pandas 而无需 将包上传到 Lambda 函数。

图片由作者提供
写入 S3 存储桶中的文件
现代数据仓库/平台中一个非常强大的工作流是直接处理 JSON 数据的能力。我们将通过将数据输出到JSON 文件中的S3 存储桶来利用这一点。
我们将创建一个函数,该函数将接收一个DataFrame并将其写入 S3 存储桶中的JSON 文件。我们将使用boto3库来完成这项工作。我们将使用 Pandas 的to_json方法创建一个 JSON 字符串,将其编码为utf-8,然后写入 S3 存储桶。
def write_to_s3(df, type, imdb_id):
# Get JSON for the DataFrame
output = json.loads(df.to_json(orient='records'))
string = str(output)
encoded_string = string.encode("utf-8")
bucket_name = "lambda-tmdb"
file_name = "out.json"
s3_path = "output/" + type + "/" + imdb_id + "-" + type + "-" + file_name
s3 = boto3.resource('s3')
object = s3.Object(bucket_name, s3_path)
object.put(Body=encoded_string)
return "Success"
当我们准备将 DataFrame 输出到 S3 时,我们会调用这个函数,并传入一些参数,例如DataFrame本身、我们要写入的数据类型以及电影或电视节目的IMDB ID。类型在这里是为了方便,这样我们可以使用单个函数写入不同的数据类型,同时为每个电影查找创建一个唯一的文件名。
write_to_s3(df_crew, "crew", imdb_id)
部署您的 Lambda 函数
部署你的函数有多种方法,其中最简单的是使用 AWS 控制台并上传一个 zip 文件。然而,在这里添加一点自动化可以简化你在代码更改时的工作。
第一步是设置 AWS CLI。你可以在 AWS 文档 Getting started with the AWS CLI 中找到如何操作的说明。你需要 创建一个新的 IAM 用户 并赋予其适当的权限以写入 Lambda 服务。
接下来,我写了一个简单的 bash 脚本,可以从本地终端调用,该脚本完成所有所需的工作。一个非常重要的步骤是将你的 Python 库以正确的层级压缩,并与 zip 文件一起打包。脚本展示了更改到 sites-packages 目录并压缩你想保留的库内容的过程。在这里明确命名你想要的库,以免上传不必要的文件。
注意: 这也假设我已经创建了本地虚拟环境并且函数代码在本地。我更喜欢以这种方式构建我的 Lambdas,以便可以在 GitHub 上进行版本控制
# delete the old zip
rm tmdb-deployment-package.zip
# change the directory to the site-packages directory and zip the contents
cd venv/lib/python3.9/site-packages
# Explicitly add the tmdbsimple package folders needed for the lambda function
zip -r ../../../../tmdb-deployment-package.zip tmdbsimple tmdbsimple-2.9.1.dist-info
# change back to the root directory and add the needed python files
cd ../../../../
zip -g tmdb-deployment-package.zip lambda_function.py
# deploy to AWS Lambda
aws lambda update-function-code --function-name lambda-tmdb --zip-file fileb://tmdb-deployment-package.zip
在你的函数成功部署后,你可以返回控制台查看你的代码和提供的包。注意结构。这些包位于根目录的文件夹中,使它们能够像其他包一样被导入,就像你在本地开发时一样。

图片来源:作者
使用 API 端点触发 Lambda 函数
作为我们函数的最终步骤,我们将通过 AWS API Gateway 使用 API 端点触发它,并将函数参数作为查询字符串传递,这是一种极其强大的方式,可以触发特定事件以处理你想要的确切数据。
进入 Configuration 选项卡,点击 Add Trigger 按钮。滚动到 API Gateway 并选择 Create New API 选项。此外,选择 HTTP API 选项,并将安全设置为 Open。

图片来源:作者
端点创建后,你将获得一个可以触发 Lambda 的 URL。我们将传递一个 ID 列表作为查询字符串参数,使我们能够将多个 ID 传递给函数并一次性处理它们。使用查询字符串参数触发你的 API 是处理批量数据的一种非常强大的方式。
我们按如下方式构建 URL:
API Gateway URL + ?ids=tt0162346&ids=tt0326900
现在,在我们的函数中,我们可以通过事件对象访问查询字符串参数列表,所有 Lambda 函数都会包含这个对象。我们将通过 multiValueQueryStringParameters 键来访问它。然后,使用我们想要访问的查询字符串参数的名称,在这种情况下是 ids。
params = event["multiValueQueryStringParameters"]
id_list = params['ids']
使用 API Gateway 监控 Lambda 函数
默认情况下,你将获得对函数的 CloudWatch 监控。我遇到过一些错误,需要扩展日志记录。你必须在日志格式中添加以下内容,以适应 API 网关阶段。
$context.integrationErrorMessage
在这里阅读完整的说明 here

作者提供的图片
函数代码
我们将把所有这些整合到最终的lambda_handler函数中。我们从查询字符串中检索 ID,然后调用我们的函数get_tmdb_api_key从密钥存储中获取 API 密钥。最后,我们循环遍历这些 ID,构建数据框,并使用write_to_s3函数将其写入 S3。
def lambda_handler(event, context):
# Get the IDs from the Query String
params = event["multiValueQueryStringParameters"]
id_list = params['ids']
# Get credentials from Secrets Manager
KEY = get_tmdb_api_key()
# Loop through the IDs passed and do something with them
for i in range(len(id_list)):
imdb_id = id_list[i]
# Get the movie details
# CODE NOT SHOWN
# Save the movie details as a dataframe
# write it to S3
write_to_s3(df_crew, "crew", imdb_id)
# return a success message in the proper format for a Lambda function
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": "Success",
}
在数据仓库中协调和处理数据
最后的步骤是触发工作流中的 API 端点,然后处理你仓库中的数据。关于 JSON 如何与 Snowflake 协作的示例可以在我的文章与 Snowflake 入门及云中 ELT 工作流的崛起中找到,我将在未来的文章中介绍协调部分。
你可以在GitHub上找到这个项目的完整代码
结论
在构建 ETL 管道时,你有许多计算选项可以选择。对于简单的任务,AWS Lambda 函数可以是丰富数据或快速高效处理的绝佳方式。我们从展示如何创建 Lambda 开始,包括设置运行它的角色。然后我们介绍了几个建议,比如利用参数和密钥扩展安全存储 API 密钥等信息。接着我们探讨了如何使用 AWS CLI 自动化部署。最后,我们通过 API 网关触发了函数,并用 CloudWatch 监控了它。我希望这篇文章能展示出 Lambda 的精彩之处,以及如何将 Lambda 添加到你的 ETL 工具包中。
如果你喜欢阅读这样的故事并想支持我作为作者,考虑注册成为 Medium 会员。每月 5 美元,可以无限访问数千篇文章。如果你通过 我的链接 注册,我将获得少量佣金,但不会额外产生费用。
如何在 Power BI 中显示没有数据的结果
原文:
towardsdatascience.com/how-to-show-a-result-when-there-is-no-data-in-power-bi-bb1f6a86dbdf
这个标题似乎有些反直觉。为什么在没有数据的情况下我还要请求结果?继续了解一下客户的要求以及我如何解决这个问题。
·发布在 Towards Data Science ·9 分钟阅读·2023 年 1 月 5 日
--

图片由 Emily Morter 供图,来源于 Unsplash
介绍
我的一个客户有一个数据集,其中包含任务、子任务和每个任务的度量值。
此外,每个任务都有一个状态。
这些状态是:
-
完成
-
进行中
-
待处理
-
已取消
-
失败
将数据导入 Power BI 后,结果如下:

图 1 — 第一个结果(作者提供的图)
但是,有时对于特定状态可能没有任务或子任务。
在这种情况下,结果可能是这样的:

图 2 — 缺少状态的结果(作者提供的图)
客户有以下两个要求:
-
每当没有结果(空白)时,他希望看到一个零
-
他总是想看到所有状态,包括那些没有任务且结果为零的状态
很简单,不是吗?
好吧,我们来看看。
第一次尝试
第一个方法是编写一个如下的度量值:
Costs with zero =
VAR Costs = SUM('Demo Data 2'[Cost])
RETURN
IF(ISBLANK( Costs ), 0, Costs)
正如你所想,结果并不是我预期的。如果结果如我所想,我不会坐在这里写一篇文章。
结果是一样的。
为什么?
为了找出答案,我将度量值改为:
Costs with zero =
VAR Costs = SUM('Demo Data 2'[Cost])
RETURN
ISBLANK( Costs )
现在,我可以看到当 Costs 变量用 ISBLANK() 函数检查时发生了什么。
结果是这样的:

图 3 — 使用 ISBLANK() 的结果(作者提供的图)
起初,这很奇怪。
然后我明白了:当表中没有行时,没有筛选上下文。因此,度量值不会被评估,我们不会看到任何结果。
为了解决这个问题,我明白我必须填补数据缺口,以便使用上面显示的度量值,并在度量值返回 NULL 时得到零。
准备工作
为了展示这个案例,我在 Excel 中创建了两个表格并将其导入到 Power BI Desktop 中:
-
演示数据:包含所有状态的行(上面的表格来自这些数据)
-
演示数据 2:与演示数据相同的数据,但行被重新分配,并且没有行的状态为失败
为了解决这个挑战,我使用了演示数据表来满足第一个要求。
之后,我使用演示数据 2 表来满足第二个要求。
用 Power Query 填补缺口
为了填补缺口,我需要为每个任务和步骤以及每个可能的状态保留一行。
这次我决定在 Power Query 中执行,以遵循以下规则(罗氏准则):
尽早转换数据并尽晚转换数据
我不能在源中完成这项工作,因为这意味着要更改源数据,这是不可行的,我希望自动化转换过程。
出于这些原因,Power Query 是一个自然的选择。
所需的步骤如下:
-
提取所有可能的状态列表
-
提取所有任务和步骤的列表,不包括带有值列的列
-
将状态列表与任务和步骤列表相乘(交叉连接)
-
将生成的列表附加到原始表中
好了,开始吧:
我启动了 Power Query 编辑器,并创建了两个演示数据表的副本:

图 4 — 复制演示数据表(图由作者提供)
我将一个表重命名为状态,另一个表重命名为任务。
接下来,我从这些表中删除所有不需要的列:

图 5 — 删除列以保留所需列(图由作者提供)
在任务中,我只保留任务名称和步骤列。
在你的情况下,你必须保留定义表中唯一组合的所有列。在我的客户的情况下,我必须保留任务/子任务所有者的名字和其他列。
在状态表中,我保留状态和状态顺序列。
为了获得唯一行的列表,我从两个表中删除重复项:

图 6 — 删除重复项(图由作者提供)
为了将任务表中的行与所有状态相乘,我在任务表中添加了一个自定义列:

图 7 — 添加状态列(图由作者提供)
接下来,我必须展开新列以添加两个列:

图 8 — 展开状态列(图由作者提供)
结果表格如下所示:

图 9 — 扩展状态表后的结果(图由作者提供)
我在这篇文章中详细解释了这个技术:
当你有年度数据时,你可能需要将其转换为月度数据。现在,我将展示如何使用 Power…
在将这个表追加到演示数据表之前,我必须添加缺失的成本列,值为 NULL:

图 10 — 添加成本列(图由作者提供)
我必须为新建的成本列保持 NULL,以避免在需要计算平均值或其他统计数据时出现错误结果。
结果表与演示数据表具有相同的结构。
现在我可以将这个表追加到演示数据表中:

图 11 — 添加任务表(图由作者提供)
结果表如下所示:

图 12 — 附加数据的演示数据表(图由作者提供)
正如你可能注意到的,成本列的数据类型不正确。我必须将数据类型更改为数值型:

图 13 — 将成本列转换为数值型(图由作者提供)
最后,由于我在 Power BI 中不需要任务和状态表,所以我禁用了这两个表的加载:

图 14 — 禁用两个新增表的加载(图由作者提供)
在这个过程中,你可能会问自己,“他为什么没有为演示数据表创建引用?”。
好吧,我本来无法将引用的表追加到作为引用表源的表中。这会创建一个循环依赖,这是不允许的。因此,我不得不复制表。
在关闭 Power Query 编辑器并加载数据后,我得到了以下结果,使用了上述测量值:

图 15 — 用零替代空白单元格的结果(图由作者提供)
现在,第一个要求已经满足。
添加缺失的状态
当我对“失败”状态的缺失行重复上述所有步骤时,我在状态表中得到了以下结果:

图 16 — 缺少失败状态的状态表(图由作者提供)
为了解决这个问题,我必须添加一个包含所有可能状态的手动表(输入数据按钮):

图 17 — 创建手动状态表(图由作者提供)
下一步是将这个手动表追加到状态表中,并删除所有重复项:

图 18 — 完整的状态表,包含所有已知状态(图由作者提供)
但我为什么需要将手动表追加到现有的状态表中?
我这样做是以防我们得到一个新的状态,这样数据仍然会完整。
我只需要将新状态添加到手动表中。
手动表的替代方案是一个包含所有可能状态的 Excel 表格。这样,你就不需要每次添加或更改状态时都修改 Power BI 文件。
其余步骤保持不变,结果如下:

图 19 — 所有状态填充为零的结果(图由作者提供)
这一步满足了第二个要求。
修改模型的大小
你可能会想,“但这样,我们会把表扩大很多行?”。
是的,确实如此。
但正如开头所述:当没有数据时,没有过滤器上下文,也没有度量被评估。
所以,我们必须将这些行添加到我们的数据中。
但这有什么后果呢?
我用 DAX Studio 提取了原始数据模型和扩展模型的度量。
这些是结果:

图 20 — 两个数据模型的度量对比(图由作者提供)
首先,查看每个表的基数列。
我们知道原始表只有 27 行,而扩展后的表有 162 行。
这种增长是巨大的。
但当我们查看总大小列时,差异是微小的。
数据大小是原来的两倍多,但字典大小保持不变。为什么?
字典存储了所有文本列的信息。文本越多,字典就越大。
基数是关键度量(列中不同值的数量)。
因为这个度量在两个版本之间没有变化,所以字典不需要更多空间,即使数据需要更多空间。
你的数据可能具有更高的基数,这可能会有所不同。但我相信字典占用了大量空间。
差异可能仍然很小。
查看 ContosoRetail 数据模型中一些维度表的度量:

图 21 — ContosoRetail 数据模型的度量(图由作者提供)
你会看到字典总是比数据大得多。
由于字典存储了每一列所有不同字符串值的编码列表,它会随着不同值的数量(更高的基数)而增长。但只要基数保持不变,即使表中的行数增加,它也会相对稳定。
DAX 与 Power Query
但我们为什么不使用 DAX 来做这个?
是的,我们也可以用 DAX 表解决上述要求。
但这种方法也有一些反对的观点:
-
没有不必要的表
我们可以在 Power Query 中禁用加载到 Power BI 中。这样,我们可以避免在数据模型中有不必要的表,从而节省内存而无任何好处。
-
更易于实现:
在 Power Query 中完成这些操作要比使用 DAX 表更容易。
-
可能更好的数据压缩
我需要深入研究最后一点:
每当 Power BI 将数据加载到数据模型中时,引擎都会执行广泛的数据分析和分析,并优化数据以实现最佳压缩率。
在参考文献部分,我添加了 Marco Russo (SQLBI) 关于优化 Tabular 模型的视频,他解释了在数据刷新期间引擎执行的优化步骤。
由于 Power BI 引擎从 Power Query 中获取数据,因此它无法区分数据的来源。即使数据是在 Power Query 中生成的,它也会执行所有可能的优化以获取最有效的数据存储。
但在最坏的情况下,Power BI 中的计算表可能无法像 Power Query 中的表一样进行优化。
结论
我们都知道,在正确的时间选择正确的工具是至关重要的。
正如你在这里看到的,当选择使用 DAX 还是 Power Query 的解决方案时,这一点尤为关键。
当转换或修改数据时,如果你不能在数据源中完成,比如在数据库上进行查询,Power Query 是合适的工具。
使用图形用户界面提供的所有工具,实施复杂解决方案变得更加容易。
所以,没有理由不开始学习 Power Query 及其可能性。

Guille Álvarez 的照片,来自 Unsplash
参考文献
Guy in a Cube 关于 Roche 数据转换原则的视频:
YouTube 视频中,Marco Russo 解释了如何优化 Tabular 数据模型:
数据集是虚构的,并且是在 Excel 中从头创建的。
我使用了 Contoso 示例数据集,就像在我之前的文章中一样。你可以从 Microsoft 这里 免费下载 ContosoRetailDW 数据集。
Contoso 数据可以在 MIT 许可下自由使用,如 这里 所述。
我扩大了数据集,以使 DAX 引擎工作得更努力。
在线销售表包含 7100 万行(而不是 1260 万行),零售销售表包含 1850 万行(而不是 340 万行)。
[## 使用我的推荐链接加入 Medium - Salvatore Cagliari
阅读 Salvatore Cagliari 的每一篇故事(以及 Medium 上成千上万其他作家的故事)。你的会员费直接……
如何使用线性规划解决优化问题
原文:
towardsdatascience.com/how-to-solve-optimisation-problems-using-linear-programming-912cc951afbb
线性规划的介绍以及如何使用图形法和单纯形算法解决线性规划问题
·发布于 Towards Data Science ·7 分钟阅读·2023 年 7 月 24 日
--

图片由 Isaac Smith 提供,来源于 Unsplash
背景
线性规划 (LP) 是一种优化技术,用于从指定的目标函数中找到最佳解决方案,受某些约束的限制。它广泛应用于从金融到电子商务等各个行业,因此,如果你是数据科学家,了解它是非常值得的。LP 的关键特性是线性部分,这意味着约束和目标函数都被表述为线性关系。在这篇文章中,我们将通过一个示例 LP 问题,讲解如何使用单纯形算法和图形法解决它。
图形法
基本问题
我们将从图形法开始,因为它最简单易懂,并能给我们真实的线性规划直觉。
假设我们经营一家小型商店,出售£3的果昔和£2的咖啡,这就是我们的两个决策变量。由于原料限制,我们每天最多只能生产75杯果昔和100杯咖啡。此外,我们每天只有140个杯子来供应我们的果昔和咖啡。现在,让我们将这个问题形式化为一个线性规划(LP)问题!
公式化
如果我们让x代表果昔,y代表咖啡,那么我们想要最大化以下目标函数c,作为决策变量的函数:

方程由作者用 LaTeX 编写。
受以下约束条件限制:

方程由作者用 LaTeX 编写。
决策变量需要是非负的。
现在是绘制这些约束条件的时候了!
绘制
由于这是一个二维问题,我们可以在笛卡尔图上将约束绘制为直线(线性部分!):

图表由作者用 Python 生成。
灰色区域被称为 可行区域,在这里任何解都是有效的,因为它满足约束条件。
从图中观察,最优解似乎位于约束条件的直线交汇的角落。这实际上被称为线性规划基本定理。 该定理指出,线性函数的最优解(最大值)位于凸多边形区域的角落(可行区域)。
这里的最优解是 (75, 65) 角落,值为 £355。这可以通过测试可行区域的每个角落简单得出。
限制
上述例子很容易理解,但如果我们有另外两种产品我们公司销售会怎样?那么问题就变成了四维的。然而,我们人类无法在纸上可视化这一点。那么我们该怎么办呢?正如每个数据科学家所知,我们使用算法!
单纯形算法
直觉
单纯形算法是由乔治·丹茨格在第二次世界大战期间发明的,用于解决 LP 问题。算法的一般流程是访问凸多边形的每个顶点并评估目标函数。一旦我们到达最优解(我们将展示如何知道它是最优的),就终止算法。
在我看来,学习单纯形算法的最佳方法是将其应用于一个问题,并解释每一步的作用。因此,事不宜迟,让我们开始吧!
算法背后的数学证明相当复杂,因此我在此帖中没有详细讨论所有细节。然而,感兴趣的读者可以在此处找到相关链接。
步骤
第 1 步:
通过使用 松弛变量 将不等式转换为线性方程组:

方程由作者用 LaTeX 编写。
这里 s_1、s_2 和 s_3 是松弛变量,它们字面上“弥补”了不等式,使其转变为方程。松弛变量被称为 基本变量,而决策变量 x 和 y 被称为 非基本变量。
第 2 步:
将方程构建成一个表格,其中每个方程有自己的一行,目标函数在底部行:

约束条件和目标函数的初始表格。图示由作者提供。
这被称为初始基本解,对应于我们之前展示的图中的(0,0)顶点。
步骤 3:
接下来,我们需要通过找到最后一行中的最负值条目来识别枢轴列。然后,我们通过将值列除以枢轴列条目来识别枢轴行:

最后一行中的最负值条目。图示由作者提供。
在这种情况下,我们的枢轴列是x,称为进入变量,对应的枢轴行是第一行,R1,因为它具有最小的商。
我们选择底部行中的最低值,因为它代表了目标函数中的最低系数。因此,优化这个列将使目标函数增加最多。
此外,我们选择商最小的行,以不违反约束条件。有关为什么这样做的更多信息可以在这里找到。
步骤 4:
使用枢轴列,x,和行,R1,我们希望使枢轴列中的所有条目除R1外都为0。我们通过将其他行加上或减去R1的倍数来实现:

使 x 列中的每一行为 0,除了第一行。图示由作者提供。
我们看到新的目标是225,这对应于我们上面展示的图中的(75,0)顶点。因此,从图形上看,我们已经从顶点(0,0)移动到顶点(75,0)。
这一步骤本质上是高斯消元法。
步骤 5:
重复步骤 3 和 4,直到最后一行(目标函数)中的所有条目都是非零的:

最后一行中的最负值条目。图示由作者提供。

使* y *列中的每一行为 0,除了第三行。图示由作者提供。
现在,我们已经达到了最佳解,因为底部行没有负值。我们问题的最佳解是£355,当x = 75和y = 65时,也就是说,(75,65)顶点。这正是我们使用图形技术上面找到的准确解!
算法步骤本身并不复杂,但理解每一步的含义可能有些棘手。如果你想更深入地探索单纯形算法并深入理解每一步的目的,我在参考文献部分提供了有用的资源链接。
其他算法
还有更多的算法可以解决线性规划问题。以下是一个列表,其中包含一些提供的链接供感兴趣的读者查看:
Python 中的线性规划
幸运的是,我们在解决线性规划问题时不需要手动进行单纯形算法!在 Python 中,有两个主要的包为我们完成繁重的计算工作:
我不会在本文中详细介绍如何使用它们,但 这里链接 提供了一个关于如何将这些包应用于你的线性规划问题的优秀教程。
摘要与进一步思考
线性规划(LP)是一个非常有用的工具,可以应用于解决广泛的问题,因此对数据科学家来说非常重要。线性规划的基本概念是将问题全部用线性方程和不等式来表述,从而实现更快的计算时间。解决线性规划问题最常见的方法是单纯形算法,幸运的是,计算包已经为我们完成了这项工作。
本博客中使用的完整代码可以在我的 GitHub 上找到:
[## Medium-Articles/Optimisation/linear-programming 在主分支 · egorhowell/Medium-Articles
我在中等博客/文章中使用的代码。通过创建账户来为 egorhowell/Medium-Articles 的开发做出贡献...
参考文献与进一步阅读
另一个事项!
我有一个免费的通讯,Dishing the Data,在这里我每周分享成为更好的数据科学家的技巧。没有“废话”或“点击诱饵”,只有来自实践数据科学家的纯粹可操作的见解。
[## Dishing The Data | Egor Howell | Substack
如何成为更好的数据科学家。点击阅读《Dishing The Data》,作者Egor Howell,这是一个 Substack 出版物。
与我联系!
如何解决 Windows 上 Anaconda 的 Python 依赖问题
原文:
towardsdatascience.com/how-to-solve-python-dependency-issues-with-anaconda-on-windows-d5033c9d2f9a
不更改绝对路径
·发布在 Towards Data Science ·5 分钟阅读·2023 年 7 月 13 日
--

图片由 kirill_makes_pics 提供,来源于 Pixabay
最近几天,我在 Windows 机器上遇到了一些 Python 依赖问题。
我尝试安装新包进行测试。确实,它们被安装了,我可以通过 $ pip show [library_name] 查看所有详细信息,但当我尝试导入刚刚安装的库时,我遇到了 import 问题。
我在使用 Jupyter Notebooks 时遇到了这些问题,因此我认为这可能是与 Anaconda 相关的问题。这是我尝试解决问题的方法:
-
我尝试更改所有 Python(和 Anaconda)包的绝对路径,但结果导致 Anaconda 崩溃了。因此,我不得不恢复之前的绝对路径。
-
我尝试了“蛮力法”:我打开了 VS CODE,并在虚拟环境中安装了我想使用的库。结果你知道吗?我遇到了不同的依赖问题……
所以,在经过 2–3 小时的麻烦之后,我决定使用最大的蛮力方法,我有三种选择:
-
卸载并重新安装 Python 和 Anaconda,但这不会解决实际问题,原因有很多(例如,某些文件可能仍保留在当前文件夹中,这可能导致路径保持不变并产生相同的问题)。
-
在我的机器上卸载并重新安装 Windows。
-
在 Ubuntu 虚拟机上安装 Anaconda。
当然,我不想重新安装 Windows,所以我决定使用第三种方法,我将向你展示如何操作,这样你在需要时也可以使用这种方法。
最终,因为我主要使用 Python 进行数据科学,所以我想在我的 Linux 机器上安装 Anaconda,这样我就可以拥有所有需要的数据相关库(远不止这些)。
但在继续之前……请考虑一下,我正尝试安装一些鲜为人知但却强大的 Python 库,用于数据处理和科学计算。我在以下文章中描述了这些库:
## 超越 Numpy 和 Pandas:挖掘鲜为人知的 Python 库的潜力
作为数据专业人士你应该了解的 3 个 Python 科学计算库
towardsdatascience.com
第 1 步:在 Windows 上安装 Linux 虚拟机
你需要执行的第一步是将 Linux 虚拟机安装到你的 Windows 机器上。
我在以下文章中描述了这个过程,章节“如何在 Windows 中使用 bash”:
[## 如何轻松在 Bash 中创建别名,以加速你的工作(即使在 Windows 上)
发现别名的力量,以加速你的 Bash 脚本
不用担心:你不需要成为“计算机魔术师”(我不是!)。这个过程很简单。
第 2 步:更新操作系统
安装了 Linux 虚拟机后,你可以通过“ MobaXterm”(如上文所述)启动 Linux 终端,并通过以下命令更新系统:
$ sudo apt update
$ sudo apt upgrade
第 3 步:下载安装文件:
现在你可以这样下载 Anaconda 安装文件:
$ wget https://repo.anaconda.com/archive/Anaconda3-2023.03-1-Linux-x86_64.sh
**NOTE:**
this was the installation file for my machine.
Copy the file URL from the [Anaconda website](https://www.anaconda.com/download#downloadsù), depending on your machine
like in the image below:

图片来源:Federico Trotta。
第 4 步:执行安装文件
当你执行 wget 命令时,软件会在过程结束时返回安装文件的名称。它大概是这样的:

图片来源:Federico Trotta
复制文件名并像这样执行:
$ bash Anaconda3-2023.03-1-Linux-x86_64.sh
你将被要求多次按“ENTER”。

图片来源:Federico Trotta。
然后,按“yes”接受安装条件,完成过程。
第 5 步:重启环境以使安装生效
在接受前面步骤中的条件后,软件会要求你关闭并重新打开你的 shell(即,如果你使用了 MobaXterm)以使安装生效:

图片由 Federico Trotta 提供
当你再次打开 shell 时,可以通过输入以下命令来验证 Anaconda 的安装:
$ conda --version
在我的案例中,我得到的是:

图片由 Federico Trotta 提供
步骤 6:启动 Jupyter Notebook
好的,Anaconda 已安装,要启动 Jupyter Notebook,只需通过 MobaXterm 输入以下命令:
$ jupyter notebook
然后你可以开始使用它了!
步骤 7:安装其他库
现在,为了安装你可能需要的其他库(就像我一样!),你可以使用 WSL,这样从依赖关系的角度来看,一切将“连接”到 WSL 环境,你将不再遇到问题。
这是因为 WSL 和 Windows 环境是分开的,它们的依赖关系也是如此。
换句话说,只需打开你的 MobaXterm(或你使用的任何工具),输入 pip install [new_library],一切就会正常工作(直到我们破坏了什么新东西!😁)。
结论
在本文中,我们已经看到如何安装 Linux 虚拟环境,以便在 Windows 机器上遇到问题时安装 Python 及其所有依赖项。
这看起来很长,但相信我:我在文章开头描述的其他方法上挣扎了很久,可以告诉你这是一种更快(也更安全)的方式。

Federico Trotta
嗨,我是 Federico Trotta,我是一名自由职业技术文档撰写员。
想与我合作?联系我。
如何解决蛋白质折叠问题:AlphaFold2
原文:
towardsdatascience.com/how-to-solve-the-protein-folding-problem-alphafold2-6c81faba670d
更深入地了解 AlphaFold2 及其神经网络结构
·发表于 Towards Data Science ·阅读时间 16 分钟·2023 年 3 月 11 日
--

蛋白质序列到形状的示意图。原始蛋白质用白色表示,AlphaFold 预测的蛋白质用彩虹色表示 [蓝色=最高信心,红色=最低信心](图示作者提供)
在这一系列文章中,我将讨论蛋白质折叠及深度学习模型,如 AlphaFold、OmegaFold 和 ESMFold。我们将从 AlphaFold2 开始!
介绍
蛋白质是执行生物体大多数生化功能的分子。它们参与消化(酶)、结构过程(角蛋白 — 皮肤)、光合作用,并在制药行业中也被广泛使用 2。
蛋白质的 3D 结构对其功能至关重要。蛋白质由 20 种称为氨基酸(或残基)的子单位组成,每种氨基酸具有不同的特性,如电荷、极性、长度和原子数。氨基酸由所有氨基酸共有的主链和每种氨基酸特有的侧链组成。它们通过肽键相连 2。

四个残基的蛋白质多肽示意图。(图示作者提供)

四个残基的蛋白质主链示意图。(图示作者提供)
蛋白质包含在特定扭转角度 φ 和 ψ 定向的残基,这些角度决定了蛋白质的 3D 结构。

φ 和 ψ 角度的示意图。(图示作者提供)
每个生物学家面临的主要问题是获得蛋白质的三维形状,这通常需要蛋白质的晶体和 X 射线晶体学。蛋白质具有各种属性,例如膜蛋白往往是疏水性的,这意味着很难确定其结晶的条件 2。因此,获得晶体是一个繁琐且(可以说)高度随机的过程,需要几天到几十年不等,这可以被视为一种艺术而非科学。这意味着许多生物学家可能会在整个博士生期间致力于结晶一个蛋白质。
如果你有幸获得了蛋白质的晶体,你可以将其上传到蛋白质数据银行,这是一个大型的蛋白质数据集:
作为 wwPDB 的成员,RCSB PDB 根据约定的标准策划和注释 PDB 数据。RCSB PDB…
这引出了一个问题:我们能否模拟折叠以从序列中获得三维结构?简短回答:可以,有点。长答案:我们可以使用分子模拟来尝试折叠蛋白质,这通常计算量很大。因此,像 Folding@Home 这样的项目试图将问题分布到许多计算机上,以获得蛋白质的动态模拟。
现在,有一个名为蛋白质结构预测关键评估(CASP)的竞赛,其中一些蛋白质的三维结构被保留,以便人们可以测试他们的蛋白质折叠模型。在 2020 年,DeepMind 参与了 AlphaFold2,超越了最先进的技术,取得了杰出的表现。

2008 年至 2020 年 CASP 竞赛中的中位全球距离测试。大约 90 的解被认为与晶体结构大致相当。AlphaFold 2 超越了所有先前的模型,达到了最先进的性能(作者插图,基于 4)。
在这篇博客文章中,我将介绍 AlphaFold2,解释其内部工作原理,并总结它如何彻底改变了我作为蛋白质设计与机器学习博士生的工作。
在开始之前,我想给 AQ Laboratory 的OpenFold一个特别的感谢,这是一个开源的 AlphaFold 实现,包括训练代码,通过这些代码我双重检查了本文中提到的张量的维度。本文的大部分信息都在原始论文的补充材料中。
0. 概述
让我们从概述开始。这是模型的整体结构:

AlphaFold 架构概述 1
通常,你会从目标蛋白质的氨基酸序列开始。请注意,获得氨基酸序列不需要晶体:通常可以通过 DNA 测序(如果你知道蛋白质的基因)或蛋白质测序获得。蛋白质可以被切割成较小的-mers,并通过质谱等方法进行分析。
目标是准备两个关键数据:多序列比对(MSA)表示和对偶表示。为了简化,我将跳过模板的使用。
MSA 表示是通过查找遗传数据库中的相似序列获得的。如图所示,序列也可能来自不同的生物体,例如鱼类。在这里,我们试图获取有关蛋白质每个索引位置的一般信息,并理解在进化的背景下,蛋白质在不同生物体中的变化情况。像 Rubisco(参与光合作用)的蛋白质通常高度保守,因此植物中的差异很小。其他蛋白质,如病毒的刺突蛋白,则变化很大。
在对偶表示中,我们尝试推断序列元素之间的关系。例如,蛋白质的位置 54 可能与位置 1 相互作用。
在整个网络中,这些表示会多次更新。首先,它们被嵌入以创建数据的表示。然后它们通过 EvoFormer,该模块提取序列和对的相关信息,最后通过结构模型构建蛋白质的 3D 结构。
1. 输入嵌入器
-
算法:3
输入嵌入器尝试创建数据的不同表示。对于 MSA 数据,AlphaFold 使用一个任意的簇编号而不是完整的 MSA,以减少经过变换器的可能序列数量,从而减少计算。MSA 数据输入msa_feat (N_clust, N_res, 49)由以下部分组成:
-
cluster_msa (N_clust, N_res, 23):MSA 簇中心序列的独热编码(20 种氨基酸 + 1 个未知 + 1 个缺口 + 1 个masked_msa_token)
-
cluster_profile (N_clust, N_res, 23):MSA 中每个残基的氨基酸类型分布(20 种氨基酸 + 1 个未知 + 1 个缺口 + 1 个masked_msa_token)
-
cluster_deletion_mean (N_clust, N_res, 1):每个簇中每个残基的平均删除数量(范围 0–1)
-
cluster_deletion_value (N_clust, N_res, 1):MSA 中的删除数量(范围 0–1)
-
cluster_has_deletion (N_clust, N_res, 1):指示是否存在删除的二进制特征
对于对偶表示,它使用唯一索引对每个氨基酸进行编码,并使用 RelPos 计算序列中的距离。这被表示为每个残基之间的距离矩阵,距离限制为 32,这意味着较大的距离被限制为 0,因此维度有效为 -32 到 32 + 1 = 65。
MSA 表示和对偶表示都经过几个独立的线性层,然后传递给 EvoFormer。
2. Evoformer

EvoFormer 的架构 1
-
算法:6
然后有 48 个 EvoFormer 块,使用自注意力允许 MSA 和对偶表示进行通信。我们首先查看 MSA,然后将其合并到对偶中。
2.1 MSA 堆叠

EvoFormer 的 MSA 堆叠。 1(作者编辑)
这由 行级门控自注意力与对偶偏置、列级门控自注意力、转换 和 外积均值 块组成。
2.1A 行级门控自注意力与对偶偏置

Evoformer 中的行级门控自注意力与对偶偏置。 1
-
算法:7
关键点在于允许 MSA 和对偶表示之间的信息交流。
首先,使用多头注意力从 MSA 表示 行 计算点积相似度 (N_res, N_res, N_heads),这意味着序列中的氨基酸将学习“概念重要性”以识别对之间的关系。实质上,是一个氨基酸对另一个氨基酸的重要性。
然后,对偶表示经过一个没有偏置的线性层,这意味着只会学习一个权重参数。线性层输出 N_heads 维度,生成矩阵对偶偏置矩阵 (N_res, N_res, N_heads)。请记住,这个矩阵最初被限制在最大距离为 32,这意味着如果氨基酸之间的距离超过 32 个索引,它的值将为 0。
此时,我们有两个形状为 (N_res, N_res, N_heads) 的矩阵,我们可以轻松地将它们相加并应用 softmax,使值介于 0 和 1 之间。一个注意力块使用相加后的矩阵作为 Queries,并将一行通过线性层作为值,以获得注意力权重。
现在我们计算点积:
-
注意力权重和
-
MSA 行的线性+sigmoid 作为键(我相信此处的 sigmoid 操作返回一个范围为 0–1 的类似概率的数组)
2.1B 列式门控自注意力

Evoformer 中的列式门控自注意力。1
-
算法:8
这里的关键点是 MSA 是与输入序列相关的所有序列的对齐版本。这意味着索引 X 将对应于每个序列的蛋白质相同区域。
通过逐列执行此操作,我们确保对每个位置上哪些残基更可能有一个总体了解。这也意味着,如果一个类似的序列有微小的差异会产生相似的 3D 形状,模型将会更强健。
2.1C MSA 过渡

Evoformer 中的 MSA 过渡。1
-
算法:9
这是一个简单的 2 层 MLP,首先将通道维度增加 4 倍,然后再缩小到原始维度。
2.1D 外积均值

Evoformer 中的外积均值。1
-
算法:10
此操作旨在保持 MSA 和配对表示之间的信息连续流。MSA 中的每一列是蛋白质序列的一个索引位置。
-
在这里,我们选择索引 i 和 j,并将它们独立地通过一个线性层。这个线性层使用 c=32,这低于 c_m。
-
然后计算外积,取均值,展平,再通过另一个线性层。
我们现在在配对表示中有了更新的 ij 条目。我们对所有配对重复这一操作。
2.2 配对堆叠

EvoFormer 的配对堆叠。1
从技术上讲,我们的配对表示可以被解释为一个距离矩阵。之前,我们看到每种氨基酸都有 32 个邻居。因此,我们可以基于配对表示的三个索引构建一个三角图。
例如,节点 i、j 和 k 将有边 ij、ik 和 jk。每条边都通过它所在的所有三角形的其他两条边的信息进行更新。

三角乘法更新和三角自注意力。1
2.2A 三角乘法更新

我们有两种类型的更新,一种用于输出边缘,一种用于输入边缘。

三角乘法更新。1
-
算法:11 和 12
对于输出边缘,完整的行或对表示 i 和 j 首先独立地通过一个线性层,生成左边缘和右边缘的表示。
然后,我们计算对应的 ij 对的表示与左边缘和右边缘的点积,分别进行。
最后,我们取左边缘和右边缘表示的点积,并与 ij 对表示进行最终点积。
对于输入边缘,算法非常类似,但请注意,如果之前我们考虑的是边缘 ik,现在我们需要考虑相反的方向 ki。在 OpenFold 代码中,这简单地实现为一个排列函数。
2.2B 三角自注意力


三角自注意力。1
-
算法:13 和 14
这个操作旨在通过自注意力更新对表示。主要目标是用最相关的边缘更新边缘,即蛋白质中哪些氨基酸更可能与当前节点相互作用。
通过自注意力,我们学习更新边缘的最佳方法:
-
(query-key)包含感兴趣节点的边缘之间的相似性。例如,对于节点 i,所有共享该节点的边缘(例如 ij,ik)。
-
第三条边(例如 jk),即使它不直接连接到节点 i,也属于三角形的一部分。
这个最后的操作在风格上类似于图消息传递算法,即使节点没有直接连接,来自图中其他节点的信息也会被加权并传递。
2.2C 过渡块
相当于 MSA 主干中的过渡块,具有一个 2 层的 MLP,其中通道首先扩展 4 倍,然后减少到原始数量。
EvoFormer 块的输出是更新后的 MSA 和对的表示(具有相同的维度)。
3. 结构模块

AlphaFold 的结构模块。1
-
算法:20
结构模块是模型的最终部分,将配对表示和输入序列表示(对应 MSA 表示中的一行)转换为 3D 结构。它由 8 层共享权重构成,配对表示用于在不变点注意力(IPA)模块中偏置注意力操作。
输出结果如下:
-
背骨框架(r, 3x3):框架表示将原子位置从局部参考系变换到全球参考系的欧几里得变换。由 N-Cα-C 组成的自由漂浮体表示(蓝色三角形);因此,每个残基(r_i)有三组(x、y、z)坐标
-
χ 侧链角度 (r , 3):表示每个可旋转侧链原子的角度。这些角度定义了残基的旋转异构体(rotamer),因此可以推导出原子的确切位置。最多到χ1、χ2、χ3、χ4。
注意χ指的是每个可旋转键的二面角。有些较短的氨基酸没有所有四个χ角度,如下所示:

赖氨酸和酪氨酸的侧链角度。酪氨酸较短,没有χ3、χ4。(由作者插图)
3.1 不变点注意力(IPA)

结构模块的不变点注意力(IPA)。 1
-
算法:22
通常,这种类型的注意力设计为对欧几里得变换(如平移和旋转)不变。
-
我们首先用自注意力更新单一表示,如前面部分所述。
-
我们还将每个残基的背骨框架信息输入以生成查询点、关键点和值点用于局部框架。这些点随后被投影到全球框架中,与其他残基交互后,再投影回局部框架。
-
“不变”一词指的是通过在 3D 空间中使用平方距离和坐标变换,强制全局和局部参考点保持不变。
3.2 预测侧链和背骨扭转角度
单一表示经过几个 MLP,并输出扭转角度ω、φ、ψ、χ1、χ2、χ3、χ4。
3.3 背骨更新
-
算法:23
此块返回两个更新:一个是由四元数表示的旋转(1, a, b, c,其中第一个值固定为 1,a、b 和 c 对应于网络预测的欧拉轴),另一个是由向量矩阵表示的平移。
3.4 所有原子坐标
此时,我们既有骨架框架又有扭转角度,我们希望获得氨基酸的确切原子坐标。氨基酸具有非常特定的原子结构,而我们有身份作为输入序列。因此,我们将扭转角度应用于氨基酸的原子。
注意,你会发现 AlphaFold 的输出中有许多结构违例,例如下面描述的情况。这是因为模型本身没有强制施加物理能量约束。为了解决这个问题,我们运行 AMBER 松弛力场来最小化蛋白质的能量。

在 AMBER 松弛之前,蛋氨酸和色氨酸被预测过于接近,形成了一个不可能的键。在 AMBER 松弛之后,旋转构象被调整以最小化能量和立体冲突。(插图作者提供)
其他优化
AlphaFold 模型包含多个自注意力层和由于 MSA 尺寸而产生的大量激活。经典的反向传播优化是减少每个节点的总计算量。然而,在 AlphaFold 的情况下,它会需要超过 TPU 核心的可用内存(16 GiB)。假设一个含有 384 个残基的蛋白质:

相反,AlphaFold 使用了梯度检查点(也称为再物化)。激活在每次一个层时被重新计算,从而将内存消耗降至约 0.4 GiB。
这个 GIF 显示了反向传播通常的样子:

反向传播。(插图作者基于 github.com/cybertronai/gradient-checkpointing 提供)
通过检查点技术,我们减少了内存使用量,尽管这有一个不幸的副作用,即训练时间增加了 33%:

为检查点修复一个层。(插图作者基于: github.com/cybertronai/gradient-checkpointing 提供)

为检查点修复一个层。(插图作者基于 github.com/cybertronai/gradient-checkpointing 提供)
设计蛋白质呢?逆折叠问题
如果你有一个通过动态模拟设计的酷蛋白质模型,而不是一个氨基酸序列呢?或者一个你建模以结合另一个蛋白质(如 COVID 蛋白质刺突)的模型。理想情况下,你会想预测出折叠成一个输入的三维形状所需的序列,这个形状可能存在于自然界中,也可能是全新的蛋白质。我将向你介绍蛋白质设计的世界,这也是我博士项目 TIMED(三维推断方法高效设计)的内容:

逆折叠问题(作者插图)
这个问题可以说比折叠问题更难,因为多个序列可以折叠成相同的形状。这是因为氨基酸类型存在冗余,并且蛋白质的某些区域对实际折叠的影响较小。
模型的输入是围绕每个氨基酸位置的骨干的网格化体素空间的立方体(“框架”)。Alpha 碳中心位于框架中,框架旋转,使 Alpha 碳到碳的键沿 x 轴。每个原子(C、N、O、alpha-C、beta-C)在不同的通道中进行独热编码,从而产生一个 4D 数组。
我们模型的输出是每个位置上所有氨基酸的概率分布。例如,在每个位置,模型输出一个氨基酸在该位置上的概率。对于一个 100 氨基酸的蛋白质,输出的形状为 (100, 20),因为有二十种氨基酸:

TIMED 的预测管道(作者插图)
AlphaFold 的一个酷炫之处在于我们可以利用它来双重检查我们的模型是否有效:

折叠和逆折叠问题(作者插图)。
如果你想了解更多关于这个模型的信息,请查看我的 GitHub 仓库,那里还有一个小的 UI 演示!

TIMED 解决逆折叠问题的 UI 演示(作者插图)。GitHub: github.com/wells-wood-research/timed-design
结论
在本文中,我们看到 AlphaFold 如何(部分)解决了一个生物学家明确的问题,即从氨基酸序列中获得三维结构。
我们将模型的结构分解为输入嵌入器、EvoFormer 和结构模块。每个模块使用多个自注意力层,并包含许多优化性能的技巧。
AlphaFold 工作良好,但这就是生物学的全部吗?不。AlphaFold 仍然计算上非常昂贵,并且使用起来不容易(不,Google Colab 不容易——它很笨重)。一些替代方案,如 OmegaFold 和 ESMFold,试图解决这些问题。
这些模型仍未解释蛋白质如何随时间折叠。还有许多挑战涉及设计蛋白质,其中逆折叠模型可以利用 AlphaFold 进行双重检查,以确保设计的蛋白质折叠成特定的形状。
在接下来的系列文章中,我们将探讨 OmegaFold 和 ESMFold!
参考文献
1 Jumper J, Evans R, Pritzel A, Green T, Figurnov M, Ronneberger O, Tunyasuvunakool K, Bates R, Žídek A, Potapenko A, 等. 使用 AlphaFold 进行高精度蛋白质结构预测。 《自然》(2021) DOI: 10.1038/s41586–021–03819–2
2 Alberts B. 细胞的分子生物学。 (2015) 第六版。纽约,NY:Garland Science,Taylor 和 Francis Group。
3 Ahdritz G, Bouatta N, Kadyan S, Xia Q, Gerecke W, O’Donnell TJ, Berenberg D, Fisk I, Zanichelli N, Zhang B, 等. OpenFold: 重新训练 AlphaFold2 提供了关于其学习机制和能力的新见解 (2022) 生物信息学。DOI: 10.1101/2022.11.20.517210
4 Callaway E. “这将改变一切”:DeepMind 的 AI 在解决蛋白质结构方面取得了巨大飞跃 (2020)。《自然》588(7837):203–204. DOI: 10.1038/d41586–020–03348–4
进一步阅读
原始的 AlphaFold 论文:
[## 使用 AlphaFold 进行高精度蛋白质结构预测 - 自然
蛋白质对生命至关重要,理解它们的结构可以促进对其机制的理解…
www.nature.com](https://www.nature.com/articles/s41586-021-03819-2?source=post_page-----6c81faba670d--------------------------------)
OpenFold 论文和代码:
[## OpenFold: 重新训练 AlphaFold2 提供了关于其学习机制和能力的新见解
AlphaFold2 通过能够以极高的精度预测蛋白质结构,彻底改变了结构生物学…
www.biorxiv.org](https://www.biorxiv.org/content/10.1101/2022.11.20.517210v2?source=post_page-----6c81faba670d--------------------------------) [## GitHub - aqlaboratory/openfold: 可训练、内存高效且支持 GPU 的 PyTorch 重现…
图:OpenFold 和 AlphaFold2 预测与 PDB 7KDX,链 B 的实验结构的比较。A…
github.com](https://github.com/aqlaboratory/openfold?source=post_page-----6c81faba670d--------------------------------)
关于 AlphaFold 的另一篇写得很好的文章:
[## AlphaFold 2 & 等变性 | Justas Dauparas
Fabian Fuchs & Justas Dauparas 几周前,在最新的 CASP 蛋白质结构预测竞赛中(…
dauparas.github.io](https://dauparas.github.io/post/af2/?source=post_page-----6c81faba670d--------------------------------)
关于蛋白质(序列)设计:
[## GitHub - wells-wood-research/timed-design:使用深度学习和工具进行蛋白质序列设计…
timed-design 是一个用于蛋白质序列设计模型和分析预测的库。我们展示了重新训练的 Keras…
github.com](https://github.com/wells-wood-research/timed-design?source=post_page-----6c81faba670d--------------------------------) [## PDBench:评估蛋白质序列设计的计算方法
蛋白质设计的目标是创建具有有用特性和功能的新型氨基酸序列。一项重要的…
academic.oup.com](https://academic.oup.com/bioinformatics/article/39/1/btad027/6986968?source=post_page-----6c81faba670d--------------------------------)
作者寄语
从文本到图像、图像到图像和修复——潜在扩散的革命
pub.towardsai.net](https://pub.towardsai.net/latent-diffusion-explained-simply-with-pokémon-3ebe15a3a019?source=post_page-----6c81faba670d--------------------------------) [## Obsidian 学术写作教程
手稿写作、海报制作和将引用导出到 Word、PDF 和 LaTeX 的实用写作技巧。
betterhumans.pub](https://betterhumans.pub/obsidian-tutorial-for-academic-writing-87b038060522?source=post_page-----6c81faba670d--------------------------------) [## 如何利用 Obsidian 提升科研生产力
管理你的 Zettelkasten、项目、阅读列表、笔记和在博士研究期间的灵感的工具和工作流。
betterhumans.pub [## 20 多个 MacOS 应用程序提升你的生产力
一系列 20 多个应用程序,能显著提升你在 MacOS 上的生产力。
如何用模拟退火解决旅行推销员问题
使用模拟退火优化算法获取旅行推销员问题的最佳解决方案。
·发表于 Towards Data Science ·6 分钟阅读·2023 年 2 月 10 日
--

照片由 Annie Spratt 提供,发布在 Unsplash。
背景
旅行推销员问题 (TSP) 是 组合优化 和数学中一个普遍存在的问题。这个问题提出了这样一个问题:‘给定一个城市及其距离的列表,什么是访问每个城市一次并返回原始城市的最短路径?’
旅行推销员问题的一个更正式的数学定义是 哈密顿回路。
这个问题之所以著名,是因为它是 NP-hard。 这本质上意味着你不能在合理的时间内找到最佳解决方案,因为新城市的增加会导致 组合爆炸 可能路径的数量。例如,4 个城市的可能路径数为 3,6 个城市的路径数为 60,然而 20 个城市的路径数达到了巨大的 60,822,550,200,000,000!
TSP 的解决方案数量为 (n-1)!/2,其中 n 是城市的数量。对于较大的 n,解决方案的数量 不可处理 现代计算标准。
那么,这就引出了一个问题:如果我们有一些难以想象的路线要尝试,我们如何找到最短距离?当然,你可以使用暴力破解(简单解法)的方法尝试每一种可能的路线,但正如我们上面提到的,对于一定数量的城市,使用当前的计算标准可能需要几年时间。事实上,对于20个城市,尝试每一条路线大约需要~1000 年!
这里我们提到了一些特殊的启发式或元启发式算法,这些算法并不保证每次都能找到最佳解,但会尝试在合理的时间内获得一个足够好的解决方案。在本文中,我们将使用一种名为模拟退火的算法(SA)来解决旅行商问题。
模拟退火
概述
模拟退火是一种随机全局搜索算法,这意味着它在寻找最佳解决方案的过程中使用了随机性。它的名字和灵感来自于退火过程,这一过程在冶金中,通过加热或冷却金属来影响其物理特性。
SA 利用温度的概念来确定过渡到更差解决方案的概率(搜索的随机性),以更广泛地探索搜索空间,并更有可能找到全局最优解。这有助于避免陷入局部最优解,这类情况常常发生在爬山算法等算法中。
理论
SA 的一般数学框架是:

作者生成的方程。
其中:

作者生成的方程。
在这些方程中,x 是当前状态,x’ 是新状态,Δy 是两个状态之间成本或得分的差异,P(x → x’) 是从当前状态过渡到新状态的概率,T 是温度。
可以看出,当新状态的得分优于当前状态时,我们总是会过渡到新状态。此外,当温度T很高时,我们也很可能过渡到得分较差的新状态。这使我们能够更容易地在搜索空间中移动,探索全局最优解。
随着温度的降低,我们转移到更差解的可能性减小。因此,过程开始趋向于最优并开始 利用 搜索空间。
温度通常以指数形式冷却:

方程由作者生成。
其中 0 ≤ γ ≤ 1 和 t 是迭代次数。
另一个常见问题是如何确定初始温度?这实际上是一个相当复杂的话题,我建议读者查看 这里 的研究论文。一般来说,你可以从一个你知道足够高的温度开始,以便在开始时接受更差的解。这只是一个经典的试错过程!
TSP 算法
使用 SA 进行 TSP 的方法如下:
-
获取一个初始解,这可以是销售员的任何有效路线。
-
随机选择两个城市,并在路线中交换它们。
-
使用 SA 计算我们是否接受这个新解的概率。
-
对一组设定的迭代次数继续此过程,并在每次迭代中降低温度(减少)。
-
始终记录你找到的最佳总体解。
Python 实现
让我们在 Python 中实现这一点,展示一个工作示例。
首先我们将生成一些数据点,这些将是我们的城市:
GitHub Gist 作者提供。

图由作者在 Python 中生成。
上图显示了我们的城市以及一个初始解。初始解是通过按字母顺序遍历城市生成的,例如 a, b, c ... r, s, a。我们有 20 个城市,这意味着我们总共有 60,822,550,200,000,000 种不同的可能路线可供尝试!
现在我们将构建一个 Python 类来执行 TSP 的模拟退火过程!
我不是最好的 Python 程序员,所以我知道以下代码片段可能不是最佳实现……
GitHub Gist 作者提供。
我们现在将运行 SA 优化算法并绘制解:
GitHub Gist 作者提供。

图由作者在 Python 中生成。

图由作者在 Python 中生成。
上述图显示了我们迭代过的解,与温度和当前最佳解一起绘制。底部图显示了我们通过 SA 算法找到的最佳解的路线。从简单的“眼睛测试”来看,这个新解比初始解要好得多,并且似乎相当接近最优。
总结
在这篇文章中,我们讨论了旅行商问题,该问题要求我们找到一组城市(点)之间的最短路径。随着城市数量的增加,可能的不同路径数量呈指数增长,无法通过穷举法解决。然而,我们可以利用像模拟退火(SA)这样的元启发式算法,在大多数情况下找到足够的解决方案。
文章中使用的完整代码可以在我的 GitHub 上找到:
[## Medium-Articles/simulated_annealing_tsp.py at main · egorhowell/Medium-Articles
我在中等博客/文章中使用的代码。通过创建一个账户来为 egorhowell/Medium-Articles 的发展做贡献…
另一件事!
我有一个免费的新闻通讯,Dishing the Data,每周分享成为更好数据科学家的技巧。没有“虚假”或“点击诱饵”,只有来自实践数据科学家的纯粹可操作的见解。
[## Dishing The Data | Egor Howell | Substack
如何成为更好的数据科学家。点击阅读《Dishing The Data》,由 Egor Howell 编写,Substack 出版物…
与我联系!
参考文献与进一步阅读
- 优化算法. Mykel J. Kochenderfer 和 Tim A. Wheeler。 2019.
如何加速数据科学交付
将敏捷方法引入数据科学,并重新建立利益相关者对团队的信任
·
Follow 发表在 Towards Data Science ·10 min read·2023 年 1 月 9 日
--
当我们开始从事数据科学工作时,其他人期望我们能像开发人员一样敏捷,但我们能否使用他们的方法和仪式来实现敏捷?
我在几年内将一个数据科学团队从 1 人扩展到大约 10 人,我想分享一下作为经理在数据科学这样一个非常不确定的领域中保持敏捷的多个策略。如果你是一名经验丰富的数据科学家,可能会发生很多项目有时无法投入生产。在项目开始时,不确定性很高,你不知道它是否会投入生产。如果这正是你的情况,我想分享一些我的策略,以减少项目开始时的不确定性。
在团队刚开始时,一切都很好。我们使用了一个单一的看板,因为团队非常小,我们可以用它管理一切。问题开始于团队的扩展,我们在公司多个领域开展工作。利益相关者希望知道截止日期以及我们将如何完成项目。我那时与许多公司进行了交谈,决定我们应该尝试转向Scrum。
我们使用了 Scrum 好几年,但随着时间的推移,我意识到团队不满意。我们遇到了多个问题:
-
开放的 Pull Requests 即使在非常小的故事中也需要很长时间才能审核;
-
科学家几乎从未能完成冲刺;
-
细化会议花费的时间过长,在与利益相关者进行一些对齐后,一切总是发生变化,整个细化会议变得毫无意义;
-
团队总是高估故事的时间和规模,因此,利益相关者开始不再信任我们的截止日期,我们也有了一个慢团队的声誉;
-
很难对一个探索性故事(无论是 EDA——探索性数据分析,还是数据本身的发现)的规模做出准确预测,在这个步骤之后,如果数据不好,可能会导致一个不好的模型或分析,而这并不是我们真正需要的来提高模型性能的东西。
现在,让我更详细地解释一下当时团队的工作情况。团队是一个章节,所以我们在一些部落内部,与其他团队一起合作以达到部落的目标。一名数据科学家通常会在部落内主导一个项目,而数据科学团队的其他成员则会在每日会议或其他仪式中提供帮助,提出想法。一些项目会有两个数据科学家,但大多数情况下,我们每个项目只有一个人,并且这个人可以得到来自数据科学章节的支持。
当时,我认为我们是敏捷的,因为我们使用了敏捷框架。实际上,敏捷是响应变化而不是遵循计划,而我列出的这些问题促使我们寻找一种新的方式来管理数据科学团队的项目。
我们发现我们最大的一个问题是试图通过产品的第一次发布来解决所有问题。这产生了一个长时间的反馈循环,生成了大量的代码(包括 Jupyter notebooks 和 Python 代码),这对审查造成了威胁。特别是在项目初期逻辑出现问题时,这种情况尤其有害,因为这会引发一连串的变化事件(有时导致项目完全无效)。
所以我们停止了。我们开始致力于验证单一假设,并且在此基础上,我们开始为每个我们验证过的假设创建生产就绪的 版本。主要的变化包括:
-
处理小版本,这些版本可以在迭代结束时投入生产。这些版本根据当前利益相关者面临的最大问题进行优先级排序。
-
开始使用软截止日期来提高团队的创造力,并为利益相关者带来更多的可见性。
-
研究审查非常重要,但它是项目中最大时间消耗的环节之一。我们将这一步骤移到了最后。
我将详细说明这些步骤:
小版本
项目的第一个版本是 MVP,之后开发新的版本。MVP 是基线版本,这种命名法有助于数据科学家考虑如何在项目中做出最小的迭代,同时也与利益相关者的期望保持一致,因为他们也会帮助调整利益相关者的期望,以及数据科学家进行最小迭代的工作,以帮助解决问题。

我们开始将数据科学项目视为具有多个迭代的任务,牢记第一次迭代不是最终版本。所有迭代都遵循完整的数据科学流程(进一步解释),生成小的交付物。在图像中,我们根据当时可用的数据进行迭代。图像由作者提供。
我们首先做的事情是理解问题,或者匹配上面的图像,即业务理解,为了简化这个步骤,我们使用机器学习画布(ML Canvas)。我通常也称这个步骤为发现,因为在这里我们实际上了解是否这是一个数据科学问题,是否需要数据科学家来解决这个问题。画布有助于评估,因为我们可以使项目的主要目标与所有受其影响的人对齐。
在画布阶段,我们了解相关方的问题、所需的主要数据,并且已经可以定义一个 MVP(最小可行产品)并达成关于下一版本交付内容的共识。虽然这看起来相当直接,但在 MVP 阶段一切都可能发生变化,这没关系,因为不确定性是数据科学项目的一部分,没有亲手处理数据,很难知道下一步会有什么,以及接下来要做什么。
在完全远程或混合环境中,你可以使用 Miro 或其他类似工具与每个人进行头脑风暴,这种初步对齐可以让我们在数据团队真正开始工作之前,了解项目的不同方面。
对于后续步骤和版本,拥有一位在数据科学家将要工作的领域内的专家作为合作伙伴是至关重要的。此人应当能够参与头脑风暴会议,帮助数据科学家获得解决问题所需的业务知识,提供假设支持,并进行版本优先级排序。我们称这位人士为业务伙伴(Business Partner),他将帮助数据科学家更好地了解该领域所面临的日常问题,并确保数据科学家的交付成果能够解决这些问题。与业务伙伴的每周会议对项目有着显著的帮助,确保数据科学家与业务保持接地气,同时让业务了解数据科学家所做的工作。
软性截止日期
我们使用了一个包含CRISP-DM各个阶段的看板,这个方法有 6 个步骤。某些步骤被组合在一起,并设有软性截止日期和与项目相关方的对齐会议,以完成这些步骤。这增强了我们与相关方之间的可信度,因为没有这些步骤时,我们没有具体的议程,有时结果也没有得到充分的整合或验证。预留一些时间来总结结果会使会议更加高效,我们可以集中讨论想要讨论的要点,而不是去发现分析中的潜在错误,从而提升该特定交付物的业务价值。
我谈到这个步骤时,人们常常会问为什么它是“软性”的。我认为这是因为每个人都习惯了软件开发的方式,而数据科学并非软件工程,项目的某些步骤存在很大的不确定性。因此,软性 这个词带有很高的不确定性,比如 EDA(探索性数据分析)在没有业务逻辑的情况下可能难以衡量。在发现和初步数据准备及探索之后,数据科学家可以安排对齐会议,并定义项目的下一步软性截止日期。
软性截止日期也为数据科学家带来了灵活性:她可以在截止日期之前做她认为/想要的所有必要工作来解决问题,并且在计划错误的情况下,她仍然有材料可以在检查点展示并收集反馈(假设她首先专注于更简单的版本,并利用其余时间进行扩展)。这将使她能够进行不同的实验,同时考虑到截止日期。

过程的每一步都设置了软性截止日期。文献修订 + 数据分析需要 2 到 3 周;算法开发 + 结果分析 + 审查需要 2 到 3 周;部署可能需要 1 到 2 周,但这更多取决于你的 DataOps 基础设施和策略。作者提供的图片。
图片展示了在项目的第二/第三版本中可以实现的内容(因此关于数据、部署、使用等的不确定性已经大大减少),并且在一个对团队成员速度非常熟悉的团队中。对于 MVP 项目,这可能完全不同,因为数据准备可能非常复杂。超出第一阶段的项目通常使用这种方法会更快,因为大部分风险已经被了解,利益相关者对工作方式也已经很熟悉。
在 EDA 完成后与利益相关者召开对齐会议,可以确保我们在下一步行动上达成一致,并且提升数据科学团队的可见性。

新流程之前:数据科学家在项目进行 1 到 2 个月后才出现,与利益相关者讨论,那个时候大家可能已经忘记了这个项目。照片由Daniel Jensen提供,来源于Unsplash
结束时的审查
正如开头所述,我们主要的问题之一是研究评审时间,这随后被移到了项目的最后阶段。从代码的角度来看,这非常糟糕,因为这不是一个可以快速进行代码审查的原子部分,从代码的角度来看,这完全正确,但对于研究来说,你不仅要看到训练模型的单一代码,还要看到评估,以了解你可以做什么来改进这个模型(是数据?超参数?仅仅看训练代码是无法知道的)。这使得审阅者能够理解业务问题,并建议更相关的修改。在此之前,一个人可能在审查 EDA,而在两周后开始审查模型评估,或者更糟糕的是,不同的人会审查每个部分,丧失上下文。在数据科学中,代码只是整个图景的一部分,还有包含洞察、图形和数据的 Jupyter 笔记本。看到模型评估而错过 EDA 并不能使审查完整,审查整个迭代也有助于数据科学家意识到迭代应该是小的。
由于这一步骤需要相当长的时间,仅有一个数据科学家审查彼此的研究,其他团队成员的修订是可选的。评估之后,必须有一个人负责审查任务,并设定一个截止日期,否则,修订将再次成为问题,这一步骤可能会阻碍整个项目。
这一步骤增加了团队的知识,也使人员过渡到另一个领域变得更加容易。如果你想了解更多关于如何使用 Github 进行 Pull 请求的信息,请查看我之前写的这篇文章。
挑战和其他学习
由于审查步骤仅为最后一步,数据科学家可能会很难进行协作和提问。这可以通过使用功能分支来解决,其中数据科学家仍然可以交付小块代码并获得小部分的帮助,但团队在功能分支进入生产时仍能保持上下文分析。这也减少了项目最终的审查时间,因为小问题已经得到解决。这有助于与团队就特定问题进行异步对话。
保持回顾会在当时非常重要,这也是团队讨论项目中什么有效或无效的好机会,并与团队其他成员交换经验。这使得章节能够持续改进,即使在这一期间没有交付,我建议按较好的频率安排会议,比如每 15 天一次。
这个方法论是我对这篇文章中所呈现内容的改编。在其中,作者提出了一种使用看板和带有下一步步骤的清单的方法。软截止日期迫使数据科学家考虑她需要遵循的步骤,这创造了更有结构的工作方式,但我从未使用过预定义的清单,数据科学家会考虑重要的事项并与团队讨论任务(如果你愿意这样理解,几乎可以看作是精炼仪式)。我在这里解释的方法实施成功了。它为数据科学家带来了灵活性和责任感,并且与利益相关者之间的可见性和透明度得到了提高。这种方法在能够专注于一个项目的团队中效果更好,但当然,所有团队都有自己的特殊性,并且会更好地适应不同的框架。
我希望这篇文章能对你可能有的问题提供一些启示,如果你尝试了这种方法,请与我分享你的经验,我很高兴听到你的反馈。
这是我原始的葡萄牙语文章的翻译和修订版。
如何在数据分析师领域脱颖而出:专注于这 5 个关键主题
SQL、Python 及其他技术技能至关重要,但这只是成功的一半。
·
关注 发表在 Towards Data Science ·9 分钟阅读·2023 年 4 月 20 日
--
图片由 ThisisEngineering RAEng 提供,发布在 Unsplash
介绍
什么使杰出的数据分析师与优秀的分析师区别开来?当我回顾自己多年来领导和学习的多个分析师的经历时,我经常思考这个问题。答案总是与心态、方法和系统等具体主题有关,而不是在任何一个技术领域的精通。
强大的技术技能组合在与下面描述的主题习惯结合时效果会显著提升。我将使用一些示例来突出说明任何人只要有一定的计划和关怀,就可以将类似的行为融入自己的工作流程中。这些主题中有些是相关的,但我们将分别讨论每一个,以便于清晰理解。虽然这些示例基于音乐流媒体领域的分析,但其核心要点在各个行业都适用。
(1) 个别系统和流程
鉴于带宽有限且接收到的请求似乎源源不断,数据分析师经常发现自己在不断地从一个操作任务跳转到另一个任务,这是可以理解的。
杰出的数据分析师意识到这种模式,并知道何时跳出来,专注于提高整体效率。他们不怕花时间前期建立系统和流程,如果这样能节省未来的时间。
假设执行团队定期联系你,解释为什么关键业务指标上升或下降。在任何一天,你都可能收到类似的问题:
-
“看起来我们昨天的整体流量大幅下降。这准确吗?发生了什么事?”
-
“我刚刚查看了我们过去一年每日听众趋势的视图,注意到一年中有几个极端值。你能解释一下这些日期发生了什么吗?对不起通知较短,但我需要这个信息用于即将召开的会议。请在明天结束前回复我。”
这两个问题都是一种常见问题主题的实例:
解释为什么关键指标的值变化大于或小于预期(请尽快!)
因为你是一个精明的数据分析师,你认识到这一主题,并决定建立几个系统,以便在未来处理这种重复性问题。
系统 #1
一个简单的电子表格,其中包含以下信息,并每天以自动化方式更新:
a) 核心业务指标的每日值
b) 指标的日变化百分比,以及 z-scores(或其他相关技术以检查显著性)
c) 一个“备注”列,以便将来手动添加评论。你可以用这个来指出在特定日期影响了核心指标的内部或外部事件。例如,“内部工程问题:移动应用程序停机 4 小时,负面影响了每日听众和流量计数。”
系统 #2
你开始制定一个简单的流程,以便反复使用来缩小指标下降的潜在原因。这个流程由一系列标准问题组成。你对这些问题的回答指导下一步的操作。
这个流程在每种情况下可能不是完美的,但它将作为一个良好的总体框架,加速你的分析。你将这个系统视为一个正在不断完善的工作,随着时间的推移不断迭代和改进。
下面是每日流量指标下降时的可能情况:

作者提供的图片
为了结束这一部分,我想分享我最近在詹姆斯·克利尔的《原子习惯》一书中发现的一句名言。这句话比我所能表达的更好地阐明了“个人系统和流程”主题的重要性:
“你不会提升到你目标的水平。你会降到你系统的水平。”
(2)了解你的 20
我们中的大多数人可能都知道帕累托原则或 80/20 法则。最简单来说,它突出了少量项目如何对最终目标或结果产生巨大影响。例如:
-
80%的流量将来自 20%的听众
-
80%的收入将来自 20%的客户
80 和 20 是大致的估算,但它们有助于突出更大的观点。这一原则在评估你工作的最有影响力领域时同样适用。在任何给定时刻,总会有一小部分定义、指标、查询或其他项目是最重要的,需要对其了如指掌。
数据分析师能够迅速识别这 20%的客户,并花时间深入了解他们的需求,会脱颖而出。假设你最近开始与付费订阅团队合作。该团队负责增加付费客户数量和公司的整体订阅收入。他们非常依赖你的分析意见,并在团队讨论中积极向你询问数据和指导。
要有意义地参与这些会议,你意识到需要掌握一组核心数据点。这些数据点对这个领域至关重要,在几乎每次讨论中都会出现。你整理出你认为这些数据点的清单,并每天预留 20-30 分钟来学习它们。你不断重复这个过程,直到这些数据和潜在的见解变得对你而言自动可得。
“20”的实际列表会根据行业和团队背景有所不同,但以下是一个与付费订阅团队合作时的示例:
-
付费订阅者的总数及其一年的趋势
-
按付费订阅者数量排名的前五大市场
-
前五大订阅计划(例如,每月学生计划、年度家庭计划等)
-
新的、回归的和重新激活的付费订阅者数量
-
各个计划的留存率(或流失率)
-
免费试用到付费订阅者的转换率
初看起来,这似乎需要记住很多信息,但通过一些关注、规划和重复,这变得非常可行。与其记住准确的数字,不如对它们的大致水平和相对排名有一个强烈的感觉更为重要。由于这些指标的实际值不太可能每周剧烈变化,花时间了解它们将对你长期有益。
这并不是说你应该在实时参考相关仪表板或报告方面从不动手。偶尔这样做是必要的。然而,如果每一个数据点都必须参考文档,那么在游戏中领先并主动参与对话就会变得更困难。彻底了解你的 20 个数据点将帮助你避免这种情况,并使你更加投入。
(3) 预见与主动性
作为分析师和数据科学家,我们通常对构建模型和做出预测感到非常兴奋。然而,在规划我们自己的日子或周时,我们往往没有花足够的时间来锻炼这种预测能力。了解那些尚未被要求但可能会被要求的事项是很有帮助的,尤其是尚未被询问的事项。
能够预测并主动分享分析、模型或简单的电子邮件,可以大大增加获得信任和建立信誉的机会。让我们举一个简单的实际例子。假设你创建了一个自动化的电子邮件报告,多个团队依赖它来跟踪日常增长和参与指标。当这个电子邮件中的某个指标显著下降时,几个团队会暂停当前工作,以了解发生了什么。
现在,假设圣诞节即将来临,你记得在之前的分析中发现,每年第一周听众数量都会出现季节性下降。为了避免同事在事后向你询问解释,你可以提前采取行动,在下降发生之前主动发送一封包含所需背景的电子邮件。
从事后来看,沟通这点可能显而易见,但在实践中却往往没有发生。我们中的大多数人在回应收到的问题或请求时表现出色,但对主动交付却关注较少。行动时机的简单调整可以显著改变它被接受的效果。
预见和主动性是同一个硬币的两个方面。能够预测需求却不采取任何主动行动将没有外部影响。它可能帮助你验证思维过程,但不会有其他作用。同样,要以有用和相关的方式表现出主动性,预见性是关键。
(4) 同理心
鉴于已经有很多关于沟通在数据分析中的重要性的文章,我想把我们的注意力集中在一项有效沟通的前提条件上 —— 共情。
作为数据专业人员,有同理心的价值不可高估。深刻理解利益相关者的优先事项、痛点、优势和弱点,将极大地提高你的效率。
想象一下你是一名与编辑团队一起工作的数据分析师。这个团队负责跨多种流派、情绪和其他主题策划播放列表。这个部门的负责人找到你说:“嗨,请分享一份所有播放列表按周流量和听众数排名的清单。谢谢!”
与其立即进行 SQL 查询或仪表盘操作,不如深入挖掘背后的动机。通过澄清问题和进一步对话,明确团队实际上想要确定的是哪些播放列表具有最高的重复收听和参与度,以便他们可以创建更多主题类似的播放列表。
有了对团队需求的更深入理解后,你决定在报告中加入播放列表的听众保留率和跳过率指标。此外,由于每位编辑专注于为特定语言创建播放列表,你还提供了按每种语言筛选报告的功能。
在这种情况下,仅仅提供最初要求的内容可能已经满足了编辑团队,但这将导致最终产品的质量不佳。对团队需求的同理心提高了你工作的质量和可操作性。
(5) 持续学习
我相信我们大家都曾对面试问题做出过或者听到别人回答过类似的话:“我学习速度很快”或者“我不知道那个,但我可以学会。”
大多数数据分析师表达了学习新技能或在新领域建立专长的愿望。然而,决定性因素并不是学习的愿望或好奇心,而是学习的方法。将学习视为持续的旅程而不是偶尔的临时任务的个人,在长时间内因其知识和技能的积累而脱颖而出。
对于许多人来说,学习的行为完全取决于工作中是否有这样的需要。然而,这使你的工作对你学习的内容和程度有了全部的控制权。这并不是一个可靠的长期策略。在新工作的前几个月,有很多需要学习的内容,这可能效果很好,但随着时间的推移,效果会变得越来越差。
相反,“始终保持”学习心态意味着每天或每周持续花费少量时间来提高某项技能。这项技能可能与当前的工作内容无关。这样,工作与学习之间的关系变得不那么僵硬或交易性,更加可持续。在某些情况下,工作会决定你需要学习的内容,而在其他情况下,你的学习内容会影响你在工作的计划。
结论
总而言之,我们大多数人在跟上可能有利于职业发展的技术工具和技能方面表现良好。尽管这很重要,但仅靠这一点是不够的。将强大的技术技能与五个主题中的习惯和行为结合起来,可以带来巨大的变化。
感谢你的阅读!
如何作为数据分析师保持领先
原文:
towardsdatascience.com/how-to-stay-ahead-as-a-data-analyst-932dd326ff8c

图片由 Wil Stewart 提供,来源于 Unsplash
..并使自己在专业市场中保持相关性和竞争力
·发布于 Towards Data Science ·7 分钟阅读·2023 年 1 月 31 日
--
“我已经在这个角色中待了很长时间。我在职场上是否仍然有价值?”
— 一位资深的项目经理朋友
这是几周前我和一位老朋友的对话摘录。这句话让我感到惊讶(了解他和他令人惊叹的职业生涯和能力),并重新激发了我那种“冒名顶替综合症”的感觉,这种感觉被我繁忙的工作掩盖了。
和他一样,尽管我在一家有声望的公司工作,表现也相当出色,为组织带来了真正的影响和价值,但我仍然经常自我怀疑。日复一日,我在公司内的一个非常特定的产品上积累领域知识,并使用公司内置的工具和平台;不确定这些是否是行业中广泛使用的工具。经过一段时间,我也开始怀疑,如果有一天我决定离开公司(甚至被裁员的话),我的技能是否仍然对行业有用。
出于好奇以及不安全感,我开始了在工作之外提升自己的探索,以确保自己仍然对行业保持相关。在这篇文章中,我将分享我在这个过程中学到的技巧。这些对你——一位在同一个职位和组织中工作了几年经验丰富的数据分析师——有帮助,可以帮助你在职场中巩固自己的位置,并为组织带来巨大价值。
作为数据分析师保持领先和相关
我将其总结为 S-E-L-S:Solid foundation(扎实的基础),Explore(探索),Learn(学习),和 Show(展示)。
扎实的基础
要能够跑得好,你需要掌握如何稳固地站立和行走。要保持领先,你需要确保自己在当前领域站稳脚跟。在数据分析师的背景下,这意味着你要具备扎实的基础领域和技术知识。
拥有批判性推理能力对于问题框架的构建和调查是必要的(对探索性分析非常有用)。你需要通过提出“正确”的问题来把握商业背景中的问题和/或机会,以进行数据探索。
这些加上技术技能:查询(SQL)、描述性统计、推断统计(最好使用 Python 或 R 等编程语言)、数据可视化(Tableau、PowerBI、Google Data Studio 等)和数据管道(关系数据库、ETL)做得好,将足以帮助你走得更远,帮助你理解复杂的算法和系统。
大多数复杂的问题可以用简单的解决方案来处理。不要因为使用“基础”方法来解决问题而感到自卑。
作为数据分析师需要具备的更多基础技能列在这篇文章中。

照片由 Frantzou Fleurine 拍摄,来自 Unsplash
探索世界提供的机会
我们不知道自己不知道的东西。
我们质疑自己是否在行业中有相关性,说明我们对行业的最新状态还不了解。
因此,我鼓励你从探索行业所提供的东西开始。我们都知道科技行业正在快速增长,新算法、库和工具不断涌现。这个练习旨在发现技术趋势——工具、平台,甚至公司,并理解行业内的常见问题或机会。嗯,你可不想错过 ChatGPT 的热潮,或者日益增长的用户对数据隐私的关注,对吧?
去哪里探索?截至目前我的前三个发现来源:(1)订阅内容创作者(2)加入科技/数据社区(3)个人网络和联系。
订阅内容创作者。 从 Twitter 账户、Medium 作者/出版者,到 Substack 通讯和 Spotify 播客,我会腾出一些时间来跟进和阅读。分享一些我最喜欢的:
加入技术/数据社区。 无论是 Reddit 空间、Facebook/Telegram 群组、Slack 频道还是 Discord 服务器,总是有技术和数据科学社区存在。无论你决定成为活跃成员还是被动听众,总会有有趣的讨论,你可以从中获益。想要开始探索 数据科学 Reddit 空间吗?
个人网络和联系。 你可能没有意识到你在 LinkedIn(以及你的社区)上已经有很多外部联系。无论是新连接还是老朋友,特别是如果他们在数据科学领域,发一条信息(礼貌而友好,当然!)在 LinkedIn 上重新联系并了解他们的工作——他们目前解决的数据问题是什么以及他们所使用的技术栈是什么,这都是有益的。

学习并保持你的能力与时俱进
在以上探索之后,你可能会遇到一些你不熟悉的新术语、新工具或新现象。这些是已知的未知——现在我们知道我们不知道的事情。
要将事情从已知的未知转变为已知的已知,你应该学习并建立你的知识。这不能盲目进行,你仍然需要筛选、优先排序,并定义你希望建立的知识深度。这个问题真的在行业的关注焦点上吗(——是否被许多人提及)?现在人们如何在行业中利用这些知识(——他们是使用技术能力还是更多的是概念)?
学习可以根据你的需求和学习风格以不同方式进行。
-
如果你发现在概念方面有所欠缺并且寻求严谨性,合适的方式可能是寻找正式的项目(即教育机构的硕士项目/训练营,Udacity 的纳米学位);以及研究论文或科学书籍。
-
如果你想要探索特定工具、库或概念,在线课程或教程可能是一个很好的资源。我从 DataCamp、Coursera 和 KDNuggets 的课程/教程中受益——学习了从构建聊天机器人到 MLOps 有用的代码。
-
如果你在寻找快速且随意的理解,网络研讨会和会议可能是答案。你可以在 Eventbrite 上查找活动或加入由社区和服务提供商管理的活动。
-
如果你希望通过实践学习并直接应用知识,你可以自己启动一些个人项目。这可以是对 Kaggle 数据集 的探索,发起一个在当前组织中的“20% 项目”,允许你探索新工具,甚至为本地非营利组织的数据项目做志愿者。你听说过 DataKind吗?
对你感兴趣的主题做好尽职调查,并找到最适合你的学习方法。

出现并展示你的工作
现在你知道了行业趋势中的“新动态”,并了解了其概念和(如果可能的话)应用,你可以通过向外界展示你的知识来进一步推进它。
你可以尝试多种渠道:
-
分享你的学习成果: 你可以在 Twitter、LinkedIn,甚至社区中发布你的学习资源,供你的联系人探索。你还可以进一步总结你的学习和项目,并在 Medium 或其他平台上发布它们。更不用说其他途径,比如在工作中团队内部分享会议。
这不仅仅是出于个人品牌目的——展示自己并获得竞争优势,以便被招聘人员或招聘经理发现,也有助于掌握并巩固你的知识,尤其是如果你是一个 动觉学习者。你还可以在此过程中帮助他人——将你的知识暴露给你的联系人(甚至更广泛的受众),同时给予你学习的人员/平台应有的信用。
我理解对某些人来说,这并不容易,当你展示或发布你的工作时,可能会有一种被视为“过于努力证明自己”的不安感。
但如果你分享的是有价值的知识,并且没有伤害到他人(甚至更有可能使他人受益),为什么不呢?

分享我们的知识,为什么不呢?(图片由作者提供,演讲于 Tableau 和我 活动 usergroups.tableau.com/tableauandme)
最后的想法
随着科技行业特别是在数据领域的快速发展,我们可能会质疑自己的能力——我们是否仍然适合当前组织之外的数据市场。考虑到当前经济前景的不确定性(考虑到科技裁员),这是一个合理的问题,我们可能会发现自己处于观望状态。
我之前分享的技巧可以作为参考,如果你不确定如何开始并发展自己成为数据分析师或其他数据科学专业人士。记住:(1)确保扎实的基础,(2)探索世界所提供的机会,(3)学习并保持你的能力更新,(4)展示自己并展示你的工作。
祝好运,感谢阅读!
如何跟上最新的 AI 研究
原文:
towardsdatascience.com/how-to-stay-on-top-of-the-latest-ai-research-e8993523ef3e
关于如何跟上迅速发展的领域的策略
·发表于Towards Data Science ·6 分钟阅读·2023 年 1 月 7 日
--

由Dmitry Ratushny拍摄,发布在Unsplash
人工智能(AI)是一个颠覆性且发展迅速的领域,其发展轨迹正在迅速加速。事实上,近年来该领域的出版物数量大幅上升。斯坦福大学年度人工智能指数报告显示,AI 出版物的数量从 2010 年的 162,444 篇增加到了 2021 年的 334,497 篇1。

2000–2021 年的 AI 出版物。图示取自2022 年人工智能指数报告(CC BY-ND 4.0)。
如果你在 AI 领域工作,可能已经注意到行业重大进展的间隔越来越短,例如 OpenAI 的DALL·E 2、GPT-3和ChatGPT,或者 DeepMind 的AlphaFold。这些只是一些吸引公众和科技行业注意的例子,因为它们被广泛报道并在社交媒体上广泛传播。然而,其他学科的进展也变得越来越频繁,涵盖了从机器人技术到药物发现、自驾车和太空探索等领域。
当然,这不断增加的、持续的新方法和出版物流使得人工智能从业者和开发者越来越难以跟上最新的研究。这篇文章将概述一些在不遭受信息过载的情况下如何导航这一领域的策略。具体来说,我们将根据建议活动的频率将文章分为每日、每周、每月和每年几个部分。
虽然这篇文章特别针对人工智能研究,但以下策略适用于大多数快速发展的领域。
每日
对于全职工作的人工智能从业者而言,挤出每日学习的时间可能比较困难。然而,有一些事情可以在不需要大块时间的情况下每天完成。
订阅每日 新闻通讯 是保持对事物了解的好方法,通常只需几分钟即可阅读。例如,包括 MIT Technology Review 的 The Download 或 TLDR。这些不一定专注于人工智能,而是技术聚焦的新闻通讯,包含了技术领域的最新发展,包括大量人工智能相关内容。
关于科学文献,arXiv 提供了一个 每日邮件提醒服务,根据自定义指定的主题发送每日的新提交列表。这些主题可以从他们的 分类法 中选择。例如,人工智能属于计算机科学类别,并标记为cs.AI。同样,Google Scholar 也提供了基于用户特定兴趣主题的 提醒服务。
也许跟进最新文献的最快方法之一是通过社交媒体关注其作者,尤其是 Twitter。许多人工智能领域的重要人物使用该平台分享他们的出版物、想法和项目,包括 Yann LeCun、Geoffrey Hinton、Richard Sutton、Andrew Ng 和 Christopher Manning。
Yann LeCun 回复了 Andrej Karpathy 关于 ChatGPT 的心理担忧。
每周
虽然每日跟进人工智能研究主要包括一些简短的参与,但每周学习应该更深入一些。每周投入几个小时似乎是合理的,以下是所述的努力方向。
最可靠和最高质量的 AI 研究来源之一是同行评审的研究论文。有许多来源和期刊可以获取这些论文,例如 IEEE、Nature Machine Intelligence、Pattern Recognition 等。此外,arXiv 是一个优秀的开放获取电子预印本的来源。唯一的警告是,尽管这些电子预印本经过内容审查过程,但它们并没有经过同行评审。
此外,播客是保持信息更新和了解人工智能研究前沿的绝佳方式。目前,存在大量的播客可以在各种平台上找到,因此需要进行选择。以下是一些顶级 AI 播客的建议:The TWIML AI Podcast 由 Sam Charrington 主持,Data Skeptic 由 Kyle Polich 主持,Talking Machines 由 Katherine Gorman 和 Neil Lawrence 主持,以及 Practical AI: Machine Learning, Data Science 由 Changelog Media 主持。

照片由 Austin Distel 提供,来自 Unsplash
最后,还有许多有用的新闻简报每周发送。显著的例子包括 DeepLearning.AI 的 The Batch, Alpha Signal 和 The Sequence。
每月
每月可以参与各种活动。然而,计划这些活动可能会有些困难,因为这些活动往往是临时安排的。例如,研讨会、网络研讨会、读书小组或讲座。虽然大多数这些活动由各种教育机构和企业定期举办,但其中只有一些对公众开放。
如果你有幸与一个定期举办此类活动的机构相关联,那是非常好的!然而,如果不是这种情况,主动参与这个领域会有很大帮助。集结你的同事,建立自己的读书小组或期刊俱乐部,在那里你们可以相互保持最新信息,并讨论你们感兴趣领域的最新研究成果。

照片由 wocintechchat 提供,来自 Unsplash
每年
AI 社区非常庞大,因此每年都会有大量会议在现场或远程举行。这是跟进该领域最新发展的好方法,了解前沿的方法,并与同行研究人员和工程师进行激动人心的交流。
最受欢迎的会议包括神经信息处理系统(NeurIPS)、国际机器学习大会(ICML)、学习表征国际会议(ICLR)、计算机视觉与模式识别(CVPR)和计算语言学协会(ACL)。这些会议涵盖了广泛的主题,如自然语言处理、机器人技术、图像处理、大脑-计算机接口等。再次强调,在这样的会议中,重要的是要有选择地参与,以避免尝试参加每一个会话而导致信息过载。

图片来源:Product School 在 Unsplash
结论
本文重点介绍了一些突出的活动——按出现频率排序——这些活动可以帮助跟上最新的 AI 研究。由于这一领域的信息每天都在增加,因此重要的是要有选择地获取信息,以避免信息过载。专注于特定的子领域也有助于缩小研究范围,从而保持对感兴趣领域的跟进。
尽管本文中的许多建议是免费的,如新闻通讯和播客,但有些确实需要订阅或参加费用,特别是某些期刊文章或会议。
最后,如果你认为自己的团队或机构在这个领域做得不够,何不采取行动,自己发起定期的读书小组或期刊俱乐部会议?这是一种保持领先、学习新知识并同时扩展网络的好方法。
参考文献
1 张等(2022),AI 指数 2022 年度报告。AI 指数指导委员会,斯坦福大学人类中心 AI 研究所,2022 年 3 月。
如何更高效地存储历史数据
原文:
towardsdatascience.com/how-to-store-historical-data-much-more-efficiently-78b0f2c8c811
一个动手教程,使用 PySpark 存储最多仅 0.01% 的 DataFrame 行而不丢失任何信息。
·发表于 Towards Data Science ·阅读时间 10 分钟·2023 年 9 月 10 日
--

图片由 Supratik Deshmukh 提供,来源于 Unsplash
在一个公司和组织收集数据比以往任何时候都多的时代,数据集往往会积累数百万行不包含任何新信息或有价值信息的多余行。本文将重点关注数据管理的一个关键方面:使用 PySpark 删除数据集中没有附加价值的行。
PySpark 比 pandas 更适合处理非常大的数据集,因为它可以在多台计算机上处理数据,从而提高处理速度和可扩展性。Pandas 适用于能够在单台机器内存中容纳的小型数据集,但对于大数据而言可能会变得缓慢甚至不切实际。
设想以下情境:你在一家房地产公司的维护部门担任数据工程师/科学家。在过去的十年里,你的公司从一个包含你建筑物条件的外部数据库中进行了所有维护数据的完整加载,并将其存储在公司的云存储中。这些数据可能例如如下所示:

[图片来源:作者]
该数据集中包含三个列:
-
id-> 建筑物的 ID。 -
condition-> 一个介于 1(糟糕)和 10(优秀)之间的整数,表示建筑物的状况。 -
import_date-> 一个 datetime 列,表示这行数据从外部软件导入的日期。
若要自己创建此数据集,请运行下面的代码片段:
from pyspark.sql import SparkSession, Row
from pyspark.sql.functions as f
from pyspark.sql.types import IntegerType, DateType
import random
# set the seed to get same results when rerunning
random.seed(42)
# create a spark session
spark = SparkSession.builder.getOrCreate()
# create id list
ids = list(range(1, 11)) # Adjust this list size to meet the desired number of unique IDs
# create two possible conditions for each id
conditions = [[random.randint(1, 10) for _ in range(2)] for _ in ids]
# create a list of tuples where each tuple is a row
rows = [(id, random.choice(conditions[id-1]), date)
for id in ids for date in ['2023-01-01', '2023-01-02', '2023-01-03']]
# some ids should be missing on some dates, so we remove some
rows = [row for row in rows if random.random() > 0.2] # Adjust this parameter to control the number of missing IDs
# create a DataFrame
df = spark.createDataFrame(rows, ["id", "condition", "import_date"])
# convert import_date column to DateType
df = df.withColumn("import_date", df["import_date"].cast(DateType()))
# sort DataFrame
df = df.sort("id", "import_date")
# show the DataFrame
df.show()
让我们看看 Building2:

[作者提供的图片]
在 1 月的第一天和第二天,建筑物的状态是 4。1 月 3 日,状态变为 5。实际上,我们不需要关于 1 月 2 日的中间行。如果我们只关注状态变化的日期,我们可以省略许多行。在这个数据集中,Building2 的第一次出现是在 1 月 1 日,状态在 1 月 3 日发生了变化。我们可以通过添加from和until列来省略关于 1 月 2 日的第二行,这些列提供了行值第一次出现的时间以及行值发生变化的时间。
这个人工数据集非常小,仅包含三个不同的导入日期。但让我们考虑一个更现实的情况。你的房地产公司拥有数千栋建筑,导入数据跨度达 10 年。这将导致数百万行没有实际信息。在我为住房公司工作的实际情况中,使用这种技术,我们只需保存大约 ~0.2% 的所有行来存储所有信息。
让我们看看添加 **from** 和 **until** 列的代码。
首先,我们必须指定表中的分区列和日期时间列。分区列类似于你在使用 pandas 时想要分组的列。
partition_columns = ["id"]
datetime_column = "import_date"
然后,我们需要指定我们希望跟踪任何可能变化的列,在我们的案例中,那就是condition。不过,为了使代码更加通用和可重用,我们可以指定我们希望跟踪变化的列是所有列,除了分区列和日期时间列。
track_columns = [column for column in df.columns
if column not in partition_columns + [datetime_column]]
现在我们需要创建一个 PySpark 窗口,它类似于 pandas 的 .groupby()。在这个窗口中,我们需要指定我们希望按datetime_column排序行:
from pyspark.sql import Window
window = Window.partitionBy(partition_columns).orderBy(datetime_column)
要检查一个track_columns的值是否发生了变化,我们可以将前一行的值加到当前行,并检查它们是否相同。
for column in track_columns:
previous_column = f"previous_{column}"
changed_column = f"changed_{column}"
df = df.withColumn(previous_column, f.lag(f.col(column)).over(window))
df = df.withColumn(changed_column, f.col(column) != f.col(previous_column))
# df = df.drop(previous_column)
运行这段代码会得到以下 DataFrame:

[作者提供的图片]
在这里,我们可以看到,根据previous_condition是否等于condition,我们可以确定值是否发生了变化,并将其存储在changed_condition中。由于指定的Window,previous_condition的值仅在每个唯一id的范围内确定。
然而,我们不能只保留在值发生变化时的行,因为那样会导致删除所有给定id的第一次出现的行。我们还必须明确保存所有第一次出现的行。
为此,我们可以使用 row_number()*。
- 在这种情况下,你也可以通过查看
previous_condition和/或changed_condition中的null值来确定第一行,但为了说明起见,我们将在这里使用row_number()。
df = df.withColumn('row_num', f.row_number().over(window))

[作者提供的图像]
现在有两个条件可以决定是否保留一行数据:
-
当
changed_condition is True出现在任何f"changed_{column}"列中。 -
当
row_num == 1
但是,如果一行被删除了怎么办?因为没有下一行,changed_condition is False,而且row_num != 1。
因此,我们需要确保检查id在最后一次导入中是否仍然存在。
首先让我们确定最后的导入日期是什么:
# determine last import date
latest_import = df.agg(f.max(datetime_column)).collect()[0][0]
print(f"{latest_import = }")
# output
latest_import = datetime.date(2023, 1, 3)
对
print(f"{variable = }")的语法不熟悉?阅读下面的文章,了解这些和其他有用的 Python 技巧!
[## 5 个 Python 技巧,区分高级开发人员和初级开发人员
通过 Advent of Code 谜题的不同方法进行说明
现在让我们确定一个id的最新导入日期:
# Add last_partition_import_date to keep track of
# what the last imported date was per partition
last_partition_window = Window.partitionBy(*partition_columns)
df = (
df
.withColumn('last_partition_import_date',
f.max(f.col(datetime_column)).over(last_partition_window)
)

[作者提供的图像]
在上面的图像中,你可以通过查看last_partition_import_date看到 Building3 在 1 月 3 日从数据集中消失了。这可能意味着建筑物已经被拆除。这是非常重要的信息,因此跟踪id是否被删除非常重要。
现在让我们过滤掉所有不相关的行。正如我们之前提到的,我们想保留的行应该具有变化的condition值,或者应该是数据集中某个建筑物的第一次出现。
from itertools import reduce
# Add condition to keep every first row
first_row_condition = f.col("row_num") == 1
# Add condition that only keeps row that have changed values
change_condition = reduce(lambda a, b: a | b, (f.col(f"changed_{column}")
for column in track_columns))
# Filter the DataFrame to keep the first rows for each partition
# and the rows where there is a change
filtered_df = df.filter(first_row_condition)
.union(df.filter(change_condition))

[作者提供的图像]
现在,我们有一个 DataFrame,其中每一行都添加了信息!让我们删除一些不再需要的列:
# Drop the 'row_num' and 'changed_*' columns
filtered_df = filtered_df.drop('row_num',
*[f"previous_{column}" for column in track_columns],
*[f"changed_{column}" for column in track_columns])
同时,让我们添加一个until列,用于指示行的值有效的截止时间。我们可以通过使用f.lag将下一行的import_date上移一行来实现,只要下一行的id仍然相同,并且行按import_date排序。为此,我们将重用之前定义的 Window:
window = Window.partitionBy(partition_columns).orderBy(datetime_column)
# Add 'until' by looking one row ahead
filtered_df = filtered_df.withColumn("until",
f.lag(f.col(datetime_column), -1).over(window))

[作者提供的图像]
正如我们所见,例如对于 Building1,第一行的until值是第二行的import_date。第二行的until值是第三行的import_date。第三行的until值是null,因为没有第四行。
我们差不多完成了!如上所述,我们仍然在until列中看到一些null值。这对于 Building1 来说不成问题,但对于 Building3 来说则有问题,因为我们不能使用until列来检测 Building3 在 1 月 3 日是否不再存在。这时我们将使用last_partition_import_date和latest_import值。
如果until是null且last_partition_import_date < latest_import,则将last_partition_import_date加一天添加到该行的until列中。
# Add a deletion date if the last date of a partition is before the last import date of the data
filtered_df = filtered_df.withColumn(
"until",
f.when(
(f.col("until").isNull())
& (f.col("last_partition_import_date") < latest_import),
f.date_add(f.col("last_partition_import_date"), 1)
).otherwise(f.col("until"))
)

[图片作者]
使用这种技术,我们可以看到 Building3 的最后一次观察日期是 1 月 2 日(我们在这里使用的是不包括的直到日期)。如果某一行在until列中仍有null值,则表示该行仍然有效。
我们需要做的最后一件事是删除last_partition_import_date列,并将import_date列重命名为from:
final_df = (
filtered_df
.drop("last_partition_import_date")
.withColumnRenamed(datetime_column, "from")
)

[图片作者]
在上面您可以看到我们的最终数据框!在现实生活中,这种技术可以节省数百万行不带信息的数据,从而节省存储费用并加快进一步处理的速度!
总结
在这篇文章中,我们学习了如何通过仅保留实际带有信息的行来节省数百万行数据,通过查看每个子集内的变化值。总结一下我们做了什么:
-
根据 ID 将数据框分组为多个分区。
-
对于每个分区,我们确定了值的连续相同组合。连续相同的值组合会被删除。
-
我们添加了
from和until作为新列,以确定连续值组合的生命周期。 -
我们检查了一个分区的最后
until值是否在整个数据框的最后until值之前,以便为删除的分区添加一个until值。
为了使上面的代码可重用,我创建了一个函数,您可以将其复制粘贴到您的项目中使用!
import pyspark.sql.functions as f
from functools import reduce
from pyspark.sql import Window
import pyspark
from typing import Optional
def remove_uninformative_rows(df: pyspark.sql.DataFrame,
partition_columns: list[str],
datetime_column: str,
track_columns: Optional[list[str]] = None)
-> pyspark.sql.DataFrame:
if track_columns is None:
track_columns = [column for column in df.columns
if column not in partition_columns + [datetime_column]]
# Define a window specification based on partition columns and ordered by datetime_column
window = Window.partitionBy(*partition_columns).orderBy(datetime_column)
# Iterate over the non-partition columns and add a new column for each column that indicates whether the value has changed
for column in track_columns:
previous_column = f"previous_{column}"
changed_column = f"changed_{column}"
df = df.withColumn(previous_column, f.lag(f.col(column)).over(window))
df = df.withColumn(changed_column, f.col(column) != f.col(previous_column))
df = df.drop(previous_column)
# Add the row number to keep track of what the first occurance of a row is
df = df.withColumn('row_num', f.row_number().over(window))
# Add last_partition_import_date to keep track of what the last imported date was per partition
last_partition_window = Window.partitionBy(*partition_columns)
df = df.withColumn('last_partition_import_date', f.max(f.col(datetime_column)).over(last_partition_window))
# Save the latest import date of the entire data set
latest_import = df.agg(f.max(datetime_column)).collect()[0][0]
# Add condition to keep every first row
first_row_condition = f.col("row_num") == 1
# Add condition that only keeps row that have changed values
change_condition = reduce(lambda a, b: a | b, (f.col(f"changed_{column}") for column in track_columns))
# Filter the DataFrame to keep the first rows for each partition and the rows where there is a change
filtered_df = df.filter(first_row_condition).union(df.filter(change_condition))
# Drop the 'row_num' and 'changed_*' columns
filtered_df = filtered_df.drop('row_num', *[f"changed_{column}" for column in track_columns])
# Add 'until' column by looking one row ahead
filtered_df = filtered_df.withColumn("until", f.lag(f.col(datetime_column), -1).over(window))
# Add a deletion date if the last date of a partition is before the last import date of the data
filtered_df = filtered_df.withColumn(
"until",
f.when(
(f.col("until").isNull()) & (f.col("last_partition_import_date") < latest_import),
f.date_add(f.col("last_partition_import_date"), 1)
).otherwise(f.col("until"))
)
# Drop last_partition_import_date and rename import_date column
final_df = (
filtered_df
.drop("last_partition_import_date")
.withColumnRenamed("import_date", "from")
)
return final_df
使用这个函数,您可以轻松删除所有无信息的行!
remove_uninformative_rows(
df=df,
partition_columns=['id'],
datetime_column='import_date'
)
如果您觉得这篇文章有用,请查看我的账户,查看更多关于 Python 和数据科学的文章!
通过使用这两种方法,轻松深入了解您的分类数据。
towardsdatascience.com
如何使用 Mage 简化数据管道中的沟通
原文:
towardsdatascience.com/how-to-streamline-communication-in-data-pipelines-using-mage-1ab2a885dd73
让机器人处理困难的沟通问题
·发布于 Towards Data Science ·6 分钟阅读·2023 年 7 月 13 日
--

照片由 Volodymyr Hryshchenko 拍摄,来自 Unsplash
你是否曾遇到过这样一种情况:你的下游数据管道因为一个小小的 Google Sheets 手动错误而被阻塞?有时候,这个表格甚至不在你的团队名下,因此你无能为力,只能追着表格的拥有者让他们修复。与此同时,许多其他关键的管道也因此而失败,你还需要处理这些问题。
你感到精疲力竭。最糟糕的是,作为工程师你几乎无能为力。这一切都涉及到无尽的沟通和利益相关者管理。Google Sheets 问题只是各种规模的源问题的一个例子。花一点时间暂停一下,考虑一下一个与你产生共鸣的问题,随着我们深入文章。
改善这种情况的一个关键是自动化你数据管道中的沟通过程。如果你的管道已经有了警报机制,那就已经是一个好的开始。然而,警报主要针对数据工程团队,而不是外部团队。
根据我的经验,与源团队或最终用户建立主动沟通同样至关重要,以确保他们对当前情况有足够了解,并可以采取相应措施。在本文中,我将使用 Mage 进行实现,Mage 是一种现代的 Airflow 替代品,因其在解决这些问题方面的有效功能而受到称赞。
自动化沟通
工程师的使命之一是自动化。这可以节省我们未来的时间,并且很有趣。没有人喜欢不断地追着数据源团队解决数据问题,或在出现问题时向最终用户逐一解释情况。我们更愿意让机器人来做这些工作。我们可以实现两个层次的自动化:
及时反馈给数据源团队 — 与其手动通知源团队数据问题,不如通过机器人建立一种自动且一致的沟通方式。每当数据测试失败时,类似回调的函数将被触发,通过电子邮件或 Slack 通知源团队,提供详细的失败原因。对于定期运行,将在每次执行时一致地发送警报,直到问题解决。这些警报的重复性质应提高团队的警觉性,并促使他们在无需人工干预的情况下解决问题。

及时反馈给数据源团队(由作者创建)
数据用户的数据服务状态页面 — 另一方面,我们需要在停机期间处理数据用户的请求。数据团队在数据停机期间经常会收到大量重复的问题。数据服务状态页面的目的是提供一个集中平台来宣布数据问题。鼓励用户首先查看该页面,因为自助服务页面应提供有关当前情况和预期结果的全面而简洁的信息。目标是减少重复票据的提交,从而使数据工程师能够优先处理和集中于最关键的任务。

数据用户的数据服务状态页面(由作者创建)
我有一篇专门讲解数据服务状态页面的文章。欢迎你自己阅读以了解更多内容。
使用回调块在 Mage 中实现
以下示例使用 Mage v0.8.100。
接下来,让我们看看如何在数据管道中集成沟通生命周期,使用 Mage。如果你还不熟悉 Mage,Mage 被视为 Airflow 的现代替代品,旨在为数据工程团队带来最佳的开发者体验和最佳的工程实践。Mage 解决了 Airflow 的一些痛点,如本地测试和任务之间的数据传递。它还拥有非常直观的 UI,帮助工程师在几分钟内构建数据管道。

Mage UI(由作者创建)
Mage 中的管道由几种类型的块组成:@data_loader、@transformer、@data_exporter、@sensor、@callback 等。在本文中,我们将使用 回调块。它是一个特殊的块,因为它不会作为单独的步骤运行,而是在父块之后运行。你可以从“附加组件”创建一个回调块,它的外观如下所示。

Mage 中的回调块(由作者创建)
每个回调块有两个函数:@callback(‘success’) 和 @callback(‘failure’)。在我们的案例中,@callback(‘failure’) 可用于发送通信消息。parent_block_data 包含诸如管道和块名称的元数据,这些数据可用于自定义消息正文。此外,Mage 努力将开发者体验优化到极致。UI 使得在不同的块中重用回调块变得非常简单。所选的块将显示在底部。

Mage 中的回调块(由作者创建)
相应地,回调块也将在每个父块的底部显示。每个父块可以有一个或多个回调块。

Mage 中的回调块(由作者创建)
在父块完成后,如果块运行成功,则触发 @callback('success') 函数,否则执行 @callback('failure')。
值得注意的是,在 v0.8.100 中,@callback(‘failure’) 只有在 @loader、@transformer 或 @data_exporter 执行失败时才会被调用,不包括 @test 部分。如果 @test 失败,@callback(‘failure’) 将不会被触发。但请在你当前使用的版本中验证这一点。
@callback 块简化了数据管道中的通信生命周期的创建。它弥合了数据团队与外部团队之间的沟通差距。在宣布重大数据更改或数据问题时,拥有一致和及时的沟通方式无疑是有利的。但在庆祝成功时,是时候释放你的创造力并邀请用户参与庆祝了。
结论
确保数据团队与利益相关者之间的一致和适当沟通是具有挑战性的,因为每个人都有自己喜欢的沟通风格。本文的关键是,自动化沟通可以显著提升数据团队的工作效率。
首先,我们可以为各种类型的数据相关通信创建模板,例如表格弃用或表格模式更改。模板创建好后,下一步是探索自动化过程的方法,确保消息在适当的时刻传递,并且尽可能减少人工干预,这正是我们在本文中看到的。
我希望这个想法能引起你的共鸣,并激发你对在数据团队中实施类似解决方案的兴趣。我很想知道你对此的看法,请在下方留言,让我们知道。祝好!
如何优化你的数据科学学习之旅
·
关注 发表在 Towards Data Science · 作为 新闻通讯 发送 · 阅读时间 3 分钟·2023 年 5 月 4 日
--
在掌握数据科学或机器学习的复杂主题时,一般有两个事实是成立的:没有真正的捷径,人们的学习节奏可能差异巨大。尽管存在这些限制,但仍有方法可以使过程更加高效,并在合理的时间内实现我们设定的目标。
本周我们突出的文章提供了数据科学家在学习过程中可以应用的务实方法,无论他们的经验水平或职业阶段如何。如果你在寻找可能适合推文的学习窍门和问题解决技巧,你可能会感到失望(见上文:没有捷径!)。相反,重点在于培养更好的习惯,建立强大的信息收集工作流程,并最大化你已经掌握的知识。
-
职业转换的教训。在担任激光物理学家多年后,Kirill Lepchenkov决定成为一名行业数据科学家,他关于将现有技能集应用于新角色的帖子对考虑从学术界转向工业界的其他人尤其有用。然而,它对技能可转移性的见解同样适用于任何需要弥补重大知识差距以推进职业生涯的数据从业者。
-
建立一个稳固的信息保留系统。在应对长长的算法、公式和 Python 库列表时,数据科学学习者有时会感到像迷失在一片黑暗、难以穿透的森林中(无论是否随机)。Madison Hunter提供了一个实用的六步路线图来组织你的学习笔记——你可以根据具体主题进行调整和定制。

由Kelly Sikkema拍摄,图片来源于Unsplash
-
找到适合你的学习路径。正如在数据科学和机器学习的特定领域内有多种方法一样,你会发现关于从一个主题到下一个主题时应该遵循的正确顺序有无数的主观看法。Cassie Kozyrkov的最新帖子提供了一个引人注目且模块化的选项,基于她丰富的教程和解释文档。
-
有一些指导没有什么不妥。如果你不是那种喜欢自主探险学习的人,而更倾向于从结构化、渐进的方法中受益,Angela Shi的详细机器学习课程是你不容错过的——它将算法分为三类,并提供了明确的优先级建议。
如果本周关于效率的重点已经让你有了一些空闲时间,可以通过阅读我们的其他推荐书单来充实自己:
-
我们最新的月刊已发布!不要错过这篇关于城市空间数据的迷人文章合集。
-
谁不喜欢一个有趣(且有用)的项目演示?Jacob Marks 博士的首篇 TDS 文章详细介绍了如何将他公司繁琐的文档转变为一个可访问和可搜索的数据库。
-
在非科技公司担任数据科学总监一年后,CJ Sullivan分享了关于招聘、预算和跨团队沟通的新见解。
-
如果你上个月无法参加在柏林举行的 PyCon DE,Mary Newhauser的文章将帮助你保持最新,了解一些最有趣和发人深省的讲座。
-
为了扩展你对 Python 生态系统的全球视角,阅读Leah Berg 和 Ray McLendon对美国版 PyCon 的总结,该活动也在四月举行。
感谢你支持我们的作者!如果你喜欢在 TDS 上阅读的文章,考虑成为 Medium 会员——这将解锁我们全部的档案(以及 Medium 上的所有其他帖子)。
直到下一个 Variable,
TDS 编辑部
如何为可重复性和可维护性构建 ML 项目
使用此模板开始你的下一个 ML 项目
·发表于 Towards Data Science ·阅读时间 7 分钟·2023 年 1 月 15 日
--
动机
开始通常是构建 ML 项目时最具挑战性的部分。你应该如何构建你的代码库?应遵循哪些标准?你的团队成员能否复现你的实验结果?

作者提供的图片
与其寻找理想的代码库结构,不如有一个模板可以开始使用?

作者提供的图片
这就是为什么我创建了 data-science-template,整合了我多年来关于数据科学项目结构的最佳实践。
## GitHub - khuyentran1401/data-science-template at prefect
此模板是我多年精炼数据科学项目结构的成果,以确保它...
此模板允许你:
✅ 为你的项目创建可读的结构
✅ 高效管理项目中的依赖关系
✅ 为可重复的任务创建简洁且易读的命令
✅ 仅重新运行管道中修改的组件
✅ 观察并自动化你的代码
✅ 在运行时强制执行类型提示
✅ 在提交之前检查代码中的问题
✅ 自动记录你的代码
✅ 在提交代码时自动运行测试
此模板中使用的工具
此模板轻量且仅使用可以广泛适用于各种用例的工具。这些工具包括:
-
Poetry: 管理 Python 依赖
-
Prefect: 组织和观察你的数据管道
-
Pydantic: 使用 Python 类型注解验证数据
-
pre-commit 插件: 确保你的代码格式良好、经过测试和文档化,遵循最佳实践
-
Makefile:使用简短的命令来自动化可重复的任务
-
GitHub Actions: 自动化你的 CI/CD 管道
-
pdoc: 自动创建你的项目的 API 文档
使用方法
要下载模板,首先安装 Cookiecutter:
pip install cookiecutter
基于模板创建一个项目:
cookiecutter https://github.com/khuyentran1401/data-science-template
按照 这些说明 试用项目。
在接下来的几个部分,我们将详细介绍这个模板的一些有价值的功能。
创建一个可读的结构
从模板创建的项目结构是标准化且易于理解的。

作者提供的图片
这里是这些文件的角色总结:
.
├── data
│ ├── final # data after training the model
│ ├── processed # data after processing
│ ├── raw # raw data
├── docs # documentation for your project
├── .flake8 # configuration for code formatter
├── .gitignore # ignore files that cannot commit to Git
├── Makefile # store commands to set up the environment
├── models # store models
├── notebooks # store notebooks
├── .pre-commit-config.yaml # configurations for pre-commit
├── pyproject.toml # dependencies for poetry
├── README.md # describe your project
├── src # store source code
│ ├── __init__.py # make src a Python module
│ ├── config.py # store configs
│ ├── process.py # process data before training model
│ ├── run_notebook.py # run notebook
│ └── train_model.py # train model
└── tests # store tests
├── __init__.py # make tests a Python module
├── test_process.py # test functions for process.py
└── test_train_model.py # test functions for train_model.py
高效管理依赖
Poetry 是一个 Python 依赖管理工具,是 pip 的替代方案。
## 如何轻松地将你的 Python 包发布到 PyPI 使用 Poetry
在 10 分钟内分享你的有用 Python 工具
[towardsdatascience.com
使用 Poetry,你可以:
-
将主要依赖和子依赖分开到两个不同的文件中(而不是将所有依赖存储在
requirements.txt中) -
移除库时删除所有未使用的子依赖
-
避免安装与现有包冲突的新包
-
用几行代码打包你的项目
以及更多。
在 这里 查找有关如何安装 Poetry 的说明。
为可重复的任务创建简短的命令
Makefile 允许你为任务创建简短且易读的命令。如果你对 Makefile 不熟悉,可以查看这个 简短的教程。
你可以使用 Makefile 来自动化设置环境等任务:
initialize_git:
@echo "Initializing git..."
git init
install:
@echo "Installing..."
poetry install
poetry run pre-commit install
activate:
@echo "Activating virtual environment"
poetry shell
download_data:
@echo "Downloading data..."
wget https://gist.githubusercontent.com/khuyentran1401/a1abde0a7d27d31c7dd08f34a2c29d8f/raw/da2b0f2c9743e102b9dfa6cd75e94708d01640c9/Iris.csv -O data/raw/iris.csv
setup: initialize_git install download_data
现在,只要别人想为你的项目设置环境,他们只需运行以下命令:
make setup
make activate
一系列命令将会被运行!

作者提供的图片
仅重新运行管道中已修改的组件
当你希望每当其依赖项被修改时运行一个任务时,make 也非常有用。

作者提供的图片
例如,让我们通过下面的图示和 Makefile 捕获文件之间的连接:

作者提供的图片
data/processed/xy.pkl: data/raw src/process.py
@echo "Processing data..."
python src/process.py
models/svc.pkl: data/processed/xy.pkl src/train_model.py
@echo "Training model..."
python src/train_model.py
pipeline: data/processed/xy.pkl models/svc.pk
要创建文件 models/svc.pkl,你可以运行:
make models/svc.pkl
由于 data/processed/xy.pkl 和 src/train_model.py 是 models/svc.pkl 目标的先决条件,make 将运行配方以创建 data/processed/xy.pkl 和 models/svc.pkl。
Processing data...
python src/process.py
Training model...
python src/train_model.py
如果 models/svc.pkl 的先决条件没有变化,make 将跳过更新 models/svc.pkl。
$ make models/svc.pkl
make: `models/svc.pkl' is up to date.
因此,通过 make,你避免了在运行不必要的任务上浪费时间。
观察和自动化你的代码
这个模板利用了 Prefect 来:
- 从 Prefect UI 观察你所有的运行。

作者提供的图片
其中,Prefect 可以帮助你:
-
当你的代码失败时重试
-
安排你的代码运行
-
当你的流失败时发送通知
让你的数据科学管道对故障具有弹性
你可以通过将你的函数转换为 Prefect 流 来访问这些功能。
from prefect import flow
@flow
def process(
location: Location = Location(),
config: ProcessConfig = ProcessConfig(),
):
...
在运行时强制执行类型提示
Pydantic 是一个通过利用类型注释进行数据验证的 Python 库。
## 使用 Pydantic 和 Prefect 构建一个完整的 ML 应用程序
用一行代码为 ML 特征工程创建一个 UI
[towardsdatascience.com
Pydantic 模型 在流参数上强制执行数据类型,并在流运行时验证其值。

作者提供的图片
如果字段的值与类型注释不匹配,你将在运行时遇到错误:
process(config=ProcessConfig(test_size='a'))
pydantic.error_wrappers.ValidationError: 1 validation error for ProcessConfig
test_size
value is not a valid float (type=type_error.float)
所有的 Pydantic 模型都在这个 [src/config.py](https://github.com/khuyentran1401/data-science-template/blob/prefect/%7B%7Bcookiecutter.directory_name%7D%7D/src/config.py) 文件中。
在提交之前检测代码中的问题
在将 Python 代码提交到 Git 之前,你需要确保你的代码:
-
通过单元测试
-
组织良好
-
符合最佳实践和风格指南
-
已记录
然而,在提交代码之前手动检查这些标准可能会很繁琐。pre-commit 是一个框架,它允许你在提交代码之前识别代码中的问题。
## 4 个 pre-commit 插件用于自动化代码审查和格式化 Python 代码
使用 black、flake8、isort 和 interrogate 编写高质量代码
towardsdatascience.com
你可以向你的 pre-commit 管道中添加不同的插件。一旦你的文件被提交,它们将会按照这些插件进行验证。除非所有检查都通过,否则代码不会被提交。

图片由作者提供
你可以在这个 [.pre-commit-config.yaml](https://github.com/khuyentran1401/data-science-template/blob/prefect/%7B%7Bcookiecutter.directory_name%7D%7D/.pre-commit-config.yaml) 文件 中找到所有在此模板中使用的插件。

图片由作者提供
自动记录你的代码
数据科学家通常与其他团队成员合作完成项目。因此,为项目创建良好的文档是至关重要的。
要基于 Python 文件和对象的 docstrings 创建 API 文档,请运行:
make docs_view
输出:
Save the output to docs...
pdoc src --http localhost:8080
Starting pdoc server on localhost:8080
pdoc server ready at http://localhost:8080
现在你可以在 localhost:8080 查看文档。

图片由作者提供
自动运行测试
GitHub Actions 允许你自动化 CI/CD 管道,加快构建、测试和部署代码的速度。
[## GitHub Actions 在 MLOps 中的应用:自动检查和部署你的 ML 模型
使用 GitHub Actions 自动化你的 ML 管道
在 GitHub 上创建拉取请求时,你的 tests 文件夹中的测试将自动运行。

作者提供的图片
结论
恭喜你!你刚刚学习了如何使用模板创建一个可重用和可维护的机器学习项目。这个模板旨在具有灵活性。根据你的应用需求随意调整项目。
我喜欢写关于数据科学概念的文章,并玩各种数据科学工具。你可以在LinkedIn和Twitter上与我联系。
如果你想查看我写的文章的代码,可以给这个仓库加星。关注我在 Medium 上,以获取我最新的数据科学文章通知:
Pytest 为数据科学家准备 [## Pytest 为数据科学家
关于你的数据科学项目的 Pytest 综合指南
检测数据管道中的缺陷,早期验证和通知 创建强大的数据管道,使用 Prefect、Docker 和 GitHub [## 创建强大的数据管道,使用 Prefect、Docker 和 GitHub
使用 Deepchecks 和 Prefect 在 Python 中构建强大的数据管道
检测数据管道中的缺陷,早期验证和通知 创建强大的数据管道,使用 Prefect、Docker 和 GitHub [## 创建强大的数据管道,使用 Prefect、Docker 和 GitHub
将你的工作流存储在 GitHub 上,并在 Docker 容器中执行
创建强大的数据管道,使用 Prefect、Docker 和 GitHub 介绍 DVC:机器学习项目的数据版本控制工具 ## 介绍 DVC:机器学习项目的数据版本控制工具
就像 Git,但用于数据!
[towardsdatascience.com
如何使用 Matplotlib 样式化图表
原文:
towardsdatascience.com/how-to-style-plots-with-matplotlib-45591394ff98
快速成功的数据科学
不要满足于默认设置!
·发表于 Towards Data Science ·8 分钟阅读·2023 年 11 月 8 日
--

设置样式(来源:Cole Keister 在 Unsplash)
几十年前,我妈妈给我送了一件栗色天鹅绒运动套装作为圣诞礼物。那真是件糟糕的东西,我回答说这不太流行。她嗤之以鼻地说,“你设置了样式!做个潮流引领者!”
不用说,我确实没有设置样式,但我的妻子还是拿“你设置了样式!”这句话来逗我。不过,当使用 Matplotlib 时,我确实会设置样式,并且这与穿着天鹅绒运动套装不同,这是一件好事。
为了方便,Python 的 Matplotlib 库允许你覆盖其默认绘图选项。你可以利用这个强大的功能,不仅可以自定义图表,还可以为报告、出版物和演示文稿应用一致、自动且可重用的样式。
在这个快速成功的数据科学项目中,我们将快速了解如何使用 Matplotlib 样式化图表。
Matplotlib 中的样式选项
如果你使用过 Matplotlib,你可能已经通过向绘图方法传递新的值来更改图表的默认设置,例如线条的颜色。但如果你想要为多个图表同时设置这些值,以便所有曲线颜色相同,或者循环使用预定义的颜色顺序,该怎么办?
好吧,你可以通过以下两种方式实现:
-
运行时配置参数
-
样式文件
-
样式表
让我们依次查看这些内容。
更改运行时配置参数
一种样式化图形的方法是运行时设置参数,使用RcParams类的实例。这个类的名称代表运行时配置参数,你可以通过pyplot方法或面向对象的风格在笔记本、脚本或控制台中运行它。(如果你不熟悉这两种方法,请参阅我的文章,解密 Matplotlib)。
绘图参数存储在matplotlib.rcParams变量中,这是一个类似字典的对象。可配置参数的列表非常长,你可以在 Matplotlib 文档中查看。
现在,让我们看一个pyplot的例子,其中我们标准化图形的大小,使用红色绘制所有线条,并循环使用两种不同的线条样式。这意味着第一条绘制的线条将始终具有一定的一致样式,而第二条绘制的线条将具有另一种一致样式。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from cycler import cycler
请注意,我们还将 Matplotlib 导入为mpl。以这种方式导入 Matplotlib 使我们可以访问比pyplot模块更多的功能。我们还导入了cycler。Cycler类允许我们指定在制作多数据图时要循环的颜色和其他样式属性。你可以在这里了解更多信息。
要访问rcParams中的属性,你可以将其视为字典键。你可以通过输入mpl.rcParams.keys()找到有效的参数名称。
在接下来的三行中,我们设置了图形大小、线条颜色和线条样式:
mpl.rcParams['figure.figsize'] = (5, 4)
mpl.rcParams['lines.color'] = 'firebrick'
mpl.rcParams['axes.prop_cycle'] = cycler('linestyle', ['-', ':'])
注意:你也可以通过
pyplot设置参数,使用类似plt.rcParams[‘lines.color’] = ‘black’的语法。
要循环样式,我们使用了axes.prop_cycle键,然后将 cycler 工厂函数传递参数(‘linestyle’)和样式列表(实线和虚线)。这些默认设置现在已经被重置为当前会话中你将要制作的所有图形。
为了测试,让我们生成一些数据并绘制它:
# Prepare the data:
x = np.arange(0, 15, 0.1)
y = np.sin(x)
# Plot the data:
plt.plot(x, y)
plt.plot(x + 1, y - 2);

样式化的图形(作者)
通常,这段代码会生成一个有两条实线的图,一个蓝色,一个橙色。然而,我们覆盖了默认设置,因此我们得到了两条红色的线,通过不同的线条样式加以区分。
请注意,如果你在前一个图中绘制三条线条,第三条线将循环回使用实线样式,你将有一条虚线和两条实线。如果你想要三种不同的样式,你需要将额外的样式添加到 cycler 中。
为了方便,Matplotlib 提供了通过关键字参数同时修改一组多个设置的函数。这里是一个例子,我们首先恢复默认设置,然后更改线宽:
plt.rcdefaults() # Restore plot defaults.
mpl.rc('lines', lw=5)
plt.plot(x, y)
plt.plot(x + 1, y - 2);

重新样式化的图形(由作者提供)
在这个图中,恢复了默认的蓝橙色调,但线条宽度被自定义为5。
注意,你也可以使用以下命令重置默认值:
mpl.rcParams.update(mpl.rcParamsDefault)
创建和使用样式文件
你可以将对 Matplotlib 默认设置的更改保存到文件中。这可以让你为报告或演示文稿标准化图形,并在项目团队内共享自定义设置。它还通过让你预设某些图形参数并将其封装在外部文件中,从而减少了代码的冗余和复杂性。
让我们创建一个简单的样式文件,为图形设置一些标准,如图形大小和分辨率、背景网格的使用,以及标题、标签和刻度的字体和大小。在文本编辑器中,输入以下内容:
# scientific_styles.mplstyle
figure.figsize: 4, 3 # inches
figure.dpi: 200
axes.grid: True
font.family: Times New Roman
axes.titlesize: 24
axes.labelsize: 16
xtick.labelsize: 12
ytick.labelsize: 12
为了让 Matplotlib 能够轻松找到该文件,你需要将其保存在特定位置。首先,在控制台中输入以下内容以找到matplotlibrc文件的位置(Matplotlib 存储其默认设置的位置):
import matplotlib as mpl
print(mpl.matplotlib_fname())
这是我电脑上的输出(你的会有所不同):
C:\Users\hanna\anaconda3\lib\site-packages\matplotlib\mpl-data\matplotlibrc
这显示了到mpl-data文件夹的路径,该文件夹包含matplotlibrc文件以及一个名为stylelib的文件夹等。将样式文件保存到stylelib文件夹中,命名为scientific_styles.mplstyle(替换.txt扩展名)。
注意:如果 Matplotlib 在后续查找该文件时出现问题,可能需要重新启动内核。
现在,让我们使用这个文件来创建标准化的图形。导入pyplot后,使用其style.use()方法加载样式文件而不带文件扩展名:
import matplotlib.pyplot as plt
plt.style.use('scientific_styles')
接下来,使用面向对象的风格生成一个空图:
fig, ax = plt.subplots()
ax.set_title('Standardized Title')
ax.set_xlabel('Standardized X-label')
ax.set_ylabel('Standardized Y-label');

标准化风格(由作者提供)
当你保存样式文件时,你可能会注意到stylelib文件夹中充满了现有的mplstyle文件。这些文件创建了许多不同的图形格式,你可以查看它们以获取编写自己样式文件的线索。在下一部分,我们将使用这些文件中的一个来覆盖 Matplotlib 的一些默认值。
应用样式表
除了让你自定义自己的图形外,Matplotlib 还提供了预定义的样式表,你可以通过使用style.use()来导入这些样式表。它们的格式与之前创建的样式文件相同。
样式表的外观与matplotlibrc文件相同,但在其中,你只能设置与实际图形样式相关的rcParams。这使得样式表在不同机器之间是可移植的,因为不必担心未安装的依赖项。但不用担心,只有少数rcParams不能重置,你可以在这里查看这些列表。
要查看可用样式表的示例,请访问 Matplotlib 的 样式表参考页面。这些示例以缩略图条带的形式展示,如下所示。一些样式表模仿了流行的绘图库,如 Seaborn 和 ggplot。

灰度样式表示例(来自 matplotlib.org)
一个重要的样式表是 seaborn-colorblind 表。这个样式表使用了针对 5%到 10%有色盲的群体设计的“色盲安全”颜色。
让我们尝试使用 Matplotlib 附带的 grayscale 样式表来绘制散点图。首先,导入 NumPy 和 Matplotlib,并选择 grayscale 样式表。
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('grayscale')
接下来,为生成两个不同的点云生成一些虚拟数据:
x = np.arange(0, 20, 0.1)
noise = np.random.uniform(0, 10, len(x))
y = x + (noise * x**2)
y2 = x + (noise * x**3)
通过设置并执行 pyplot 方法来完成图表的绘制。对两个轴使用对数刻度:
plt.title('Grayscale Style Scatterplot')
plt.xlabel('Log X')
plt.ylabel('Log Y')
plt.loglog()
plt.scatter(x, y2, alpha=0.4, label='X Cubed')
plt.scatter(x, y, marker='+', label='X Squared')
plt.legend(loc=(1.01, 0.7));

使用灰度样式表制作的图(作者提供)
你看到的点的位置可能与该图不同,因为使用了随机生成的数据。
如果你打开 grayscale.mplstyle 文件,你会发现它看起来非常像我们在“创建和使用样式文件”部分中制作的 scientific_styles.mplstyle 文件。因此,如果现有的样式表不完全适合你的目的,你总是可以复制该文件,编辑它,并将其保存为新的样式表!
将样式限制在代码块中
如果你只想为 特定代码块 使用某种样式,而不是整个脚本或笔记本,样式包提供了一个上下文管理器来将更改限制在特定范围内。有关更多信息,请参阅 文档中的“临时样式”。
总结
Matplotlib 提供了三种主要的方法来为图表设置样式。你可以在脚本中更改 运行时配置参数,制作自己的 样式文件 并将其保存在 stylelib 文件夹中,或者使用 stylelib 文件夹中的预定义 样式表。你可以将这些样式更改应用于整个代码或指定的代码块。
将样式参数存储在文件中,可以让你与团队成员共享这些参数,以便每个人的图表都有一致的、统一的外观。除了帮助自定义图表,这些技术还让你通过抽象过程编写更简洁的代码。
谢谢!
感谢阅读,请关注我以获取未来更多 快速成功的数据科学 项目。有关更多 Matplotlib 的技巧和窍门,请查看我的书的第十九章, Python 工具。
[## Python 工具:使用 Anaconda、JupyterLab 和 Python 科学工具的介绍…
科学家的 Python 工具:使用 Anaconda、JupyterLab 和 Python 科学库的入门介绍…
如何在采用现代数据堆栈时应对数据爆炸
从多个数据平台中筛选数据可能具有挑战性。以下是我们如何借助数据发现平台解决这个问题的方法。
·
关注 发布于Towards Data Science ·6 分钟阅读·2023 年 2 月 3 日
--
我在 Fivetran 负责分析产品和运营,Fivetran 是一家市值 56 亿美元的“互联网管道工”,在数据集成领域处于市场领先地位。在 Fivetran 之前,我在 J.P.摩根构建了第一个企业数据堆栈。
在 Fivetran,我们努力建立一个强大的数据文化。超过 90% 的员工定期使用数据工具。在如此强大的数据文化背后,存在一个普遍存在于所有规模公司中的问题,这些公司都在经历数字化转型——数据爆炸,其中数据的数量和种类变得令人难以承受。今天,我想分享一些工具和最佳实践来解决这个问题。
数据爆炸问题
采用现代数据栈会导致数据源、用户角色和数据资产的激增。这种爆炸是由以下原因造成的:
-
SaaS 工具、事件流和其他生成数据的系统的激增。2021 年,平均企业部署了 187 个 SaaS 应用程序。各类组织都在努力将来自数百个来源的数据集中到一个数据库中以进行分析。
-
现代数据管道使得将大量和多样的数据轻松地集中到一个地方变得极其简单。
-
许多不同的人依赖于报告来进行日常操作。这些人涵盖了许多团队和所有级别的职位,从高管到个人贡献者。
-
商业智能平台和仪表盘的激增可能使得找到正确的数据变得困难:难以确定参考哪个仪表盘;在浏览类似资产时的困惑,以及在数据模型中存在许多难以理解、定义不明确或模糊的字段、值等。
许多数据团队面临的最大痛点之一是不断收到建立更多——更多数据源、更多仪表盘和更多指标的请求。由于数据的庞大数量和多样性,他们通常很难找到合适的数据使用。
这些是跨越数字化转型鸿沟的不可避免的、非预期的后果。对于数据领导者而言,这可能使企业数据管理变得难以应对。对于业务用户而言,尽管他们经常使用一套操作仪表盘,但他们常常感到探索超出初始范围的数据令人畏惧。
解决方案:一个有效的地图
为了控制数据爆炸,我们需要一个帮助我们将一切重新拼凑起来并使其合理的平台。
这需要:
-
所有数据资产的完整清单,包括表格、仪表盘和指标。
-
每个数据资产的相关背景。这包括谁拥有这些资产,这些资产的受欢迎程度,谁使用它们最多,以及最重要的,列级数据溯源(每个字段如何与上游源和下游资产相关)。
-
易于使用。管理企业数据已经很复杂;没有理由在数据管理过程中增加更多负担。理想的平台应该直观易用,能够正常工作。
在未来的状态下,数据消费者应能够轻松学习哪个数据资产最适合他们的日常工作以及临时的数据探索。数据团队可以轻松识别并淘汰未使用和重复的数据资产,组织和标准化数据资产,并创建易于维护的数据清单。
总之,为了解决数据资产的爆炸式增长,我们需要一个将所有数据资产集中在一个地方、提供有用数据背景并使数据资产易于搜索的数据发现平台。过去,人们称之为数据目录工具。
评估标准和决策过程
我们已经研究了市场上所有主要的数据发现工具。我们考虑工具选择的几个关键标准:
标准 1 — 兼容现代数据栈:我们需要一个兼容包括 BigQuery、Snowflake 和 Redshift 在内的云数据仓库、如 Looker 和 Tableau 的业务智能工具以及如 Fivetran 的管道工具的工具。
标准 2 — 价格。我们有一个预算,设定为业务智能工具总预算的不到 10%(这不包括云数据仓库费用)。
标准 3 — 易用性。这涵盖了 3 个主要方面:
-
易于设置 — 只需轻量配置即可。
-
直观易用 — 分析师和业务用户可以轻松找到他们所需的相关资产。
-
最小的持续维护开销 — 我们不想做“数据图书管理员”!
虽然大多数人认为数据发现是一个高度拥挤的领域,我们的候选工具初筛中有超过 10 个工具,但对我们来说,决策过程实际上非常简单。这可能听起来有些夸张,但让我解释一下:
-
现有的竞争者包括 Collibra、Informatica、Alation 和 Data.World,并不完全适合我们的数据栈。它们对我们的预算来说非常昂贵。它们的核心用户是 IT 人员,而不是数据分析师和业务用户。
-
我们也没有考虑像 开源工具 如 Amundsen 和 DataHub,因为我们想要一个易于使用、开箱即用的解决方案。这样,我们的团队就不必花时间和资源来配置新工具。因为我们想节省时间,所以能够随时获得客户支持也很重要。
-
我们没有考虑像 Sled 这样具有垂直整合的工具,因为我们在内部不使用 Snowflake。Sled 是一个现代的数据目录工具,与 Snowflake 生态系统集成。他们在解决数据发现时的垂直方法,即深入而非广泛,是不同且有趣的:Sled 涵盖了从自动化数据质量检查到数据发现和血缘,从文档到指标层的能力。
-
我们将选择范围缩小到 Select Star 和 Atlan。我们进行了为期一个月的试用,最终选择了 Select Star。它最适合我们的用例,因为它提供的核心数据发现体验优于市场上的其他工具。
具体来说,Select Star 非常适合我们的需求,原因如下:
-
他们的价格合理。
-
我们可以轻松地与 BigQuery、Looker 和 Sigma 设置集成。借助 Select Star,我们能够在一天内启动实例并配置我们的数据源。
-
它能正常工作。我们非常明确地表示,我们不希望分析师变成“数据运维”或“数据管理员”。虽然许多工具期望数据团队进行大量配置以使其有用且可维护,但用户可以轻松找到所需的资产及其来源。
跨越探索鸿沟:在企业数据管理中保持领先
我们选择了一个帮助促进数据发现的工具,但接下来该做什么?无论你是像 J.P. Morgan 这样的强大企业中的数据高管,还是像 Fivetran 这样的快速增长的企业,都需要考虑数据治理的五个核心支柱,以便管理一个成功的数据组织。
质量 — 确保数据从源头到目的地的完整性、准确性和低延迟。
完整性 — 保护和维护集中指标的有效性。
分类法 — 定义和维护关键业务指标的数据分类法。
标准化 — 定义和维护一致的逻辑数据模型和指标,以支持跨功能的用例。
访问:
-
权限管理 — 确保对数据的个体访问控制和一致性。
-
发现 — 确保数据资产易于发现。
虽然企业数据治理可能让人感到不知所措,但我将分享一些简单的行动点,帮助你入门:
-
投资于源系统中的干净、可靠的数据 — 大多数数据质量问题来源于源系统中的不良数据。你需要投资于确保源数据的完整性和准确性的能力。
-
投资于强大的数据管道工具 — 构建定制化数据管道的过程费时费力、成本高昂、极其脆弱,并且需要不断维护。你的数据管道工具必须可靠且易于使用。你不希望你的数据团队变成数据管道工。该工具不应需要繁重的配置和维护来确保数据质量。
-
利用数据发现平台或数据使用工具来组织资产 — 你不希望你的数据团队成为数据运维或数据清理人员,但你需要设计一个过程来修剪你的数据花园。投资于与核心数据平台无缝集成的数据发现工具,或者内部构建一个数据使用工具,以便轻松淘汰未使用、过时和重复的资产。大多数商业智能工具都有使用信息。
-
建立一个审议委员会以批准新资产——每个组织都有用于存储资产的公共文件夹。这些公共文件夹中还包含了优先级最高的仪表板和北极星指标的金资产。公共文件夹中的新资产和金资产的更改应由审议委员会进行审查。否则,你的数据花园将会杂乱无章。
我希望这篇文章能帮助你更好地赋能你的组织,使其在大规模数字化转型中保持领先。尽管数据管理方面已经取得了重大技术进步,但我相信这仅仅是开始。
如何在不重新采样的情况下应对类别不平衡
原文:
towardsdatascience.com/how-to-tackle-class-imbalance-without-resampling-47bbeb2180aa
超越重新采样、阈值调整或成本敏感模型的类别不平衡分类
·发布于 Towards Data Science ·阅读时间 7 分钟·2023 年 3 月 1 日
--

Denise Johnson 的照片,发布于 Unsplash
不平衡分类是一个相关的机器学习任务。这个问题通常通过三种方法中的一种来处理:重新采样、成本敏感模型或阈值调整。
在本文中,你将学习一种不同的方法。我们将探讨如何使用聚类分析来解决不平衡分类问题。
引言
许多现实世界的问题涉及不平衡的数据集。在这些问题中,某一类是稀少的,通常对用户来说更重要。
以欺诈检测为例。欺诈案件在大量常规活动中是稀有的实例。准确检测稀少但欺诈性的活动在许多领域都是至关重要的。其他涉及不平衡数据集的常见例子包括客户流失或信用违约预测。
不平衡的分布是机器学习算法面临的一项挑战。关于少数类的信息相对较少。这阻碍了算法训练出良好模型的能力,因为算法往往偏向于多数类。
如何处理类别不平衡
处理类别不平衡有三种标准方法:
-
重新采样方法;
-
成本敏感模型;
-
阈值调整。

Viktor Talashuk 的照片,发布于 Unsplash
重新采样可以说是处理不平衡分类任务中最受欢迎的策略。这种方法通过转换训练集来提高少数类的相关性。
重采样可以用来为少数类创建新案例(过采样)、丢弃多数类案例(欠采样),或两者结合。
下面是使用imblearn库的重采样方法工作的示例:
from sklearn.datasets import make_classification
from imblearn.over_sampling import SMOTE
X_train, y_train = make_classification(n_samples=500, n_features=5, n_informative=3)
X_res, y_res = SMOTE().fit_resample(X_train, y_train)
重采样方法是多用途的,易于与任何学习算法结合。但它们也有一些限制。
对多数类进行欠采样可能导致重要信息的丢失。过采样可能增加过拟合的风险。这种情况发生在重采样传播了来自少数类的噪声时。
有一些替代方案用于重采样训练数据。这些包括调整决策阈值或使用成本敏感模型。不同的阈值会导致不同的精准率和召回率得分。因此,调整决策阈值可以提高模型的性能。
成本敏感模型通过对误分类错误分配不同的成本来工作。少数类的错误通常更为昂贵。这种方法需要领域专业知识来定义每种类型错误的成本。
利用聚类处理类别不平衡
大多数重采样方法通过寻找接近决策边界的实例来工作——即分割多数类和少数类实例的前沿。边界情况原则上是最难分类的。因此,它们用于驱动重采样过程。

支持向量机模型的决策边界。原始图片:Alisneaky Vector: Zirguezi, CC BY-SA 4.0. 图片来源。
例如,ADASYN 是一种流行的过采样技术。它使用来自少数类的样本创建人工实例,这些样本的最近邻来自多数类。
使用聚类分析寻找边界情况
我们还可以使用聚类分析来捕捉哪些观察点接近决策边界。
假设有一个聚类,其观察点全部属于多数类。这可能意味着该聚类距离决策边界较远,位于多数类的一侧。通常,这些观察点容易建模。
另一方面,如果一个实例属于一个同时包含两类的聚类,则可以认为它是边界实例。
我们可以利用这些信息构建一个层次化模型来处理不平衡分类。
如何构建一个层次化模型以应对不平衡分类
我们基于两个层级构建一个层次化模型。
在第一层级,建立一个模型以将简单实例与边界实例分开。因此,目标是预测输入实例是否属于至少包含一个少数类观察点的聚类。
在第二层级,我们丢弃简单的案例。然后,我们建立一个模型来解决原始分类任务,使用剩余的数据。第一层级通过从训练集中移除简单的实例来影响第二层级。
在两个层次中,不平衡问题被减少,这使得建模任务更简单。
Python 实现
上述方法称为 ICLL(层次学习的不平衡分类)。以下是其实现:
from collections import Counter
from typing import List
import numpy as np
import pandas as pd
from scipy.cluster.hierarchy import linkage, fcluster
from scipy.spatial.distance import pdist
class ICLL:
"""
Imbalanced Classification via Layered Learning
"""
def __init__(self, model_l1, model_l2):
"""
:param model_l1: Predictive model for the first layer
:param model_l2: Predictive model for the second layer
"""
self.model_l1 = model_l1
self.model_l2 = model_l2
self.clusters = []
self.mixed_arr = np.array([])
def fit(self, X: pd.DataFrame, y: np.ndarray):
"""
:param X: Explanatory variables
:param y: binary target variable
"""
assert isinstance(X, pd.DataFrame)
X = X.reset_index(drop=True)
if isinstance(y, pd.Series):
y = y.values
self.clusters = self.clustering(X=X)
self.mixed_arr = self.cluster_to_layers(clusters=self.clusters, y=y)
y_l1 = y.copy()
y_l1[self.mixed_arr] = 1
X_l2 = X.loc[self.mixed_arr, :]
y_l2 = y[self.mixed_arr]
self.model_l1.fit(X, y_l1)
self.model_l2.fit(X_l2, y_l2)
def predict(self, X):
"""
Predicting new instances
"""
yh_l1, yh_l2 = self.model_l1.predict(X), self.model_l2.predict(X)
yh_f = np.asarray([x1 * x2 for x1, x2 in zip(yh_l1, yh_l2)])
return yh_f
def predict_proba(self, X):
"""
Probabilistic predictions
"""
yh_l1_p = self.model_l1.predict_proba(X)
try:
yh_l1_p = np.array([x[1] for x in yh_l1_p])
except IndexError:
yh_l1_p = yh_l1_p.flatten()
yh_l2_p = self.model_l2.predict_proba(X)
yh_l2_p = np.array([x[1] for x in yh_l2_p])
yh_fp = np.asarray([x1 * x2 for x1, x2 in zip(yh_l1_p, yh_l2_p)])
return yh_fp
@classmethod
def cluster_to_layers(cls, clusters: List[np.ndarray], y: np.ndarray) -> np.ndarray:
"""
Defining the layers from clusters
"""
maj_cls, min_cls, both_cls = [], [], []
for clst in clusters:
y_clt = y[np.asarray(clst)]
if len(Counter(y_clt)) == 1:
if y_clt[0] == 0:
maj_cls.append(clst)
else:
min_cls.append(clst)
else:
both_cls.append(clst)
both_cls_ind = np.array(sorted(np.concatenate(both_cls).ravel()))
both_cls_ind = np.unique(both_cls_ind)
if len(min_cls) > 0:
min_cls_ind = np.array(sorted(np.concatenate(min_cls).ravel()))
else:
min_cls_ind = np.array([])
both_cls_ind = np.unique(np.concatenate([both_cls_ind, min_cls_ind])).astype(int)
return both_cls_ind
@classmethod
def clustering(cls, X, method='ward'):
"""
Hierarchical clustering analysis
"""
d = pdist(X)
Z = linkage(d, method)
Z[:, 2] = np.log(1 + Z[:, 2])
sZ = np.std(Z[:, 2])
mZ = np.mean(Z[:, 2])
clust_labs = fcluster(Z, mZ + sZ, criterion='distance')
clusters = []
for lab in np.unique(clust_labs):
clusters.append(np.where(clust_labs == lab)[0])
return clusters
聚类部分自动完成,无需用户输入。因此,你唯一需要定义的是每个层次的学习算法。
以下是如何使用该方法的示例。在这个例子中,每个层次的模型是一个随机森林。
import pandas as pd
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier as RFC
# https://github.com/vcerqueira/blog/blob/main/src/icll.py
from src.icll import ICLL
# creating a dummy data set
X, y = make_classification(n_samples=500, n_features=5, n_informative=3)
X = pd.DataFrame(X)
# creating a instance of the model
icll = ICLL(model_l1=RFC(), model_l2=RFC())
# training
icll.fit(X, y)
# probabilistic predictions
probs = icll.predict_proba(X)
一个更严重的例子
层次方法与重采样的比较如何?
下面是基于糖尿病相关数据集的比较。你可以查看参考文献 1 获取详细信息。以下是如何将这两种方法应用于该数据集:
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve, roc_auc_score
from imblearn.over_sampling import SMOTE
# loading diabetes dataset https://github.com/vcerqueira/blog/tree/main/data
data = pd.read_csv('data/pima.csv')
X, y = data.drop('target', axis=1), data['target']
X = X.fillna(X.mean())
# train test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
# resampling with SMOTE
X_res, y_res = SMOTE().fit_resample(X_train, y_train)
# creating the models
smote = RFC()
icll = ICLL(model_l1=RFC(), model_l2=RFC())
# training
smote.fit(X_res, y_res)
icll.fit(X_train, y_train)
# inference
smote_probs = smote.predict_proba(X_test)
icll_probs = icll.predict_proba(X_test)
下面是每种方法的 ROC 曲线:

每种方法的 ROC 曲线。图片由作者提供。
ICLL 的曲线更接近左上角,这表明它是更好的模型。
在参考文献 2 的论文中进行了更多实验,其中介绍了 ICLL。结果表明 ICLL 在不平衡分类问题中表现具有竞争力。你可以在 Github 上查看实验代码。
关键要点
-
不平衡分类是数据科学中的一项重要任务;
-
对训练集进行重采样是处理这些问题的常见方法。但是,这可能导致信息丢失或噪声传播。常见的替代方法包括阈值调整或成本敏感模型;
-
你还可以使用层次方法来处理不平衡问题;
-
ICLL 是一种用于不平衡分类的层次方法。除了学习算法外,它不需要任何用户参数。ICLL 提供了与重采样方法相媲美的性能。
希望你觉得这个方法有用。感谢阅读,下次见!
参考文献
1 皮马印第安人糖尿病数据集(GPL-3 许可证)
2 Cerqueira, V., Torgo, L., Branco, P., & Bellinger, C. (2022). 通过层次学习的自动不平衡分类。机器学习, 1–22.
3 Branco, Paula, Luís Torgo, 和 Rita P. Ribeiro. “对不平衡领域预测建模的综述。” ACM 计算调查(CSUR) 49.2 (2016): 1–50。
如何与非数据人士讨论数据和分析
原文:
towardsdatascience.com/how-to-talk-about-data-and-analysis-to-non-data-people-2457dc600219
面向数据专业人员的逐步教程
·发表于 Towards Data Science ·14 分钟阅读·2023 年 9 月 22 日
--
数字盲症是新的文盲
在我最近的 文章 中,我指出今天许多公司面临的一个重大挑战是可用数据的庞大数量和它们在决策中有效使用数据的能力有限。这个问题的核心主要是人为因素。因此,迫切需要建立 数据素养。如果公司真心希望从海量数据中受益,它们必须提升整体的数字能力。
88% 的潜在受众可能在阅读数字、图表或计算时感到困难。
根据全球识字研究,仅有 12% 的成年人在数字方面具备基本能力。这意味着88% 的潜在受众可能在阅读数字、图表或计算时感到困难。¹ 这与全球的识字率形成鲜明对比,后者目前超过 86%。²
值得注意的是,许多社会忽视了这个问题。更有甚者,许多人对自己的数字盲症感到自豪。³ 你是否听过这样的借口:
-
我不是一个擅长数字的人。
-
统计学从未引起过我的兴趣。
-
物理学对我而言似乎像巫术。
或者是“终极”的:
- 我更倾向于人文学科。
这真的算是一个有效的借口吗?
通常,这不是能力不足的问题,而是缺乏合适的工具。考虑一下这种情况:
我们尝试过马路。我们观察到交通从两个方向接近。我们必须判断是否有足够的时间安全过马路。远处的那辆车呢?它的速度有多快?它的速度是在增加吗?在减少吗?司机注意到我们了吗?

来源:作者提供的图片
实际上,过马路是一个复杂的概率挑战。然而,我们每天都会处理这个问题,通常会多次进行,并且通常能做对。⁴
这只是冰山一角。
想想以 60 英里每小时的速度驾驶汽车。涉及多少因素?驾驶时,你还在监控速度计和各种其他仪器。这与解读管理仪表板上的复杂视觉信息并无不同。再说早上选择穿什么衣服。你在做出判断和预测,这些都是即使是复杂的 AI 工具也会面临的挑战:预测天气,判断流行趋势,以及搭配颜色。
人脑每天处理大约 74 GB 的数据。⁵ 值得注意的是,它仍然能做出决策。与此同时,我的 ChatGPT 在处理一个 20 万行的 Excel 电子表格时出现了问题。

来源:图片由 Alexander Wivel 提供,CC BY 3.0,链接。
使用“人文主义者”标签也不是一个有效的借口。考虑拆解复杂的文学作品,比如诗歌。或者处理深奥的哲学难题?或者拼凑复杂的历史战斗计划?这些任务往往比构建统计模型更具挑战性。
我为什么提到这些?我的观点是,不算数的情况通常并不是由于能力的缺乏。而是由于缺乏工具和无效的沟通。
讽刺的是,许多声称不懂统计的人在日常任务中表现出处理复杂概率评估和决策的能力。
为什么我们在沟通中使用数据?
有效的沟通在商业中至关重要;整合数据分析可以提升它。以下是方法:
-
提高意识: 使用数据突出问题,解释其原因,提出解决方案,并预测结果。
-
强调重要性: 数据客观地排序,指导决策,例如先处理哪些项目。
-
证明因果关系: 企业常常混淆相关性和因果关系。只有合适的数据工具才能澄清这一点。
-
讲故事的基础: 使用数据来构建叙事,提供背景,阐明问题,并得出结论。
-
预测能力: 许多公司依赖直觉进行情景规划。这是一种有风险的方法。数据提供了更清晰的前进路径。⁶
谁应该负责讨论数据?
在观看了许多 YouTube 视频和阅读了无数文章后,我能给出的简单答案是“数据科学家”。但这并不正确。请允许我详细说明我的理由。

来源:图片由作者基于 Rebecca Nugent | TEDxCMU 制作:我们都是数据科学家。
你看到上面图片中的独角兽了吗?从理论上讲,它代表了“完美的数据科学家”,具备硬技能和软技能的最佳结合。 然而,找到这样的人才就像寻找真正的独角兽一样困难。当你找到时,确保你的口袋足够深。
过分依赖这些稀有的人才有其风险。许多企业在这些顶级人才转向新机会时面临挫折。组织如何减轻这一风险? 可以在整个团队中培养这些结合的技能,而不是集中在一个人身上。 我将介绍一个关于小团队的简短案例研究,以更好地说明我的观点。
小案例研究:数据科学家和业务分析师之间的合作
方法论
这个小案例使用 ChatGPT Plus 和高级数据分析准备。具体来说,我利用它创建了合成数据和数据可视化。
设置
设想一个由数据科学家(DS)和业务分析师(BA)组成的小团队。他们的任务是探查和阐明零售公司日常销售中的不规律现象,该公司主要依赖店内购买。
合作模型
第 1 步: 数据科学家(DS)与业务分析师(BA)合作,精炼问题范围,确定分析的数据,建立时间框架,并选择最有效的沟通方式。
第 2 步: 然后数据科学家(DS)开始分析,过程如下:

来源:作者根据数据分析过程步骤制作的图片
第 3 步。 数据科学家(DS)采用结合移动平均和标准差的方法。这有助于检测销售数据中的异常。一个数据点超出设定的阈值(距离移动平均 2 个标准差)被标记为异常。
第 4 步。 完成后,数据科学家(DS)将发现传达给业务分析师(BA)。

来源:作者使用 ChatGPT 和高级数据分析生成的图片
第 5 步。 业务分析师(BA)解读异常:
-
我们店内的促销(第 50–60 天,2 月底): 这里的销售上升可能是由于店内促销,导致顾客访问和购买增加。
-
竞争对手的新店开张(第 150–160 天,6 月初): 附近的新店可能吸引了一些我们的顾客,导致我们的销售额下降。
-
临时店铺关闭(第 250–260 天,9 月中旬): 9 月份销售显著下降,表明我们的一家门店可能因翻修或意外事件暂时停止运营。
-
另一个促销活动(第 320–330 天,11 月下旬): 这个时期销售的激增可能源于我们店内的另一个促销或销售活动。
第 6 步。 业务分析师(BA)和数据科学家(DS)一起审查发现。这种合作使数据科学家(DS)获得业务方面的见解,同时业务分析师(BA)提升数据处理能力。
第 7 步。在相关建议的补充下,最终的分析结果会呈现给相关利益相关者。
结论
这个简洁的案例研究强调了数据科学家与业务分析师之间的协同关系。数据科学家专注于通过统计技术进行异常检测,而业务分析师则将这些发现置于背景中,描述潜在原因并提供实际见解。正如所示,两个角色之间的强大沟通对这一协作的成功至关重要。
就像那些“没有数学能力”的人可能会避开数字和图表一样,“有数学能力”的人常常也会犹豫参与人际互动。
沟通也是一门科学
就像那些“没有数学能力”的人可能会避开数字和图表一样,“有数学能力”的人常常也会犹豫参与人际互动。
你知道吗?这是一个极其不公正的刻板印象。
虽然我认识一些能够轻松沟通的数学能力强的人,即使你是一个内向的数学能力强者,你也可以利用你的技能,将沟通视为一种方法论,就像任何统计挑战一样。
沟通可以提炼为三个主要组成部分:

来源:基于沟通的谁、为什么、怎么做和什么的作者图片
这个简单的模型不仅是一种结构化沟通的方法。它还为我的演示提供了一个即时的结构,我可以始终如一地应用它。
现在,让我提供一些关于如何处理这三个组成部分的一手指导。
WHY: 关注他人
背景至关重要,正如我最新的一篇文章所示。背景对叙述的影响高达 80%。故事的背景涵盖三个核心方面:情境、功能和数据驱动。这些基础信息为叙述者和听众提供了准备,解答了关于故事受众、核心主题和传达方式的关键问题。拥抱并融入背景对于故事与其目标听众的连接和理解至关重要。
数据专业人员常见的一个疏忽是过于依赖技术语言。这种情况很容易发生,尤其是当我们忽视上下文时。例如,针对特定观众使用某些术语是否合适?如“cohort”或“longitudinal”这样不常在学术界外使用的短语,以及“surveillance”这样解释多样的术语,可能会引起困惑。类似地,“probability”或“risk”这样的概念也可能被误解。如果这些术语不可或缺,尽量以易于理解的方式呈现。即便详细的论点制作得再精细,也应避免陷入细节的泥潭。理解许多人基于直觉捷径而非逻辑模型进行操作是有益的,这些捷径受到诸如锚定效应、误解因果关系、忽视随机性等偏见的影响。⁶
如何:编写好的故事
精心编写的故事在企业沟通中的影响显而易见,特别是当有数据支持时。但在这次讨论中,我想深入探讨这些故事是如何构建的。考虑一下Brent Dykes所倡导的叙事弧。

来源:作者基于Brent Dykes 的帖子的图片
漫长的开头可能会毁掉使用数据的故事。 这可能是故意的,也可能是无意的。例如,观众可能突然缩短了一个精心组织的故事,导致我们不得不对其进行删减。有时只是因为故事计划得不好。我们如何避免这些问题呢?
Cole Nussbaumer Knafflic,全球畅销书的著名作者⁷,推荐了一种方法:3 分钟故事。
考虑制作一个简洁的、3 分钟的演讲版本。这种方法有两个好处。首先,它作为备用方案,以防你分配的时间意外减少到仅有的 3-5 分钟。其次,它作为一个完善工具。如果你能有效并快速地将演讲缩减到 3 分钟,从完整版本中剔除多余内容就会变得轻而易举。
排练
我建议的另一种策略是排练。尽可能地练习你的演讲,最好是大声朗读。虽然在镜子前排练是有益的,但请可信的同事提供反馈可能更有价值。
如何:使用类比
类比是将两个事物进行类比,通常用于阐明或澄清。例如,考虑我之前使用的“过马路”例子。使用类比时,我们旨在通过更熟悉的场景来揭示复杂的分析。

来源:作者基于数据讲故事:类比的力量的图片
类比背后的概念是将视角从具体的数据项目转移到一个更广泛、更熟悉的背景中,在那里解释,然后再将理解带回到原始数据背景中。类比作为有用的连接器,将复杂的数据主题与日常情况联系起来,使其更易于理解。要制作有意义的类比,重要的是深入理解数据,脑暴相关的情境,评估其适用性,然后重新聚焦于数据的核心问题。
那么,在哪里可以找到好的类比呢? 现实生活中的例子往往能够准确打击目标。然而,挑战在于当需要时能够随手拿到它们。一个现代解决方案是咨询 ChatGPT 以获得创意建议。以下,我提供了与之前讨论的迷你案例研究相关的类比示例。

来源:作者截图
另一种策略需要更多的投入:持续收集和存储类比想法。 这可以是一个实体笔记本、一个数字平板或任何适合你的方法。关键是保持这些笔记有条理并且触手可及。
然而,关于类比还有一个警示点。 过度延伸类比的风险。你对背景越不熟悉,你偏离的可能性就越大。如果人们认为比较过于简单、粗鲁或幼稚,这可能会成为问题。

不要让你的观众感觉像幼儿园的孩子一样。 来源:作者图像
如何:注意时间安排
为了管理时间问题,我为自己设定了一个指导方针。通常,我为每张幻灯片分配大约 1.5 分钟用于复杂的数据驱动型演示文稿,包括标题、议程、休息幻灯片等。我不区分内容丰富的幻灯片和较为简洁的幻灯片。在较长的演示文稿中,这种做法往往能达到平衡。对于不那么复杂的演讲,其中幻灯片的数据量较少,我可能将时间减少到每张幻灯片 1 分钟。在这种情况下,我总是优先提前排练这些演示文稿。有时,这种减少是基于之前的排练估算出来的。
如果给定了具体的时间,例如 15 分钟,特别是针对高级管理人员的演示文稿,我使用以下公式来确定最大幻灯片数量:

最优幻灯片数量公式。
Nslides表示演示文稿中的最大幻灯片数量(四舍五入到最接近的整数值)。Tminutes是分配的时间。对于少于十张幻灯片的简短演示文稿,我省略了议程或问答幻灯片。
对于 15 分钟的时间段,这个公式允许我制作最多 7 张幻灯片。因此,我会将我的演示结构为一个标题幻灯片,接着最多 6 张内容幻灯片。这将分解为一个背景幻灯片,最多四个分析幻灯片,以及一个专门用于主要结论和行动呼吁(CTA)的幻灯片。如有必要,我可能会略微调整这个分配,也许为 CTA 保存一个独立的幻灯片。
为什么要如此简洁?通常这样就足够了。此外,我们还需要考虑到不可预见的情况,例如:
-
连接显示设备的时间延长
-
主要参与者迟到
-
分配时间用于积极讨论
-
任何其他不可预见的中断或延迟。
如何:关注数字
一个关键的方面是我们展示数字的方法。为了说明这一点,我将提供一个示例。首先,让我们查看“错误的”图表。

来源:作者提供的图片
现在是修正后的版本。

来源:作者提供的图片
看出区别了吗?
第二张图展示了“有效数字”的指南。从我的经验来看,三个似乎是显示数字的最佳数量。使用两个数字虽然很诱人,但对于像公司收入这样的关键数据,有过度简化的风险,可能会误导。
然后是“友好数字”原则。它涉及以最简单、最易记的方式呈现数字。例如,与其详细说明 USD 2,954,321.51,我们可能会四舍五入到大约 USD 3 百万。但使用这种方法时要小心:这可能会无意中扭曲数据,从而误导受众。
如何:利用标题的力量
标题在数据驱动的讲述中扮演着至关重要的角色。一个引人注目的标题吸引了观众,并且通常提供了重要的见解,消除了从表格或图表中进行详细数据分析的需要。它使他们能够决定是否深入了解内容或等待更相关的信息。
在制作标题时,考虑利用新闻业和互联网中的“力量词”技巧。这些词汇能够巧妙地管理注意力和设定期望。例如,“迅速的胜利”,“唯一可行的解决方案”或“项目中的红旗”可能具有很大的影响力。然而,使用力量词时需谨慎:如果我们最初将一个解决方案呈现为简单快捷,但随后的内容揭示了复杂的挑战,我们的可信度可能会受到影响。
此外,避免使用纯技术性的标题,例如“报告给 IT 的事件数量”。相反,选择 功能性标题,提供背景信息,例如“实施后,报告的事件数量增加。” 这能确保你的受众能够迅速理解内容的相关性和影响。
如何:战略性地使用颜色
颜色是数据驱动沟通中的强大工具。然而,它是一个需要负责任使用的工具。我们应抵制仅仅为了装饰目的使用颜色的诱惑。在所有情况下,颜色应具有功能性意义,突出关键信息。为说明这一点,让我们看看下面的图表。
装饰性使用颜色:

来源:作者提供的图像
战略(功能性)使用颜色:

HOW: 明智选择视觉效果
正如这篇文章中探讨的那样,三种可视化方式是可靠的,可以传达广泛的数据相关信息而不会引起混淆。虽然这个列表并不详尽,但遵循它会显著提高成功的可能性。

来源:作者提供的图像
使用这三种可视化方式:
-
柱状图(或条形图): 适用于比较累积或离散的值。可以用于趋势展示,前提是没有重大变化。
-
折线图: 有效展示方向和趋势的变化,特别适用于连续变量。多个折线可以在同一图表上进行比较。
-
饼图: 适合显示某部分对整体的贡献(例如,某一特定产品产生的收入占总销售额的百分比)。无论如何都要避免使用饼图进行比较!

来源:作者在 Adobe Photoshop 2024 中生成的图像
创建图表时,遵循上述规则(例如,颜色和标题),并考虑适当的缩放(特别是纵轴,通常应从 0 开始,除非有充分理由)。通过去除框架、网格线、坐标轴标签或图例等多余元素来简化图表。
WHAT: 使用 CTA
本文的最后一条建议是使用行动号召(CTA)。
没有 CTA,我们的演示最多只能是一个引人入胜的或许有趣的信息。一个精心制作的 CTA 赋予数据驱动的讲故事以独特的特征。
什么使 CTA 有效?
-
使用主动语态而非被动语态。
-
小心使用祈使句。
-
清楚地识别出负责执行商定行动的个人或团队。
-
在演示后立即跟进决策,并随后在短时间内进行跟进以确保进展。
结论

成功: 作者在 Adobe Photoshop 2024 中生成的图像
在数据沟通中,数字能力和数字盲之间存在显著差距。尽管全球只有 12%的成年人具备数字素养,大多数人却难以理解数据。然而,讽刺的是,许多自称数字盲的人在日常任务中展示了处理复杂概率评估和决策的能力。挑战不在于他们的固有能力,而在于传达数据所使用的工具和方法。
有效的沟通策略通过制作相关的类比、战略性地使用视觉效果和采用深思熟虑的叙事来弥合这一鸿沟。通过掌握观众的背景并减少技术术语,数据专业人士可以提高可及性。
最终,数据讲故事和沟通的核心在于简化复杂性、与观众建立联系,并推动可操作的洞见。无论你是数据科学家还是商业分析师,关键在于使用观众的语言进行对话,确保数据的力量保持清晰。
你喜欢这篇文章吗?考虑订阅以获取我发布的新故事通知或关注我。
资源
-
安·怀利:各国的数字能力水平是多少?
-
世界人口审查:2023 年各国识字率
-
密苏里科技大学:数字盲:误传的结果
-
丽贝卡·纽金特 | TEDxCMU:我们都是数据科学家
-
奇思妙想:人脑每天学习多少信息?
-
国家癌症研究所:让数据发声:一本工作手册
-
科尔·努斯鲍默·克纳夫利克:用数据讲故事:商业专业人士的数据可视化指南
如何使用 Pytest 测试您的 Python 代码
原文:
towardsdatascience.com/how-to-test-your-python-code-with-pytest-c8f055979dd7
编程,Python
简化您的测试过程,并通过 Pytest 提升代码质量
·发布在 Towards Data Science ·5 分钟阅读·2023 年 6 月 1 日
--

作者提供的图像
您是否厌倦了花费几个小时调试代码和修复那些本可以轻易避免的错误?我们知道当您的代码不能正确工作时,尤其是在需要迅速交付结果的压力下,这会让人感到非常沮丧。但是别担心!有一个解决方案:Pytest。
Pytest 是一个强大的测试框架,帮助确保您的代码在不同的场景和条件下按预期工作。将 Pytest 集成到您的代码中可以帮助您在问题变成麻烦之前发现并修复错误,从而节省时间和精力。
在本文中,我们将向您展示如何开始使用 Pytest。从设置环境到编写覆盖所有代码的测试,我们为您提供了完整的指导。那么还等什么?让我们开始测试,确保您的代码每次都能完美运行。
我们将涵盖:
-
Pytest 概述
-
实际示例
让我们从 Pytest 的概述开始描述。
Pytest 概述
Pytest 是一个关注简单性和易用性的 Python 测试框架。要安装 Pytest,请运行以下命令:
$ pip install pytest
Pytest 遵循模块化和可扩展的架构,允许开发人员将测试组织成单独的测试函数或类。
当您使用 Pytest 构建测试时,必须遵循以下步骤:
-
创建一个新的脚本
test_<mytest>.py用于您的测试。将 Pytest 导入到脚本中。 -
定义您的测试函数:测试函数是一个 Python 函数,用于验证代码行为的特定方面。每个测试函数应该是自包含的,专注于单个测试用例。Pytest 会自动发现并执行这些测试函数。
-
测试命名规范:每个测试函数必须以
test_开头,以便 Pytest 正确识别它。 -
测试断言:使用测试断言来定义函数的行为。Pytest 提供了广泛的强大断言函数。
-
测试夹具:夹具是提供可重用和预配置的资源或测试数据的函数,供你的测试函数使用。
一些资产函数列表
下面是 Pytest 提供的一些主要断言:
-
assert:通过检查给定条件是否为真来进行简单断言。 -
assertEqual:比较两个值的相等性。它检查期望值是否等于实际值。 -
assertTrue和assertFalse:验证给定条件是否为真或假。 -
assertRaises:检查在执行代码时是否抛出特定异常。 -
assertIn和assertNotIn:验证值是否在给定集合或序列中存在或不存在。 -
assertAlmostEqual:将浮点值与指定的精度进行比较。 -
assertDictEqual:检查两个字典是否具有相同的键值对集合。 -
assertRaisesRegex:验证是否抛出特定异常,并且异常消息是否匹配正则表达式模式。
现在你已经了解了 Pytest 背后的基本概念,让我们进入实际示例。
实际示例
让我们设想构建一个计算器并测试其实现是否正确。首先,我们定义核心函数,然后编写测试。
核心函数
核心函数实现了计算器的操作。例如,我们可以编写以下简单代码来实现主要操作:
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
def divide(a, b):
return a / b
将文件保存为 calculator.py。
测试函数
在我们保存核心函数的相同目录中,添加一个名为 test_calculator.py 的脚本。首先,导入核心函数和 Pytest 库:
import pytest
from calculator import add
然后,为 calculator.py 文件中的每个函数添加测试。对于每个函数,添加一个单独的测试。例如,要测试 add() 函数,编写以下测试:
def test_addition():
result = add(2, 4)
assert result == 6
result = add(-1, 3)
assert result == 2
result = add(0, 0)
assert result == 0
类似地,你可以为其他函数编写测试。你可以在这个 GitHub 仓库中找到完整的示例。
要运行测试,打开终端,并从包含两个脚本的文件夹中运行以下命令:
pytest
你应该得到以下输出:
% pytest
================================================== test session starts ===================================================
platform darwin -- Python 3.8.10, pytest-7.1.3, pluggy-1.0.0
rootdir: /Users/angelica/CNR/Git/data-science/Tests/Calculator
plugins: typeguard-2.13.3, anyio-3.3.4
collected 4 items
test_calculator.py .... [100%]
=================================================== 4 passed in 0.02s ====================================================
使用测试夹具
让我们修改前面的示例以支持测试夹具。首先,创建一个新目录和一个名为 calculator.py 的新文件。将计算函数封装在一个类中:
class Calculator:
def __init__(self):
self.result = 0
def add(self, a, b):
self.result = a + b
return self.result
def subtract(self, a, b):
self.result = a - b
return self.result
def multiply(self, a, b):
self.result = a * b
return self.result
def divide(self, a, b):
self.result = a / b
return self.result
然后,在相同目录中创建另一个名为 test_calculator.py 的脚本。在脚本开始时导入 Pytest 和 Calculator 类:
import pytest
from calculator import *
使用 @pytest.fixture 装饰器定义一个名为 calculator 的夹具。夹具是提供可重用资源或设置操作的函数。在这种情况下,calculator 夹具创建了 Calculator 类的实例并返回它。
@pytest.fixture
def calculator():
# Create an instance of the calculator
calc = Calculator()
return calc
现在我们可以在测试中使用测试夹具 calculator:
def test_multiplication(calculator):
result = calculator.multiply(4, 3)
assert result == 12
def test_division(calculator):
result = calculator.divide(10, 2)
assert result == 5
最后,运行测试:
pytest
你应该得到以下输出:
pytest
================================================== test session starts ===================================================
platform darwin -- Python 3.8.10, pytest-7.1.3, pluggy-1.0.0
rootdir: /Users/angelica/CNR/Git/data-science/Tests/CalculatorFixture
plugins: typeguard-2.13.3, anyio-3.3.4
collected 4 items
test_calculator.py .... [100%]
=================================================== 4 passed in 0.01s ====================================================
你可以在这个 GitHub 仓库中找到完整的示例。
总结
恭喜!你刚刚学习了如何使用 Pytest 为你的 Python 代码添加测试!将 Pytest 作为你的测试框架可以确保你的 Python 代码的可靠性和长期稳定性!
使用 Pytest 非常简单:你只需编写直观的测试函数!
你可能还感兴趣的是……
如何在 Docker 中删除镜像,从基础镜像重建镜像,将一些配置文件传递给 Docker……
medium.com ## 如何安装 Spark NLP
关于如何在本地计算机上使 Spark NLP 工作的分步教程
[towardsdatascience.com ## Python Pandas 数据清洗入门
一个使用流行的 Python 库进行数据清洗的实际示例。
[towardsdatascience.com
如何测试你的单元测试
原文:
towardsdatascience.com/how-to-test-your-unit-tests-afe18d6e19d8
单元测试
突变测试简介
·发表于 Towards Data Science ·阅读时长 5 分钟·2023 年 1 月 16 日
--

图片由 Erik Mclean 提供,来自 Unsplash
在我上一篇文章中,我讲述了单元测试如何成为数据科学家工作中的一个重要方面,因为错误的代码会导致分析结果出现问题。提高单元测试的一种方法是将属性驱动测试与示例驱动测试结合使用,即常规的单元测试方式。你可以在这里查看我上一篇文章:
Python 中使用 Hypothesis 进行属性驱动测试的指南
towardsdatascience.com
在发布这篇文章后,我收到了来自.com 软件的回应,向我推荐了突变测试,我今天想向你解释一下。长话短说:
突变测试可以帮助你发现弱测试。
弱测试是那些给你错误安全感的测试,因为它们即使明知道不该通过也会通过,其中最典型的就是assert True。替换这些测试可以使你的整个测试套件更有意义和可信。
现在,让我们深入了解一下这是什么吧!
弱测试
考虑这个非常简单的函数:
def mean(x, y):
return (x+y) / 2
假设你想编写单元测试,尽管这个函数非常简单。一个test_mean.py 文件可能如下所示:
def mean(x, y):
return (x+y) / 2
def test_mean():
assert mean(4, 0) == 2
调用pytest test_mean.py当然会导致测试通过。一切都是绿色的,我们甚至达到了 100%的覆盖率!是时候继续下一个功能了,对吧?不!
让我们仔细看看测试代码中的断言:函数的第二个参数是零,这意味着函数中的y基本上被忽略了。输入具有特殊的形状,使测试比预期更容易通过。例如,如果函数是
def definitely_not_the_mean(x, y):
return (x-y) / 2 # test_mean still passes
即使在这种简单的函数变异——将加号改为减号——test_mean仍然通过。
这不应该发生,如果测试足够好,它应该在我们更改函数定义时失败!
像这样的测试就像一个金属探测器,即使面对木材也会发出声音。这就是为什么我们称test_mean为薄弱的测试。
这个薄弱的测试在这里可能很容易被发现,但如果你写的代码稍微复杂一些,你可能会意外地创建出薄弱的示例却没有注意到,从而给你一种虚假的安全感。
变异测试
我们可以使用变异测试来发现薄弱的测试。变异测试的理念已经相当古老:它最初由理查德·利普顿在 1971 年作为学生提出。它也非常简单,实际上我在上一章已经简单介绍过了。
对你想要测试的函数源代码进行小的修改,并查看测试是否仍然通过。如果它们通过了,那它们就是薄弱的。如果它们失败(并且原函数没有失败),它们幸存下来并可能是强的。
你已经看到了一个变异,即从 + 变为 -。此外,你还可以改变:
-
- 改为 +
-
/ 改为 * 或反之亦然,
-
≥ 改为 > 或者 <,
-
≤ 改为 < 或者 >
-
= 改为 != 或反之亦然
-
从整数中加减 1
-
向字符串中添加一些随机字符
-
…
你明白了吧。只需简单的调整,仍然生成有效的源代码,将你想要测试的原函数变成稍微不同的东西。
当然,你可以手动完成这些操作,几次更改源代码,每次之后都运行一次pytest。但因为这没有趣味,有人创建了一个包来自动完成这项工作并记录结果。
Mutmut 方便的变异测试
Anders Hovmöller aka boxed和许多其他贡献者创建了一个名为mutmut的包来为你完成这项工作。
首先,通过pip install mutmut安装它。然后,你需要将你想要测试的函数提取到一个文件中,我们称之为function.py。我们还假设测试在test_mean.py中。然后你可以调用
mutmut run --paths-to-mutate function.py
它将创建一些包含我们想要测试的mean函数的function.py的变异。
如果你在 mutmut 运行时在一个会不断重新加载文件的编辑器中打开 function.py ,你甚至可以看到文件如何实时变化!但不用担心:mutmut 会在进行修改之前先创建原始文件的副本。
输出将如下所示:

图片由作者提供。
你可以在这里看到几个方面,尤其是在底部:
-
创建了三个突变体。(旁注:+变为-,/变为,在我们的案例中是 2 变为 3*)
-
这三个突变体中的两个被杀死,意味着之前通过的单元测试在文件更改后失败了,这很好 🎉。
-
一个突变体幸存了,这意味着尽管源代码发生了变化,测试仍然通过。这很糟糕 🙁。我们已经看到过这个幸存者:
def mean(x, y):
return (x-y) / 2 # the surviving mutant
你还可以通过 mutmut html 创建一个 HTML 报告并在浏览器中打开:

图片由作者提供。
设计可以更好,但它包含了关于幸存者的主要信息。
注意: 有时两个较弱的单独测试可以形成一个更好的测试套件。例如,如果你添加了断言
*assert mean(-1, 1) == 0*— 另一个较弱的测试,因为突变体*return (x+y) * 2*将幸存 — 总的来说,没有突变体会幸存于 mutmut 的程序,从而改进测试套件。
结论
在本文中,我们看到通过测试甚至 100%的测试覆盖率可能会令人误解。通过的测试很好,但如果即使在更改被测试函数后仍然通过,这可能是一个红旗,表明测试可能较弱。
突变测试提供了一种结构化的方式来更改(突变)函数。Python 库 mutmut 甚至允许我们自动更改源代码(事先创建了备份),并检查哪些测试在突变后仍然通过(🙁),哪些测试失败(🎉)。
这使你的测试套件更加健壮,从而让你生成正确的代码,这对于无错误分析是必要的。
我希望你今天学到了新的、有趣的、有用的东西。感谢阅读!
最后一点,如果你
-
想支持我写更多关于机器学习的内容和
-
计划无论如何获得一个 Medium 订阅,
为什么不通过 这个链接支持我呢?这对我帮助很大!😊**
为了透明起见,对你来说价格不会改变,但约一半的订阅费用直接归我。
非常感谢,如果你考虑支持我!
如果你有任何问题,请在 LinkedIn上写信给我!
如何使用 Gensim 从头开始训练 Word2Vec 模型
原文:
towardsdatascience.com/how-to-train-a-word2vec-model-from-scratch-with-gensim-c457d587e031
在本文中,我们将探索 Gensim,这是一种非常流行的 Python 库,用于训练基于文本的机器学习模型,以从头开始训练一个 Word2Vec 模型
·发表于 Towards Data Science ·9 分钟阅读·2023 年 2 月 6 日
--

作者提供的图像。
Word2Vec 是一个机器学习算法,它允许你创建单词的向量表示。
这些表示被称为嵌入,在许多自然语言处理任务中使用,如单词聚类、分类和文本生成。
Word2Vec 算法标志着自然语言处理(NLP)世界的一个时代的开始,它在 2013 年由 Google 首次介绍。
它基于通过神经网络训练的非常大的数据语料库创建的单词表示。
Word2Vec 的输出是向量,每个向量对应训练词典中的一个单词,有效地捕捉了单词之间的关系。
在向量空间中相互接近的向量基于上下文具有相似的含义,而相距较远的向量具有不同的含义。例如,“强大”和“雄伟”这两个词在向量空间中会很接近,而“强大”和“巴黎”这两个词则会相对远离。
这比基于简单计算文本数据语料库中存在的标记的袋装单词模型性能有了显著改进。
在本文中,我们将探索 Gensim,这是一种流行的 Python 库,用于训练基于文本的机器学习模型,以从头开始训练一个 Word2Vec 模型。
我将使用我个人博客中的意大利语文章作为本项目的文本语料库。 随意使用你希望的任何语料库——这个流程是可扩展的。
这种方法适用于任何文本数据集。你将能够自己创建嵌入向量并进行可视化。
让我们开始吧!
项目要求
让我们列出一些行动步骤,这些步骤将作为项目的基础。
-
我们将创建一个新的虚拟环境。
(阅读这里以了解如何操作:如何为机器学习设置开发环境)
-
安装依赖项,其中包括 Gensim
-
准备我们的语料库以交给 Word2Vec
-
训练模型并保存它
-
使用 TSNE 和 Plotly 来可视化嵌入,以便直观理解 Word2Vec 生成的向量空间。
-
附加:使用 Datapane 库创建一个互动 HTML 报告,与我们想要分享的人共享
到文章结尾时,我们将拥有一个很好的基础,以便开展更复杂的推理,例如嵌入的聚类等。
我假设你已经正确配置了你的环境,所以在这篇文章中我不会解释如何操作。我们直接开始下载博客数据吧。
依赖项
在开始之前,我们需要通过在终端中运行pip install XXXXX来确保安装以下项目级别的依赖。
-
trafilatura -
pandas -
gensim -
nltk -
tqdm -
scikit-learn -
plotly -
datapane
我们还将初始化一个logger对象,以便在终端接收 Gensim 消息。
获取语料库数据
如前所述,我们将使用我个人博客中的意大利语文章(diariodiunanalista.it)作为我们的语料库数据。
在 Deepnote 中它的样子是这样的。

我们以 pandas 数据框格式收集的数据。图片来源于作者。
我们将要使用的文本数据位于article列中。让我们看看随机文本的样子。

无论语言如何,这些数据在交给 Word2Vec 模型之前都应进行处理。我们需要去除意大利语停用词,清理标点符号、数字和其他符号。这将是下一步。
数据语料库的准备
首先需要导入一些用于预处理的基本依赖。
# Text manipulation libraries
import re
import string
import nltk
from nltk.corpus import stopwords
# nltk.download('stopwords') <-- we run this command to download the stopwords in the project
# nltk.download('punkt') <-- essential for tokenization
stopwords.words("italian")[:10]
>>> ['ad', 'al', 'allo', 'ai', 'agli', 'all', 'agl', 'alla', 'alle', 'con']
现在让我们创建一个preprocess_text函数,它接收一些文本作为输入并返回一个干净的版本。
def preprocess_text(text: str, remove_stopwords: bool) -> str:
"""Function that cleans the input text by going to:
- remove links
- remove special characters
- remove numbers
- remove stopwords
- convert to lowercase
- remove excessive white spaces
Arguments:
text (str): text to clean
remove_stopwords (bool): whether to remove stopwords
Returns:
str: cleaned text
"""
# remove links
text = re.sub(r"http\S+", "", text)
# remove numbers and special characters
text = re.sub("[^A-Za-z]+", " ", text)
# remove stopwords
if remove_stopwords:
# 1\. create tokens
tokens = nltk.word_tokenize(text)
# 2\. check if it's a stopword
tokens = [w.lower().strip() for w in tokens if not w.lower() in stopwords.words("italian")]
# return a list of cleaned tokens
return tokens
让我们通过使用带有.apply的 lambda 函数将这个函数应用到 Pandas 数据框中。
df["cleaned"] = df.article.apply(
lambda x: preprocess_text(x, remove_stopwords=True)
)
我们得到了一个干净的系列。

每篇文章都经过了清理和分词处理。图片来源于作者。
让我们检查一下文本,看看我们预处理的效果。

单个清理后的文本的样子。图片来源于作者。
现在文本看起来已经准备好被 Gensim 处理了。我们继续。
Word2Vec 训练
首先要做的是创建一个变量texts,它将包含我们的文本。
texts = df.cleaned.tolist()
现在我们准备好训练模型了。Word2Vec 可以接受许多参数,但现在我们不必担心这些。训练模型非常简单,只需要一行代码。
from gensim.models import Word2Vec
model = Word2Vec(sentences=texts)

Word2Vec 训练过程。图片来源于作者。
我们的模型已经准备好,并且嵌入已经创建。为了测试这一点,让我们尝试找出单词overfitting的向量。

“overfitting”一词的词嵌入。图像来源:作者。
默认情况下,Word2Vec 创建 100 维的向量。这个参数可以更改,还有许多其他参数,在我们实例化类时进行设置。无论如何,关联的维度越多,神经网络对单词本身及其与其他单词关系的了解就越多。
显然,这具有更高的计算和内存成本。
请注意:Word2Vec无法为词汇表中未出现的单词生成向量(称为 OOV — 词汇外单词)是其最重要的限制之一。

W2V 的一个主要限制是无法为词汇外单词映射嵌入。图像来源:作者。
因此,为了处理新单词,我们必须训练一个新模型或手动添加向量。
计算两个单词之间的相似度
使用余弦相似度,我们可以计算向量在空间中的距离。
使用下面的命令,我们指示 Gensim 查找与overfitting最相似的前三个单词
model.wv.most_similar(positive=['overfitting'], topn=3))

与“overfitting”最相似的词。图像来源:作者。
让我们看看单词“when”(quando 在意大利语中)在这个结果中是如何呈现的。将类似的副词包含在停用词中以清理结果会比较合适。
要保存模型,只需执行model.save("./path/to/model")。
使用 TSNE 和 Plotly 可视化嵌入
我们的向量是 100 维的。除非我们做些事情来减少它们的维度,否则可视化它们是个问题。
我们将使用 TSNE,这是一种降低向量维度的技术,并创建两个组件,一个用于 X 轴,另一个用于 Y 轴上的散点图。
在下面的.gif 中,你可以看到感谢 Plotly 功能嵌入在空间中的单词。

我的意大利博客的嵌入在 TSNE 投影中呈现的效果。图像来源:作者。
这是生成此图像的代码。
def reduce_dimensions(model):
num_components = 2 # number of dimensions to keep after compression
# extract vocabulary from model and vectors in order to associate them in the graph
vectors = np.asarray(model.wv.vectors)
labels = np.asarray(model.wv.index_to_key)
# apply TSNE
tsne = TSNE(n_components=num_components, random_state=0)
vectors = tsne.fit_transform(vectors)
x_vals = [v[0] for v in vectors]
y_vals = [v[1] for v in vectors]
return x_vals, y_vals, labels
def plot_embeddings(x_vals, y_vals, labels):
import plotly.graph_objs as go
fig = go.Figure()
trace = go.Scatter(x=x_vals, y=y_vals, mode='markers', text=labels)
fig.add_trace(trace)
fig.update_layout(title="Word2Vec - Visualizzazione embedding con TSNE")
fig.show()
return fig
x_vals, y_vals, labels = reduce_dimensions(model)
plot = plot_embeddings(x_vals, y_vals, labels)
这种可视化有助于识别数据中的语义和句法倾向。
例如,这非常有助于指出异常,如因某种原因倾向于聚集在一起的单词组。
Word2Vec 的参数
通过检查 Gensim 网站,我们可以看到 Word2Vec 接受许多参数。最重要的参数包括vectors_size、min_count、window和sg。
-
vectors_size : 定义了我们向量空间的维度。
-
min_count: 训练前,会将频率低于 min_count 的单词从词汇表中移除。
-
window: 句子中当前词与期望词之间的最大距离。
-
sg:定义训练算法。0 = CBOW(连续词袋),1 = Skip-Gram。
我们不会详细讨论每一个。我建议感兴趣的读者查看Gensim 文档。
我们尝试使用以下参数重新训练我们的模型。
VECTOR_SIZE = 100
MIN_COUNT = 5
WINDOW = 3
SG = 1
new_model = Word2Vec(
sentences=texts,
vector_size=VECTOR_SIZE,
min_count=MIN_COUNT,
sg=SG
)
x_vals, y_vals, labels = reduce_dimensions(new_model)
plot = plot_embeddings(x_vals, y_vals, labels)

基于 Word2Vec 新参数的新投影。图片由作者提供。
表示发生了很大变化。向量的数量与之前相同(Word2Vec 的默认值为 100),而min_count、window和sg已从默认值进行了更改。
我建议读者更改这些参数,以了解哪种表示更适合自己的情况。
奖金:使用 Datapane 创建一个互动报告
我们已经到了文章的结尾。我们通过创建一个 HTML 格式的互动报告,利用 Datapane 使用户能够在浏览器中直接查看之前用 Plotly 创建的图表来结束这个项目。

使用 Datapane 创建的互动报告。图片由作者提供。
这是 Python 代码
import datapane as dp
app = dp.App(
dp.Text(text='# Visualizzazione degli embedding creati con Word2Vec'),
dp.Divider(),
dp.Text(text='## Grafico a dispersione'),
dp.Group(
dp.Plot(plot),
columns=1,
),
)
app.save(path="test.html")
Datapane 高度可定制。我建议读者查阅文档,以便整合美学和其他功能。
结论
我们已经看到如何使用 Gensim 和 Word2Vec 从头构建嵌入。如果你有结构化的数据集,并且知道 Gensim 的 API,这个过程非常简单。
使用嵌入我们可以做很多事情,例如
-
做文档聚类,在向量空间中展示这些聚类
-
研究单词之间的相似性
-
将嵌入作为机器学习模型中的特征
-
为机器翻译奠定基础
等等。如果你对延伸至这里讨论主题的内容感兴趣,请留言告诉我 👍
通过这个项目,你可以丰富你的 NLP 模板组合,并向利益相关者展示你在机器学习背景下处理文本文件的专业知识。
下一篇文章 👋
如果你想支持我的内容创作活动,请随时通过下面的推荐链接关注我并加入 Medium 会员计划。我将获得你投资的一部分,同时你也能无缝访问 Medium 上大量的数据科学等相关文章。
[## 使用我的推荐链接加入 Medium - Andrea D'Agostino
阅读 Andrea D'Agostino(以及 Medium 上其他数千名作家的)每个故事。你的会员费用直接…
medium.com](https://medium.com/@theDrewDag/membership?source=post_page-----c457d587e031--------------------------------)
如何训练 BERT 进行掩码语言建模任务
原文:
towardsdatascience.com/how-to-train-bert-for-masked-language-modeling-tasks-3ccce07c6fdc
从零开始使用 Python 和 Transformers 库构建 MLM 任务语言模型的实用指南
Medium Towards Data Science
Ransaka Ravihara
·发表于 Towards Data Science ·阅读时长 7 分钟·2023 年 10 月 17 日
--

介绍
近年来,大型语言模型(LLMs)吸引了整个机器学习社区的关注。在 LLMs 出现之前,我们经历了一个重要的研究阶段,研究了各种语言建模技术,包括掩码语言建模、因果语言建模和序列到序列语言建模。
从上述列表来看,诸如 BERT 这样的掩码语言模型在分类和聚类等下游 NLP 任务中变得更加实用。由于 Hugging Face Transformers 等库的帮助,调整这些模型以适应下游任务变得更加可及和可管理。感谢开源社区,我们有大量的语言模型可以选择,涵盖了广泛使用的语言和领域。
本教程的 GitHub 仓库
GitHub [## GitHub - Ransaka/Train-BERT-for-Masked-Language-Modeling-Tasks: GitHub repo for TDS article "How to…
GitHub 仓库用于 TDS 文章 "如何训练 BERT 进行掩码语言建模任务" - GitHub …
微调还是从头开始构建?
在将现有语言模型适应于特定用例时,有时我们可以在不进一步调整(即所谓的微调)的情况下使用现有模型。例如,如果你需要一个英语情感/意图检测模型,你可以进入 HuggingFace.co 并找到适合你用例的模型。

然而,您只能对现实世界中遇到的一些任务抱有这种期望。这就是我们需要一种称为微调的额外技术的地方。首先,您必须选择一个将要微调的基础模型。在这里,您必须小心选择的模型和目标语言的词汇相似性。
然而,如果您找不到适用于所需语言的合适模型,请考虑从头开始构建一个。在本教程中,我们将实现用于掩码语言模型的 BERT 模型。
BERT 架构
尽管描述 BERT 架构超出了本教程的范围,但为了清晰起见,我们还是非常简略地介绍一下。BERT,或双向编码器表示来自变换器,属于仅编码器的变换器家族。它于 2018 年由 Google 的研究人员引入。
论文摘要:
我们介绍了一种新的语言表示模型,称为 BERT,代表双向编码器表示来自变换器。与最近的语言表示模型不同,BERT 旨在通过在所有层中共同考虑左右上下文来从未标记文本中预训练深度双向表示。因此,预训练的 BERT 模型可以通过添加一个额外的输出层来进行微调,从而创建广泛任务的最先进模型,如问答和语言推理,而无需对任务特定的架构进行 substantial 修改。
BERT 从概念上讲很简单,从经验上讲却非常强大。它在十一项自然语言处理任务中获得了新的最先进结果,包括将 GLUE 分数推高至 80.5%(绝对提高 7.7 个百分点),MultiNLI 准确率提升至 86.7%(绝对提高 4.6 个百分点),SQuAD v1.1 问答测试 F1 提升至 93.2(绝对提高 1.5 个点),以及 SQuAD v2.0 测试 F1 提升至 83.1(绝对提高 5.1 个点)。
在上面,我们可以看到一个有趣的关键词——双向。双向特性赋予了 BERT 类似于人类的能力。假设您需要填入一个像下面这样的空白,
“战争有时可能是一种必要的恶行。但无论多么必要,它始终是一种 _____,从来不是一种好事。”
要猜测单词填入空白位置,您需要注意以下几点:空白前的单词、空白后的单词,以及整个句子的上下文。根据这种人类特性,BERT 也以相同的方式工作。在训练过程中,我们隐藏一些单词,并要求 BERT 尝试预测这些单词。训练完成后,BERT 可以根据前后单词来预测被掩码的标记。为此,模型应对输入序列中的单词分配不同的注意力,这可能会显著影响对掩码标记的预测。

图片来源:作者,huggingface.co/spaces/exbert-project/exbert
如您所见,模型将适合隐藏位置的词视为 evil,并且将第一个句子的 evil 视为进行此预测所必需的。这是一个显著的点,表明模型理解输入序列的上下文。这种上下文感知使得 BERT 能够为给定任务生成有意义的句子嵌入。进一步,这些嵌入可以用于下游任务,如聚类和分类。说够了 BERT;让我们从头开始构建一个。
定义 BERT 模型
我们通常有 BERT(base) 和 BERT(large)。两者每个头部都有 64 维度。large 变体包含 24 个编码器层,而 base 变体只有 12 层。不过,我们并不局限于这些配置。令人惊讶的是,我们可以完全控制使用 Hugging Face Transformers 库定义模型。我们需要做的就是使用 BertConfig 类定义所需的模型配置。
我选择了 6 个头和 384 个总模型维度,以符合原始实现。这样,每个头都有 64 个维度,类似于原始实现。让我们初始化我们的 BERT 模型。
from transformers import BertConfig, BertForMaskedLM
config = BertConfig(
hidden_size = 384,
vocab_size= tokenizer.vocab_size,
num_hidden_layers = 6,
num_attention_heads = 6,
intermediate_size = 1024,
max_position_embeddings = 256
)
model = BertForMaskedLM(config=config)
print(model.num_parameters()) #10457864
训练标记器
在这里,我不会描述标记化的底层工作原理。相反,让我们使用 Hugging Face tokenizers 库从零开始训练一个标记器。请注意,原始 BERT 实现中使用的标记器是 WordPiece 标记器,这是一种基于子词的标记化方法。您可以通过下面的 HuggingFace 资源深入了解这种标记化。
## WordPiece tokenization — Hugging Face NLP Course
我们正在通过开源和开放科学来推进和普及人工智能。
这里使用的数据集是 Sinhala-400M 数据集 (under apache-2.0)。您可以使用任何您拥有的数据集来进行相同的操作。

正如您可能注意到的,有些僧伽罗语单词也用英语键入。让我们为这些语料库训练一个标记器。
首先让我们导入必要的模块。使用 Hugging Face Tokenizers 库训练标记器的好处是,我们可以使用现有的标记器并仅根据我们的训练语料库替换词汇表(并在适用时合并)。这意味着标记化步骤,如预标记化和后标记化,将被保留。为此,我们可以使用 train_new_from_iterator 方法来自 BertTokenizer 类。
from tokenizers.implementations import ByteLevelBPETokenizer
from tokenizers.processors import BertProcessing
from transformers import AutoTokenizer
from datasets import Dataset
import pandas as pd
#load base tokenizer to train on dataset
tokenizer_base = AutoTokenizer.from_pretrained("bert-base-cased")
# convert pandas dataset to HF dataset
dataset = Dataset.from_pandas(df.rename(columns={"comment":'text'}))
# define iterator
training_corpus = (
dataset[i : i + 1000]["text"]
for i in range(0, len(dataset), 1000)
)
#train the new tokenizer for dataset
tokenizer = tokenizer_base.train_new_from_iterator(training_corpus, 5000)
#test trained tokenizer for sample text
text = dataset['text'][123]
print(text)

# let's check tokenization process
input_ids = tokenizer(text).input_ids
subword_view = [tokenizer.convert_ids_to_tokens(id) for id in input_ids]
np.array(subword_view)

你可以看到像“cricketer”这样的词被分解为cricket和##er,这表明标记器已经经过充分训练。然而,可以尝试不同的词汇大小;我的词汇量是 5000,相对较小,但适合这个玩具示例。
最终,我们可以将训练好的标记器保存到我们的目录中。
tokenizer.save_pretrained("tokenizer/sinhala-wordpiece-yt-comments")
定义数据整理器并标记化数据集。
让我们定义一个用于 MLM 任务的整理器。在这里,我们将掩盖 15%的标记。无论如何,我们可以设置不同的掩盖概率。
from transformers import DataCollatorForLanguageModeling
data_collator = DataCollatorForLanguageModeling(
tokenizer=tokenizer, mlm=True, mlm_probability=0.15
)
让我们使用之前创建的标记器对数据集进行标记化。我用自定义类替代了原始的LineByLineTextDataset,利用了 Hugging Face accelerate。
import torch
from torch.utils.data import Dataset
from accelerate import Accelerator, DistributedType
class LineByLineTextDataset(Dataset):
def __init__(self, tokenizer, raw_datasets, max_length: int):
self.padding = "max_length"
self.text_column_name = 'text'
self.max_length = max_length
self.accelerator = Accelerator(gradient_accumulation_steps=1)
self.tokenizer = tokenizer
with self.accelerator.main_process_first():
self.tokenized_datasets = raw_datasets.map(
self.tokenize_function,
batched=True,
num_proc=4,
remove_columns=[self.text_column_name],
desc="Running tokenizer on dataset line_by_line",
)
self.tokenized_datasets.set_format('torch',columns=['input_ids'],dtype=torch.long)
def tokenize_function(self,examples):
examples[self.text_column_name] = [
line for line in examples[self.text_column_name] if len(line[0]) > 0 and not line[0].isspace()
]
return self.tokenizer(
examples[self.text_column_name],
padding=self.padding,
truncation=True,
max_length=self.max_length,
return_special_tokens_mask=True,
)
def __len__(self):
return len(self.tokenized_datasets)
def __getitem__(self, i):
return self.tokenized_datasets[i]
让我们对数据集进行标记化。
tokenized_dataset_train = LineByLineTextDataset(
tokenizer= tokenizer,
raw_datasets = dataset,
max_length=256,
)
好的,让我们编写训练循环代码。
from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
output_dir="./model",
overwrite_output_dir=True,
push_to_hub=True,
hub_model_id="Ransaka/sinhala-bert-yt-comments",
num_train_epochs=2,
per_device_train_batch_size=32,
save_steps=5_000,
logging_steps = 1000,
save_total_limit=2,
use_mps_device = True, # disable this if you're running non-mac env
hub_private_repo = False, # please set true if you want to save model privetly
save_safetensors= True,
learning_rate = 1e-4,
report_to='wandb'
)
trainer = Trainer(
model=model,
args=training_args,
data_collator=data_collator,
train_dataset=tokenized_dataset_train
)
我们可以使用其train()方法调用训练器。
trainer.train()

在充分训练后,我们的模型可以用于下游任务,如零样本分类和聚类。你可以通过使用这个 Hugging Face 空间的示例获得更多详细信息。
[## Sinhala Embedding Space - Ransaka 的 Hugging Face 空间
发现由社区制作的惊人 ML 应用
huggingface.co](https://huggingface.co/spaces/Ransaka/sinhala-embedding-space?source=post_page-----3ccce07c6fdc--------------------------------)
结论
在资源有限的情况下,预训练模型可能只识别特定的语言模式,但它们在特定的使用案例中仍然很有用。强烈建议在可能的情况下进行微调。
在本文中,除非另有说明,否则所有图片均为作者提供。
参考文献
-
一个可探究的 BERT —
huggingface.co/spaces/exbert-project/exbert -
BERT 论文 —
arxiv.org/abs/1810.04805
如何训练 LILT 模型并在发票上进行推断
原文:
towardsdatascience.com/how-to-train-the-lilt-model-on-invoices-and-run-inference-8fd6b3cfae1b
分步教程
·发布于Towards Data Science ·5 分钟阅读·2023 年 1 月 3 日
--

图片来源:Zinkevych_D来自Envato
在文档理解领域,深度学习模型发挥了重要作用。这些模型能够准确地解读文档的内容和结构,使其成为处理发票、简历解析和合同分析等任务的宝贵工具。深度学习模型在文档理解中的另一个重要好处是它们能够随着时间的推移不断学习和适应。随着新类型文档的出现,这些模型可以继续学习并提升性能,使其在文档分类和信息提取等任务中具有高度的可扩展性和效率。
其中一个模型是 LILT 模型(Language-Independent Layout Transformer),这是一个为文档布局分析任务开发的深度学习模型。与其前身 LayoutLM 不同,LILT 最初设计为语言无关,这意味着它可以分析任何语言的文档,同时在许多下游任务应用中相较于其他现有模型表现优越。此外,该模型拥有 MIT 许可证,这意味着它可以用于商业用途,与最新的 LayoutLM v3 和 LayoutXLM 不同。因此,创建一个关于如何微调该模型的教程是值得的,因为它有潜力被广泛用于各种文档理解任务。
在本教程中,我们将讨论这一新型模型架构,并展示如何在发票提取上进行微调。然后,我们将使用该模型对一组新的发票进行推断。
LILT 模型架构:
使用 LILT 模型的一个关键优势是其处理多语言文档理解的能力,表现出色。作者通过将文本和布局嵌入分离到各自的变压器架构中,并使用双向注意力补充机制(BiACM)实现两种数据类型之间的跨模态交互,从而达到了这一点。编码后的文本和布局特征被连接在一起,并添加了额外的头部,允许模型用于自监督预训练或下游微调。这种方法不同于 layoutXLM 模型,该模型涉及收集和预处理大量多语言文档数据集。

LILT 模型架构。 来源
该模型的关键创新在于使用 BiACM 来捕捉文本和布局特征在编码过程中的交互。仅仅连接文本和布局模型输出会导致性能下降,这表明编码流程中的交互对于模型的成功至关重要。有关更深入的细节,请阅读 原始文章。
模型微调:
类似于我之前关于如何 微调 layoutLM 模型 的文章,我们将使用相同的数据集来微调 LILT 模型。数据通过使用 UBIAI 文本注释工具手动标记 220 张发票获得。关于标记过程的更多细节可以在这个 链接 中找到。有关深入的视频教程,请查看以下链接:
LILT 教程
要训练模型,我们首先预处理来自 UBIAI 的数据输出,以准备模型训练。这些步骤与之前训练 layoutLM 模型的笔记本中的步骤相同,这里是笔记本:
编辑描述
我们从 Huggingface 下载 LILT 模型:
from transformers import LiltForTokenClassification
# huggingface hub model id
model_id = "SCUT-DLVCLab/lilt-roberta-en-base"
# load model with correct number of labels and mapping
model = LiltForTokenClassification.from_pretrained(
model_id, num_labels=len(label_list), label2id=label2id, id2label=id2label
)
对于此模型训练,我们使用以下超参数:
NUM_TRAIN_EPOCHS = 120
PER_DEVICE_TRAIN_BATCH_SIZE = 6
PER_DEVICE_EVAL_BATCH_SIZE = 6
LEARNING_RATE = 4e-5
要训练模型,只需运行 trainer.train() 命令:

作者提供的图片:模型训练进行中。
在 GPU 上,训练大约需要 1 小时。训练完成后,我们通过运行 trainer.evaluate() 来评估模型:
{
'eval_precision': 0.6335952848722987,
'eval_recall': 0.7413793103448276,
'eval_f1': 0.6832627118644069,
}
我们的精度、召回率和 F-1 分数分别为 0.63、0.74 和 0.68。LILT 模型的 F-1 分数为 0.68,表明模型在准确分类和预测结果方面表现良好,准确率中等到良好。然而,值得注意的是,仍有改进的空间,继续标注更多数据将有助于进一步提高模型性能。总体来看,LILT 模型的 F-1 分数为 0.68 是一个积极的结果,表明模型在其预期任务中表现良好。
为了评估模型在未见数据上的表现,我们在新发票上运行了推理。
我们确保保存模型,以便稍后使用以下命令进行推理:
torch.save(model,'/content/drive/MyDrive/LILT_Model/lilt.pth')
模型推理:
要在新的发票上测试模型,我们运行下面的推理脚本:
编辑描述
colab.research.google.com](https://colab.research.google.com/drive/1om_xsTuuwOXzrldhkQj76HXSQkLYftRz?usp=sharing&source=post_page-----8fd6b3cfae1b--------------------------------)
以下是结果:

作者提供的图片:LILT 在发票 1 上的输出
LILT 模型正确识别了广泛的实体,包括卖方名称、发票号码和总金额。让我们再尝试几张发票:

作者提供的图片:LILT 在发票 2 上的输出

作者提供的图片:LILT 在发票 3 上的输出
如我们所见,LILT 模型能够处理各种不同格式和不同上下文的内容,尽管有一些错误,但准确性相对较好。总体而言,LILT 模型表现良好,其预测结果与 layoutLM v3 产生的结果相似,突显了其在文档理解任务中的有效性。
结论
总结来说,LILT 模型已经证明在文档理解任务中是有效的。与 layoutLM v3 模型不同,LILT 模型采用 MIT 许可证,这允许研究人员和开发者广泛商业使用,使其成为许多项目的理想选择。下一步,我们可以通过标注和改进训练数据集来提升模型性能。
如果你想高效便捷地创建自己的训练数据集,可以免费体验UBIAI 的 OCR 标注功能。
如何将家庭作业转化为数据科学职位
逐步指南,教你如何从数据集中创建引人注目的故事
·
关注 发表在 Towards Data Science · 8 分钟阅读 · 2023 年 1 月 23 日
--

图片由 Patrick Federi 提供,来源于 Unsplash
家庭作业是许多数据科学面试中的常见步骤,通常在筛选过程的后期阶段进行。第一轮考察你的统计学知识(假设检验等),通常包括实践编码问题(SQL、R 等)。如果你需要巩固这些领域的技能,请查看以下文章。
## 如何将你的 SQL 从零基础提升到数据科学家水平 — 第 1/3 部分
设置 SQL 并执行你的第一个 SELECT 查询
towardsdatascience.com ## 我作为数据科学家在进行一年 AB 测试后的收获 — 第 1/2 部分
按照这些简单步骤设置你的 A/B 测试,像数据科学家一样
towardsdatascience.com
在许多方面,家庭作业模拟了公司对你期望的情况,如果你被聘用并遇到类似的问题。在本文中,我根据我作为候选人和招聘经理的经验,创建了一个逐步指导如何应对这一挑战。该指南将按以下结构进行:
-
家庭作业的基础知识
-
数据的快速 QA
-
探索阶段: 查找数据中的模式
-
解释阶段: 构建你的故事
1. 家庭作业的基础知识

照片由Dennis Scherdt提供,来自Unsplash
1.1 家庭作业的结构是什么?
家庭作业由两个元素组成。一个数据集和一个作业概述。
-
数据集通常是公司数据的虚拟版本。它以.csv 格式提供,附有所有列的描述。很少情况下,你可能会得到多个数据集,并且需要将它们结合起来。
-
作业概述 提供了任务的一些背景信息以及他们期望你对数据集做什么的简短描述。例如,在我为 Deliveroo 的数据科学职位做的家庭作业中,任务是分析他们的 RGR(Rider Gets Rider)推荐程序与其他营销渠道的表现。然后评估其是否成功,如果没有(哈哈!),我们应该考虑更改的一些重要因素。任务的最后一步涉及向高级非技术利益相关者提供信息摘要,并建议下一步行动,例如收集额外的数据。
1.2 通过家庭作业期望获得的洞察是什么?
家庭作业使你同时处于一种独特的优势和劣势的位置。一方面,你已经有了问题定义和数据集,这通常需要项目流程中的大量时间。你还可以自由选择如何进行分析。另一方面,你无法与业务利益相关者进一步沟通,这很可怕。这正是测试评估的内容。能够接受这样的模糊作业,并且在不让其过于复杂的情况下(事实上,这也是你将被评估的内容之一),提供以下内容:
对数据集有清晰的理解,使你能够发现其中隐藏的问题(或机会)。
此外,你能够 pinpoint 可能的问题来源,并为你的目标受众提供清晰的建议和下一步计划。
换句话说,他们希望你展示你能够从数据中提供价值!
2. 数据的快速质量检查

在我们继续之前,我们需要快速检查数据集的健康状况。在计算机科学中,“垃圾进,垃圾出”(GIGO)是一个概念,表示有缺陷或无意义(垃圾)的输入数据会产生无意义的输出。虽然跳过这一步直接进入分析是很诱人的,但你不希望因为这样一个简单的错误而失败。
-
确定作为主键的列(即,对于数据集中的每一行都是唯一的)。此信息通常会提供。
-
检查列中的缺失值并相应地标记它们(NA 适用于 R,NULL 适用于 SQL 等)。隐藏的缺失值,如 999 的可疑峰值,也可以标记为NA。
-
确保所有列格式正确。例如,二进制属性(值为 0 和 1)标记为类别型,而不是整数,或者日期不会被错误标记为字符型。
-
检查是否有不合理的值。当所有其他观察值都在 0 到 1000 之间时,1 亿的值可以在可能的情况下进行修正,否则排除(确保在你的展示中解释任何排除的决定)
3. 探索阶段:发现数据中的模式

照片由 Daniel Lerman 拍摄,来源于 Unsplash
3.1 创建 KPI
对于大多数公司来说,兴趣点集中在数值属性上。这些属性可以是任何东西,取决于公司。例如,在我们以 Deliveroo 为例的情况中,可能是推荐次数;而对于像 Netflix 这样的公司,可能是观看视频的数量。对于大多数公司来说,这些属性将是货币性的(即基于收入的)。
首先,我们需要更好地理解这些数值属性。我们可以利用描述性统计和图表来实现这一目标。
-
描述性统计: 总和、均值、中位数、众数、四分位距(IQR)、标准差
-
可视化: 离散属性使用条形图,连续属性使用直方图或箱形图
我们现在需要确定我们的 KPI(关键绩效指标)。KPI 是对业务相关者有意义的描述性统计的子集。例如,相比 IQR,KPI 还有更好的选择。KPI 最常用的统计量是平均值(每个骑手的平均推荐次数或每个用户的平均观看视频数)。你需要根据数据的分布选择适当的统计量作为你的 KPI。如果存在离群值,你可以选择 95%截断均值;如果分布高度偏斜,你可以选择中位数。
重要的是要让业务相关者了解这些决策。解释由于极端离群值的存在,均值为$10000,因此选择 95%截断均值,其更具代表性的值为$120,这样可以展示 a) 你已经识别出了离群值,b) 你知道如何处理这些离群值!
3.2 添加维度
我们的分析仍然过于高层次。我们需要将分类属性纳入分析中。你可以把它们看作是我们 KPI 的维度。以 Deliveroo 为例,我们可以按城市或月份拆分每个骑手的中位推荐次数。日期是最常用的维度列之一。
一些分类属性可能会有许多稀有类别(类别是分类属性的唯一值,例如“男孩”和“女孩”)。这些稀有类别可以被合并或重新分配。我们还可以以类似的方式对数值属性进行分组,以创建新的分类维度。例如,通过使用分位数,我们可以将收入分成四个区间(或桶)。但尽量避免过度复杂化分配;除非有充分的理由,否则应避免对数值属性进行分箱。
现在我们可以通过按分类属性或日期拆分我们的关键绩效指标来开始进行比较:
-
我们的关键绩效指标(KPI)在一个城市的表现是否优于另一个城市?
-
总收入与前几个月相比是下降还是上升?
-
我们还可以结合维度。如果收入同比下降了 15%,这是因为一个或两个城市的表现更差,还是所有城市的降幅几乎都达到 15%?
3.3 根本原因分析
根本原因分析旨在识别导致问题的潜在因素。让我们通过一个例子来看这如何运作。假设一个任务中总收入相比上个月下降了 25%。你将城市作为维度,观察到差异几乎完全来自纽约和伦敦。其他城市的总收入月月保持不变。首先,我们将把总收入分解为两个组成部分。
总收入 = 总订单数 * 平均订单价值
从(虚构的)数据来看,两个城市的总订单数都增加了 9%,但平均订单价值却下降了 38%!好吧,现在我们有了一些进展。我们还可以检查用户经历的漏斗。例如,如果订单下降了,我们可以先检查一下有多少访客,多少人将产品添加到购物篮中,多少人进入了结账页面,最后有多少人确认了订单。
当然,上述内容依赖于我们可以获取的数据量。但我们只能使用现有的数据,因此如果由于数据限制导致上述步骤无法进行,请不要担心。但请确保在分析建议和下一步计划时将其作为潜在数据源添加进去。
4. 解释阶段:构建你的故事

图片由 RetroSupply 提供,来源于 Unsplash
这是你的作业的最终部分,也可能是最重要的部分。你的分析是评估的关键部分,但同样重要的是你的展示的简洁性和清晰度(利益相关者只有在理解你的观点时才能欣赏它们)。你需要遵循三个关键规则:
-
你的演示文稿应该简洁。不要展示你打开的每一只牡蛎,而只展示珍珠
-
在你的幻灯片中使用视觉效果和摘要,使洞察更易于理解。任何不促进这一方向的元素应当被移除或更改
-
在分析的基础上给出明确的建议和下一步计划
你也可以在下面找到有关如何创建视觉效果和结构化演示文稿的更详细文章。
公共演讲是否是数据科学家的致命弱点?
帮助我提升演讲技巧的三个关键经验
towardsdatascience.com
总结
🚀🚀 通过最后一步,我们已经完成了指南。以下你还可以找到步骤的简要总结:
✅ 仔细阅读描述并理解任务和目标
✅ 在开始分析之前对数据进行质量检查
✅ 创建关键绩效指标,并按维度拆分以精准定位问题
✅ 进行根本原因分析,以识别可能解释问题的因素
✅ 仅传达分析中的关键点。抵制展示所有开过的牡蛎的冲动
我想强调的最后一点是根据家庭作业的质量对公司进行自我评估的重要性。描述是否清晰?数据集是否具有一定挑战性?在问题和数据集上投入了足够的努力,还是看起来像是草率的工作?
如果你有任何问题或需要进一步的帮助,请随时在下方评论,我会及时回答。
保持联系!
如果你喜欢阅读这篇文章并想了解更多,请别忘了订阅,以便直接将我的故事发送到你的邮箱。
在下面的链接中,你还可以找到一个关于在实际商业场景中使用数据科学技术和最佳实践完成客户群体分析的免费 PDF 指南。👇
我是一名拥有 7 年以上分析经验的数据科学家,目前在英国伦敦的一家游戏公司工作。我的……
如何将时间序列转换为深度学习格式
原文:
towardsdatascience.com/how-to-transform-time-series-for-deep-learning-3b6abbbb3726
使用深度神经网络进行预测
·发布于 Towards Data Science ·6 分钟阅读·2023 年 2 月 14 日
--

图片由 Claudio Testa 提供,来源于 Unsplash
使用时间序列的监督学习
监督学习涉及使用输入数据集来训练机器学习模型。这个数据集通常是一个矩阵:一个由行(样本)和列(特征)组成的二维数据结构。
时间序列是按时间排序的值的序列。因此,它需要转换以进行监督学习。
在 上一篇文章中,我们学习了如何将单变量时间序列从序列转换为矩阵。这是通过滑动窗口完成的。每个观察值基于过去的最近值进行建模,这些值也称为滞后。
以下是使用 1 到 10 序列的转换示例:

将序列转换为带滑动窗口的矩阵。图片由作者提供。
这种转换实现了一种称为自回归(auto-regression)的建模类型。在自回归中,使用时间序列的过去最近值(滞后)作为解释变量来建立模型。这些值用于预测未来的观察值(目标变量)。自回归的命名直观上是因为时间序列与自身回归。
在上述示例中,滞后是前 5 列。目标变量是最后一列(下一个时间步的系列值)。
深度学习中的自回归
虽然大多数方法适用于矩阵,但深度神经网络需要不同的结构。
深度神经网络如 LSTM 或 CNN 的输入是一个三维数组。实际数据与放入矩阵中的数据相同,但结构不同。
除了行(样本)和列(滞后),额外的维度指的是序列中的变量数量。在矩阵中,你会将所有属性连接在一起,而不考虑它们的来源。神经网络则更整洁。输入是通过第三维度按序列中的每个变量组织的。
让我们做一个实际示例来澄清这个概念。
实践操作

在本教程中,你将学习如何使用 LSTM(长短期记忆网络)转换时间序列以进行监督学习。LSTM 是一种特别适合建模时间序列的神经网络类型。
我们将时间序列转换过程分为两个步骤:
-
从值序列到矩阵;
-
从矩阵到深度学习的 3 维数组。
首先,我们将进行一个单变量时间序列的示例。多变量时间序列将在后面介绍。
单变量时间序列
首先,我们来读取数据。我们将使用与不同类型葡萄酒销售相关的时间序列。你可以在参考文献1中查看源数据。
import pandas as pd
# https://github.com/vcerqueira/blog/tree/main/data
data = pd.read_csv('data/wine_sales.csv', parse_dates=['date'])
data.set_index('date', inplace=True)
series = data['Sparkling']
我们以香槟酒的销售数据为例进行单变量情况的示范。这个时间序列如下所示:

从值序列到矩阵
我们应用滑动窗口将此序列转换为监督学习所需的格式。你可以在之前的文章中了解更多关于此过程的信息。
# src module here: https://github.com/vcerqueira/blog/tree/main/src
from src.tde import time_delay_embedding
# using 3 lags as explanatory variables
N_LAGS = 3
# forecasting the next 2 values
HORIZON = 2
# using a sliding window method called time delay embedding
X, Y = time_delay_embedding(series, n_lags=N_LAGS, horizon=HORIZON, return_Xy=True)
这里是解释变量(X)和相应目标变量(Y)的示例:

转换时间序列为 2 维矩阵结构后的解释变量(左)和目标变量(右)的样本。图片由作者提供。
这个数据集是训练传统机器学习方法的基础。例如,线性回归或 xgboost。
from sklearn.linear_model import RidgeCV
# training a ridge regression model
model = RidgeCV()
model.fit(X, Y)
从矩阵到深度学习的 3 维结构
你需要重塑这个数据集来训练像 LSTM 这样的神经网络。以下函数可用于完成此操作:
import re
import pandas as pd
import numpy as np
def from_matrix_to_3d(df: pd.DataFrame) -> np.ndarray:
"""
Transforming a time series from matrix into 3-d structure for deep learning
:param df: (pd.DataFrame) Time series in the matrix format after embedding
:return: Reshaped time series into 3-d structure
"""
cols = df.columns
# getting unique variables in the time series
# this list has a single element for univariate time series
var_names = np.unique([re.sub(r'\([^)]*\)', '', c) for c in cols]).tolist()
# getting observation for each variable
arr_by_var = [df.loc[:, cols.str.contains(v)].values for v in var_names]
# reshaping the data of each variable into a 3-d format
arr_by_var = [x.reshape(x.shape[0], x.shape[1], 1) for x in arr_by_var]
# concatenating the arrays of each variable into a single array
ts_arr = np.concatenate(arr_by_var, axis=2)
return ts_arr
# transforming the matrices
X_3d = from_matrix_to_3d(X)
Y_3d = from_matrix_to_3d(Y)
最终,你可以使用得到的数据集训练一个 LSTM:
from sklearn.model_selection import train_test_split
from keras.models import Sequential
from keras.layers import (Dense,
LSTM,
TimeDistributed,
RepeatVector)
# number of variables in the time series
# 1 because the series is univariate
N_FEATURES = 1
# creating a simple stacked LSTM
model = Sequential()
model.add(LSTM(8, activation='relu', input_shape=(N_LAGS, N_FEATURES)))
model.add(RepeatVector(HORIZON))
model.add(LSTM(4, activation='relu', return_sequences=True))
model.add(TimeDistributed(Dense(N_FEATURES)))
model.compile(optimizer='adam', loss='mse')
# compiling the model
model.compile(optimizer='adam', loss='mse')
# basic train/validation split
X_train, X_valid, Y_train, Y_valid = train_test_split(X_3d, Y_3d, test_size=.2, shuffle=False)
# training the model
model.fit(X_train, Y_train, epochs=100, validation_data=(X_valid, Y_valid))
# making predictions
preds = model.predict_on_batch(X_valid)
多变量时间序列
现在,我们来看一个多变量时间序列的示例。在这种情况下,目标是预测多个变量的未来值,而不仅仅是一个。因此,你需要一个用于多变量和多步预测的模型。

关于不同类型葡萄酒销售的多变量时间序列。图片由作者提供。
转换过程与之前类似。
要将多变量时间序列转换为矩阵格式,可以对每个变量应用滑动窗口方法。然后,将所有结果矩阵合并为一个。
这是一个示例:
# transforming each variable into a matrix format
mat_by_variable = []
for col in data:
col_df = time_delay_embedding(data[col], n_lags=N_LAGS, horizon=HORIZON)
mat_by_variable.append(col_df)
# concatenating all variables
mat_df = pd.concat(mat_by_variable, axis=1).dropna()
# defining target (Y) and explanatory variables (X)
predictor_variables = mat_df.columns.str.contains('\(t\-|\(t\)')
target_variables = mat_df.columns.str.contains('\(t\+')
X = mat_df.iloc[:, predictor_variables]
Y = mat_df.iloc[:, target_variables]
解释变量对于两个变量如下(其他变量为了简洁省略):

你可以使用相同的函数将数据转换为三维:
X_3d = from_matrix_to_3d(X)
Y_3d = from_matrix_to_3d(Y)
训练部分与之前相似。序列中变量数量的信息在 N_FEATURES 常量中提供。顾名思义,这个常量表示时间序列中的变量数量。
model = Sequential()
model.add(LSTM(8, activation='relu', input_shape=(N_LAGS, N_FEATURES)))
model.add(Dropout(.2))
model.add(RepeatVector(HORIZON))
model.add(LSTM(4, activation='relu', return_sequences=True))
model.add(Dropout(.2))
model.add(TimeDistributed(Dense(N_FEATURES)))
model.compile(optimizer='adam', loss='mse')
X_train, X_valid, Y_train, Y_valid = train_test_split(X_3d, Y_3d, test_size=.2, shuffle=False)
model.fit(X_train, Y_train, epochs=500, validation_data=(X_valid, Y_valid))
preds = model.predict_on_batch(X_valid)
以下图表展示了一个步进预测样本。

LSTM 的一步预测。图像作者提供。
预测效果不是很好。时间序列较小,我们也没有对模型进行优化。深度学习方法以数据为食。如果你选择这种方法,请确保你有足够的数据。
关键要点
深度学习在时间序列应用中越来越相关。
在本文中,我们探讨了如何为深度学习转换时间序列。
传统机器学习算法的输入是一个矩阵。但是,像 LSTM 这样的神经网络使用三维数据集。因此,时间序列需要从序列转换为这种格式。
这种转换基于滑动窗口,该窗口应用于序列中的每个变量。
感谢阅读,下次故事见!
相关文章
-
预测的机器学习:转换和特征提取
-
预测的机器学习:使用多变量时间序列的监督学习
参考文献
1 Rob Hyndman 和 Yangzhuoran Yang (2018). tsdl: 时间序列数据库. v0.1.0. (GPL-3 许可证)
转行到数据分析的路线图
原文:
towardsdatascience.com/how-to-transition-to-data-analytics-128a3dca54d5
·发表于 Towards Data Science ·11 分钟阅读·2023 年 11 月 6 日
--
如果你在其他领域工作,如何转行到数据分析?
你可能拥有一个与数据分析无关的大学学位,或者在一个完全不同的领域工作过。你可能一直对转行数据分析角色感兴趣,但不知道从哪里开始。如果这听起来像你,继续阅读吧!😉

图片来源:Myriam Jessier 在 Unsplash
进入数据分析的两种方式
本质上,有 2 种进入数据分析的方式:
-
(1) 完全自学:然后巧妙地将分析技能与当前领域知识(无论是来自早期的学习还是工作)结合,以获得竞争优势;
-
(2) 选择数据分析学位或训练营。
在这篇博客文章中,我们将主要关注自学的途径,目标是成为数据分析师,这也是许多其他数据科学角色的良好起点。这也是我 6 年前开始的方式,当时我对医疗分析非常感兴趣,所以我从这一领域起步。
在这篇文章中,我们将讨论你应该学习和准备的内容:
-
所需技能
-
作品集项目
-
如何接触招聘人员
事情变化迅速,许多人担心人工智能可能取代工作,这可能会影响你在追求数据分析工作时的情况。我将在本文最后分享一些关于这一点的见解。
你还可以观看下面的博客文章视频版本 👇
www.youtube.com/@Thuvu5/videos
那么数据分析师实际上做什么?
实际上,有很多不同的职位涉及到处理数据并将其转化为见解。很久以前,从事这类工作的人员在保险行业工作时会被称为统计学家或精算师。
如今,我们有时对职位头衔过于执着,这很快就会变得令人困惑。如果我们查看招聘信息,就会看到业务分析师、研究分析师、分析顾问、初级数据科学家等等。更不用说数据分析 + X 这类职位,这些职位特定于某个领域,比如 HR 数据分析师、销售分析师、市场分析师等。
我发现了解工作的本质和你实际使用的技能更为有用。因此,在本文中,我将所有这些头衔简化为数据分析师。
数据分析师是一个分析数据的职位。
数据分析是一个检查、清理、转换和建模数据的过程,目的是发现有用的信息、得出结论并支持决策。
因此,数据分析师通常会做大量的数据科学工作,除了预测分析、机器学习和软件工程,这些通常是数据科学家更关注的领域。因此,作为数据分析师可以为你进入数据科学家、数据工程师和其他数据科学职位打下坚实的基础。
职业前景
在美国,数据分析师的中位薪资为 95,000 美元。对既具备领域知识又能分析数据的人才需求不断上升。
作为数据分析师,你将有机会在医疗保健、金融、银行、物流、人力资源等多个领域工作。你可以为初创公司、代理机构、企业或像我这样的咨询公司工作。你可以全职或自由职业,甚至远程工作。所以你在职业生涯中有很大的灵活性!
所需技能
说到数据分析师最重要的技能,让我展示这个技能模型来帮助你可视化它。

图片来源:作者。
(1) 基础数学和统计:
在最基本的层面上,你需要一些基础数学和统计学。仅仅是基础的高中或本科数学和统计学就足够了,除非你希望在工作中进行更高级的分析或机器学习。大多数时候,作为数据分析师,你会对数据进行一些描述性统计,比如计算平均值、最小值、最大值、中位数、标准差等。根据项目的不同,你可能还会进行离群值检测、假设检验、线性回归、聚类甚至机器学习。这取决于你的工作及其技术复杂性。我强烈推荐你阅读达雷尔·哈夫的经典书籍《如何用统计数据撒谎》。这本书读起来非常有趣,指出了我们在从数据中得出结论时最常犯的错误。我认为每个从事数据工作的人都应该读一读。
(2) 技术技能:
其次,数据分析师需要工具,即特定的技术或软件。作为数据分析师,你几乎每天需要完成 3 件事:
-
提取数据
-
数据分析
-
可视化数据以传达洞察力并讲述故事

有各种工具可以帮助你完成这些任务中的每一个,或所有任务。最受欢迎的技能是 SQL、Excel、Tableau、Python 和 R。你也可以选择学习 PowerBI。如果你已经掌握了 Tableau,学习 PowerBI 应该很快,反之亦然。未来,我们可以预期作为数据分析师将处理大数据。因此,学习这些工具的大数据版本可能对你有用,例如用于查询大数据的 SparkSQL,以及用于以 Python 风格处理大数据的 PySpark。
(3) “软”技能:
在下一个层级,我们有软技能。数据分析师能够沟通洞察力是至关重要的,因为你将与业务利益相关者、产品经理及其他关键利益相关者合作。你需要提出问题,理解业务问题,然后与他们一起迭代不同的分析和结果,最终为业务提供一些洞察和建议。大多数时候,沟通和讲故事的形式包括演示文稿、文档、电子邮件、仪表盘等。

图片来源于 path digital 在 Unsplash
(4) 领域知识
最终,在顶层,我们需要领域知识。它让你真正理解业务问题,并且更好地理解数据。例如,如果你在航空公司工作,你需要了解航空公司的运作。如果你在医疗保健领域进行数据分析,了解医疗保健领域也是至关重要的。这些领域知识可以通过你的教育背景或在该领域的工作经验获得。
如何学习这些技能——从哪里开始?
学习数据分析角色所需技能的方式有很多。以下是一些建议:
-
关于数学和统计学,很容易陷入一个无限循环中,总觉得自己学得不够。对我来说,即使经过 6 年我仍然有这种感觉。但拥有基础到中级的理解是可以的。很多东西你可以在工作中学到,所以不要太担心从一开始就不知道足够的内容。
-
关于 Excel,我建议你真正掌握它。你需要像对自己手背一样了解基本功能,如 VLOOKUP、XLOOKUP、Index match、条件格式、数据透视表等。你还可以学习更高级的工具,如宏和 VBA 来自动化重复性任务,如果需要连接不同的数据源,可以选择 PowerQuery。未来,很多小任务可以通过 AI 自动化。因此,我们需要的主要能力是了解事物的运作方式。如果你已经了解 VBA 的工作原理,使用 ChatGPT 为你编写一些 VBA 代码以填充 Excel 模板会更容易!
-
学习编程时使用迁移学习:如果你已经掌握了一种编程语言,例如 SQL 或 R,那么将这些技能迁移到 Python 等新语言上会比较容易。当我第一次学习 Python 时,我经常尝试将 R 中的知识关联起来。例如,如果我想合并两个数据框,我会搜索“如何在 Python 中按行绑定数据框”,因为这是我在 R 中的描述方式。此外,所有的基本概念,如变量、数据类型、函数、词法作用域,几乎都是相同的。因此,随着时间的推移,你会培养出如何做事和解决问题的直觉,无论使用什么语言。其余的只是练习!
-
关于数据可视化,它可能是数据分析师工作中最有趣的部分。数据可视化可以是你在 R、Python 或 Excel 中创建的独立图表,或以仪表板或演示文稿的形式存在。初学者可以参考一些好的书籍,如《用数据讲故事》。对于仪表板的制作,你可以通过一些 Tableau 或 PowerBI 课程快速学习,这些工具是拖拽式的,无需编码。
-
关于讲故事、提问和演讲等软技能,你会随着时间的推移而不断提高。但你也可以通过主动练习来提高沟通和讲故事的能力,比如写关于你项目的博客,将想法和见解展示给朋友和家人,看看他们的反馈。
准备一个数据作品集
在众多候选人中,你绝对需要脱颖而出。我相信一个好的个人作品集将帮助你最好地展示你的技能,并吸引招聘者的注意。做项目也是检验你技能的好方法。这会给你带来自尊和信心,这可能是最重要的方面。
如果你不知道做什么项目,我在我的频道上有一些项目教程,你可以使用并根据你的具体使用案例或领域进行调整。
这里有一些额外的建议:
-
确保你的作品集里有 3–5 个项目。我认为 3–5 个项目是展示你技能的最佳数量。
-
做一个你关心的项目,找到或创建一个你真正感兴趣的数据集,并投入一些LOVE ❤️!你的热情会显露出来!通过对比两个项目,人们可以很容易地看出哪个作者投入了更多的思考和承诺。
-
一切都是困难的,直到你搞明白!有时候需要很大的勇气才能克服心理障碍开始或继续。有几个月前我开始了抱石攀岩。攀岩教会了我很多关于克服恐惧、完全投入到一个困难动作中的课程。知道我可以训练自己去想:“我害怕,但我还是要去做”令人振奋。
接触招聘人员
线下 & 线上网络
在掌握了一些技能和作品集项目之后,是时候让自己走出去。我发现像朋友、亲戚、以前的同学或同事这样的线下网络是找到工作的最简单方法,这也是我之前有效的方式。但在线上也有很多机会。
LinkedIn 也是一个联系已有领域人员和招聘人员的好地方。我实际上从未尝试过在 LinkedIn 上联系招聘人员,也许是因为 7-8 年前这并不常见。但如果你试一试,请在信息中保持真诚和礼貌。如果你与公司有任何联系,哪怕是微小的,比如你参加过公司的活动或与那里的某人交谈过,请在信息中提及!
你也可能想要使用 Grammarly 来检查你的语法,或者让朋友帮你校对。小错误可能会留下不好的印象,特别是当这是你唯一能留下的印象时!
不要低估自己!
从我的经验来看,低估自己是一个常见的错误。如果你真的在这个领域很新,你可能会想“我刚毕业,还在学习”。这可能是真的,但这不是雇主所寻找的。你可以以更自信的方式来表达,强调你所做的事情和你能贡献的东西。同时,确保向招聘人员发送一份最新的简历,其中应包括指向你的作品集项目的链接,如 Github 仓库、作品集网站或其他在线项目提及。
如果你有之前的工作经验或一些“领域知识”,你应该充分利用这些经验。即使你曾在超市做过咖啡师或收银员,这也算作你的领域经验。数据分析离不开背景和业务问题。如果我是你,我会提到这些经历,并尝试将它们与我申请的工作联系起来,讲述一个小故事,并展示这些经历所学的内容如何对公司有益。这并不总是简单直接的,但我认为这是一个非常有效的聪明策略。
ChatGPT 将如何影响数据分析师的工作?
说到 AI,毫无疑问,语言模型和 AI 工具将在某种程度上改变分析工作。最近的一项分析发现,大型语言模型如 GPT 可能对 80%的美国劳动力产生一些影响。一些最易受影响的创意和高薪工作包括作家、网页和数字设计师,以及金融量化分析师和区块链工程师。
但我认为好处将超过坏处。一项最近的研究表明,AI 工具对技能和成就最低的工人帮助最大,缩小了员工之间的表现差距。换句话说,能力较弱的数据分析师将会变得更好,而优秀的分析师则会变得更快!
正如你可能已经知道的,数据预处理占据了许多数据分析师和数据科学家工作的大约 70–80%。这些工作大多非常枯燥乏味。我认为像 ChatGPT 和 GPT-4 这样的工具可以帮助我们自动化很多这些任务,从而简化和优化数据分析过程。AI 工具如 ChatGPT 在数据分析中的一些最佳应用场景包括:
· 它可以创建用于构建数据库、进行简单数据清理、探索性数据分析和创建各种图表的代码。
· 优化你的代码并添加注释和解释以供文档使用。
· 为如何将信息传达给不同的受众——高层管理人员、部门主管、经理等,提供建议。
· 建议数据来源,例如“我在哪里可以找到关于荷兰金融欺诈的数据?”
· 你还可以让它创建合成数据以用于模型训练或算法测试。
· 提供基于互联网上开放资源的法规、法律流程的一般建议。
尽管如此,有些生成型 AI 还做不到,幸运的是,至少目前还不行!即便是今天最先进的语言模型也仍然缺乏关键能力,如批判性思维、战略规划和复杂问题解决能力。如今,企业不仅仅是要以更便宜的方式做相同的事情。公司需要创新和跳出框框思考,如果他们想要保持业务的持续性。因此,作为数据分析师,你有能力帮助他们做到这一点。语言模型也还不能编写笑话,而幽默和可爱在建立信任和关系中出乎意料地重要。因此,我们可以预期人类数据分析师在这些方面能够大放异彩。
对我们来说,关键是持续学习,掌握良好的基础技能,跟上利用 AI 工具和技术以提高工作效率的步伐。同时,我们比以往任何时候都更加关注同理心、沟通和关系建设等软技能。
结论
我希望这篇博客文章能让你更好地理解如何从其他领域过渡成为数据分析师,并为你提供一份路线图。祝你在职业生涯的下一步好运!😊🍀
如何将数据转化为可操作的商业洞察
原文:
towardsdatascience.com/how-to-translate-data-into-actionable-business-insights-1321ce7edc2a
你的数据只是故事的一半
·发布在Towards Data Science ·6 分钟阅读·2023 年 7 月 18 日
--

图片来源:Matt Popovich在Unsplash
在当今迅速扩展的世界中,对能够为企业翻译数据的专业人士需求旺盛——分析数据并为企业制定行动建议。一切都在于迈出那一步,使数据对企业更具可读性。
数据无处不在。企业希望利用这些数据来做出更好的决策,并在市场中保持灵活和竞争力。现在,当我们谈论企业时,许多做决策的高管不一定理解原始数据及其分析方法。这就需要数据专业人员来完成工作并提供可操作的洞察。
即使在较低层级,你的同事可能也不完全理解预测模型的准确性意味着什么。我听过有人说“这信息很好,但我接下来该怎么做?”因此,关键是你不仅要分析数据,还要以企业也能理解的方式呈现数据。
在这篇博客中,我想强调数据转化和讲故事如何相辅相成,以及将数据转化为可操作商业洞察的六步流程。
数据与洞察有何不同?
人们往往将数据和洞察互换使用,而实际上它们的含义有很大区别。
数据是原始且未处理的。它可能是数字、文本、图像、音频或视频文件等,这些数据可能独立存在时没有意义,也没有为企业提供有价值的输入。
洞察是通过转化原始数据来分析信息并得出结论的结果。今天的企业利用洞察而非数据来最大化盈利、提高成本效益并创造价值。
将数据转化为业务的六个步骤
1. 了解业务并提升洞察力
解决任何商业问题的第一步是理解业务本身。除非你理解一个业务的挑战、需求、要求和机会,否则你如何能为更好的未来优化现在的状况?
了解你的组织及其业务书籍是一个持续的过程。由于组织的业务方式是如此动态并快速发展,因此数据专业人员必须调整他们的数据模型并理解业务需求,这是至关重要的。
在讲故事方面,理解商业背景就像理解你故事所基于的城市和文化——没有这些,你的故事就会失去灵魂。
2. 首先理解问题
作为数据专业人员,你的第一反应可能是提出创建一个模型作为解决方案,但没有明确、简洁的问题陈述,数据专业人员将很难将这些文字转化为代码行或 Excel 表格。
当你对问题陈述有了充分的理解,并查看任务的数据时,接受或拒绝这些数据变得非常容易。你可以考虑数据生命周期中的下一步。
我通常遵循下面的五步过程来解决问题 —
-
多读几遍问题陈述或那封电子邮件
-
将你的问题分解成更小的步骤
-
确定你能解决的问题部分
-
探索所有可能的支持信息(阅读文档!)
-
决定一种方法(你的数据模型、可视化等)
3. 通过漏斗分析来发现你的 KPI
每个部门可能有比其他部门更关键的 KPI,因为他们负责推动特定的业务影响。每当你被要求解决商业问题时,首先从所有相关的 KPI 和对你的建模和分析重要的指标开始。
有效的漏斗分析需要一致的数据分析。首先假设问题(或创建你的漏斗),并使用 A/B 或多变量测试来开始筛选出可能对你的问题陈述的意图没有价值的 KPI。根据最终的 KPI,这时你可以开始创建数据分析和模型。
漏斗分析的例子——假设你在一家饮料公司工作,你的任务是预测公司目录下一个品牌在整体饮料市场中的市场份额。在这种情况下,创建漏斗分析可以帮助你揭示你最畅销的产品。你问怎么做?方法如下 —
-
绘制理想的端到端业务流程图
-
确定每个漏斗阶段的问题
-
收集尽可能多的相关数据用于分析
-
监控关键绩效指标(KPIs)
-
创建一个行动优先矩阵,以便在四个象限中映射高低优先级的努力和影响。
-
根据上述矩阵中的任务重要性进行排序,将会得到一个待办事项列表,这就是你的漏斗分析的结果。
4. 使用三原则
人类以有趣的方式处理信息。我们擅长模式识别,而三是创建模式所需的最小元素数,因此当信息以三的形式呈现时,易于消化。
故事讲述的核心形式:电影、戏剧和故事通常遵循三幕剧结构,其中第一幕、第二幕和第三幕通过建立情节、对比和高潮来捕捉故事。类似地,三原则可以让你的内容更具吸引力,将你的话总结为三个行动项或关键要点。例如,你可以将你的演示或关键要点总结为——
-
重点 1 —
-
重点 2—
-
重点 3—
这样可以让你的观众更容易记住你的内容,并在需要时采取行动。
5. 使其易于理解
我会让这部分非常易读易懂——
-
在演示文稿(或任何沟通中)使用较少或不使用行话。
-
不要在演示文稿中使用过多的颜色,保持视觉的舒适。
-
使用易于理解且与上下文相符的视觉效果。
-
保持幻灯片中的格式简单且一致。
-
在图表中标注坐标轴,添加颜色图例,并在需要的地方添加注释。
6. 创建一个成功的数据展示
大多数人不了解数据。
大多数人理解故事。
销售故事,而不是数据。
利用我刚刚提到的五个要点,现在是将这些要点整合起来,创建一个有效、简洁且成功的数据展示的最佳时机——
-
了解你的观众——映射观众的知识水平,并根据他们是同事、利益相关者还是高管和领导者来准备内容。
-
构建你的内容流程——一个好的故事应该从介绍议程到呈现关键发现,提供支持数据,讨论方法论,再到重新审视关键发现,整个过程应无缝衔接,这也是整个博客的重点:提供建议或下一步行动。
-
像讲故事一样叙述——就像每部好电影都有情节、冲突和高潮一样,你的数据故事应该有一个开始、中间和结尾,以形成一个全面的叙述。
-
确定你的故事类型——根据问题陈述、使用的方法和洞察,你的故事可以是一个演示、仪表板中的可视化,或者只是 Excel 中的数据。
-
保持简洁明了——每张幻灯片最多花费 5 分钟时间,并始终预留 10 分钟用于问答环节。
总之,仅仅通过数据讲解是不够的。数据讲故事是一项极其重要的技能(几乎是一种艺术),这正是我们必须追求的——将数据转化为业务价值。
关于这篇博客,我就说到这里。感谢阅读!希望你觉得这篇文章有趣。请在评论中告诉我你在讲故事方面的经验、你的数据之旅,以及你对 2023 年的期望!
如果你喜欢阅读这样的故事,可以考虑通过这个链接注册成为 Medium 会员。
祝数据探险愉快!
拉希是一位来自芝加哥的数据专家,热衷于分析数据并创作数据故事以传达洞见。她是一名全职医疗数据分析顾问,周末时会喝着咖啡撰写数据博客……
如何将数据视为产品
通过应用产品管理原则,最大化你从数据中获得的杠杆效应
·
关注 发表于 数据科学的前沿 ·10 分钟阅读·2023 年 2 月 8 日
--
许多组织希望他们的技术从一个成本变成一个差异化的竞争因素——数据技术也是如此。你经常会听到这种愿景被表达为“我们想要 将数据视为产品”。

图片由 Brands&People 提供,来源于 Unsplash
不同的定义
几年前,许多高管所说的“将数据视作产品”是指他们希望通过直接出售数据(例如在数据市场上)来实现数据的货币化。然而,今天这些市场大多包含的是由专门从多个来源汇聚数据的公司创建的数据(例如零售客流量、信用卡收据、产品评论)。很少有公司在货币化其第一方数据方面取得成功。
那么,当一个典型的企业希望将数据视作产品时,这意味着什么呢?有几种相互竞争但又互补的定义。Tableau 的定义——任何使用数据来帮助企业改善决策和流程的应用程序或工具都是数据产品——强调了数据的有用性。McKinsey 的定义——一套高质量、随时可用的数据,组织内的人员可以轻松访问并应用于不同的业务挑战——强调了标准化。Montecarlo 的定义——数据以可用的形式存在于公司内部(即使最后的环节涉及自助式转换)——强调了数据的治理。
将产品管理原则应用于数据
我倾向于将其视为结合期望结果和达到结果的过程。
期望的结果是,组织将通过将数据视作产品来最大化数据的杠杆效应,在这里,上述定义中强调的特征(有用性、标准化、治理)很重要。像 Tableau 一样,我对数据产品的定义持扩展性观点——数据集符合要求,但数据管道、仪表板、依赖数据的应用程序和机器学习模型也是如此。
期望的结果只有在有通向结果的路径时才有价值。要将数据视作产品,请在构思和构建数据产品时应用产品管理原则。哪些产品管理原则?(1)制定产品战略,(2)以客户为中心,(3)进行轻量级产品发现,以及(4)专注于找到市场契合点。我建议采纳以下与这些原则相一致的 10 个数据实践:

要将数据视作产品,采纳这些 10 种实践,将产品管理原则应用于构思和构建数据产品。
1. 理解并维护企业中的数据流图
产品经理的一个关键工作是简化。过于频繁地,当有人问“你有什么数据”时,答案是一个包含公司各个业务部门收集的数百个数据集的电子表格。这并不太有用。
将数据视为产品意味着你(数据产品团队)维护一个高层次的数据流模型,以便于发现和沟通。维持这个地图在多个粒度层级上。在最高层次,对于一个电子商务网站,它可能是:
-
网络流量
-
产品目录
-
网页内容
-
订单
-
库存
-
客户调查
在下一个粒度层次上,网页流量可能被拆分为会话数据、页面数据等。记录每个数据集是如何收集的、如何处理的、哪些角色可以访问以及如何访问、是否存在 PII 或其他属性、有哪些质量保证措施等。此外,记录每个数据集的生产使用案例。
正如你所看到的,当你从较高的粒度层次降到较低的层次时,映射开始包括你的数据平台实现的细节。它开始变成一个数据目录。
2. 识别关键指标
数据目录只是记录当前存在的内容。它不记录数据为何重要或数据是否适合用途。它不会告诉你需要改进什么。
数据产品策略的一个重要部分是使企业内的关键指标达成一致——你将测量什么、如何测量以及指标的目标数值是什么(目标会随时间变化)。你跟踪的指标范围应包括:
-
业务 KPI:需要通过数据实现哪些业务成果?
-
SLA:数据的可用性?数据质量?刷新率?
-
参与度:数据在公司内的使用范围和频率如何?
-
满意度:客户(可能是内部客户)对可用数据及其易用性满意度如何?
对于我们假设的电子商务网站,业务成果可能包括提高客户生命周期价值、增加免费层转化率等。对于内部采购者(用于补货)显示的库存的服务水平协议(SLA)可能是:其可用率达到 99.99%,每小时刷新,并保持在下周预测销售额之上。我们可能希望库存预测不仅被内部采购使用,还被物流团队使用并纳入到仪表盘中。我们还可能会有一个衡量预测库存数量被覆盖的频率的指标。
3. 已达成的标准、承诺的路线图和远见的待办事项
数据目录记录了当前存在的内容。指标捕捉了你的目标。两者都没有解释你接下来要去哪里。
根据客户反馈、利益相关者意见和市场条件,随着时间的推移调整产品愿景非常重要。在这一过程中,你的利益相关者会要求你提供功能和时间表,并期望你履行承诺。要处理变化和用户反馈,你需要三个方面:
-
优先级标准 是利益相关者事先达成一致的——这能够在组织内部实现透明度和对产品路线图的认可。
-
产品路线图本身是通过产品发现过程来指导的,以便团队可以避免在没有信息和原型的情况下同意时间表。产品发现很重要,我会深入探讨这一点。
-
我们认为重要但尚未纳入路线图的事项将被记录在产品待办事项列表中。通常,产品待办事项列表包含需要解决的客户问题(而非必须构建的功能)。在许多方面,待办事项列表(而非路线图)形成了你的长期产品愿景。组织待办事项列表以讲述一个清晰的故事。
路线图需要高度承诺——你应该能够对路线图上的时间表和功能做出承诺。一种很好的做法是达成优先级标准的共识,进行产品发现,并维护产品待办事项列表。
对于我们假设的库存预测数据产品,我们需要达成一致,如何衡量预测的准确性——是否我们很少出现缺货?我们是否最小化了采购和存储成本?缺货发生在仓库层面?还是公司层面?这些构成了优先级标准。如果有人要求你为易腐品定制库存模型,这值得做吗?你会首先将其添加到产品待办事项列表中。然后,你会进行产品发现,以确定进行这样的项目的投资回报率——例如,这将包括增加/减少仓库冷藏的成本。只有在了解了价值之后,你才会将其添加到你的产品路线图中。
4. 为你已有的客户构建
数据团队经常被技术口号困扰:他们只提供 API,或坚持要求每个人将数据发布到企业数据仓库中,或期望遵循单一词典。
借鉴产品管理的经验,深入了解你的客户是谁。他们在做什么?移动应用还是月度报告?他们知道什么?SQL 还是 Java?他们使用什么工具?仪表板还是 Tensorflow?他们是否需要数据变化时的警报?他们是否需要实时数据的移动平均值?他们是否关心测试覆盖率?
然后,以目标客户能够使用的方式提供数据。例如,你可能将数据提供在数据仓库中(供数据分析师使用),通过 API 使其可访问(供开发者使用),在功能存储中发布(供数据科学家使用),或提供一个可以在仪表板中使用的语义层(供业务用户使用)。
如果我们假设的库存预测数据产品将被内部采购员(即业务用户)使用,预测结果必须在用于订购补货的应用程序中提供。因此,预测结果可能需要通过 API 供该应用程序的开发者使用。
5. 不要将变更管理的负担转移
变更和冲突是不可避免的。数据供应商将更改格式;数据消费者将有新的需求;数据流速将发生变化;相同的数据可能通过多个渠道提供;你的客户可能因为成本而转向其他供应商。这些问题不仅仅是发生变更的团队或使用数据的团队的问题。
将数据视为产品的一大部分是确保数据用户不必承担变更管理的责任。尽可能确保演变的架构和服务对下游用户保持透明。
当不可逆的变更不可避免地发生时,为变更版本化,并与利益相关者合作,将他们从数据的旧版本迁移到新版本。这可能涉及创建一个迁移团队,其工作是将企业从一个版本迁移到下一个版本。
变更管理的原则同样适用于安全。确保为个人身份信息(PII)和合规性建立保护措施,而不是将负担转嫁给你的数据产品用户。
假设我们假设的库存预测数据产品被定制为包括易腐商品的预测。如果这涉及请求关于正在销售的商品的额外信息,你将不得不承担确保你的商品目录得到增强的责任。这项数据工程工作是项目范围的一部分,并影响是否值得进行这项工作的投资回报。
6. 采访客户以发现他们的数据需求
你如何演进产品待办事项,优先排序需求,并添加到路线图中?一个重要的原则是确保你不断与客户沟通,发现他们需要什么数据来解决他们遇到的问题。他们在当前数据产品中有哪些不足需要绕过?这些问题会成为你的产品待办事项,供你优先处理和解决。
在任何新的数据产品想法进入产品路线图之前,确保该产品的需求已经得到了潜在(内部或外部)客户的验证是非常重要的。基于规范的开发(“造出来,他们就会来”)是极其冒险的。更安全的方法是构建已经得到客户验证的想法的实现。
你怎么做呢?
7. 广泛进行白板讨论和原型设计
与希望使用数据产品的客户一起进行白板设计。这确保了你在数据平台上落地的内容能够满足他们在质量、完整性、延迟等方面的需求。在你构建任何数据管道或转化之前,与他们讨论数据的潜在用途。
这里一个很好的工具是原型。许多数据用例可以通过构建最小可行原型来验证,而不是产品。原型。
我什么意思?如果销售团队认为构建一个客户数据平台将帮助他们交叉销售产品,通过从各个产品的销售管道中挑选一组记录,手动进行匹配,并尝试对结果客户进行交叉销售来验证这一点。
使用这样的原型和与最终产品潜在用户的访谈来界定问题,具体包括:
-
需要构建的内容:识别项目成功所需的所有内容,从数据管道到用户界面。
-
你可以期待的商业关键绩效指标的投资回报
在编写任何代码之前做到这一点。只有当你对需要构建的内容和预期的投资回报有清晰的想法时,才应将项目添加到你的路线图中。在此之前,将问题保留在待办事项中。
在我们假设的库存预测数据产品的情况下,你需要与产品的关键用户验证输入模式和预测的使用,确保冷藏仓库能容纳多少等。在编写任何代码之前,你可能会在电子表格中进行预测,并对各种产品的整个场景进行模拟。
8. 仅构建立即需要的功能
优先考虑快速投入生产而不是构建所有必要功能。这意味着你应使用敏捷、迭代的过程,仅构建立即需要的数据集、数据管道、分析等。
使用产品待办事项来捕捉未来的需求。在你确定了会使用这些功能并能在白板/原型设计会议中提供反馈的客户之后,再构建这些能力。
9. 标准化常见实体和关键绩效指标
提供标准化的、丰富的常见实体和关键绩效指标数据集,这些数据集在整个业务中都是标准的。通常,这些丰富的实体驱动大量高投资回报的用例(例如客户数据平台、内容管理平台)或用于监管/合规目的(例如计算税收的方式)。
通常,你会有仅仅少数这些标准化的数据集和指标,因为这种增强需要跨业务单元的大量协作,并降低了它们的发布速度。
10. 在你的数据平台中提供自助服务功能
你必须在适合你组织的方式下平衡灵活性和标准化。不要过度实施第 9 条。不构建包含任何人可能想要的所有内容的集中数据集。相反,赋能团队实现自给自足。这是微服务原则在数据中的应用。
实现这一平衡的一种方式是提供小型、自包含的数据集,客户可以通过与其他数据集以特定领域的方式进行联合来定制这些数据集。通常,这种方法实现为数据网格,每个业务单元负责其发布到共享分析中心的数据集的质量。
总结
将数据视为产品,运用产品管理原则制定你的数据产品战略,以客户为中心,通过白板和原型设计来发现产品,并在标准化和灵活性之间找到合适的平衡。
如何使用日志模块调试 Python 脚本
原文:
towardsdatascience.com/how-to-troubleshoot-python-scripts-with-the-logging-module-aaf630c35fe3
打印语句只能带你走到一定程度…
·发布于 Towards Data Science ·阅读时间 7 分钟·2023 年 8 月 26 日
--

图片来源:Tima Miroshnichenko: www.pexels.com/photo/a-person-writing-on-a-notebook-5336909/
目录
∘ 介绍
∘ 日志模块
∘ 日志级别
∘ 配置级别
∘ 调试级别配置
∘ 创建日志文件
∘ 格式化日志消息
∘ 关键要点
介绍
考虑以下情况:你编写了一段代码,该代码要么返回错误,要么产生意外的值。
x1 = function1(x)
x2 = function2(x1)
x3 = function3(x2)
为了找出错误的代码行,你会写一个打印语句…
x1 = function1(x)
print(x1)
x2 = function2(x1)
x3 = function3(x2)
然后再添加一个打印语句…
x1 = function1(x)
print(x1)
x2 = function2(x1)
print(x2)
x3 = function3(x2)
然后再跟进另一个打印语句。
x1 = function1(x)
print(x1)
x2 = function2(x1)
print(x2)
x3 = function3(x2)
print(x3)
一旦你识别并修复了问题,这些打印语句就变得无用。所以,你一个个地删除或注释掉它们:
x1 = function1(x)
#print(x1)
x2 = function2(x1)
#print(x2)
x3 = function3(x2)
#print(x3)
如果你的故障排除经验类似于上述场景,你一定对使用打印语句处理错误代码行的挫败感感同身受。
幸运的是,Python 中有一个工具提供了更有效的调试策略:日志模块。
在这里,我们深入探讨日志模块的基本功能,并探索使其成为强大故障排除工具的特点。
日志模块
日志模块是为那些希望有效跟踪程序中某些事件的程序员设计的。
幸运的是,学习使用该工具没有任何前提条件,特别是如果你已经熟悉 Python 的打印语句。
从语法上看,日志模块的命令与打印语句非常相似,允许用户使用简单的一行代码生成消息。
import logging
logging.warning('Hello World')

代码输出(由作者创建)
话虽如此,日志记录包括了常规 print 语句所不提供的功能,我们现在将介绍这些功能。
日志等级
在日志模块中,并非所有消息都是相等的。日志模块中的事件被分解为不同的重要性等级。
日志模块的文档列出了 5 个日志等级,并解释了它们的使用场景。

日志等级(作者创建)
换句话说,当用户使用日志记录消息时,他们还可以设置该消息的重要性。INFO 消息最适合监控事件状态,WARNING 消息最适合发出警告,而 ERROR 消息最适合报告错误。
因此,用户可以根据消息的目的使用等级来分配其消息的重要性。例如,在加载数据集时,可以编写 INFO 和 ERROR 等级的消息。
try:
mock_data = pd.read_csv('random_data_source.csv')
logging.info('random_data_source.csv has been loaded')
except:
logging.error('random_data_source.csv does not exist')
通过对消息分配不同的重要性等级,用户可以定义他们希望看到或不希望看到的消息的重要性阈值。
默认情况下,日志模块只考虑 WARNING 及以上等级的日志。

日志等级(作者创建)
这意味着,未进行额外配置的消息记录不会返回 DEBUG 和 INFO 等级的消息。

代码输出(作者创建)
如输出所示,实际上只有 WARNING、ERROR 和 CRITICAL 等级的消息被生成。
配置等级
与 print 语句不同,日志允许用户配置应该生成的消息的重要性等级。
例如,假设我们需要记录 INFO 及以上等级的消息。

日志等级(作者创建)
我们可以通过使用basicConfig方法并定义level参数来指示程序生成 INFO 等级的消息:
现在,所有 INFO 及以上等级的消息都会被显示。

代码输出(作者创建)
由于程序已配置为接受 INFO 及以上等级的日志,因此记录了所有非 DEBUG 等级的日志。
调试等级配置
通过basicConfig方法,用户可以调整日志等级的阈值,以包含所需等级而忽略不需要的等级。
例如,假设我们正在对一个变量进行转换,并希望在每次转换后找到该变量的值。在这里,我们对变量 x 执行数学运算:
import logging
logging.basicConfig(level=logging.DEBUG)
x = 5
logging.info('Performing mathematical operations on x')
x = x * 2
logging.debug(f'x * 2 = {x}')
x = x + 6
logging.debug(f'x + 6 = {x}')
x = x / 4
logging.debug(f'x divided by 4 = {x}')
阈值日志等级已设置为 DEBUG,这意味着所有日志消息都会被创建。运行代码将返回如下结果:

代码输出(作者创建)
这些消息可能很有用,但一旦调试完成,它们便没有任何价值。那么,我们如何阻止这些消息在未来的迭代中被生成呢?
如果我们使用打印语句,解决方案是删除或注释掉每一个不需要的打印语句。幸运的是,使用日志记录时,可以通过简单地修改basicConfig方法中的日志级别阈值来移除不需要的调试消息。
现在,我们可以将日志级别更改为 INFO,从而省略所有 DEBUG 级别重要性的日志消息。
logging.basicConfig(level=logging.INFO) # Change from DEBUG to INFO
x = 5
logging.info('Performing mathematical operations on x')
x = x * 2
logging.debug(f'x * 2 = {x}')
x = x + 6
logging.debug(f'x + 6 = {x}')
x = x / 4
logging.debug(f'x divided by 4 = {x}')

代码输出(由作者创建)
这一次,即使日志命令仍在使用,它们也会被省略,因为 DEBUG 级别低于分配的 INFO 级别阈值。
同样,当我们希望开始调试过程时,我们可以使用日志记录来做相反的事情:降低日志消息的阈值重要性,以便程序生成较低重要性的消息。
创建日志文件
使用打印语句时,消息会显示在命令行或控制台上,但不会存储在任何位置。
使用日志模块的basicConfig方法,用户可以创建一个专门记录所有由模块创建的消息的文件。
例如,以下代码片段使用basicConfig方法将所有消息保存到一个名为“demo.log”的新创建的文件中。
import logging
logging.basicConfig(filename='demo.log',
level=logging.DEBUG)
logging.debug('Debug message')
logging.info('Info message')
现在,当运行脚本时,控制台不会显示任何消息。相反,消息将直接存储在“demo.log”文件中。

demo.log(由作者创建)
此外,这些记录的消息不会被覆盖;当添加更多日志时,它们会保留在“demo.log”文件中。如果我们再次运行相同的脚本,新日志消息将会被添加到新的一行中。

demo.log(由作者创建)
格式化日志消息
日志中的消息并不总是那么具有信息性和可读性。
为了生成更适合给定用例的消息,用户可以使用basicConfig方法配置消息的格式。
在下面的代码片段中,我们配置消息的格式为时间戳、日志级别和消息内容。
logging.basicConfig(filename='demo.log',
level=logging.DEBUG,
format='%(asctime)s %(levelname)s %(message)s'
)
logging.debug('Debug message')
logging.info('Info message')

代码输出(由作者创建)
确立日志的格式可以使生成的消息既具有信息性又具有可读性。当然,理想的格式取决于程序员的个人偏好。
关键要点

照片由Prateek Katyal提供,来源于Unsplash
总的来说,尽管打印语句通常被视为检查或调试代码的首选工具,但日志模块是更合适的工具。
日志提供了一个系统,将消息分类为不同的重要级别。此外,它们还具备一些功能,使用户能够轻松配置生成哪些消息、如何格式化这些消息以及它们应该存储在哪里。
日志模块能够让调试过程更快捷、无压力,因此,如果你厌倦了使用普通的、通用的打印语句,考虑采用它。
感谢阅读!
如何理解和使用 Jensen-Shannon 散度

作者提供的图片
关于 JS 散度的数学、逻辑和实际应用的入门 — 包括如何在漂移监控中最佳使用它
·
关注 发表在 Towards Data Science ·9 分钟阅读·2023 年 3 月 2 日
--
本文由 Arize AI 的首席执行官兼联合创始人 Jason Lopatecki 共同撰写
在机器学习系统中,漂移监控对于提供高质量的 ML 至关重要。一些在生产 ML 系统中漂移分析的常见用例包括:
-
检测训练和生产之间的特征变化,以在性能下降之前发现问题
-
检测两个生产周期之间的预测分布变化,以作为性能变化的代理(特别适用于延迟真实情况)
-
使用漂移作为重新训练的信号——以及重新训练的频率
-
捕捉特征转换问题或管道中断
-
检测错误使用的默认回退值
-
查找新的数据以进行标记
-
查找对模型有问题的新数据集群,特别是在非结构化数据中
-
查找不在训练集中的异常数据集群
虽然没有完美的漂移度量,但在过去十年里这个领域已经学到了很多,有一些经过良好测试的方法和度量在不同的使用场景中非常有用。
一种这样的度量是詹森-香农散度(JS Div)。此外 也被称为相对于平均值的总散度或信息半径,JS 散度是一个基于信息理论的统计度量。 JS 散度相比于其他度量的优势主要与某些事件或箱的空概率问题有关,这些问题会影响 Kullback-Leibler 散度(KL 散度)和人口稳定性指数(PSI)。JS 散度在比较两个分布时使用混合概率作为基线。在 PSI 和 KL 散度的离散版本中,当出现 0 概率事件时,方程会出现爆炸。
本博客文章涵盖了 JS 散度的定义、它与 KL 散度的不同、如何在漂移监测中使用 JS 散度,以及混合分布如何解决常见的测量问题。
JS 散度概述
詹森-香农是一种非对称度量,测量相对熵或两个分布所表示信息的差异。与 KL 散度密切相关,它可以被视为测量两个数据分布之间的距离,显示这两个分布之间的差异。
下图展示了与 KL 散度的对称性:

图片由作者提供
以及 JS 散度的离散形式:

图片由作者提供
其中混合分布是:

图片由作者提供
更多背景信息中,关于 JS 散度的技术论文由 Sony Computer Science Laboratories 的 Frank Nielsen 撰写。
在模型监测中,JS 散度的离散形式通常用于通过数据分箱来获取离散分布。JS 的离散形式和连续形式会随着样本和箱数趋于无穷大而收敛。有优化的选择方法来选择箱数以接近连续形式。
JS 散度如何在漂移监测中使用?
在模型监控中,JS 散度类似于 PSI,因为它用于监控生产环境,特别是特征和预测数据方面。JS 散度还用于确保生产中的输入或输出数据没有从基准数据中发生剧烈变化。基准数据可以是数据的训练生产窗口或训练/验证数据集。
漂移监控对于那些收到延迟地面真相以与生产模型决策进行比较的团队尤为有用。团队依赖于预测和特征分布的变化作为性能变化的代理。
JS 散度通常应用于每个特征独立地;它不是作为协方差特征测量的设计,而是显示每个特征如何独立于基准值发生偏离的度量。虽然 JS 散度确实支持多分布混合方法,但它实际上并不适用于比较完全不同的分布——它不是一个多变量漂移测量。
JS 散度的挑战 — 也是它的优势 — 是比较基准是一个混合分布。可以将 JS 散度视为两个步骤进行:
步骤 1:
使用生产和基准分布创建混合分布进行比较;

作者提供的图片
步骤 2:
比较生产和基准与混合。

作者提供的图片
上图展示了 A 分布、B 分布和混合分布。JS 散度是通过将 JS 分布与 A 和 B 进行比较来计算的。
✏️注意: 有时非从业者对捕捉数据变化的数学完美目标过于热衷。在实践中,重要的是要记住真实数据在生产中一直在变化,许多模型可以很好地适应这些修改的数据。使用漂移度量的目标是拥有一个可靠、稳定且非常有用的度量,以便进行故障排除。
JS 散度是对称度量
JS 散度类似于 PSI,因为它是一个对称度量。如果你交换基准分布 p(x) 和样本分布 q(x),你将得到相同的数字。这相比于 KL 散度在故障排除数据模型比较中有几个优势。有时团队希望在故障排除工作流中将比较基准替换为不同的分布,而拥有一个度量使得 A / B 与 B / A 相同,可以使比较结果更容易。

作者提供的图片
JS 散度的优势:处理零 bin
JS 散度的主要优势在于混合分布允许计算处理 bin 比较到 0。对于 KL 散度,如果你比较 0 bin,方程式基本上会爆炸。

作者提供的图片
如上图所示,有两个桶,其中一个桶在当前时间段内为 0,另一个有一个值。处理 0 桶的 JS 散度方法是将 JS 散度中的两个项假设其中一个为0 (0ln(0) = 0)*,因为函数是光滑的,并且在接近 0 时有一个极限,而另一个有一个值:

假设一个项为 0,你在 0 桶中得到:

这在 KL 散度或 PSI 中不起作用,因为你会在分母中除以 0:

在 q(x) = 0 的情况下,你有:

优势:零桶自然处理,无问题
✏️注意:在需要时,也可以进行修改,以允许 KL 散度和 PSI 用于 0 桶的分布。
JS 散度的劣势:混合分布移动
JS 散度的劣势实际上源于它的优势,即比较分布是两个分布的“混合”。
在 PSI 或 KL 散度的情况下,基线比较分布是静态比较分布,在每次比较时间段内固定。这使你能够获得一个稳定的指标,在每次比较和每个时期都具有相同的含义。例如,如果一天的 PSI 值为 0.2,那么一周后仍然是 0.2,这意味着这两天与基线的熵差异是相同的。这在 JS 散度中不一定如此。
在 JS 散度的情况下,每次进行比较时混合分布都会改变,因为生产分布在每个样本周期内都会变化。

每个监测日的混合分布计算(图片由作者提供)
上图展示了为两个不同时间框架计算的混合分布的示例。混合分布像是一个缓慢移动的基线,通过平均差异将时间 A 的基线平滑地连接到时间 B。
连续数值和分类特征之间的差异
JS 散度可以用来测量数值分布和分类分布之间的差异。

图片由作者提供
数值
对于数值分布,数据基于分割点、桶大小和桶宽度被分成桶。桶化策略可以是均匀桶、五分位数或复杂的混合策略,最终影响 JS 散度(请关注未来关于桶化策略的文章)。
分类
JS 散度的监控跟踪分类数据集中大的分布变化。对于分类特征,通常有一个大小点,卡尔多性变得过大,导致度量值不再有用。理想的大小在 50–100 个独特值左右——随着分布的卡尔多性增加,两个分布的差异及其重要性变得模糊。
高卡尔多性
对于高卡尔多性特征监控,现成的统计距离通常效果不好——建议使用以下其中一种选项:
-
嵌入: 在一些高卡尔多性的情况下,使用的值——例如用户 ID 或 内容 ID——已经用于内部创建嵌入。监控嵌入可以有所帮助。
-
纯高卡尔多性分类变量: 在其他情况下,当模型对输入进行大规模编码时,仅监控前 50–100 个最重要项的 JS 散度,而将所有其他值视为“其他”,可能会很有用。
当然,有时你可能想监控的是非常具体的内容——例如某一时期内新值或箱的百分比。此类情况更适合使用数据质量监控工具。
JS 散度示例
这是一个包含数值和分类特征的 JS 散度示例。

作者提供的图片
假设你在一家信用卡公司工作,负责为欺诈模型制定数值分布。模型是基于上图所示的基线进行训练的。我们可以看到费用分布发生了变化。行业标准在 PSI 阈值方面有许多,但如你所见,JS 散度的值却非常不同。0.2 的 PSI 标准不适用于 JS 散度。在 Arize(完全披露:我在 Arize 工作),我们通常查看多日周期内的移动窗口值,以为每个特征设置阈值。
该示例展示了一个数值变量及其分布上的 JS 散度。在上述示例中,值得注意的是,bin 95–97 从 12% 降到 3% 导致 JS 散度移动了 1.4。对于 bin 105–107 的 3% 到 12% 的九个百分点增加,JS 散度也有类似的变化。PSI 以类似对称的方式工作,这与 KL 散度不同。对于 KL 散度,12%->3% 会导致数值上更大的变化。
JS 散度的直觉
理解一些关于度量和基于分布变化的度量变化的逻辑非常重要。

作者提供的图片
上述示例展示了从一个分类 bin 到另一个分类 bin 的迁移。以“医疗”为输入的预测(贷款用途)从 2% 增加到 8%,而以“度假”为输入的预测从 23% 减少到 17%。
在这个例子中,与“医疗”相关的 JS 散度组件是 0.96,远大于“度假”百分比移动的 0.023。这与 KL 散度得到的结果正好相反。

图片由作者提供
结论
JS 散度 是一种常见的漂移测量方法。它具有对称性,并自然处理 0 桶比较,但在使用移动混合作为基准时也有一些缺点。根据你的使用场景,它可能是一个很好的漂移度量选择。
也就是说,这并不是唯一的选择。另一种替代方法是使用 人口稳定性指数 结合分布外分桶技术来处理零桶。利用分布外分桶技术可以让团队调节度量对分布外事件的敏感性,并轻松与固定基准分布进行比较(没有混合)。敬请关注更多关于此主题和分桶最佳实践的后续内容!
如何理解因果关系的世界
因果关系是一个广泛而复杂的领域。这里有一张地图帮助你理解它。
·
关注 发表在 Towards Data Science · 11 分钟阅读·2023 年 2 月 18 日
--
因果关系地图。图片由作者创作。
因果关系的世界大致分为两个主要领域:
-
因果推断的主要领域: 因果推断关注的是理解你所采取行动的效果。因果推断提供了工具,使你能够隔离并计算系统内变更的效果——即使该变更在实践中从未发生。因果推断可以用来回答以下问题:我因为服用了某种药物而变得更好吗?我需要增加多少广告支出才能实现我的收入目标?课堂规模对教育成就的影响是什么?
-
因果发现岛:因果发现方法利用数据确定其中的因果关系。关键是,因果发现揭示的关系不仅仅是统计相关性。因果关系对变化是不变的,代表了理解系统的更基本基础。
实验山脉 ⛰️
探索因果关系的最明显起点是实验山脉。这些山脉在景观中高耸入云,理由很充分。这些方法包括用于理解因果效应的金标准方法,具有最大的确定性。
理解这些方法有效性的关键在于掌握随机分配的意义。通过干预世界,实验随机分配那些接受某种处理和那些不接受处理的人——即对照组。处理可以是药物剂量、施加在作物上的肥料量、晨间例程等任何事物。
由于处理是随机化的,如果你从人群中选择一个足够大的样本,那么从统计学上讲,你的处理组和对照组之间唯一的差异就是处理本身。你已经成功排除了所有其他通常会偏倚结果的因素。这些对你的处理和结果有因果关系的外部因素被称为混杂因素。你在这篇文章中阅读的所有因果推断技术的目标都是消除混杂因素。因此,处理组和对照组之间的任何差异都必须是由于处理——从而使你能够计算处理效果。
以你是一个农民并希望了解使用一种新型肥料对作物的效果为例。与其盲目地将肥料施加到整个田地上,你可以将田地划分为若干个方块。对于每一个方块,你掷硬币,根据结果决定是否施加肥料。通过这样做,你已经随机化了是否施肥,从而排除了可能偏倚你选择的因素。此外,由于你在同一片田地内操作,天气等混杂因素也得到控制,不会影响结果。然后你可以简单地比较施肥方块和未施肥方块的作物产量,从而得到处理效果。

随机化实验如何计算处理效果的示例。图片由作者创建。
自然实验之城 🌆
在许多情况下,进行现实世界的实验实际上是不可行的。实验可能是非伦理的、费用过高、在后勤上不可能,或是这些因素的组合。那么,如果你面临一个无法进行实验的情况,你该怎么办?
各领域的科学家们几代人都面临这些情况。经济学家无法将受控实验扩展到国家的规模。生物学家无法干预分配可能有害的治疗。因此,这些科学家们发展了一系列技术来识别自然随机化,使他们能够去除偏差并识别处理效果,而无需直接干预自己。这些技术填充了自然实验的城市。
城市中的所有技术行为类似,利用自然随机化来计算处理效果,但要更深入理解它,现在是时候专注于一种:工具变量。这些变量不会导致或与结果相关,也不会与其他混杂因素相关,但它们对处理有直接的因果影响。在图形形式中,工具变量看起来与前一部分农民掷硬币的情况相同。
让我们回到农业例子。 农民选择的作物是玉米,他们希望了解改变价格会对销售量产生多大影响。农民知道有多种因素影响玉米销售:运输成本、当年的总体产量、消费者趋势等。然而,我们的农民知道在降雨较少的年份,产量较低,而产量较低时,农民会提高价格。
现在为了使天气成为一个好的工具变量,我们的农民假设:
-
天气和玉米销售不会被另一个因素混杂;即导致它们两个的因素。
-
天气对玉米销售没有影响。
如果这些假设是正确的,并且农民对玉米市场的领域知识也认为天气引入的随机化以及足够大的数据集,会让农民计算价格对玉米销售的影响——所有这些都不需要进行真实世界的实验!
因果图桥梁 🌉
因果推断可以看作是一个两阶段过程:识别和估计。识别是确定你需要控制的变量集合,即保持不变,以隔离感兴趣的因果效应的过程。估计则是应用统计技术到你的数据中以计算效果。因果图是执行识别的事实工具。
因果图可视化你希望探究的数据中的因果关系。在前面的部分,你已经看到了一个因果图,考虑了肥料对农作物产量的影响。
因果图是有向无环图(DAGs),这意味着它们将变量表示为节点,节点之间的有向边显示了一个变量对另一个变量的因果影响。边还表示变量之间的双向相关性,而因果关系仅是单向的,相关性或统计关联是双向的。因此,相关性可以表明因果关系,但不能作为证据。

如何理解因果图。图像由作者创作。
理解因果图的一种方式是将其视为对数据生成方式的估计。因果关系描述了单独的一个特征或与其他特征组合,如何导致另一个特征——最终形成你希望研究的特征。
在之前的章节中,你学习了如何通过随机化来消除混杂变量的偏倚效应。因果图允许你在不进行随机化的情况下实现相同的目标。这意味着你不一定需要进行实验或识别自然的随机源来解开数据中的因果关系。
因果发现岛 🏝️
因果发现是将算法和领域知识相结合,以找到适当的因果图的过程。
然而,因果发现的岛屿是一个难以生存的地方。你正试图估计一个表示数据生成过程的因果图,而这个过程你在现实中永远无法完全观察到——没有绝对的真相。因此,对于大多数现实世界的数据,因果图是数据生成过程的最佳估计,不能被验证为对现象的真实表征。
这并不意味着从因果发现中恢复的因果图,以及支撑大多数因果推断的图,毫无用处。恰恰相反——这些图为纯统计方法的机器学习提供了强大的进展,使你更进一步地理解你感兴趣的系统的机制。
你可以应用许多不同的算法来分析收集的数据,以揭示因果关系。最常见的两种因果发现算法类别是:
-
基于约束的:通过执行条件独立性测试,在测试中控制数据集中的不同变量,并测量这样做对其他变量的影响,算法可以识别出某些因果模式。在整个数据集中迭代地应用条件独立性测试,然后可以建立出更完整的潜在因果图。
-
基于评分: 这类算法提出了一系列不同的因果结构,然后根据它们与基础数据的适配程度来分配一个评分。该算法从基本结构开始,然后通过重复评分的回合,建立最适合的结构,从而形成一个包含可用变量的因果结构。
应用算法到你的数据时的难点在于,它们永远无法获取一个完全解决的因果图。输出总会有一些没有明确因果方向的边,这时人类领域知识显得尤为重要。领域专业知识在创建可用的因果图中至关重要,因此对于获得准确的因果发现结果至关重要。
因果发现的最终难题是存在未观察到的混淆变量。这些变量混淆了你的兴趣变量,但在你的数据集中不存在。由于没有观察到这些混淆变量,许多因果推断方法无法有效工作。因果发现方法和人类领域专业知识在这里非常强大,因为它们可以帮助标记未观察到的混淆变量可能影响数据生成过程的地方。
匹配森林 🌲
匹配森林是因果图桥梁的落脚点。因果图使你能够轻松了解在尝试估计因果效应时需要控制哪些变量。匹配森林中的技术为你提供了从图形世界到数据应用的工具。匹配技术在文献中被广泛理解和使用——这就像一片茂密的绿色森林。
匹配是通过构建根据一组匹配变量相似的比较组,来去除处理与结果之间的混淆效应的过程。这些匹配变量通常通过你的因果图来确定。
这里的直觉是你正在构建一个具有与处理组相似属性的对照组。因此,两个组之间的任何结果变异都必须归因于处理,从而得到因果效应的估计。
最简单的匹配工具是子分类。

使用胆固醇与运动在年龄的混淆下的子分类示例。与 Judea Pearl 的《为何之书》中使用的示例相同。图像由作者创建。
当你将治疗(运动)与结果(胆固醇)数据进行绘图时,你会注意到一个下降的趋势,如上图的左侧。然而,通过按混杂变量(年龄)对数据进行分组,从而创建子类,你可以观察到治疗与结果之间的真实关系——见右侧。子分类直观易懂,但随着需要控制的变量数量增加,所需的数据量急剧增加。这限制了子分类的适用性,导致了其他方法的出现,这些方法充实了匹配森林。
建模沼泽 🍃
建模沼泽是事情开始变得有些模糊的地方。建模沼泽是一些最熟悉的因果推断工具的家园,相比之下,新兴工具则较少被建立。模型提供了强大的因果效应估计方法,虽然有些依赖于完全指定的因果图,但其他模型在没有该要求的情况下也能有效运行。
建模沼泽中最受欢迎的方法是普通回归。普通最小二乘(OLS)回归是一个极其灵活且有价值的因果效应估计工具。它之所以受欢迎也有充分的理由:
-
理论上已被很好理解: OLS 和其他类型的回归方法从统计学角度非常清楚。这意味着应用回归到你的挑战中的假设是明确的,允许你对结果做出明智的选择。
-
可解释性强: 回归模型易于解释,不像现代机器学习技术。这使得它们在高风险情况下,如涉及监管时,表现优异。
-
因果推断很简单: 控制混杂变量并使用回归估计因果效应是直接的。回归方程中的学习系数是给定变量的因果效应的估计,同时控制其他变量——请参见下面的图示。

普通最小二乘回归的参数描述了每个变量如何随结果的变化而变化,同时控制其他变量。图像由作者创建。
这里值得更详细了解的第二种方法是结构因果模型(SCM)。SCM 直接基于因果图的基础,并学习由领域专业知识或算法因果发现确定的因果关系的数学形式。
这意味着你因果图中的边缘和节点现在有了从数据中学到的数学关系。这是非常强大的,因为它使你能够通过干预模型来轻松创建“如果……会怎样”的情境。干预只是意味着改变图中一个节点的值。然后,SCM 描述了这种变化如何传递到其他变量,最终影响处理结果。结果是,通过一个具有代表性的 SCM,你可以开始探索各种不同的情境,并比较不同动作的影响。

结构性因果模型学习到的边函数的一个例子。图片由作者创建。
决策智能沙漠 🐪
决策智能沙漠是荒凉而偏远的,但也有绿洲。这一地图区域包含了超越处理效果估计的各种新兴技术。为了说明沙漠中包含的方法类型,我们来考虑一下算法性救济。
在机器学习中,一种常见的解释性技术是反事实解释。反事实解释提出了一个问题:为了让结果有所不同,需要改变什么?
例如,设想一个保留预测机器学习模型,它预测一个客户将会流失。一个反事实解释来帮助解释为什么这个人可能会流失可能是:如果他们是 65 岁或以上的高级客户,他们会续订。
算法性救济从反事实解释的概念出发,但重点在于提供你行动的能力,而不仅仅是理解。因此,算法性救济提供了推荐行动以改变不利结果的能力,使你能够干预系统以防止这些结果。
这最终将上述的流失示例从不可操作的解释,转变为可操作的建议。应用救济以防止此人流失:如果他们获得 10%的折扣,他们会续订。由于折扣是可以采取行动的,这使你能够在现实世界中产生变化。
结论 🏁
你已经对一个深奥而迷人的主题进行了 whirlwind 之旅。我希望你喜欢对因果关系的探索,并感到有动力去学习更多。
如果你希望更深入地了解,我强烈推荐以下书籍和资源作为起点:
-
Brady Neal 的因果推断课程(Youtube):这是一个很好的入门视频系列,可以让你了解本文讨论的许多主题。
-
效果,或 因果推断混音带:这些书更侧重于因果推断的传统技术。这些书将为你提供一个强大的基础,帮助你继续前行。两本书的作者都很慷慨地免费提供,但如果你能负担得起,我总是建议你获取纸质版!
-
勇敢且真实的因果推断:这是一本非常有趣的书,探讨了因果推断和机器学习如何碰撞。书中包含了很好的实践代码示例,让你可以实际学习!
如何将初级数据科学代码升级为高级数据科学代码
这 4 个技巧将帮助你像高级数据科学家一样编写代码
·
关注 发布于数据科学的方向 ·7 分钟阅读·2023 年 6 月 12 日
--
你做到了。经过多年的努力工作,你成功获得了初级数据科学家的职位。你最初的几周迅速过去,公司入职培训结束后,你还没来得及反应,几年已经过去了。你参与了无数项目,无论是独立完成还是团队合作,你的解决方案都对公司产生了积极的影响。
但现在,你已经准备好迎接下一个挑战:成为一名高级数据科学家。但是,你如何弥补这个差距呢?一名高级数据科学家需要知道哪些内容?最重要的是:你如何将初级数据科学代码转化为高级数据科学代码?
幸运的是,最后一个问题是最容易回答的,也是提升技能的最简单途径,以便你在成为高级数据科学家的道路上不断进步。我已经列出了四个主要领域,你的初级代码可以在这些领域转变为任何公司都愿意提升你为高级数据科学家职位的代码。关键是掌握基础知识,摆脱“意大利面条”代码,开始实施测试和质量保证技能,并学会优化你的代码。
掌握数据科学代码的基础知识
你不能在走路之前跑步,因此在你能够编写高级数据科学家级别的代码之前,你需要掌握代码的基础知识。
在数据科学之旅的开始阶段,仅仅编写能正常运行的代码就是一种成就。然而,现在是时候开始掌握这些基础知识,使得你的代码能够顺利运行不再令人惊讶。
这是一个无法加速的建议,只能通过花时间做工作来实现。在你作为初级数据科学家的前几年里,你每天都会有机会专注于掌握数据科学代码的基础知识,从编程基础到算法,再到数据结构和设计模式。
此外,现在是通过学习其他编程语言(可能是你公司使用的语言或是你自己有时间学习的语言)和其他可以提高工作质量的技术(例如,用于组织项目的 Notion,版本控制的 Git,代码编辑器中的语法检查扩展等)来深化你的知识基础的时机。一些语言和工具会让你受益终身,而其他工具则会提供有价值的经验,即使你不再使用它们,也会让你成为更好的数据科学家。
现在也是扩展你的能力并开始探索数据科学中更复杂概念的时机。例如,你可能目前是在解释过去事件原因的数据分析师角色。然而,你的老板现在希望你转向预测分析,这需要你开始学习机器学习和人工智能。推动自己学习这些主题将使你能够进入更高级和监督的角色,在这些角色中,你可以开始将你的知识传递给那些像你一样刚开始的新初级数据科学家。
专注于编写干净、可维护和易读的代码
我常在以前的文章中开玩笑说数据科学家写的代码很糟糕。意大利面条代码确实存在,尤其是在你刚开始的时候。这在你作为初级数据科学家的前两年可能是可以接受的,但随着经验的增加,写出凌乱的代码就变得越来越不可接受。
使你脱颖而出的一个方面是你能够编写干净、可维护和易读的代码。这不仅让你在工作中更容易合作且非常专业,还表明你能够将这些技巧传授给未来在你指导下的初级数据科学家。
因此,要将你的初级代码提升到高级代码水平,你需要专注于使你的代码始终保持干净、可维护和易读。
Python 和 R 都有关于最佳实践和风格的很好的指南,这些指南可以帮助你开始更专业地格式化代码。代码的整洁性、可维护性和可读性是数据科学家的基石,这样的数据科学家是合作起来令人愉快的,这也是为什么这些标准应该铭记于心(或者至少,在你桌面上显眼的位置,随手可得)。最佳实践和风格是你在提交最终提交或将代码发送到软件工程部门进行生产级代码转换之前,应该始终考虑和仔细审查的两个方面。
这也意味着你应该至少遵循DRY 编码原则(至少),以及可维护性 编码原则(在更高级的层次上),以确保你编写出尽可能好的代码。虽然这些原则可能在你主要编写的代码不会被其他人触及或仅在小型内部机器上运行时不太相关,但如果你换工作或开始编写生产级代码,熟练掌握这些原则并不坏。
此外,在你职业生涯的这个阶段,你应该成为行业/公司代码标准的标杆。你推送到仓库的每一个代码提交都应该是你所在行业或公司所期望的光辉范例,应该是可以打印出来用作培训手册的内容。是的,这会花费你额外的时间,但这额外的细致思考将在公司内部晋升时带来回报。他们会关注什么?一个持续编写干净、可维护且易读代码的员工——这应该就是你!
这份快速简便的 7 步检查清单将帮助你编写更好的数据科学 Python 代码 [## 这份快速简便的 7 步检查清单将帮助你编写更好的数据科学 Python 代码
这个检查清单将帮助你编写干净且易于管理的 Python 代码
开发测试和质量保证技能
精通单元测试、集成测试和自动化测试框架是立即提升代码水平的好方法。虽然这些技能你作为初级数据科学家应当了解,但作为高级数据科学家你应该精通这些技能。
测试和质量保证技能是你开始编写优秀代码的地方,这些代码按设计工作,并能与其他代码片段协同工作。在以前,你可能只是将代码发送到软件工程部门,由他们准备一切以进行集成,而现在你将像高级数据科学家一样编写代码,必须确保你的代码正常运行,并能够集成到更大的代码库中。
虽然你的公司可能有特定的单元测试和集成测试要求你运行,但开始构建自己的测试以确保代码的运行和集成方式是一个不错的主意。自己的质量保证方式是对自己的代码负责的好方法,并确保如果代码能通过你自己的测试,它也能顺利通过公司测试。这不仅让你成为一个更好的数据科学家,而且使你在编写代码时变得更加高效。
发展测试和质量保证技能是向公司展示你致力于提升技艺和关心工作质量及推送到生产环境的代码的好方法。这些都是让你成为高级数据科学家职位优秀候选人的属性。
单元测试和数据科学的 Python ## 单元测试与数据科学的 Python
使用 nose2 和参数化测试尽早发现错误
[towardsdatascience.com
将性能优化作为优先事项
没有什么比在将代码提交给软件工程部门后,听到与收到数据科学家的代码相关的抱怨声更能激励你学习如何优化代码了。这是每个数据科学家都应该经历的一个谦逊的体验。
学习代码优化不仅仅是为了与软件部门保持良好的工作关系——更是为了让自己成为一个更加稳健的数据科学家,能够在没有其他部门支持的情况下编写出优秀的代码。能够在第一次就编写出稳定、优化的代码,是成为高级数据科学家的一个重要步骤。
在诸如缓存(将数据副本存储在主要数据存储前面——并非所有应用程序都相关,但在为客户制作仪表板时可能有用)、时间复杂度(算法运行所需的时间)、数据库索引(可以加速数据库表中数据检索操作的结构)和查询优化(找出改善查询性能的最佳方法)等主题上进行学习,是优化数据科学代码的良好起点。
虽然上述提到的所有主题并不适用于所有类型的数据科学工作,但它们都是很好的工具,可以放在备用工具中,无论是为了未来的工作,还是在某次需要时能够迅速上手解决问题——这是高级数据科学家的一个重要特质。
订阅以直接将我的故事发送到您的收件箱:故事订阅
请成为会员,通过我的推荐链接获得无限制访问 Medium 的权限(您不会额外产生费用,我将获得少量佣金):Medium 会员
支持我的写作,通过捐赠来资助更多类似故事的创作:捐赠
如何利用参数解析提升机器学习工作流的效率
如何使用 argparse 来提升命令行应用程序的效率,并且如何将其应用于机器学习项目的完整指南
·发表在 Towards Data Science ·阅读时间 6 分钟·2023 年 3 月 29 日
--

图片来源于作者。
如果你在数据科学或软件工程领域有过一些探索,你很可能遇到过需要使用命令行接口(CLI)的应用程序。常见的例子包括用于管理 Azure 资源的 Azure CLI 或用于版本控制和源代码管理的 Git。
同样的功能和程序互动性也可以通过你自己定制的 Python 应用程序来实现。命令行参数是丰富应用程序功能的绝佳工具,它允许你和你的用户无缝配置和定制程序的行为,从而增加了必要的灵活性。
一个流行且(也许是)最常用的 Python 库来解析命令行参数是 argparse。在本文中,我们将探索一些其核心功能,并通过具体示例详细了解如何高效地将其应用于 Python 应用程序。
argparse 简介
Python 的 argparse 模块提供了一种直观且用户友好的方式来解析命令行参数。简而言之,你只需完成以下三步:(1)创建一个 ArgumentParser 对象,(2)通过 add_argument() 方法添加参数规范,(3)使用 parse_args() 方法运行解析器。现在让我们详细探讨这三个步骤,并看看如何将它们组合成一个功能齐全的命令行解析器。
首先,[ArgumentParser](https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser) 对象作为一个容器,保存了必要的信息,如程序的名称或简要描述。用户可以通过帮助参数 -h 或 --help 检索这些信息,从而更好地理解程序的意图。
import argparse
parser = argparse.ArgumentParser(
prog = 'Sample Program',
description='Description of the sample program'
)
其次,我们可以通过 add_argument() 方法添加位置参数或可选参数。位置参数通过直接提供参数名称来指定,而可选参数需要通过 — 前缀进行识别。一个单破折号用于指定参数的缩写版本,通常是一个字母,而双破折号用于提供更具描述性的参数名称。
# adding positional argument
parser.add_argument('filename')
# adding optional argument
parser.add_argument('-p', '--parameter')
最后,我们可以使用 parse_args() 方法运行解析器,这样我们就可以访问和操作 CLI 中指定的参数。
args = parser.parse_args()
# print the parsed arguments
print("filename: " + args.filename + "\n" + "parameter: " + args.parameter)
现在我们可以在 CLI 中运行这个程序——我们称之为 program.py——并使用一些随机参数来查看它的工作情况:
>>> python program.py 'sample_data.csv' -p 10
filename: sample_data.csv
parameter: 10
我们现在已经构建了一个功能,允许我们直接在命令行上指定输入参数,然后使用这些参数执行任何我们想要的操作。你现在可能已经能够想象这对于任何需要重复运行程序的开发过程有多么有用,从而实现了一种简单而无缝的交互方式。
机器学习中的实用工具
假设你已经在 Python 中构建了一个机器学习或深度学习模型,你希望使用不同的超参数,如学习率、批处理大小或训练轮次,并将结果存储在不同的目录中。
直接在命令行上指定这些超参数大大简化了与该程序的交互方式。它使你能够在不实际修改底层源代码的情况下对不同的模型配置进行实验,从而减少了引入意外错误的可能性。
示例:训练随机森林分类器
想象一下,你想建立一个实验工作流,使你能够无缝且反复地训练随机森林分类器。你希望以一种方式进行配置,以便你只需将训练数据集、一些超参数和模型的目标目录传递给 CLI,它就会运行、训练模型并将其存储在指定位置。
在这个例子中,我们将使用公开的 鸢尾花种类 数据集。我们可以通过 seaborn 加载数据集并将其保存为 iris.csv。
import searborn as sns
iris = sns.load_dataset("iris")
iris.to_csv('iris.csv', index=False)
为了更好地了解我们的数据,我们可以使用 成对图 可视化它:

作者提供的图片。使用的数据集:鸢尾花种类。许可证:CC0 公开领域。
现在开始我们的主要任务:在我们的 Python 程序中构建一个解析器功能。为了训练随机森林分类器,我们希望将训练数据集传递给它——这将是我们的位置参数——两个超参数,以及一个我们希望保存模型的目标路径。后者将是我们的可选参数。
对于可选参数,我们还将通过type关键字指定类型,通过default关键字指定默认值,并通过help关键字提供参数的有用描述。
然后,我们将解析参数并将结果存储在变量args中,稍后我们将使用这些结果来指定我们正在读取的数据集、训练分类器的超参数以及我们希望模型保存的位置。
这段代码的样子如下:
import argparse
import pandas as pd
import pickle
from sklearn.ensemble import RandomForestClassifier
# Define and parse command-line arguments
parser = argparse.ArgumentParser(
prog = 'Model training',
description = 'Train a random forest classifier on the iris dataset'
)
parser.add_argument(
'train_data', help='training data (.csv format)'
)
parser.add_argument(
'--n_estimators', type=int, default=100,
help='number of trees in the forest (default: 100)'
)
parser.add_argument(
'--max_depth', type=int, default=None,
help='maximum depth of the tree (default: None)'
)
parser.add_argument(
'--model_path', type=str, default='./model.pkl',
help='path to save the trained model (default: ./model.pkl)'
)
args = parser.parse_args()
# Read the dataset
iris = pd.read_csv(args.train_data)
X = iris.loc[:, iris.columns != 'species']
y = iris['species']
# Train a random forest classifier with the specified hyperparameters
clf = RandomForestClassifier(
n_estimators=args.n_estimators,
max_depth=args.max_depth,
random_state=42)
clf.fit(X, y)
# Save the trained model to a pickle file
with open(args.model_path, 'wb') as f:
pickle.dump(clf, f)
现在,让我们将脚本保存为train_rf.py并将其放置在与我们的训练数据集iris.csv相同的目录中。
接下来,我们打开一个终端窗口,从中可以用自定义参数调用这个程序。在下面的示例中,我们将n_estimators指定为 100,将max_depth指定为 10。至于model_path,我们对默认路径感到满意,因此在这种情况下无需指定。
>>> python .\train_rf.py 'iris.csv' --n_estimators 100 --max_depth 10
这行代码将训练我们的随机森林分类器,过一会儿,你将会看到一个名为model.pkl的文件出现在你的目录中,你可以用它来在测试集上验证你的模型或生成预测。
我希望这篇文章给你提供了一些关于如何在 Python 中使用 argparse 直接从 CLI 解析参数的见解,从而改善你的机器学习应用程序的用户体验和互动性。
编程愉快!
更多资源:
喜欢这篇文章吗?
让我们联系一下吧!你可以在 Twitter 和 LinkedIn 上找到我。
如果你希望支持我的写作,你可以通过 Medium 会员 来实现,这将为你提供访问我所有故事以及 Medium 上成千上万其他作者故事的权限。
[## 通过我的推荐链接加入 Medium - Thomas A Dorfer
阅读 Thomas A Dorfer(以及 Medium 上成千上万的其他作者)的每一篇文章。您的会员费直接支持……
medium.com](https://medium.com/@thomasdorfer/membership?source=post_page-----2f637eaf5f6a--------------------------------)
如何使用 Chat-GPT 和 Python 在 Neo4j 中基于你自己的文章构建知识图谱
一个包含 120 多篇关于数学和数据科学的文章结构化知识的图形
·发布在 Towards Data Science ·8 分钟阅读·2023 年 8 月 26 日
--

作者截图
在这篇文章中,我将展示如何利用图形技术和一些编程来结构化和探索你自己的文章内容。
使用 NLP 技术来结构化非结构化数据的想法并不新鲜,然而,最新的大型语言模型(LLMs)的进展激发了无数机会。Chat-GPT 的普及技术使得业余爱好者对 LLMs 和生成模型产生了很多关注。
实际上,生成式 AI 已经成为许多公司议程中的一部分!
在这篇文章中,我们将通过编程语言 Python 使用 OpenAI 的开发者 API 来处理技术。我们将处理来自 Medium 的数据(哈哈,元数据?),并构建一个知识图谱。听起来可能有些复杂,但实际上开始时非常简单。
开始
首先,攻击计划如下。
-
使 API 正常工作并通过 Python 访问它。
-
使用示例文本进行提示工程,以确保 GPT-4 模型理解你的需求。
-
从 Medium 下载你的文章(如果你愿意,也可以使用其他文本),并对数据进行预处理。
-
提取和收集 Chat-GPT 的输出。
-
对 Chat-GPT 的输出进行后处理
-
编写代码使用 Cypher 查询语言进一步将数据结构化为图形。
-
玩弄你的新伙伴并探索你的文章。
话不多说,让我们通过快速设置基础技术来开始吧。
设置
我们需要在本地计算机上安装编程语言 Python 和图形数据库 Neo4j。
首先,你需要确保你在 OpenAI 有一个付费账户,以便可以使用 GPT-4。其次,你应该确保你已注册 API 使用。一旦准备好,你需要生成一个API 密钥。然后你需要 pip install openai。
在连接 ChatGPT 之前,让我们去浏览器中尝试找到询问此任务的正确方式。这被称为提示工程,正确处理非常重要。通过使用我的一篇随机文章作为示例,尝试不同的询问方式,我发现正确的方法是提供一个详细且有指导性的预设,然后再给出实际文本。
我最终得到了以下的预设:

作者截图
作为示例,我给它了一段关于 Gamma 函数的文章摘录,这篇文章是我很久以前写的:

作者截图
它得出的结果是:

作者截图
尽管它显然并没有真正理解任务,但它做得还不错,特别是在格式上。然而,有时它会创建重复项,并且注意到它有时会虚构一些实体和关系,尽管我们要求它不要这样做。恼人的不听话的机器!我们稍后会处理这个问题。
为了将来使用,我们将把这个预设存储在一个名为 prompt_input.py 的 Python 文件中。
现在基础设置已经就绪,让我们测试一下它是否真的有效。
如果代码仅供你自己使用,并且只在本地机器上,你可以将 API 密钥硬编码到 Python 文件中,否则,你可以将其设置为环境变量或放在一个你不会推送的配置文件中!
让我们测试一下这个设置是否有效。我们创建一个名为 connect.py 的文件,包含从 Python 连接 ChatGPT 的基本代码。
我们验证这个是否有效!
数据
我需要从我的 Medium 账户中提取文章。在写作时,我已经发布了 123 篇文章,但从 Medium 下载功能返回了 259 个文件!这是因为它也将评论和草稿分类为帖子。我们只想要已发布的文章,但这还不是唯一的问题。这些文件是 HTML 文件!如果你想在浏览器中阅读当然没问题,但如果你想处理纯文本就不太适用了。
好吧,中等水平,你做得不错,但这不能阻止一个装备了编程语言和各种窍门的数据科学家!
我们还注意到下载文件的文件名相当混乱。一个标准名称的例子是“2020–12–11_The-Most-Beautiful-Equation-in-the-World-5ab6e49c363.html”
让我们将这些文件存储在一个名为 raw 的文件夹中。
我们编写一个名为 extract_text_from_html.py 的小模块,功能是从这些文件中提取文本:
在我们能够实际从 ChatGPT 获取结果之前,我们需要能够将文本拆分成批次。原因是 GPT-4 有一个令牌限制。幸运的是,这很简单。在名为preprocess.py的文件中,我们编写:
现在我们准备实际从 ChatGPT 获取一些数据。
我们编写一个名为process_articels.py的文件,在其中循环处理文章,从令人恐惧的文件名中检索标题,从 HTML 文件中提取实际的文本,将每批文本通过ChatGPT处理,从文件中收集结果,并将模型的输出保存到一个名为data的文件夹中。我们还将实际的文本保存在一个名为cleaned的文件夹中,以便以后使用。
呼!!那真是一大堆工作。但实际上,代码很简单,因为我们已经在其他文件中完成了一些工作。
上述代码可能需要一段时间来执行,因为 GPT-4 模型相对较慢,与其他表现较差的模型相比。我们确保使用缓存设置,这样如果程序崩溃,我们不会从头开始,而是从中断的地方继续。
现在(经过几个小时的痛苦之后),我们有了一个结构化的 GPT-4 结果数据集。完美。现在我们“只”需要从中构建图谱。
构建知识图谱
我们将预处理和图谱创建过程合并为一个单独的函数。这通常不是很明智(关注点分离),但由于我们在预处理阶段需要查看“关系和实体”层级,我们可以在双手沾满污垢时创建图谱中的节点和关系。
我们创建一个小型 API,包含一个驱动程序,以便我们可以与图形进行交互。
我们需要循环处理结果,确保实体不太长,清理结果,并定义 gpt 模型输出中的节点和关系,我们不希望用相同的查询多次调用图形,我们希望实体与原始文章相关联,然后我们需要确保 Chat-GPT 提出的每个实体和关系实际存在于文本中,以免我们构建一个机器梦的图谱!
上述要求的最后一点是提高我们信任图形的概率,即使它不是万无一失的,考虑到这一点。
没问题!我们写下以下内容:
当然,有很多方法可以为知识图谱创建模式。不是总能容易看出什么应该是节点,什么应该是关系,但由于我们不希望关系之间有关系,所以我们选择了上述方法。
此外,我为这篇文章选择了简约的方法。通常,我们会用更多的属性来丰富节点和关系。
现在我们只需要一个主要入口点。
就这样。现在我们有了一个包含 Medium 上我文章信息的知识图谱。事实上,我们有大约 2000 个节点和 4500 个关系。
探索图谱
那么我们可以用这个做什么?我们应该问它什么?
让我们尝试找出不同的人出现在多少篇文章中。我们有如下结果:

作者截图
不出所料,欧拉排在榜首,但我惊讶于拉马努 jan 和牛顿出现在我的 4 篇文章中。当然,我可以找到这些文章的标题,但让我们继续。好吧,这很有趣,但你不需要一个图表就能搞清楚这一点。
我们再试试别的。让我们看看有多少篇文章同时提到瑞曼和欧拉。

作者截图
让我们看看我的文章提到了多少欧拉的发现。

作者截图
嗯,没有欧拉线?我得写一篇关于这个的文章。
让我们找出有多少篇文章与文章群论共享了一些数学关键词。

作者截图
结果显示在这里,与上图中非橙色节点连接的 27 篇其他文章。尽管这只是一个玩具示例,但可以想象它同样可以显示业务相关文档如何通过一些在某些学科(如 GDPR 或审计)中重要的敏感关键词相互关联。
收获
显然,这项工作应该被视为我们所谓的“概念验证”。我们不能真正地利用我的文章,但如果这些文本来自一个公司,包含关于他们的客户和员工的信息,从电子邮件到 Word 文档、PDF 等等,这可以用来绘制客户之间的关系以及哪些员工密切合作。
这将使我们能够 360 度了解数据如何在整个组织中流动,谁是特定信息流的最重要人物,谁是了解特定主题或文档的合适人选,我们部门联系的客户曾被另一个部门联系过,等等。
极其宝贵的信息。当然,我们不能用 ChatGPT 来做这个,因为我们不知道我们发送的数据会发生什么。因此,询问它关于敏感或业务关键的信息不是一个好主意。我们需要做的是下载另一个仅存在于我们笔记本电脑上的 LLM(大型语言模型)。一个本地 LLM。我们甚至可以在自己的数据上进行微调。目前许多公司已经在进行这种操作,用于构建聊天机器人、助手等。
但如果你问我,用它来构建一个关于你非结构化数据的知识图谱是更高层次的工作,我认为我已经展示了这完全是可行的!
如果你的公司想知道我们如何利用你的数据为你的业务编织可能性网络,请联系我或我的同事Kenneth Nielsen。
感谢你的阅读。
如果你喜欢在 Medium 上阅读像这样的一些文章,你可以 获取会员资格 以获得全部访问权限。要加入社区,只需点击 这里。
如何使用 ChatGPT 生成图表
原文:
towardsdatascience.com/how-to-use-chatgpt-to-generate-diagrams-a78fb6693057
提示工程,SVG
快速教程:如何编写合适的提示以让 ChatGPT 生成图表
·发布于 Towards Data Science ·阅读时长 4 分钟·2023 年 5 月 29 日
--

图片来源:Christina @ wocintechchat.com 在 Unsplash
你可能知道 ChatGPT 在生成文本方面非常出色。你是否想过它也能为你创建令人惊叹的图表?如果你还没听说过,也不用担心,因为我在这里告诉你所有的相关信息。
在这篇文章中,我们将向你展示如何利用 ChatGPT 生成令人惊叹的图表。所以,让我们直接开始吧!
我们将涵盖以下内容:
-
入门指南
-
提示的结构
-
一个更高级的示例
入门指南
让我们假设你想使用 ChatGPT 创建以下图形:

作者提供的图像
创建视觉上引人注目且易于理解的图表的关键在于让 ChatGPT 生成 SVG 代码。是的,你没听错——ChatGPT 掌握着你成功的关键,你需要做的就是学习如何挖掘它的无限潜力。
向 ChatGPT 写下以下提示:
编写 SVG 代码生成以下图像:一个白色矩形,边框为绿色。将以下文本居中放置在矩形内:hello world!
ChatGPT 将生成以下代码:
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="200">
<rect x="50" y="50" width="200" height="100" fill="white" stroke="green" stroke-width="2"/>
<text x="150" y="100" text-anchor="middle" alignment-baseline="middle" font-size="20" fill="black">Hello World!</text>
</svg>
复制并粘贴到一个新文件中。将文件保存为 SVG 扩展名并打开它!你将看到生成的图像!
提示的结构
让 ChatGPT 绘制复杂图表不仅仅是生成代码。要掌握图表绘制的艺术,你需要将指令分解为小的、具体的步骤。只有这样,你才能释放创造力,设计出既美观又实用的图表。
为了让 ChatGPT 绘制复杂的图表,我们可以将它们拆分成单独的组件,然后给出如何构建每个组件的指令。我们可以按如下方式组织 ChatGPT 提示:
1. 初始声明
在这里,我们指示 ChatGPT 构建生成 SVG 图像的代码:
Write the svg code to generate the following image width 600 height 300:
2. 生成组件列表
现在我们将组件生成列表指定为一个项目符号列表:
- A
- B
- C
对每个组件,指定一些细节作为嵌套列表:
- A
-- A1
-- A2
- B
-- B1
-- B2
-- B3
- C
-- C1
细节包括与每个组件相关的具体元素,如大小、颜色等。
一个更高级的例子
现在,让我们进入一个更高级的例子。假设我们要绘制以下图表:

图片由作者提供
将问题分成三个独立的部分:
-
绘制带有文本的矩形
-
绘制带有文本的圆形
-
绘制带有文本的箭头。
让我们指示 ChatGPT 执行这三个步骤。
首先,给 ChatGPT 一些通用建议:
Write the svg code to generate the following image width 600 height 300:
然后,指示 ChatGPT 绘制矩形。尽可能具体:
- a white rectangle with:
-- width 150, height 50
-- green borders.
-- center the following text inside the rectangle: Application
现在,关于圆形的建议:
- on the right of the rectangle, draw a white circle with:
-- cx 500
-- radius 50
-- green borders.
-- center the following text inside the circle: lake
最后,指定如何构建箭头:
- a green arrow from the rectangle to the circle:
-- the arrow must end when the circle begins
-- add the following text centered above the arrow: request
就这些!ChatGPT 将为你生成以下代码:
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="300">
<rect x="225" y="125" width="150" height="50" fill="white" stroke="green" stroke-width="2" />
<text x="300" y="150" text-anchor="middle" alignment-baseline="middle">Application</text>
<circle cx="500" cy="150" r="50" fill="white" stroke="green" stroke-width="2" />
<text x="500" y="150" text-anchor="middle" alignment-baseline="middle">lake</text>
<path d="M375,150 L450,150" fill="none" stroke="green" stroke-width="2" marker-end="url(#arrowhead)" />
<text x="412.5" y="125" text-anchor="middle">request</text>
<defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" />
</marker>
</defs>
</svg>
将代码粘贴到一个空文件中,并将其保存为 SVG 扩展名。然后,用浏览器打开该文件以查看生成的图表!
总结
恭喜!你刚刚学会了如何在 ChatGPT 中生成图表!主要有两个要素:
-
让 ChatGPT 为你生成 SVG 代码
-
将指令拆分成小而具体的步骤。
尝试不同的形状、颜色和布局,以创建视觉上吸引人且易于理解的图表。
记住,成功生成图表的关键是将指令拆分成小而具体的步骤。这将使你能够专注于每个图表元素,并确保一切都正确放置。
利用 ChatGPT 强大的 AI 技术,生成图表从未如此简单!无论你是学生、商业专业人士,还是喜欢视觉沟通的人,ChatGPT 都可以帮助你快速创建令人惊叹的图表。那么为什么不今天就试试看,看看你能想到什么令人惊叹的图表呢?
如何在数据可视化中使用颜色
原文:
towardsdatascience.com/how-to-use-color-in-data-visualizations-37b9752b182d
利用颜色提升数据理解
·发表于Towards Data Science ·15 分钟阅读·2023 年 10 月 7 日
--

长臂猿。 来源:Adobe Stock,标准许可。
上图突出了弗朗索瓦长臂猿,这是一种最不常见的猴类物种之一。 值得注意的是,它们的幼崽拥有鲜艳的橙色皮毛,随着成长逐渐变为黑色。现有理论认为,这种橙色使父母能够在树顶环境中轻松监控幼崽¹。这对于及时识别和应对威胁,如即将到来的捕食者攻击至关重要。如果成年猴子能够用语言表达,它可能会说:
抓住橙色的!
这里应该给自然母亲以赞美。多亏了颜色的有效使用,最小和最脆弱的存在有可能被保留。
为什么颜色在数据可视化中如此重要?
我必须承认,我犹豫了很久才发布这篇文章。原因是这个话题已经被广泛讨论。然而,我仍然看到一个细分领域,特别是在实际运用颜色的细节展示上。
我已经在最近的一篇文章中介绍了颜色概念的功能性用途。现在,我想分享一些在这一领域的额外提示。希望你会发现它们既实用又客观。让我们从一些无效的颜色使用例子开始。
1. 装饰优先于功能
如果我们没有明确解释为什么在特定可视化中使用了颜色,这个问题就会出现。以这张图表为例。你能解释一下为什么这里应用了如此多的颜色吗?

来源:作者提供的图片。
有时候,美学是一种可行的解释。但这很棘手。我们进入了一个充满个人品味、喜好和厌恶的模糊领域。对某些人而言视觉上令人愉悦的东西可能对其他人来说是不可接受的。
2. 缺乏一致性和客观性
有时,我们在可视化中不一致地使用饱和度或亮度级别。我们通过从调色板(如 PowerPoint 中)选择颜色来做到这一点,而不确保幻灯片之间的一致性。这有时可能是由于疏忽,但主要是由于匆忙。虽然大多数人会忽略这种“问题”,但一些敏锐的观察者可能不会。

来源:作者的屏幕截图。
疏忽通常表现为关键信息突出显示不一致:一张幻灯片上是黄色,另一张幻灯片上是蓝色。同样,不一致的颜色编码也会发生错误——当一种颜色代表一个元素(如某一页的预算),而在另一页上代表结果或预测时。
3. 忽视色彩障碍用户
众多杰出的出版物巧妙地突出了这个问题。通过如Adobe Color这样的工具可以深入了解这个问题,该工具展示了不同颜色组合如何适应各种视觉障碍。即使我没有视力障碍(或我认为我没有),我仍然很难区分某些色调和渐变。

Adobe Color 工具展示了不同视力障碍如何影响颜色的解读。 来源:作者的屏幕截图。
4. 忽视上下文
颜色不是孤立看到的:它们的周围环境会影响它们。尽管我们的眼睛通过光波长来检测颜色,但我们通过与邻近颜色的比较来看到颜色。
请看下面的例子。同一个方块在从白色到黑色的渐变中放置在不同的位置时,看起来不同。我们看到每个方块的方式不同,因为它旁边的灰色阴影各异。它在浅灰色背景下看起来比在深色背景下更暗。²

来源:基于 2 的作者图片。
5. 使用颜色进行精确可视化
虽然颜色可以表示值,但对于精确比较并不是最佳选择。如下面所示,色调和饱和度在精确度领域的排名较低。这表明它们在值评估中不够准确,更容易出现用户错误。

来源:基于SAP,数据讲述手册的作者图片。
为什么正确使用颜色如此重要?
正如上述例子所示,颜色深刻影响我们对信息的感知。这就是为什么它在数据可视化中扮演了关键角色。
你是否遇到过“前注意处理”这个术语?它指的是我们两步信息吸收过程中的初始阶段。这个阶段是自动的,涉及启发式方法、反射和模式识别——这些元素是我们不会主动思考的。前注意处理主要注意到呈现给我们的信息中最显著的特征,例如形状、大小和颜色!然而,它并非完美无缺。由于其冲动的性质,它有时会误导我们。
考虑这个(非色彩示例):
A 项和 B 项的费用为$110,其中 A 项比 B 项贵$100。许多人可能本能地说 B 项是$10。但那么,A 项的费用就是$110,两个项目的总费用是$120。B 项的正确价格是$5. 在许多情况下,那些回答$10 的人被前注意处理所误导。
这就是为什么 我们必须有意识地使用它的证据!
简要色彩理论³
在我展示有关有效使用颜色的想法之前,我想展示一些理论设置。
色彩理论有两个基本组成部分(根据我的简化理解):
-
色彩解剖学,和
-
色彩层次。
色彩解剖学
色彩解剖学,分析我们如何感知颜色,包含三个核心组件:

色调、饱和度与明度。 来源:作者提供的图片。
色调是指感知到的颜色或色调。简单来说——那就是我们所说的颜色,例如红色、绿色、蓝色等。
饱和度表示颜色的强度,高饱和度产生鲜艳的颜色,而低饱和度则产生较暗、较白的色调。
明度虽然类似于饱和度,但涉及的是色调的深浅——黑白的程度——而不是颜色的亮度。
明度和饱和度都创建了一个显著的强度尺度,可用于突出差异。
色彩和谐
色彩和谐体现了特定颜色组合可以产生视觉对比或协调的思想。根据你希望通过数据传达的叙事,你选择的安排可以优化效果。

Adobe Color 中的各种色彩和谐。 来源:作者提供的图片,基于 Adobe Color 的屏幕截图。
如何在数据可视化中使用颜色?

来源:作者在 Adobe Photoshop 2024 中生成的图像。
为了让用户成功地与可视化中的信息互动,他们必须:
-
找到它,
-
阅读它,并且
-
理解它。⁴
“找到它”
可视化应使用户能够以最小的认知努力找到关键信息。更重要的信息应比不那么关键的数据更显著。旨在区分操作上关键数据的颜色编码必须始终可辨别。
“阅读它”
在所有条件下展示操作性重要信息时,易读性至关重要。为了确保易读性:
-
确保符号(或文本)与背景之间有足够的对比度。
-
符号或文本应当具有足够的大小和笔画宽度,以确保可见性和可读性。
“理解它”
一旦信息被找到并阅读,颜色应当以最小的认知努力和错误风险来促进用户的理解。
-
颜色编码: 颜色代码用于区分关键数据类别,应当易于识别、数量有限,并在所有背景下都能区分。
-
一致性: 报告或演示文稿中的颜色代码应保持一致:每种颜色代码只能代表单一的数据类别(或者单一颜色可以代表所有数据类别)。
-
惯例: 遵循文化惯例是有帮助的(例如,红色表示警告,黄色表示注意,绿色表示安全)。⁴
颜色不仅仅是为了让你的数据看起来更漂亮;它是为了引导你的观众通过一个故事,并使复杂的信息易于消化。
如何处理数据展示中的颜色?
为什么,为什么,为什么?
这是选择可视化和其元素时的关键问题。颜色必须专门用于数据讲述,以实现明确的沟通目的。 颜色不应仅仅用于装饰图形。虽然美化图表可能有助于品牌意识或广告目标,但这会分散对数据讲述真正重要内容的关注。而这就是我们信息中的数据。²
从灰度开始!
那么,我如何处理颜色?正如Cole Nussbaumer Knafflic 在 YouTube 采访中建议的,我的可视化初稿通常使用灰度(轴线或标签可能有例外)。然后,我应用不同的颜色,但总是有明确的目的,比如吸引注意力或帮助理解。
与 Cole Nussbaumer Knafflic 的视频采访。 来源:YouTube。
我在展示这种方法的有效性时感到满足,同时“提升”所谓的意大利面图表,如上图所示。这些图表通常作为沟通方法存在缺陷。然而,一个简单的策略——一次专注于一个系列,同时将其他(用灰色标记)作为背景信息(例如,用来说明趋势)——可能非常有效。

来源:作者灵感来源于 5。
重新考虑你的视觉效果⁵
好吧,我们从灰度开始,应用了颜色,最后变成了圣诞树。字面上的。
有时,过度使用颜色的倾向源于选择不适当的视觉效果。 请参考以下例子,这些例子受到了 Datawrapper 博客的启发。虽然渐变颜色可以有效地显示模式,例如在 choropleth 地图上,但它们挑战了实际值和差异的辨别。选择使用条形图、位置或区域来显示最重要的值,将颜色保留用于区分类别或集中注意力。这应能使读者更快地解读值。

来源:作者的图片,灵感来源于 5。
如果图表需要超过七种颜色,考虑使用不同的图表类型或对类别进行分组。虽然颜色有助于读者区分数据类别,但使用过多的颜色可能会由于频繁查阅颜色键而阻碍快速数据理解。

来源:作者的图片,灵感来源于 5。
使用不同的颜色
好的。所以,我们已经选择了正确的视觉效果。然而,在决定颜色时,我们必须确保它们是不同的。色调在创建颜色区分度方面至关重要。虽然亮度和饱和度的调整可以增加变化,但它们可能会暗示虚假的重要性层次——除非这是你的意图(我稍后会展示)。例如,我们应该避免使用两个具有相同色调但不同亮度和饱和度的颜色,除非它们相关联的值本质上是相关的。⁷
对比度规则
颜色需求 上下文,正如前面的段落中展示的那样。 我建议考虑以下两个规则以确保颜色之间的对比。
规则 1: 确保表格或图表中相同颜色的对象看起来相同,通过提供匹配的背景颜色来实现。
规则 2: 为了使表格或图表中的对象清晰可见,使用与对象有足够对比度的背景颜色。
规则 1 在图表中的直接应用是避免使用背景颜色渐变或变化的背景颜色。 避免不必要的图表装饰对清晰和有说服力的数据展示至关重要。²
我应用的对比度基本规则是:
-
浅色背景配以深色元素(例如,白色背景,黑色字体,深灰色图表填充,蓝色突出显示填充)。
-
深色背景配以浅色元素(例如,黑色背景,白色字体,浅灰色图表填充,黄色突出显示填充)。
背景颜色
两种颜色有效:白色或黑色。
开玩笑的! 几乎任何颜色都适用,无论是浅色还是深色,只要你遵循一些特定的指导方针,其中一些我将在下面概述。我经常遵循的一条规则是对于较长的演示文稿使用浅色背景,因为它在长时间观看时较少疲劳。我可能会选择深色背景用于较短的演示文稿,主要是出于审美原因或个人喜好。然而,我会避免所有闪亮的颜色变体。

这些颜色不适合用作背景。 来源:作者提供的图片。
三色规则
选择背景颜色后,我必须决定其他元素。在这里,我尝试应用“三色规则”。三色计数不包括背景,背景作为“上下文设置器”。说实话,我通常无法完全遵守这个规则。不过没关系。只要我尽量减少颜色数量,效果通常不会远离理想状态。
对于浅色背景(通常是白色),我使用以下颜色:
-
颜色 1: 通常黑色或深灰色用于文本(标题、正文)。
-
颜色 2: 通常中灰色用于较少相关的图表元素(填充、标签、网格等)。
-
颜色 3: 通常使用蓝色突出显示重要文本或可视化元素。有时,我使用绿色表示积极,红色表示消极,以指示变化。
对于深色背景(在我的演示文稿中大多数是纯黑色):
-
颜色 1: 文字用白色。
-
颜色 2: 如果颜色容易辨别,这可以与浅色背景的情况相同。
-
颜色 3: 我可能会选择柔和的黄色,同时适用绿色或红色规则。
可能会有例外,但仅在必要时(例如,为了区分直接放置在可视化上的标签而添加白色或黑色字体)。
仅此而已。这是它的可能效果。

遵循三色规则的幻灯片示例。 来源:作者提供的图片。
参考调色板

颜色调色板。 来源:作者在 Adobe Photoshop 2024 中生成的图片。
有时,我对颜色选择感到困惑,尤其是当我选择标准 PowerPoint 调色板之外的颜色时。但我有一个技巧可以帮助我缓解这个问题。我在精心设计的公司模板中发现了它。它们有时包含一个颜色调色板,通常放在最后一张幻灯片上。如果没有这样的模板,我会用填充参考颜色的形状来制作我的参考调色板。每当我不确定选择哪个颜色时,我就会去最后一张幻灯片,使用颜色选择器选择颜色。这是一种可靠的方法!

参考调色板示例。 来源:作者提供的图片
如果你制作大量演示文稿,建立一套标准的优质颜色可能会很有效。 理想情况下,创建几种颜色调色板,每种调色板设计用于特定用途。至少,开发三种调色板:一种包含鲜艳的颜色,第二种包含对人眼容易辨别的中等色调,第三种则包含浅色、几乎透明的颜色。²
使用直观的颜色
我强烈建议选择与受众文化理解相符的颜色调色板,使用他们自然会将数据与之联系起来的颜色。尽可能做到这一点。请参见下方的可视化,这一可视化再次受到 Datawrapper 博客文章的启发。⁵

来源:作者基于 5 的图片
一点说明:虽然绿-红组合通常用于表示好-坏关系,但重要的是要注意,大约 5%的人群难以区分这些颜色。例如,给绿色加上蓝色的色调可以帮助确保更广泛的观众能清楚辨别。⁶
注意颜色心理学
说到颜色直观,值得注意的是最常见的颜色解释。
这些是跨文化中最广泛的颜色解释:

颜色的文化解释。来源:作者基于 8 的图片
在商业中,这可能略有不同。以下是根据商业目的可以使用的颜色:

颜色的商业解释。来源:作者基于 8 的图片
颜色差异的灰度测试

来源:由作者在 Adobe Photoshop 2024 中生成的图像
尽管我缺乏科学证据,不能保证其普遍适用(例如,帮助那些有特定视力障碍的人),但这种方法曾多次帮助过我。我称之为“灰度测试”。你可以在 PowerPoint 中轻松完成此操作。在打印选项卡中选择“灰度”选项,而不是“彩色”选项。然后,“打印预览”窗口中的显示颜色将发生变化。如果你仍然能区分可视化中的系列和类别,你可以祝贺自己:你做得很好。

来源:作者的打印屏幕
在下方的图像中,我展示了我的意思。右侧的图表使用灰度展示。即便如此,仍然可以看到类别之间的差异。

如果在灰度下有效,那么在其他地方也应该有效! 来源:作者的图片
使用颜色表示数字中的顺序
在利用颜色表示数值的序列范围时,遵循单一色调(或一组相关色调),从浅色到深色、饱和度更高的颜色来表示低值到高值。我们直观地将颜色强度的增加感知为数值的增加,但这种感知不适用于不同颜色。
这些颜色可以分配给图表中的四组柱,以指示四个部门在一年中的表现,从最差(最暗)到最好(最浅)。

使用颜色表示序列。 来源:受 2 启发的作者提供的图片。
在热图中使用一组有序颜色可以直观地呈现高销售值,快速识别出哪些产品卖得最好或最差,哪些省份或产品表现最好或最差,即使没有具体数值。²
序列和分歧调色板
序列调色板包括一组按顺序排列的颜色,感知上呈递增。相比之下,如这里所示,分歧或双步骤调色板由两个颜色序列组成,这些序列从中央中点对称地增强,每个方向相反。
分歧调色板有效地展示了逻辑中点以上和以下的数值。例如,一家公司的利润可以通过热图使用分歧调色板表示:一种调色板用于利润,另一种用于亏损,以零作为中点。²

序列和分歧调色板。 来源:作者提供的图片。
一些有用的工具
有许多工具可以帮助你有效选择颜色。我之前提到过Adobe Colors。这里是一些你在设计有效颜色调色板时可以考虑使用的其他工具:
-
ColorZilla(Chrome 浏览器扩展)
我如何使用这些工具?
首先,我决定关键颜色。这可以是你公司标志中的颜色,或者用于企业沟通的颜色,或任何激发你灵感的颜色。第二步是使用应用程序选择一个颜色和谐:适合背景和预期效果。最后,我尽量减少颜色的数量(注意三色规则!)。

Viz Palette 工具。 来源:作者截图。

数据颜色选择器。 来源:作者提供的图像。
使用 LLM 和生成式 AI
ChatGPT 和 DALL-e 3
最后,我们可以利用 LLMs 自我帮助。以下是一个使用 ChatGPT 和 DALL-e 3 生成的简单示例。
这是我的初步构思: 初始提示和一些生成的创意。

提示 1。 来源:作者截屏。

由 ChatGPT 和 DALL-e 3 生成的第一个提案。 来源:作者截屏。
这是经过一些微调后的第 4 版(我花了 3-4 个额外提示)。我可以用它来激发后续幻灯片的灵感,选择颜色,使用类似字体等。

第 4 版提案的微调版本。 来源:作者截屏。
ChatGPT 和 Canva
你还可以使用 ChatGPT 和 Canva 插件。

使用 ChatGPT 和 Canva 插件生成幻灯片创意。 来源:作者截屏。
这是我得到的其中一个创意。

ChatGPT 和 Canva 插件建议的模板。 来源:作者截屏。
ChatGPT 独立使用
好的。上述功能适用于 ChatGPT Plus(付费)用户。不过,如果你使用的是 ChatGPT 免费版,仍然可以用它来提供帮助,如下所示。

使用 ChatGPT 生成的特定背景色的演示文稿颜色建议。 来源:作者截屏。
结论
颜色不仅仅是为了让你的数据看起来漂亮;它还引导你的观众理解故事,使复杂信息易于消化。 从观察自然到与我们多彩的计算机屏幕互动,颜色影响着我们对体验和信息的感知、理解和记忆。
有效使用数据可视化中的颜色不仅仅是选择吸引人的色调。它包括:
-
清晰沟通: 确保数据易于查找、阅读和理解。
-
保持一致性: 采用统一的调色板以在视觉效果中保持颜色含义的一致性。
-
包容性: 选择对色盲人士可见的调色板,认识到颜色感知的差异。
秘诀在于将美学吸引力与清晰度和精确度结合起来,制作引人入胜且启发性的视觉效果。
通过遵循基本的色彩理论原则,优先考虑可访问性,并使用战略性和一致性的色彩调色板,你可以将数据可视化从简单的图表转变为引人入胜的叙述。让颜色成为讲述数据故事的盟友,引导观众通过可视化,并提供连贯、可访问且令人难忘的用户体验。
你喜欢这篇文章吗?考虑订阅以获取我发布的新故事通知,或关注我。
参考资料:
-
安迪·杰弗里,婴儿时期的弗朗索瓦长臂猿身披霓虹橙色外套
-
Skuteczne Raporty,九条有效使用颜色的原则 (颜色有效使用的十条规则)
-
Revuint,色彩理论在数据可视化中的作用
-
NASA AMES 研究中心,颜色使用指南层级
-
丽莎·夏洛特·穆斯,选择数据可视化颜色时需要考虑的事项
-
威罗妮卡·加瓦尔斯卡-蒂沃内克,数据可视化中色彩的功能:一个简单(但完整)的指南
-
迈克尔·易,如何为数据可视化选择颜色
-
迪米拉·特涅瓦,颜色心理学:不同颜色在营销中的含义
如何在 Neo4j 图数据科学库中使用 Cypher 聚合
利用 Cypher 聚合功能,在内存图中展示所有 Cypher 查询语言的灵活性和表现力
·
关注 发表在 Towards Data Science ·7 min 阅读·2023 年 3 月 27 日
--
Cypher 聚合是 Neo4j 图数据科学库的一个强大功能,允许用户使用灵活而表达力强的方法来投影内存中的图。尽管使用 Cypher 投影可以在很长一段时间内投影内存中的图,但它缺乏一些功能,最显著的是无法投影无向关系。因此,GDS 中添加了一种新的内存图投影方法,称为 Cypher 聚合。这篇博客将探讨 Neo4j 图数据科学库中 Cypher 聚合投影选项的语法和常见用法。
环境设置
如果你想跟随示例,可以打开一个 Neo4j Sandbox 中的图数据科学项目。该项目包含有关机场、其位置和航班路线的小数据集。
我们可以使用以下 Cypher 语句可视化图模式:
CALL db.schema.visualization()

图模式。图片由作者提供。
使用 Cypher 聚合投影内存中的图
首先,让我们快速回顾一下 Neo4j 图数据科学库的操作方式。

图数据科学库工作流。图片由作者提供。
在我们执行任何图算法之前,首先必须投影内存中的图。内存图不必是数据库中存储图的精确副本。我们可以选择图的子集,或者如你稍后将学到的,也可以投影未存储在数据库中的虚拟关系。投影内存图后,我们可以执行任意多的图算法,然后直接将结果流式传输给用户,或将其写回数据库。
使用 Cypher 聚合投影内存中的图
Cypher 聚合功能是图数据科学工作流的第一步,即在内存中投影图。它提供了 Cypher 查询语言的完全灵活性,可以在投影过程中选择、过滤或转换图。Cypher 聚合函数的语法如下:
gds.alpha.graph.project(
graphName: String,
sourceNode: Node or Integer,
targetNode: Node or Integer,
nodesConfig: Map,
relationshipConfig: Map,
configuration: Map
)
只有前两个参数(graphName 作为 sourceNode)是必需的,但你需要指定 sourceNode 和 relationshipNode 参数以定义单个关系。我们将详细介绍你可能需要的大部分选项,以帮助你使用 Cypher 聚合来投影图。
我们将从一个简单的示例开始。假设我们想要投影所有 Airport 节点及其之间的 HAS_ROUTE 关系。
MATCH (source:Airport)-[:HAS_ROUTE]->(target:Airport)
WITH gds.alpha.graph.project('airports', source, target) AS graph
RETURN graph.nodeCount AS nodeCount,
graph.relationshipCount AS relationshipCount
Cypher 语句以 MATCH 子句开始,该子句选择相关的图。要使用 Cypher 聚合定义关系,我们输入 source 和 target 节点。
当然,Cypher 查询语言提供了选择图的任何子集的灵活性。例如,我们可以只投影大洋洲的机场及其航班路线。
MATCH (source:Airport)-[:HAS_ROUTE]->(target:Airport)
WHERE EXISTS {(source)-[:ON_CONTINENT]->(:Continent {name:"OC"})}
AND EXISTS {(target)-[:ON_CONTINENT]->(:Continent {name:"OC"})}
WITH gds.alpha.graph.project('airports-oceania', source, target) AS graph
RETURN graph.nodeCount AS nodeCount,
graph.relationshipCount AS relationshipCount
在这个例子中,匹配的 Cypher 语句变得稍微复杂了一些,但 Cypher 聚合函数保持不变。airports-oceania 图包含 272 个节点和 973 条关系。如果你对 Cypher 有经验,你可能会注意到上述 Cypher 语句不会捕获任何在大洋洲没有航班路线的机场。
假设我们也想在投影中显示孤立机场。在这种情况下,我们需要稍微修改 Cypher 匹配语句。
MATCH (source:Airport)
WHERE EXISTS {(source)-[:ON_CONTINENT]->(:Continent {name:"OC"})}
OPTIONAL MATCH (source)-[:HAS_ROUTE]->(target:Airport)
WHERE EXISTS {(target)-[:ON_CONTINENT]->(:Continent {name:"OC"})}
WITH gds.alpha.graph.project('airports-isolated', source, target) AS graph
RETURN graph.nodeCount AS nodeCount, graph.relationshipCount AS relationshipCount
关系数量保持不变,而节点数量增加到 304。因此,32 个大洋洲的机场没有任何航班路线通往其他大洋洲机场。
在处理图中的多个节点和关系类型时,我们可能希望在投影过程中保留节点标签和关系类型的信息。在图投影过程中定义节点和关系类型使我们可以在算法执行时对其进行筛选。
CALL {
MATCH (source:Airport)-[r:HAS_ROUTE]->(target:Airport)
RETURN source, target, r
UNION
MATCH (source:Airport)-[r:IN_CITY]->(target:City)
RETURN source, target, r
}
WITH gds.alpha.graph.project('airports-labels', source, target,
{sourceNodeLabels: labels(source),
targetNodeLabels: labels(target)},
{relationshipType:type(r)}) AS graph
RETURN graph.nodeCount AS nodeCount, graph.relationshipCount AS relationshipCount
当投影多个不同的图模式时,我更倾向于使用UNION子句。然而,具体使用什么 Cypher 匹配语句完全取决于你。由于我们投影的是两种类型的节点和关系,保留它们的标签和类型信息可能是个好主意。因此,我们使用了sourceNodeLabels、targetNodeLabels和relationshipType参数。在这个例子中,我们使用了现有的节点标签和关系类型。
然而,有时我们可能希望在投影过程中使用自定义标签或关系类型。
CALL {
MATCH (source:Airport)-[r:HAS_ROUTE]->(target:Airport)
RETURN source, target, r
UNION
MATCH (source:Airport)-[r:IN_CITY]->(target:City)
RETURN source, target, r
}
WITH gds.alpha.graph.project('airports-labels-custom', source, target,
{sourceNodeLabels: CASE WHEN source.city = 'Miami'
THEN 'Miami' ELSE 'NotMiami' END,
targetNodeLabels: ['CustomLabel']},
{relationshipType: CASE WHEN type(r) = 'HAS_ROUTE'
THEN 'FLIGHT' ELSE 'NOT_FLIGHT' END}) AS graph
RETURN graph.nodeCount AS nodeCount, graph.relationshipCount AS relationshipCount
正如你所见,我们可以使用 Cypher 动态定义节点或关系类型,也可以直接硬编码它。如果自定义的节点标签或关系类型比较复杂,也可以在 Cypher 匹配语句中计算。
CALL {
MATCH (source:Airport)-[r:HAS_ROUTE]->(target:Airport)
RETURN source, target, r,
CASE WHEN source.city = target.city
THEN 'INTRACITY' ELSE 'INTERCITY' END as rel_type
UNION
MATCH (source:Airport)-[r:IN_CITY]->(target:City)
RETURN source, target, r, type(r) as rel_type
}
WITH gds.alpha.graph.project('airports-labels-precalculated', source, target,
{sourceNodeLabels: labels(source),
targetNodeLabels: labels(target)},
{relationshipType: rel_type}) AS graph
RETURN graph.nodeCount AS nodeCount, graph.relationshipCount AS relationshipCount
有时,我们也希望投影节点或关系属性。
MATCH (source:Airport)-[r:HAS_ROUTE]->(target:Airport)
WITH gds.alpha.graph.project('airports-properties', source, target,
{sourceNodeLabels: labels(source),
targetNodeLabels: labels(target),
sourceNodeProperties: {runways: source.runways},
targetNodeProperties: {runways: target.runways}},
{relationshipType: type(r), properties: {distance: r.distance}}) AS graph
RETURN graph.nodeCount AS nodeCount, graph.relationshipCount AS relationshipCount
节点或关系属性被定义为一个映射对象(对于 Python 或 JS 开发者来说是字典或 JSON 对象),其中键表示投影的属性,值表示投影的值。这种语法允许我们投影在投影过程中计算出的属性。
MATCH (source:Airport)-[r:HAS_ROUTE]->(target:Airport)
WITH gds.alpha.graph.project('airports-properties-custom', source, target,
{sourceNodeLabels: labels(source),
targetNodeLabels: labels(target),
sourceNodeProperties: {runways10: source.runways * 10},
targetNodeProperties: {runways10: target.runways * 10}},
{relationshipType: type(r),
properties: {inverseDistance: 1 / r.distance}}) AS graph
RETURN graph.nodeCount AS nodeCount, graph.relationshipCount AS relationshipCount
同样,我们可以利用 Cypher 的所有灵活性来计算任何节点或关系属性。与节点标签类似,我们也可以在MATCH子句中计算自定义属性。
一个重要的注意事项是当前的投影行为是引擎在第一次遇到节点时存储节点属性。然而,在随后的节点遇到中,它会完全忽略节点属性。因此,你必须小心为源节点和目标节点计算相同的节点属性。否则,投影结果可能与预期存在差异。
Neo4j 图数据科学库中的一些图算法期望无向关系。关系不能以无向方式存储在数据库中,必须在图投影期间明确地定义。
假设你想将所有投影关系视为无向关系。
MATCH (source:Airport)-[r:HAS_ROUTE]->(target:Airport)
WITH gds.alpha.graph.project('airports-undirected', source, target,
{}, // nodeConfiguration
{}, // relationshipConfiguration
{undirectedRelationshipTypes: ['*']}
) AS graph
RETURN graph.nodeCount AS nodeCount, graph.relationshipCount AS relationshipCount
我们可以使用 undirectedRelationshipType 来指定哪些关系应被投影为无向关系。在实际操作中,你可以观察到,当我们投影无向图时,关系数量翻了一倍。
有时你可能希望将某一类关系投影为无向关系,而将其他关系视为有向关系。
CALL {
MATCH (source:Airport)-[r:HAS_ROUTE]->(target:Airport)
RETURN source, target, r
UNION ALL
MATCH (source:Airport)-[r:IN_CITY]->(target:City)
RETURN source, target, r
}
WITH gds.alpha.graph.project('airports-undirected-specific', source, target,
{},
{relationshipType:type(r)},
{undirectedRelationshipTypes: ['IN_CITY']}) AS graph
RETURN graph.nodeCount AS nodeCount, graph.relationshipCount AS relationshipCount
在这个示例中,HAS_ROUTE 关系被视为有向关系,而 IN_CITY 关系被视为无向关系。当我们想要指定特定的关系类型被视为无向关系时,必须在关系配置中包含 relationshipType 参数。
最后,我们还可以投影虚拟关系。虚拟关系是一种未存储在数据库中的关系。

虚拟关系。作者提供的图像。
假设你想根据城市的航班连接来检查城市。数据库中没有城市之间的航班关系。你可以在图投影期间计算这些关系,而不是在数据库中创建它们。
MATCH (sourceCity)<-[:IN_CITY]-(:Airport)-[:HAS_ROUTE]->(:Airport)-[:IN_CITY]->(targetCity)
WITH sourceCity, targetCity, count(*) AS countOfRoutes
WITH gds.alpha.graph.project('airports-virtual', sourceCity, targetCity,
{},
{relationshipType:'VIRTUAL_ROUTE'},
{}) AS graph
RETURN graph.nodeCount AS nodeCount, graph.relationshipCount AS relationshipCount
正如你所观察到的,使用 Cypher 聚合投影投影虚拟关系非常简单。我们计算了各个城市之间的路线数量,并将其添加为投影图中的关系属性。
让我们基于 PageRank 算法计算最重要的城市,以完成这篇博客文章。
CALL gds.pageRank.stream('airports-virtual')
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).name AS city, score
ORDER BY score DESC
LIMIT 5
结果
总结
Cypher 聚合是使用 Cypher 语句在 Neo4j 图数据科学库中投影内存图的新选项。具体来说,它可以用于投影无向关系,这在旧的 Cypher 投影中是不可能的。然而,随着在投影期间选择和转换图的灵活性增加,性能成本也随之增加。因此,如果可能的话,你应该出于性能考虑使用原生投影。另一方面,当你有特定的用例需要投影图的某一特定子集、计算自定义属性或投影虚拟关系时,Cypher 聚合是你的好帮手。
如何使用 dbt 种子
它们是什么,何时使用
·发布于 Towards Data Science ·阅读时间 4 分钟·2023 年 4 月 25 日
--

engin akyurt 在 Unsplash 上的照片
我必须诚实地说,我已经很久没有使用 dbt seeds 了。实际上,我是在几天前试图验证对我们一个数据模型所做的更改时重新发现了它们。
我的经理给我发送了一个包含在我们数据模型中发现的真正重复值的 CSV 文件。我正在处理在我们一个源的临时模型中删除这些重复项。修改后,我需要验证这些重复项是否不再出现在模型中。
我没有手动将 CSV 文件上传到我们的数据仓库,这在 Redshift 中几乎不可能。我使用了 dbt 种子将 CSV 文件导入我们的仓库。然后,我能够快速运行一个验证查询,以确认重复项已被删除。
这太简单了!作为一名分析工程师,我喜欢事情简单。因为老实说,它们很少如此简单。这让我思考我们如何进一步利用 dbt 种子来简化我们的分析工程师工作。
更不用说,dbt 种子还可以被数据科学家和机器学习工程师用来测试他们的模型。你可以用它来摄取测试数据和训练数据,使它们在你的数据仓库中存在为两个不同的参考点。
什么是 dbt 种子?
在 dbt 中,有三种主要类型的对象——源、模型和种子。你可以像对待源和模型一样测试和记录种子。唯一的区别是它们不是从你的 dbt 项目中的 SQL 代码创建的。它们不是数据模型。它们只是你已读取到 dbt 中的 CSV 文件,用于作为模型的参考。
dbt 种子命令
要使用 dbt seeds,你需要一个要导入到数据仓库的 CSV 文件。首先,确保你能找到 CSV 文件的位置。然后,将其移动到 dbt 项目中的 seeds 目录。如果你已经在 dbt 项目中,可以运行以下命令将其移动到 seeds 目录:
mv <CSV file path> seeds
每个 dbt 项目自动具有此目录,所以你不需要自己重新创建它。
注意:确保为这个 CSV 文件起一个描述性的名字!最好在你的 dbt 风格指南 中设定关于 seeds 命名的标准。
然后,简单地运行 dbt seed 命令:
dbt seed
这将以你的 CSV 文件名在数据仓库的目标模式中创建一个表。现在,你可以像引用自己创建的 dbt 模型一样引用这个表!
如果你不熟悉,选择 dbt 模型的语法是:
{{ ref('duplicate_users') }}
如果我的 CSV 文件名为 duplicate_users.csv,那么目标模式中的表将命名为 duplicate_users,我会像上面那样在另一个模型中引用这个表。
如何使用 dbt seeds
dbt seeds 可以用于许多不同的原因。每当你需要查询一个静态 CSV 文件时,你可以将其创建为 seed 并使用 dbt 转换这些数据!
验证
我在介绍中提到过一个使用 dbt seeds 进行验证的例子。这是我最近作为分析工程师使用 seeds 的方式。我有别人为我拉取的数据,我用它来检查对数据模型所做的一些更改的输出。
当业务团队找你寻求帮助时,他们提供给你一个 CSV 或 Excel 文件,指出某些数据问题,这时使用这个数据是很好的。你可以直接在 dbt 中使用这些数据来解决问题,而不是自己重新创建问题。
静态参考表
我看到大多数人使用 dbt seeds 来创建静态参考表。因为这些表始终保持不变,并且不需要更改,所以使用 seeds 将它们导入数据仓库是有意义的。
一些很好的例子是 dim_date 表、country_code 表或 zipcode 映射表。这些表在分析中经常使用但从不更改。与其手动创建这些表作为 dbt 数据模型,不如重用公开的文件并将其导入你的仓库。这样,你就不会增加额外的工作量!
测试和训练模型
与其处理大型数据集,不如使用 dbt seeds 导入 CSV 文件作为测试和训练数据集。这将节省运行模型的时间和在数据仓库中存储大量数据的空间,从而节省资金。
将业务知识转移到你的数据仓库
最后,你可以使用 seeds 来移动仅以电子表格形式存在的数据。如果公司还没有数据仓库,大多数数据通常存储在 Excel 电子表格中。
dbt seeds 允许你轻松导出这些电子表格,并将它们创建为数据表,旨在创建单一的真实数据源。只需记住,不要在业务团队仍在使用的电子表格上频繁种子/重新种子。这更适用于捕捉不再更新的静态或历史数据。
结论
当正确使用时,dbt seeds 功能强大。如果你已经是 dbt 用户,它们是将 CSV 文件移动到数据仓库的简单解决方案。它可以快速验证并创建静态数据表。因此,下次你需要使用 CSV 进行分析时,考虑一下种子文件吧!
想了解更多关于 dbt 和分析工程的信息,请查看我的每周免费通讯。
正确使用 DAX 中的 FILTER
原文:
towardsdatascience.com/how-to-use-filter-in-dax-the-correct-way-eb621b49527a
DAX 中的 FILTER()函数可能难以驾驭。你可能会陷入一些陷阱,导致 DAX 代码的性能不佳。以下是如何使用 FILTER()以及不该如何使用的一些示例。
·发表于Towards Data Science ·阅读时长 11 分钟·2023 年 2 月 13 日
--

图片由Andrea De Santis在Unsplash提供
介绍
一年多前,我写了一篇关于FILTER()函数的文章:
FILTER()函数在 DAX 中非常强大,但它有一些复杂之处。让我们深入探讨这些细节,以构建一个良好的…
towardsdatascience.com
我在那里解释了一些关于这个强大函数的细节。
我没有做的是解释和展示 FILTER()的一些性能相关细节。
如果你对这个函数不熟悉或不确定如何使用它,可以跳到那篇文章以获得基本了解。
我的旧文和这篇文章之间有一些冗余,但学习基础知识总是正确的。
除了展示如何使用 FILTER()函数外,我还将展示每种变体对性能和效率的影响。
为此,我将使用DAX Studio和 DAX Studio 中的服务器时间来获取性能统计数据。
如果你不知道 DAX Studio 中的这个功能,或者对那里显示的度量解释不确定,可以阅读这篇文章,我将在其中深入探讨这个功能:
## 如何使用 DAX Studio 从 Power BI 获取性能数据
有时我们有一个慢报表,需要找出原因。我们将看到如何收集性能数据以及……
towardsdatascience.com
基本查询
为了确定我们的起点,我在我的演示数据集中定义了一个基本查询(详见参考部分):
我想获取国家和相应的销售数据,但我希望将销售产品的品牌限制为这三个:
-
Contoso
-
Northwind Traders
-
Fabrikam
基本查询如下:
// Basic Query with the Measure using a direct Filter
DEFINE
MEASURE 'All Measures'[RestrictedRetailSales] =
VAR ListOfBrands = {"Contoso", "Northwind Traders", "Fabrikam" }
VAR Result =
CALCULATE(
[Sum Online Sales]
,'Product'[BrandName] IN ListOfBrands
)
RETURN
Result
EVALUATE
CALCULATETABLE(
ADDCOLUMNS(
SUMMARIZE(Geography, Geography[RegionCountryName])
,"Restricted Online Sales", [RestrictedRetailSales])
)
我将展示如何更改行 ‘Product’[BrandName] IN ListOfBrands 并查看这些更改的后果。
结果如下(片段):

图 1 — 基本查询的部分结果(图由作者提供)
我们得到 35 行所有已知国家的值,包括中国、德国和美国。
基本查询的服务器时间如下:

图 2 — 基本查询的服务器时间(图由作者提供)
查询执行得相当快,持续时间为 0.3 秒,效率也不错,因为 91.6%的时间花费在存储引擎上。
好的,让我们做一些修改并查看后果。
第一个变体 — 引入 FILTER()
这是查询的第一个变体:
// Query with the Measure using a Filter using the FILTER Function on the table
DEFINE
MEASURE 'All Measures'[RestrictedRetailSales] =
VAR ListOfBrands = {"Contoso", "Northwind Traders", "Fabrikam" }
VAR Result =
CALCULATE(
[Sum Online Sales]
,FILTER('Product'
,'Product'[BrandName] IN ListOfBrands)
)
RETURN
Result
EVALUATE
CALCULATETABLE(
ADDCOLUMNS(
SUMMARIZE(Geography, Geography[RegionCountryName])
,"Restricted Online Sales", [RestrictedRetailSales])
)
这次,我在产品表上添加了一个过滤器,以根据这三个品牌进行过滤。
这里是服务器时间:

图 3 — 第一个变体的服务器时间(图由作者提供)
总执行时间几乎相同,但存储引擎所花费的时间多了 100 毫秒。
并且,我们有五个存储引擎查询,而不是三个。
这些查询中的两个使用了 CallbackDataID 调用,这意味着存储引擎调用公式引擎执行其无法完成的操作。
在这种情况下,品牌列表创建了两次进行过滤。
DAX Studio 将包含 CallbackDataID 的查询标记为粗体,因为此调用不适合性能和效率。
此外,前两个查询各返回了超过 2’500 行。这称为物化。
我们应该尽量减少 DAX 表达式中的物化大小。
在这种情况下,2’517 是整个产品表的行数,这意味着公式引擎获取了整个表格,并需要随后进行过滤。
想象一下我们有一个包含数十万行的表。这个变体将加载整个表格,所需时间为:
· 很多内存
· 很多时间
好的,使用 FILTER() 这种方式完全不可取。
第二个变体 — 使用 ALL() 的 FILTER
使用以下变体,我们将ALL()函数添加到 FILTER()调用中。
ALL() 函数移除给定表或列上的任何过滤器,并返回一个包含所有值的表。
// Query with the Measure using a Filter using the FILTER Function on the Product[Brand] column with ALL()
DEFINE
MEASURE 'All Measures'[RestrictedRetailSales] =
VAR ListOfBrands = {"Contoso", "Northwind Traders", "Fabrikam" }
VAR Result =
CALCULATE(
[Sum Online Sales]
,FILTER(ALL('Product'[BrandName])
,'Product'[BrandName] IN ListOfBrands)
)
RETURN
Result
EVALUATE
CALCULATETABLE(
ADDCOLUMNS(
SUMMARIZE(Geography, Geography[RegionCountryName])
,"Restricted Online Sales", [RestrictedRetailSales])
)
结果仍然相同,但服务器时间与第一种变体的差异很大:

图 4 — 第二种变体使用 ALL() 的服务器时间(图由作者提供)
使用这个变体,我们得到的查询和时间几乎与原始查询相同,而无需使用 FILTER()。
如果我们仔细观察,我们会发现存储引擎查询在两者之间是相同的。
因此,这两个查询在性能和效率上是等效的。
这非常有趣,因为它展示了两个引擎的智能。
差异如下:
-
基础和第二种变体替换了品牌列上的过滤器。
-
第一种变体添加了对品牌列的过滤器,而没有改变现有的过滤器上下文。
这会导致额外的查询。
由你决定使用哪种变体。作为一个懒人,我更喜欢输入更少的代码来获得相同的结果。
第三种变体——添加 VALUES() 代替 ALL()
让我们将 ALL() 函数替换为VALUES()函数。
VALUES() 函数对于一个列返回一个包含源列唯一值(排除重复项)的表。
// Query with the Measure using a Filter using the FILTER Function on the Product[Brand] column with VALUES()
DEFINE
MEASURE 'All Measures'[RestrictedRetailSales] =
VAR ListOfBrands = {"Contoso", "Northwind Traders", "Fabrikam" }
VAR Result =
CALCULATE(
[Sum Online Sales]
,FILTER(VALUES('Product'[BrandName])
,'Product'[BrandName] IN ListOfBrands)
)
RETURN
Result
EVALUATE
CALCULATETABLE(
ADDCOLUMNS(
SUMMARIZE(Geography, Geography[RegionCountryName])
,"Restricted Online Sales", [RestrictedRetailSales])
)
结果仍然相同:35 行所有国家和三个值。
这些是服务器时间:

图 5 — 第三种变体使用 VALUES() 的服务器时间(图由作者提供)
再次,我们看到相同的三个 SE(存储引擎)查询(与基础和第二个查询看起来相同)和几乎相同的持续时间。
数字在执行之间变化很小,但它们与基础查询非常相似。
这表明使用 VALUES() 就像使用简单的过滤谓词或使用 FILTER() 与 ALL() 相同。
第四种变体——添加一个额外的过滤器
除了过滤品牌外,我还想按产品颜色进行过滤:
// Query with the Measure using a Filter using the FILTER Function on the Product[Brand] column with ALL()
// This time with two columns
DEFINE
MEASURE 'All Measures'[RestrictedRetailSales] =
VAR ListOfBrands = {"Contoso", "Northwind Traders", "Fabrikam" }
VAR Result =
CALCULATE(
[Sum Online Sales]
,FILTER(ALL('Product'[BrandName], 'Product'[ColorName])
,'Product'[BrandName] = "Contoso"
&& 'Product'[ColorName] = "Red"
)
)
RETURN
Result
EVALUATE
CALCULATETABLE(
ADDCOLUMNS(
SUMMARIZE(Geography, Geography[RegionCountryName])
,"Restricted Online Sales", [RestrictedRetailSales])
)
当然,查询结果中的数字会变化,因为我们只想获取红色产品的销售数据。
以下是服务器时间:

图 6 — 第四种变体过滤两个列的服务器时间(图由作者提供)
结果很吸引人。
查询比之前快得多,并且只使用了两个 SE 查询。
如上图所示,我们得到一个查询,该查询在产品表中使用一个 WHERE 过滤器来过滤两个列。
这意味着 SE 在组合一个表上的过滤器时非常智能,因为它消除了第一个查询,只执行了两个而不是三个查询。
第五种变体——从两个表中过滤列
下一步是尝试从两个不同的表中过滤两个列:
-
品牌 = “Contoso” 在产品表中
-
大陆 = “欧洲” 来自客户表
这是满足这一要求的起点。
// Query with the Measure using a Filter using the FILTER Function on the Product[Brand] column with VALUES()
// This time with two columns from two different tables
DEFINE
MEASURE 'All Measures'[RestrictedRetailSales] =
VAR ListOfBrands = {"Contoso", "Northwind Traders", "Fabrikam" }
VAR Result =
CALCULATE(
[Sum Online Sales]
,FILTER(ALL('Product'[BrandName])
,'Product'[BrandName] = "Contoso"
&& 'Customer'[ContinentName] = "Europe" - < Why this cannot function? )
)
RETURN
Result
EVALUATE
CALCULATETABLE(
ADDCOLUMNS(
SUMMARIZE(Geography, Geography[RegionCountryName])
,"Restricted Online Sales", [RestrictedRetailSales])
)
这不起作用:

图 7 — 使用 FILTER()时过滤来自不同表的列的错误(图由作者提供)
不能工作的原因是第二列来自另一个不属于 FILTER 函数输入的表:
FILTER(ALL('Product'[BrandName]) <--Input table
,'Product'[BrandName] = "Contoso"
& 'Customer'[ContinentName] = "Europe" <-- Not part of input table
)
让我们尝试另一种方法:
// Query with the Measure using a direct Filter in CALCULATE, but without FILTER
// Again with two columns from two different tables
DEFINE
MEASURE 'All Measures'[RestrictedRetailSales] =
VAR ListOfBrands = {"Contoso", "Northwind Traders", "Fabrikam" }
VAR Result =
CALCULATE(
[Sum Online Sales]
,'Product'[BrandName] = "Contoso"
,'Customer'[ContinentName] = "Europe" - < This works, But without FILTER()
)
RETURN
Result
EVALUATE
CALCULATETABLE(
ADDCOLUMNS(
SUMMARIZE(Geography, Geography[RegionCountryName])
,"Restricted Online Sales", [RestrictedRetailSales])
)
这次我们使用直接过滤器(谓词),不使用 FILTER()函数。
这种方法有效,并且执行速度非常快且高效:

图 8 — 第五个变体的服务器时间,使用直接过滤器(图由作者提供)
但我们想看到其他内容。
我们如何在来自不同表的列中使用 FILTER 函数?
第六个变体 — 为 FILTER()构建表
我们必须从不同的源表中构建一个输入表以实现这一点。
为此,我们使用SUMMARIZECOLUMNS()来生成一个结合给定列中值的表:
// Query with the Measure using a Filter using the FILTER Function with two columns from two separate tables
DEFINE
MEASURE 'All Measures'[RestrictedRetailSales] =
VAR ListOfBrands = {"Contoso", "Northwind Traders", "Fabrikam" }
VAR Result =
CALCULATE(
[Sum Online Sales]
,FILTER(SUMMARIZECOLUMNS('Product'[BrandName]
,'Customer'[Continent])
,'Product'[BrandName] = "Contoso"
&& 'Customer'[Continent] = "Europe" - < This works, as we build a table with SUMMARIZECOLUMN() as the input for FILTER()
)
)
RETURN
Result
EVALUATE
CALCULATETABLE(
ADDCOLUMNS(
SUMMARIZE(Geography, Geography[RegionCountryName])
,"Restricted Online Sales", [RestrictedRetailSales])
)
SE 可以将这个变体解释为执行仅一个查询以获得结果(第二个查询仅检索国家列表):

图 9 — 第六个变体的服务器时间和 SE 查询(图由作者提供)
此外,总执行时间非常短。
但在我们得出结论之前,我还有一个变体给你。
第七个变体 — 使用 CROSSJOIN()进行 FILTER()
最后的变体使用了CROSSJOIN()函数。
这个函数返回一个表,其中包含第一个输入表的每一行与第二个表的每一行的乘积。
详细信息请阅读上面链接的DAX Guide文章或观看视频。
// Query with the Measure using a Filter using the FILTER Function with two columns from two different tables
DEFINE
MEASURE 'All Measures'[RestrictedRetailSales] =
VAR ListOfBrands = {"Contoso", "Northwind Traders", "Fabrikam" }
VAR Result =
CALCULATE(
[Sum Online Sales]
,FILTER(CROSSJOIN(ALL('Product'[BrandName])
,ALL('Customer'[ContinentName]))
,'Product'[BrandName] = "Contoso"
&& 'Customer'[ContinentName] = "Europe" - < This works, as we build a table with SUMMARIZECOLUMN() as the input for FILTER()
)
)
RETURN
Result
EVALUATE
CALCULATETABLE(
ADDCOLUMNS(
SUMMARIZE(Geography, Geography[RegionCountryName])
,"Restricted Online Sales", [RestrictedRetailSales])
)
结果仍然没有改变。但执行模式却大相径庭:

图 10 — 使用 CROSSJOIN()的第七个变体的服务器时间(图由作者提供)
尽管总执行时间没有多长,但现在我们有四个 SE 查询。
但前两个查询执行速度极快。
第一个查询检索所有大洲的列表(6 行),第二个查询检索所有品牌的列表(14 行)。
第三和第四个查询与之前的变体相同。
但这个变体还有更多需要考虑的地方:
-
Cross-Join 由公式引擎(FE)完成。对于如此少量的值来说,这不是问题。但当你有数百或数千个值时,这将花费大量时间。
-
这个变体生成一个包含两个列(品牌和大洲)的表,其中包含所有可能的组合。该表用两个谓词(Brand = “Contoso”和 Continent = “Europe”)进行了过滤。最后,生成的表被作为过滤器应用于 CALCULATE()函数。
-
使用 CROSSJOIN()函数必须配合使用 ALL()函数。如果不使用 ALL()函数,你将会遇到错误。作为替代方案,可以使用 VALUES()函数,它会产生相同的结果。
-
这种变体需要大量编写而没有任何好处,因为 SE 查询与上述两个变体中的查询相同。但当你需要以这种特定方式构造 FILTER()的输入表时,现在你知道这是可能的。

由Afif Ramdhasuma在Unsplash拍摄的照片
结论
正如你所见,使用 DAX 中的 FILTER 函数有很多可能的变体。
但请注意。在我的示例中看起来很快的操作,在你的场景中可能会大相径庭,因为你可能会面临不同的数据情况。
你需要过滤的列中独特的值越多,你就越需要注意如何使用 FILTER()。
在我看来,直接过滤的方法是第一种也是最有效的方法,因为它需要最少的努力和输入。在大多数情况下,它可以正常工作。
所以,不要过度工作或过度思考你的度量值,因为最简单和直接的方法可能是正确的。
如果你想了解更多关于这种方法的信息,可以阅读以下内容:
这篇文章的标题似乎违背直觉。为什么你不应该用最优化的方式来开发你的解决方案……
参考文献
我使用了 Contoso 示例数据集,就像我之前的文章中提到的那样。你可以从微软这里免费下载 ContosoRetailDW 数据集。
Contoso 数据可以在 MIT 许可证下自由使用,如这里所述。
我扩大了数据集,以使 DAX 引擎更加费劲。
在线销售表包含 7100 万行(而不是 1260 万行),零售销售表包含 1850 万行(而不是 340 万行)。
## 通过我的推荐链接加入 Medium - Salvatore Cagliari
阅读 Salvatore Cagliari 的每一个故事(以及 Medium 上其他成千上万的作家)。你的会员费直接……
如何使用 Google 的 PaLM 2 API 与 Python
自定义并将 Google 的 LLM 集成到你的应用程序中。
·
关注 发表在 Towards Data Science ·11 分钟阅读·2023 年 8 月 14 日
--
图片由 Alexandre Debiève 提供,来源于 Unsplash。
生成型人工智能无处不在。我们看到越来越多的公司投资于这项强大技术,因为它的潜力变得越来越明显。正如 Gartner 所说:在不久的将来,[生成型人工智能]将成为竞争优势和差异化的关键。
“在不久的将来,[生成型人工智能]将成为竞争优势和差异化的关键。”
不幸的是,开发生成性 AI 模型不仅是复杂的工程工作,而且通常是一个相当昂贵的项目。幸运的是,我们不必自己开发这些模型 — 我们可以重用已经为我们预先开发的:通过 APIs!所以,不要再等了 — 让我们直接深入了解如何通过集成生成性 AI 到我们的应用程序中来利用它。
对于本文,我们将查看谷歌对 LLM 的回答:PaLM 2 API。PaLM 2 是谷歌最新版本的Pathways 语言模型,这是一个大型语言模型,使用的训练数据量约是他们在 2022 年发布的初始模型的五倍。
在本文中,我将介绍一些代码示例并展示如何认证到 Google Cloud,使用以及自定义PaLM 2 APIs,并使用 Python 3.11。
1 | 入门
PaLM 2 APIs 可以通过Google Cloud的 Vertex AI 平台访问。因此,在我们进行任何 API 调用之前,我们需要设置我们的 Google Cloud 账户。你可以 在这里注册 并获得 $300 的免费额度 来开始使用这些服务。
一旦你的账户和项目设置完成,我们可以继续 创建一个服务账户,我们将用它来认证到 Vertex AI APIs。我们使用服务账户,是因为我们可以通过仅给予它们特定的 IAM 权限来确保对 Google Cloud 资源的访问控制。对于我们的用例,我们将授予服务账户 Vertex AI User 角色。这可能对你的用例来说过于宽泛,所以我建议你检查 可用的访问角色 并选择适合你需求的角色。
在创建了服务账户并赋予其正确的权限后,我们可以继续 生成一个服务账户密钥。选择 JSON 作为密钥类型,并将文件保存在安全的位置。
很好 — 我们准备动手操作了!👏
2 | 认证到 Google Cloud
在这个示例中,我们将使用 OAuth 2.0 进行认证,并借助我们在前一步生成的服务账户密钥请求访问令牌。
为了便利这个过程,我们可以使用 [google-auth](https://pypi.org/project/google-auth/) Python 库,如下面的代码示例所示:
使用 google-auth Python 库认证到 Google Cloud。
这个代码示例使用服务账户密钥文件“key.json”来请求和生成一个访问令牌,我们可以用它来访问 Google Cloud APIs。在获得访问令牌后,我们可以开始使用它调用 Vertex AI PaLM 2 APIs。
3 | 调用 PaLM 2 API
截至今天,Google Cloud 中提供了两种不同的 PaLM 2 模型:PaLM 2 for Text(即[text-bison](https://cloud.google.com/vertex-ai/docs/generative-ai/text/text-overview))和PaLM 2 for Chat(即[chat-bison](https://cloud.google.com/vertex-ai/docs/generative-ai/chat/chat-prompts))。文档建议对可以用一个回复完成的文本任务使用text-bison,对需要更多对话互动的文本任务使用chat-bison。
让我们从text-bison模型开始。对于这些示例,我们将使用 Python 的[requests](https://pypi.org/project/requests/)库来进行 API 调用。如果你愿意,也可以使用Vertex AI SDK。
PaLM 2 for Text:情感分析
PaLM 2 for Text 模型可用于各种与文本相关的任务:包括摘要、回答问题、情感分析等。它接受以下参数作为输入:
-
prompt:我们希望模型执行的任务的指令。 -
temperature:控制模型的“创造性”。如果我们希望模型在回复中更具开放性和创造性,则应提高温度。如果我们希望模型更具确定性,则温度应降低。值的范围在 0 到 1 之间。 -
maxOutputTokens:生成输出的 token 数量(1 个 token = 4 个字符)。值的范围在 1 到 1024 之间。 -
topK:改变模型选择生成输出的 token 的概率。在每个 token 选择步骤中,具有最高概率的topK个 token 被采样,然后进一步通过topP筛选。值越高,响应越随机。值的范围在 1 到 40 之间。 -
topP:改变模型选择生成输出的 token 的概率。token 的选择直到其概率总和等于topP。值越高,响应越随机。值的范围在 0 到 1 之间。
有关参数的更多细节,请参见 此文档。
在这个第一个例子中,我们将对一些示例产品评论进行情感分析:
sentences = ["I have been using this product for a long time. Somehow the
product I received this time seems to be a fake one. It's
very thick and the smell is very chemical.",
"A good book for for dog owners. The book dosen't just
focus on dog training itself, but also on nutrition and care.
The book contains all the information needed for beginner dog
owners.",
"This lamp was better than I expected when it comes to its
quality. Nonetheless, the colours are not exactly as
displayed and it does not really fit with the curtains that I
purchased recently. Therefore I give this 3 out of 5 stars."]
我们给模型的指令(即提示)需要清楚地说明我们希望模型执行的任务,以及我们期望它生成的输出。在我们的例子中,我们要求它遍历sentences列表中的每个评论,并告诉我们这些评论的情感。我们还指示它将输出提供为 Python 列表。
prompt = f"What is the sentiment of each of these reviews: {sentences}.
Output should be in a python list."
最后,我们需要定义输入参数值:
-
我们将
temperature设置为0,因为对于这个任务,我们希望避免模型过于创意。通过降低温度,我们使得模型更有可能输出我们请求的确切结构。 -
我们将
256设置为maxOutputTokens,因为它大约相当于 200 个单词,并且是适合我们任务的长度。 -
我们将
topK设置为 40,因为这是模型的默认值。 -
我们将
topK设置为 0.95,因为这是模型的默认值。
我们现在可以像进行任何其他 API 调用一样发出 API 调用,使用[requests](https://pypi.org/project/requests/)库,如下所示:
向 Google Cloud PaLM 2 for Text API 发出 API 调用。
我们可以通过response.json()[“predictions”][0][“content”]来获取响应的输出:
"['negative', 'positive', 'positive']"
很棒!现在,让我们做完全相同的操作并尝试将temperature参数设置为1.0。这将意味着我们的模型输出将变得越来越富有创意。我们得到的输出如下:
"The sentiment of each of these reviews is:
1\. Negative
2\. Positive
3\. Positive"
的确,我们可以看到输出比以前更具创意,并且没有正确遵循我们提到的“输出应该是一个 python 列表”的提示。因此,这是一个很好的教训,即选择合适的参数值非常重要,以从模型中获得期望的输出。
现在你可能会问自己:“这是否意味着我不能确定模型输出是否符合正确的格式?” 有多种机制可以设置以测试模型的输出并检查其是否符合你预期的格式。从理解并正确配置模型的输入参数,到优化你的提示(即提示工程),以及实施额外的静态测试以验证输出的结构。强烈建议应用这些技术,因为生成式 AI 可能会出错,我们希望在它们发生之前就解决这些问题。
PaLM 2 for Text: 文本生成
在我们的下一个示例中,让我们要求 PaLM 2 为新客户生成欢迎文本,他们正在访问一个电子商务网站。我们想欢迎他们,并在首次购买时提供 20%的折扣码。对于这个用例,我们希望我们的模型提出一些更具创意的内容。因此,我们将temperature设置为0.5。
discount_code = "2023-promo"
prompt = f"Can you write 2-3 sentences that can be displayed on an
e-commerce webpage if someone first visits it? It should contain
the code '{discount_code}' which gives the customer 20% discount
on their first purchase."
输出如下:
"**Introducing our 20% off first purchase offer!**
Use the code **2023-promo** at checkout to save 20% on your first
purchase. This offer is valid for a limited time only, so don't miss out!
**Shop now and start saving!**"
很好!它甚至以 markdown 格式给出了输出。让我们再次尝试将temperature调整为1.0。输出如下:
"**20% OFF YOUR FIRST PURCHASE**
Use code 2023-promo at checkout to save 20% on your first purchase from
our store. This offer is valid for new customers only and expires on
March 31, 2023.
**Shop now and save big!**"
我们再次可以看到我们的模型相比之前的输出变得更加富有创意。它甚至提出了一个优惠过期日期,而我们并没有明确要求它这样做。
PaLM 2 for Chat: 对话助手
在最后一个示例中,我们将测试专注于生成对话体验的 PaLM 2 for Chat API。我们将使用的模型称为chat-bison。它接受以下参数作为输入:
-
messages:包含与聊天机器人之间的消息和消息历史记录。 -
context:定义聊天机器人行为的“指南”。例如:聊天机器人的名称是什么,它的角色是什么,包含/排除的词汇等。 -
examples:示例输入和输出,展示了聊天机器人在对话中的响应方式。 -
以及
temperature、maxOutputTokens、topK和topP,它们的作用与前一节中的text-bison模型相同。
有关参数及其具体作用的更多详细信息,请参见 此文档。
我们来创建一个聊天机器人,作为在线园艺商店“GardenWorld”的客户支持代表。这个机器人应当能够回答有关植物和花卉类型、园艺工具等问题。我们希望机器人始终对客户友好和热情,并且应该用“Hoowdy gardener! 🌱”来问候客户,并激励客户注册新闻通讯以获得首次购买的 10%折扣码。
我们可以通过设置context和examples参数来定义这些,如下所示:
context = "You're a customer assistance bot for an online gardening store
called GardenWorld and you want to help customers. You give
advice around gardening best practices, plant and flower types.
Always welcome the customer by telling them to subscribe to
the GardenWorld newsletter to get a 10% discount on their first
purchase."
examples = [{
"input": {"content": "Hi!"},
"output": {"content": "Hoowdy gardener! 🌱"}},
{
"input": {"content": "Hello"},
"output": {"content": "Hoowdy gardener! 🌱"}
},
{
"input": {"content": "Why should I choose GardenWorld?"},
"output": {"content": "GardenWorld has been the award winning
supplier for best quality plants and
gardening equipment. With over 25 years
of experience, we continuously provide
the best in class service to our
customers."}}
]
我们将给模型三个示例,说明输出应是什么样的。当然,我们提供的示例越多,机器人可以定制的程度就越高,模型将能够更好地学习我们期望的内容。由于我们的用例非常简单,这三个示例应该能给机器人足够的信息来提供正确的答案结构。我们将temperature设置为1.0,maxOutputTokens设置为256,topK设置为40,topP设置为0.95。
现在,我们开始对话,先说“Hi!”。
messages = [{
"author": "user",
"content": "Hi!",
}]
然后我们可以进行 API 调用:
向 Google Cloud PaLM 2 进行聊天 API 调用。
几秒钟后,我们得到以下回复:
"Hoowdy gardener! 🌱 How can I help you today? Don’t forget to subscribe
to the GardenWorld newsletter to get a 10% discount on your first
purchase!"
好的!我们的模型准确地完成了我们的要求:它使用了正确的问候语(甚至包括了表情符号🌱),并提到了 10%折扣码。
为了继续与机器人对话,我们需要相应地更新对话历史。我们可以定义两个简单的函数,在每次 API 调用后调用它们:
更新聊天机器人的消息历史的功能。
update_history()将最新的机器人回复添加到messages列表中。然后我们可以使用send_message()向机器人发送一个新问题“我需要一株易于照料的漂亮室内植物。你有什么推荐?”。
现在我们的messages变量如下所示,最新的消息位于列表末尾:
[{"author": "user",
"content": "Hi!" },
{"author": "1"
"content": "Hoowdy gardener! 🌱 How can I help you today? Don’t forget
to subscribe to the GardenWorld newsletter to get a 10%
discount on your first purchase!" },
{"author": "user",
"content": "I need a nice houseplant that is easy to take care of. What
can you recommend?" }]
我们可以在下一次 API 调用中包含messages历史记录,得到的回复是:
"A snake plant is a great choice for a houseplant because it is very low
maintenance and can thrive in a variety of conditions. They are also very
tolerant of neglect, so they are perfect for people who are busy or
forgetful."
它建议我们选择“蛇尾兰”,这是一种被认为易于照料的室内植物。让我们将这条消息也添加到messages历史记录中。
然后,我们提出最后一个问题“你能建议一些替代方案吗?另外,我可以获得折扣吗?”我们得到的回复是:
"Sure! We offer a 10% discount to all first time customers who subscribe
to our newsletter. A ZZ plant is another great option for a low-
maintenance houseplant. They are also very tolerant of neglect and can
thrive in a variety of conditions.'
很好!如上所示,机器人建议了我们的折扣码,感谢messages历史记录,它记住了我们之前提出的问题,并建议了一个容易打理的植物“ZZ plant”。
结果展示了我们如何轻松且迅速地让一个自定义聊天机器人运行起来:只需提供三个示例和一个高层次的上下文,机器人就能够理解我们的期望,并在对话中正确返回信息。想象一下如果我们提供数百甚至数千个额外的示例——潜力巨大!
4 | 结论
在本文中,我们看到利用 Google Cloud 的PaLM 2 APIs是多么简单,使用了text-bison和chat-bison模型。我们了解到如何使用 Python 的[google-auth](https://pypi.org/project/google-auth/)库进行远程认证,以及如何使用[requests](https://pypi.org/project/requests/)库调用这些 APIs。最后,我们看到如何通过调整和玩转输入参数来定制这些 APIs。
希望这篇文章对你有帮助,并给你一些灵感和关于如何开始使用 PaLM 2 APIs 的想法! 🪴🤖
🔜🔜 在即将到来的第二部分中,我们将专注于使用 PaLM 2 APIs 进行提示工程,并探讨如何更好地优化输入提示和模型参数选择。
反馈或问题?请随时在评论区联系我! 💬
📚 想了解更多?查看 Google Cloud 上的生成 AI 或尝试 Google Cloud Skills Boost 上免费的 生成 AI 学习路径。
参考文献
1 Gartner, Gartner 专家回答您的企业生成 AI 顶级问题,访问日期:2023 年 8 月 13 日。
2 CB Insights, 生成 AI 的现状:7 张图表,2023 年 8 月 2 日。
3 Google Cloud, Vertex AI 上生成 AI 支持概览,访问日期:2023 年 8 月 13 日。
如何在自己的领域中使用大型语言模型(LLM)
原文:
towardsdatascience.com/how-to-use-large-language-models-llm-in-your-own-domains-b4dff2d08464
一些经典的、经过研究验证的技术,用于将大型语言模型适应于领域特定任务,以及为什么这些技术有效的直觉。
·发表于Towards Data Science ·阅读时间 9 分钟·2023 年 3 月 20 日
--

自从 2022 年底 ChatGPT 普及以来,大型语言模型(LLM)吸引了研究和工业界的极大关注。虽然通用聊天机器人是大型语言模型的明显应用,但企业们正在考虑如何将大型语言模型集成到其业务工作流程中,以利用这一最新的 AI 进展。然而,业务特定集成的一个基本前提是能够将大型语言模型适应于定制的业务领域,因为 LLM 通常在开放互联网信息上进行训练,这些信息包含过多噪声,并不总是与具体业务背景紧密相关。
尽管已有许多优秀的博客文章详细介绍了大型语言模型本身,但似乎缺乏关于如何利用 LLM 的实质性介绍。在这篇博客文章中,我们探讨了从近期研究文献中获取的一些经典方法,将 LLM 适应于领域特定任务。目标是激发一些灵感,真正实现 LLM 的民主化,使其对更广泛的世界开放。
这篇博客文章设想的场景是,你以某种方式获得了一个已经预训练的通用大型语言模型。你可以访问模型中的所有参数。模型可能来自开源、商业选项、与其他组织的合作(如谷歌的 PaLM 和 OpenAI 的 GPT-3),或由你的组织从头开始训练。现在你有一系列特定业务背景下的任务(如问答、摘要、推理等),你希望基于大型语言模型来完成这些任务。
传统的微调
将通用机器学习模型调整到特定任务的传统方法是使用来自特定领域的标记数据来端到端地训练通用模型。在上训练过程中,模型中的部分或所有可学习参数都通过反向传播进行微调。这种类型的微调在大型语言模型中往往是不受欢迎的。如今的 LLM 大得多,一些拥有数百亿个参数。端到端微调不仅消耗大量计算资源,而且还需要大量特定领域的标记数据,这些数据获取起来很昂贵。随着人工智能领域的进步,模型可能只会变得更大,这使得对每个单独的定制任务进行端到端的微调变得越来越繁琐。
尽管如此,一种常常被期望的端到端微调形式是指令微调1。大型语言模型通常在一般文本上进行训练。一个简单的类比是,LLM 就像一个阅读了大量书籍的人(请记住这个类比,我们会在后续继续参考它以建立我们的直觉)。但他不知道如何利用这些知识。指令微调的目的是让模型养成执行一些常见任务的习惯。这是通过在输入前添加模板化的指令,如“回答以下问题”、“总结以下文档”、“计算结果”、“翻译这句话”等来实现的。输出则是这些指令的预期结果。使用这种输入/输出对来进行端到端的微调,将使模型更容易对未来的输入“采取行动”。请注意,指令微调不需要特定于某个领域,除非你的领域需要不寻常的“行动”。而且,你所拥有的预训练大型语言模型可能已经经过指令微调(例如 Google 的 Flan-PaLM)。
提示
在深入探讨将 LLM 调整到领域特定任务的方法之前,我们需要介绍提示的概念,剩下的博客内容基于这一概念。
提示是我们与 LLM 互动的方式。LLM 实际上是序列到序列的文本生成器。如果这有助于建立直觉,你可以将它们视为递归神经网络,但请注意,如今最先进的 LLM 是基于 Transformer,特别是 Transformer 的解码器部分,这不是 RNN。提示是输入序列到模型中。
回到我们上面的“知识渊博的人”类比,提示就是向这个人提问的行为。显然,为了获得有用的答案,你的问题需要足够好。网上有一些资源关于如何提出清晰和具体的问题以从 LLM 中获得良好的答案。这些是有用的技巧,但它们不是我们在这篇博客中将要讨论的微调提示类型。
想一想,为什么“提示”有效?因为模型被训练以使其输出基于输入序列。在训练于开放互联网的 LLMs 的情况下,所有的人类“知识”都被打包在模型中,并以数字形式重生。提示是为了设定数学条件,以便可以构建出适当的输出。最佳的数学条件可能不在传统意义上的“清晰和具体”,尽管这仍然是一个很好的通用规则。而且最重要的是,正如你可能猜到的,这些数学条件是特定于领域的,你应该专注于调整这些条件以使 LLM 适应你的领域。 模型参数本身保持不变。再用我们的“知识丰富的人”类比,他已经是知识丰富的,所以不需要改变他。事实上,由于他已经掌握了所有人类知识,他已经具备了你领域的潜在知识,因为你的领域最终建立在人的知识之上。
提示的艺术与科学
那么,我们应该如何提示模型以便将其微调到特定的业务领域呢?以下是几种经典的方法。
少样本示例
最简单而又非常有效的方法是提供一些示例作为提示。学术术语是通过示例进行的少样本学习2。为了简单说明,假设你想执行的任务是算术计算。
Input:
Jane has 2 apples. She bought 3 more. How many apples does she have in total?
Expected Output:
The answer is 5.
现在,如果你只将上述输入提供给 LLM,你可能不会得到正确的结果。因为“知识丰富的人”虽然具备算术能力,但并不知道他被要求做算术。因此,你需要在输入中编码一些你想从 LLM 获得的示例。
Input:
Joe has 3 oranges and he got 1 more. How many oranges does he have?
The answer is 4.
Jack has 8 pears and he lost 1\. How many pears does Jack have now?
The answer is 7.
Jane has 2 apples. She bought 3 more. How many apples does she have in total?
Expected Output:
The answer is 5.
最终的输出只是回答输入中的最后一个问题。然而,LLMs 可以根据输入中的前文来获取“做什么”的提示。显然,你任务的最终界面只是接受用户的实际问题。示例在幕后被添加到用户问题之前。你需要进行一些实验,以找到几个相对好的示例,作为模型输入的前缀。
链式思维
基于上述少样本示例,我们不仅想告诉 LLMs“做什么”,还想告诉他们“如何做”。这可以通过链式思维提示来实现。直觉是,如果“知识丰富的人”看到几个如何做任务的示例,他也会模仿“推理”过程。所以上述算术场景变成了:
Input:
Joe has 3 oranges and he got 1 more. How many oranges does he have?
Starting with 3 oranges, then add 1, the result is 4.
The answer is 4.
Jack has 8 pears and he lost 1\. How many pears does Jack have now?
Starting with 8 pears, then minus 1, the result is 7.
The answer is 7.
Jane has 2 apples. She bought 3 more. How many apples does she have in total?
Expected Output:
Starting with 2 apples, then add 3, the result is 5.
The answer is 5.
研究2表明,链式思维提示显著提高了 LLMs 的性能。你可以选择是否要将推理部分展示给最终用户——“从 2 个苹果开始,然后加上 3,结果是 5”。
为了进一步改善思维链的结果,认识到通常有多条推理路径可以达到相同结果。人类可以以多种方式解决问题,如果多个解决方案得出相同结果,我们对结果的信心会更强。这种直觉可以再次被融入。LLMs(大规模语言模型)是概率模型。每次输出都是从样本中生成的。因此,如果你运行多次,输出可能会不同。我们感兴趣的是那些推理不同但最终答案相同的输出。这模拟了人类通过多种方式故意解决问题并从输出的“自我一致性”3中获得信心的思维过程。
Input:
[same]
Output1:
Starting with 2 apples, then add 3, the result is 5.
The answer is 5\. [correct]
Output2:
2 apples and 3 apples make 6 apples.
The answer is 6\. [incorrect]
Output3 [repeat of final result 5 with a new reasoning - good]:
2 apples plus 3 apples equal 5 apples.
The answer is 5\. [correct]
Output4 [repeat of final result 6 with identical reasoning - ignore]:
2 apples and 3 apples make 6 apples.
The answer is 6\. [incorrect]
简而言之,最终结果中拥有最多不同推理方式的选项胜出。上面的Output1和output3准确地得出了最终正确答案5。
可学习的提示
上述方法仅使用了来自你领域特定标记数据集的一些示例。但如果你有更多数据,你自然会希望充分利用它们。你还应该问的另一个问题是如何确保你选择的示例在数学上是最优的。这就是可学习提示发挥作用的地方。
关键洞察是输入中的前缀不必来自固定的词汇。归根结底,每个输入标记在馈入模型之前都会被转化为嵌入。嵌入只是一个数字向量。最佳的数字是可以学习的。
你需要做的是在真实输入之前有一组前缀标记。这些前缀标记可以通过从你领域特定词汇中采样词语来初始化。然后,通过反向传播更新这些前缀标记的嵌入。模型参数本身仍然是冻结的。但梯度从预期与实际输出的差异开始,通过模型参数,一直到输入层,更新那些前缀标记的嵌入。经过在你领域特定标记数据集上的训练,这些学习到的标记嵌入就成为推理时的固定输入前缀。从某种意义上说,这是一种软提示,其中输入前缀不再受限于从固定词汇中经验性地提取。相反,它们是为你的领域在数学上优化的。这被称为提示调优4(图-1)。

图-1 提示调优:可训练前缀与领域特定输入连接形成模型输入。梯度从输出层通过模型参数传播到输入层,以更新可训练前缀。在整个架构中,其余参数保持不变。
结语
这篇博客文章提供了对常见且有效的微调机制的直观解释,这些机制可以帮助你以数据高效和计算高效的方式将大型语言模型(LLMs)适配到你特定领域的任务中。这是一个快速变化的领域,新研究成果以令人眼花缭乱的速度不断出现。希望这篇博客文章为你开始利用 LLMs 提供了坚实的基础。
参考文献
1 扩展指令微调语言模型 arxiv.org/abs/2210.11416
2 语言模型是少样本学习者 arxiv.org/abs/2005.14165
3 自一致性改善了语言模型中的链式思维推理 arxiv.org/abs/2203.11171
4 参数高效提示调优的规模效应 arxiv.org/abs/2104.08691
如何在 R 中使用映射函数进行数据科学
原文:
towardsdatascience.com/how-to-use-map-functions-for-data-science-in-r-8180c2ed6d79
从 tidyverse 中学习强大的函数式编程工具
·发表于 Towards Data Science ·7 分钟阅读·2023 年 2 月 2 日
--

所有数据科学家都需要重复代码。无论是为多个数据集拟合模型,还是一次更改多个值,多次运行相同的代码都是必不可少的。
重复代码有很多方法。然而,尽管大多数程序员使用循环,还有更简洁、可读性更高且效率更高的替代方案。引入map家族的函数,来自 purrr 包。
在本文中,我将解释什么是映射,以及如何使用map、map2和pmap函数,这些函数来自R 的 purrr 包。
什么是“映射”,以及如何在 R 中实现?
purrr 包所做的映射并不是大多数人熟悉的地理类型。R 拥有优秀的地理空间分析工具,但这里讨论的不是这些。
“映射”是编程中的一个专业术语,指的是在一组参数上反复应用一个函数。
我们可以通过一个简单的例子来理解这一点。假设我们有一个列表,每个元素包含 100 个数字。如果我们想计算每组数字的均值,我们可以使用map以直接的方式来完成。
library(tidyverse)
# Set seed for reproducibility
set.seed(1234)
# Generate list of numeric values for this example
values_list <- list(rnorm(100, 100, 15),
rnorm(100, 110, 20),
rnorm(100, 75, 15))
# Use map to get the mean of each set of numbers
map(values_list, mean)
在这里,我们首先加载 tidyverse,它包含提供映射函数的 purrr 包。然后,我们使用map来获取每个列表元素的均值。
基本的map函数接受两个参数。首先,我们指定一个要应用函数的列表或向量;在本例中是values_list。其次,我们命名要应用的函数;mean。这使得map成为一个“高阶函数”,因为它将另一个函数作为参数。运行此代码会给出values_list中每个元素的均值,如下所示。

除了命名我们想应用的函数外,我们还可以使用公式来定义函数。当定义带有附加参数或更复杂表达式的函数时,这一点尤其有用。在下面的代码中,我们使用这样的公式来计算每个列表元素的均值,即使数据中存在缺失值。
# Add some missing values to the second element of the list
values_list[[2]][c(2, 24, 93)] <- NA
# Get the means with map again, this time discounting NAs
map(values_list, ~ mean(., na.rm = T))
在这段代码中,我们将函数定义为一个带有波浪号(~)符号的公式。在mean内部,我们使用点(.)符号来引用作为参数的列表元素。用公式定义mean函数使我们可以指定额外的参数,例如na.rm = TRUE,用于处理values_list中的 NA 值。这会生成一个均值列表,如之前的例子所示。
为什么使用 map 函数而不是循环?
如果你熟悉 for 循环,你可能会发现map的操作和你如何使用循环解决上述问题之间的相似之处。这里是将相同操作用循环表达的方式:
# Expressing the operation above as a loop
for (i in values_list) {
print(mean(i, na.rm = TRUE))
}
这个循环非常简单。每次迭代时,它从values_list中获取一个新元素,并打印该元素中值的均值,就像map示例一样。甚至有一个R 包可以将map语句转换为循环!
所以,如果你可以将 map 语句表达为循环,你为什么还要学习 map 呢?
与循环相比,映射有一些优势:
-
map函数通常更简洁,占用一行,而不是至少三行。这意味着它们可以更容易地内联使用和嵌入其他函数中,从而开启了强大的新使用可能性。 -
与循环不同,映射迫使你定义函数。这会导致更整洁、更具模块化的代码,便于重用。
-
相关于上一点,定义函数通常会加速代码的执行。这意味着映射操作可能比循环更快(尽管这并不一定保证)。
循环仍然有其用武之地,有时在它们是最快选项或在重复非常复杂或不寻常的操作时使用它们更为合适。也就是说,map系列也能处理更复杂的函数。
如何使用 map 的变体:map2 和 pmap
map2 和 pmap 函数是 map 的直接扩展。
map2
map2 允许你应用一个接受两个参数的函数。例如,你可以使用它来拟合几个线性模型,这些模型以 x 和 y 变量作为参数。
# Define combinations of variables to model
x <- c("mpg", "hp", "wt")
y <- c("hp", "wt", "mpg")
# Apply the lm function to these variables using map2
map2(x, y, ~ lm(get(.x) ~ get(.y), data = mtcars))
在这里,我们创建了两个向量 x 和 y,包含了 mtcars 数据集中的变量组合。在我们的 map2 函数表达式中,我们将这些变量称为 .x 和 .y。get 函数只是帮助 lm 使用名称定位 mtcars 中感兴趣的列。这导致三个线性模型被拟合;每个指定的变量组合对应一个。
pmap
pmap 允许你对具有两个以上参数的函数表达式进行操作。例如,可以将三个不同列表中的值连接起来进行进一步分析。
# Define some example values from a clinical trial
baseline <- c(101, 92.3, 98.2)
treatment <- c(103.3, 92.1, 99.8)
followup <- c(112.1, 95.4, 104.2)
# Concatenate those values together, rowwise
pmap(list(baseline, treatment, followup), ~ c(..1, ..2, ..3))
调用 pmap 时,我们将输入数据封装在一个列表中。每个输入被称为 ..1, ..2, ..3 等。结果是一个连接值的列表,如下所示。

如何控制 map 函数的输出
你可能已经注意到,到目前为止所有的示例都返回了列表作为输出。然而,也可以让 map 函数返回不同类型的向量和数据框。
回到本文的第一个示例,我们可以使用 map 获取一个均值列表。但我们也可以通过使用 map_dbl 函数来获取一个平坦的均值向量。
map_dbl(values_list, ~ mean(., na.rm = TRUE))

上述代码的结果:一个均值向量,而不是列表。
向我们的 map 函数名称添加 “_dbl” 实现了这一点。还有一些其他变体适用于 map、map2 和 pmap。以下是它们的列表及其功能:
-
“_dbl” 返回一个双精度向量
-
“_lgl” 返回一个逻辑(TRUE/FALSE)向量
-
“_int” 返回一个整数向量
-
“_chr” 返回一个字符向量
-
“_raw” 返回一个原始向量
-
“_dfr” 返回一个 tibble,每行是一次 map 操作的结果
-
“_dfc” 返回一个 tibble,每列是一次 map 操作的结果
使用这些函数变体返回向量和 tibble 可以解锁许多可能性。我经常在其他 tidyverse 函数中使用 map_dbl 和 map_chr 来基于自定义函数创建新列。我也使用 map_dfr 和 map_dfc 来用一两行代码替代长循环。这些函数很棒,毫无疑问值得学习和记住。
是否应该使用 map 函数代替 base-R 的 “apply” 函数?
正如我们所建立的,purrr 包提供了一整套 map 函数,可以执行各种操作。
然而,R 也配备了一套内置的映射函数;apply 函数。这些函数的功能与 purrr 的基本 map 函数相同,但在语法和处理数据类型的方式上有所不同。
尽管 base-R 的 apply 函数被广泛使用,但我更喜欢 purrr 的 map 函数,原因有几个:
-
这样更容易确切了解
map函数将输出何种类型的数据。这在 base-R 函数中并不总是如此,后者有时会根据输入表现出不可预测的行为。 -
它们被设计为与其他 tidyverse 包兼容,这对于已经在你的工作流程中使用它们来说非常有用。
-
Purrr 还包含了更多高级的映射函数,这是
apply系列无法匹敌的。学习map可以让你更轻松地掌握这些函数。
有时你可能会更倾向于使用 apply。如果你正在开发一个包,并且不想引入额外的依赖,内置函数是最佳选择。apply 函数的运行速度也可能比 map 函数更快,因此在重复计算密集型操作时,它们可能更值得使用。
尽管如此,map 函数对于新手来说更容易学习,这也是我在这里重点讲解它们的原因。此外,一旦你掌握了 map 的使用,apply 函数也会变得容易理解。
学习 R 中的 map 函数是扩展你的数据科学工具集的好方法。与循环相比,映射通常节省空间,而不牺牲可读性。它还适用于许多任务,是高效重复代码的一个极佳标准方法。因此,既然你知道了如何使用 map,就试试看,享受 tidyverse 中最强大的工具之一吧。
如何利用微认证进入数据科学领域
微认证是数据科学入门的一个起点,只要你知道如何恰当地利用它们
·
关注 发表在 Towards Data Science · 8 min read · 2023 年 1 月 24 日
--
图片由 Corinne Kutz 提供,来源于 Unsplash
如今,你可以在从动画到编篮子等各种领域获得微认证,涵盖了所有领域。
在过去的 7 年里,微认证在学习领域掀起了风暴,使人们能够通过获得和展示特定技能来获得徽章。一些最早的微认证是针对编程技能的,但由于高等教育机构迅速发展和推出微认证,现已涵盖各种行业的技能,以帮助填补区域技能缺口。
数据科学的微认证也不例外,通过简单的谷歌搜索,可以找到成百上千个来自世界各地的微认证结果,这些认证来自于高等教育机构、私营公司和学习平台。
那么,微认证在数据科学就业市场中究竟具有何种价值?它们能帮助你获得机会吗?对于新入行的数据科学家或那些希望保持相关性的行业从业者而言,它们是否是一个好主意?所有这些问题将在这里得到解答,我们将探讨如何利用数据科学微认证来增强简历,并帮助你作为一名新兴的数据科学家获得第一份工作。
首先,什么是微认证?简短的历史介绍。
微认证是授予学习、展示和展示非常具体技能的学生的数字徽章——通常是那些可以在 8 到 12 周内或比典型大学课程(约 16 周,具体取决于课程)短的时间内教授或学习的内容。
微认证由高等教育机构、私营公司或非营利组织以及学习平台(如 Udemy 或 Coursera)提供。近年来,微认证取得了长足的发展,现在许多认证是通过政府参与和行业领导者与大学合作创建的,旨在针对特定技能缺口的认证。
根据《福布斯》的报道,西方世界目前由于婴儿潮一代的大规模退休而面临的人才短缺问题正在对几乎所有行业和企业产生重大影响。研究表明,大多数高管发现,尽管愿意工作的人员不乏其人,但却越来越难以找到具备适当技能和才能的员工。
微认证的好处(当设计得当时)包括可扩展且成本效益高的培训(以大学学位的几分之一成本提供小块学习机会)、按需和个性化学习(微认证可以在短时间内完成,通常按自己的节奏进行),更不用说微认证旨在将商业需求与职业抱负对齐。
微认证在数据科学就业市场中的价值是什么?
一次简单的谷歌搜索“数据科学的微认证”会返回数百个来自高等教育机构、私营公司和学习平台的课程结果。然而,问题仍然存在:即便每个人都在生产微认证,它们在数据科学就业市场中是否有价值?
根据托马斯·戈蒂耶(Thomas Gauthier)于 2020 年进行的一项研究,戈蒂耶是棕榈滩州立学院贸易与工业系的副院长,“微认证为申请人的成绩单提供了价值,同时突显了在真实环境中获得的技能。”戈蒂耶进一步表示,由认证机构提供的微认证所提供的有效性可以让雇主在发出聘用邀请之前对候选人的能力有一个“清晰的理解。”
幸运的是,数据科学作为技术领域的一部分,已经在接受来自潜在员工的另类教育方面走在了前列,这得益于许多数据科学家自学成才而没有在该领域接受直接的大学教育。研究表明,越来越多的组织正在转向基于技能的招聘,这在技术领域已经持续了几十年。
我在与科技行业的同事们合作时亲身经历过这一点。例如,我合作的一位同事拥有计算机科学的正式教育背景,但通过微认证和在线课程获得了数据科学技能。他们能够展示我们所需的数据科学技能,足以使她扩展工作描述,包括数据科学任务。即使他们的教育背景来自微认证而非大学学位,这一点也无关紧要,因为他们能够完成我们要求的工作,甚至更多。
微认证是否能被新数据科学家用来获得他们的第一份行业工作?
虽然拥有本科或研究生学位在数据科学领域仍然对行业招聘标准有一定影响,但微认证正迅速建立起弥合当前技能差距的声誉。
令人鼓舞的是,例如,许多雇主开始将 Coursera 证书视为有价值且有效的候选人技能证明。这种招聘政策的变化,加上行业参与开发微认证的增加,表明微认证正成为新数据科学家进入行业的门户。虽然微认证可能不足以立即让你获得数据科学家梦寐以求的六位数薪水,但足以帮助你获得第一份工作。
正如我之前提到的关于我的同事,微认证足以让你开始从事数据科学工作。大多数雇主关心的是你是否能胜任工作,而不是你是如何学习这些技能的。
如果对微认证是否能给你职业发展带来提升有疑虑,尝试选择由高等教育机构设计的微认证,例如一些加拿大顶尖大学提供的:
这种类型的微认证将短期的技能培训经验与受尊敬的高等教育机构的名字结合起来,提供了双重优势。
如何利用数据科学微认证获得第一份行业工作作为新数据科学家
像其他替代形式的数据科学教育和经验一样,微认证只需与强大的作品集和简历相结合,并正确地营销给适当的客户(即:雇主)。
然而,在我们急于行动之前,让我们从头开始,讨论你应该关注哪些微认证。
能够让你获得最大价值的微证书是那些由高等教育机构开发的,并且得到行业和政府输入的。这些微证书会得到潜在雇主的高度重视,因为它们不仅来自高等教育机构(这在今天的社会中仍然很重要),而且还得到了行业领袖和政府官员的认可,他们正在努力填补技能差距。此外,你应该专注于完成那些包含顶点项目的微证书。顶点项目不仅提供了学习证据,还可以添加到你的专业作品集中。
完成你的微证书后,你可能希望立即开始运用你学到的技能。微证书的进度通常较快,这意味着你可能不会像在典型大学课程中那样保留大量信息。因此,现在是时候开始建立你的专业作品集,创建可销售的数据科学产品,在博客上分享你的数据科学知识,甚至为当地企业做无偿的数据科学工作了。
下一步(可以与之前讨论的步骤同时进行)是开始制定你的简历,并学习如何营销你的技能。由于你的学习路径与其他求职者有所不同,你需要展示你如何将微证书应用到个人项目、无偿工作和在数据科学领域的权威记录中。这些项目,实际的工作经验(无论是有偿还是无偿),是雇主在寻找能够迅速上手的人时所关注的。
例如,我的同事通过在 LinkedIn 上发布信息内容来展示他们的数据科学专长。这种方式来营销你的技能是向雇主展示你从微证书中学到了立即可用的知识,同时也展示你能够在接受培训后教会其他人做同样的事情的好方法。
利用数据科学微证书的关键之一是向雇主展示你在短时间内学会了数据科学技术,这使得你能够在他们雇用你时立即上手。这不仅展示了你的可教性和自我启动能力,还表明你不会退缩于挑战,并且能够完成任务。我的同事在完成微证书后,开始主动承担项目中与数据科学相关的小任务,这让我们的雇主有机会看到他们的能力。这使得我的同事参与了公司项目中更多的数据科学相关工作。
解决这些问题会让招聘人员查看你的简历超过 7.4 秒钟。
towardsdatascience.com ## 如何创建一个专业的 GitHub 作品集,以帮助获得你的数据科学第一份工作
你的作品集在告诉雇主为什么他们应该聘用你方面发挥着至关重要的作用。
towardsdatascience.com ## 如何有效展示你在数据科学简历中的个人项目
简洁的项目描述是吸引招聘人员注意的必备条件。
towardsdatascience.com
最后,你需要开始准备面试,并计划如何让潜在雇主留下深刻印象。如果你已经为每种可能遇到的面试形式做好了充分准备,从拥有大学学位的候选人中脱颖而出就不会那么困难。简而言之,你在非技术面试中需要表现得有吸引力、专业和有知识,而在技术面试中则需要表现得沟通能力强、富有创意和透明。
如果你知道如何正确准备,大型科技公司的面试并不需要让人感到害怕。
towardsdatascience.com
结束语
数据科学微证书向雇主展示了你花费时间和金钱完成了该领域的结构化教育,这点需要证明你的证书至少和那些在大学里花费 4 年学习数据科学的人的证书一样好,甚至更好。
获取数据科学首份工作的真正关键在于将您的学习扩展到课程之外的相关领域。这意味着将相关的自学与您的结构化课程材料结合起来,使您对未来的雇主尽可能相关。要认识到,相关性 是这里的关键术语,它将帮助您与其他数据科学候选人区分开来。
订阅以将我的故事直接发送到您的收件箱:故事订阅
请成为会员,以通过我的推荐链接获取对 Medium 的无限访问权限(这不会增加您的额外费用,我会获得少量佣金):Medium 会员
通过捐款支持我的写作,以资助更多类似的故事创作:捐赠
如何使用 OpenAI 的代码解释器来分析数据
原文:
towardsdatascience.com/how-to-use-openais-code-interpreter-to-analyze-data-45f5b2c57c1e
在 AI 的帮助下探索 SaaS 收入倍数
·发表于Towards Data Science ·阅读时间 6 分钟·2023 年 7 月 17 日
--

由 OpenAI Dall-E 工具生成的照片
我已经暂时搁置了新的数据分析文章。投资回报率似乎太低了。收集数据、清洗数据和编写精细的绘图代码需要很长时间,并且涉及大量枯燥的工作。
但时代变了!我看到 OpenAI 终于发布了他们的代码解释器工具,这可能解放了我,使我不再需要做枯燥和重复的数据任务。让我们看看 AI 如何在一个我拖延了几个月的项目上帮助我——对我的故事《SaaS 收入倍数、利率与 R 建模》的后续跟进
获取数据
这是代码解释器目前无法帮助的一个领域。我不得不自己出门收集数据,来源如下:
-
Macrotrends上的股票价格与销售数据
-
圣路易斯联邦储备银行页面上的联邦基金利率
-
财政部网站上的国债收益率数据
没有 AI 的帮助,我还编写了 Python 代码来解析和合并这些来源到一个 CSV 文件中。在这里,我也应该使用 AI 工具。例如,ChatGPT 在生成用于数据处理的 Python pandas 代码方面表现出色。
无论如何,这里是我们将输入到机器人朋友中的数据快照。来自 10 多年和近 100 家公司中的 2000 多个价格/销售数据点。
typhon/saasrevenuewinterestrates | 工作区 | data.world

启用解释器
如果你像我一样为 ChatGPT 付费,那么你应该可以访问代码解释器。只需前往设置启用此 Beta 功能。

加载数据
启用解释器后,我们现在可以上传我们的 CSV 文件,并询问解释器关于它的问题。还有一个新的“展示工作”标签,可以让我们查看解释器生成的 Python 代码。例如:


解释器能够弄清楚数据集是什么,并正确解释列名。特别是,3 Mo、6 Mo、1 Yr、2 Yr、3 Yr、5 Yr、7 Yr、10 Yr、20 Yr、30 Yr 列确实是美国国债收益率数据,而 FedFundsRate 是联邦储备银行支付给银行的利率。它还弄清楚了 growth_rolling_avg 是一个增长率指标(在我们公司的收入情况下)。
基本分析
你可以像和 ChatGPT 交流一样与代码解释器对话。在这里,我问了探索性数据分析的第一个问题之一,即是否存在任何有趣的相关性?

解释器不仅生成了良好的代码,它提供的描述也大体上是正确的。

代码解释器甚至识别了上述相关性的可能原因。例如,较新上市的公司通常具有更高的 P/S 比率,而更高的 M2 货币供应量会导致更高的股票价格。
工具没有对价格/销售与价格本身之间的最奇怪的相关性进行推测。我的猜测是,这反映了 P/S 比率逐渐上升的长期趋势。Adobe 是一个很好的例子,突出了这一点。

绘图
让我们用新的 AI 朋友来测试一个理论,即最近上市的公司拥有更高的价格/销售比率。

哎呀,我们遇到了一些问题。我可以理解为什么这个东西还在 Beta 阶段。

幸运的是,在开始新的会话并重新上传后,解释器表现完美。

我们得到了一张很好的图形,显示了最极端的 P/S 数字出现在 2021 年,而且这些数字主要涉及最近上市的公司。
我们还可以看到生成此图的代码:

我个人认为,使用“色调”来着色 ipo_year 点有点令人困惑,因为年份是一个离散变量。让我们看看是否能说服解释器做得更好。
尽管有我的拼写错误,但它完成了工作。

接下来,让我们看看代码解释器能否在我不直接询问的情况下弄清楚我想要什么。它能决定哪种图表最佳吗?它能找出轴上使用的正确变量吗?

太棒了!正是我想要的。唯一缺少的是趋势线,工具在我要求后轻松添加了。

正如预期的那样,一旦 30 年期收益率超过 2.5%,数据中的疯狂 P/S 倍数就消失了。
高级分析
到目前为止,代码解释器成功地完成了初级数据分析师的工作。现在让我们看看它能否处理真正的数据科学家工作。

幸运的是我的拼写错误无法阻止代码解释器
好的,这变得有趣了。代码解释器可以帮助我们生成一个不错的线性回归模型。当然,我们必须对机器保持警惕。它在我上一个提示上过度索引,只使用了 30 年期收益率作为输入参数。正如预期的那样,这样一个简单(单变量)的模型误差范围很大。

不过经过一点劝说,它扩展了线性模型以包含这些特征(与价格销售比高度相关)

现在我们的结果看起来好多了。

当然,我们在使用 ps_prev(上一季度的价格销售比)时有点作弊。凭借这些信息、增长率和货币条件的数据,代码解释器的模型可以在大约 2 个单位内预测价格/销售比。不算差,但也不算好。
公平地说,这并不是代码解释器的错。数据集太小且波动太大,无法进行准确的 P/S 预测。我尝试过的更复杂模型效果也不比这好,但代码解释器得出这个结论的速度快得多。
最终思考
OpenAI 的新代码解释器工具确实很棒。最重要的是它快速。你可以探索数据,进行简单分析,并利用自动生成的代码进行更精细的分析。总的来说,我强烈推荐这个工具来加快你的工作进度。
然而,使用 AI 工具时特别需要谨慎。它们不一定理解你的目标或你所工作的领域。它们会犯错误。在数据分析项目中,总是容易自找麻烦。代码解释器使得这一点比以往任何时候都容易。特别是初级分析师不应完全信任工具的输出,应该尝试直接与生成的代码互动。
尽管如此,经验丰富的数据分析师应能通过这些 AI 工具显著提高工作效率。随着我们摆脱繁琐的工作,也许我们会有时间解决新问题,更深入地探究我们周围的数字世界。
如何使用 OpenAI 的函数调用
原文:
towardsdatascience.com/how-to-use-openais-function-calling-e35bdac88ae7
函数调用概述及其对构建 LLM 应用的影响
·发布于Towards Data Science ·阅读时间 6 分钟·2023 年 7 月 25 日
--

图片来源:由 Midjourney 生成
结构化非结构化数据
函数调用是OpenAI的一项创新,扩展了在大型语言模型应用开发方面的可能性。
然而,我发现它仍然被一些人误解。在这篇文章中,我旨在在你泡一杯咖啡的时间内澄清函数调用的概念。
如果你有意构建 LLM 应用,将 LLM 集成到你的业务中,或仅仅是想扩展你在这方面的知识,那么这篇文章适合你。
函数调用有什么了不起的地方?
函数调用使我们能够在现有的 API 上开发自然语言接口。如果这听起来令你困惑,不用担心——随着你继续阅读,细节会变得更加清晰。
那么,自然语言 API 是什么样的?我认为最好通过示意图来展示。这是一个示例应用,使用函数调用来帮助用户查找航班。

图片来源:函数调用的示意图
如果没有函数调用,要实现这种类型的应用,你需要提示用户通过菜单、选择框等输入航班信息。
函数调用还使用户可以通过语音发出请求。你只需一个额外的转录服务,便可以拥有一个 AI 个人助手。
希望你现在已经对函数调用及其用途有了更清晰的了解。让我们通过一些技术演练来巩固这些新知识。
单一函数的函数调用
让我们来查看一个简单的天气 API 使用案例。
最基本的用例涉及一个函数的调用。为了说明这一点,我建模了一个提供温度预报的 API —— 请见这里。
请花一点时间研究一下这个温度 API;注意它有两个参数:位置和日期。
现在,假设我们想基于这个 API 构建一个应用程序。我们可能采取的第一步是编写一个利用函数调用处理用户查询的脚本。它可能如下所示:
作者脚本:一个函数的调用示例
让我们讨论一下函数调用。我们将函数定义为字典的列表。该函数允许我们从用户查询中提取参数,然后我们可以将其用来馈送到 API 中。在我们的情况下,我们希望提取的参数是“位置”和“日期”。
我们声明函数的名称,在这种情况下是“get_temperature”,并提供它的功能描述。
接下来,我们需要描述函数的参数,这一点至关重要。我们从函数调用中收到的内容受我们如何描述的影响。请查看我如何为两个参数“位置”和“日期”概述描述和类型。
描述很重要,因为函数调用是由 OpenAI 的大型语言模型驱动的,这些模型解释描述并作出相应的回应。
接下来,我们设置我们的 OpenAI 模型。该设置类似于标准的文本生成用例,但有两个关键区别——“functions”参数和“function_call”参数。
我们将字典中定义的函数传递给“functions”参数。对于“function_call”参数,我们将其设置为“auto”,允许大型语言模型决定何时使用函数或根本不使用函数。
那么,让我们看看当我们通过这个函数调用运行查询时会发生什么。
这是我们的查询:
query = "2023 年 9 月 1 日柏林的天气预报是多少?"
这是响应:
<OpenAIObject at 0x1fd8de69ea0> JSON: {
"role": "assistant",
"content": null,
"function_call": {
"name": "get_temperature",
"arguments": "{\n \"location\": \"Berlin\",\n \"date\": \"2023-09-01\"\n}"
}
}
首先需要注意的是,函数调用返回了一个结构化的响应,即一个 JSON 对象。
其次,函数调用从我们的查询中确定了我们希望使用“get_temperature”函数,并相应地解析了该函数的参数(位置和日期)。
这很好,因为我们现在可以将这些参数发送到我们构建的天气 API 并获得有关温度的响应。
# Parse the location and date from our function call response
def parse_function(response):
parsed_output = response["function_call"]["arguments"]
parsed_output = json.loads(parsed_output)
return parsed_output
location = parse_function(response)['location']
date = parse_function(response)['date']
# Get our weather data
data = get_temperature(location, date)
这是我们从天气 API 获得的响应:
'{"location": "Berlin", "date": "2023-09-01", "temerature": 9.72, "scale": "Farenheit"}'
注意:理解函数调用并不会执行函数。这只是从自然语言输入中解析出函数的参数。
多函数调用
我们甚至可以指定多个函数进行“调用”。当我们有一个自然语言接口覆盖多个 API 时,这尤其有用。让我们通过一个例子来说明。
为了补充我们的天气 API,我创建了一个新闻 头条 API,根据主题生成模拟新闻头条。
这个主题本身将从自然语言查询中请求,我们可以通过函数调用来解析。
我们可以将其添加到现有的函数调用中。它是这样显示的:
作者脚本:带有两个函数的函数调用示例
由于我们将“function_call”参数设置为“auto”,大语言模型可以仅基于查询推断出最合适的函数进行调用。让我们看看这对两个不同的查询是如何工作的。
query_1 = "2023 年 9 月底纽约的天气预报是什么?"
查询 1 的响应:
<OpenAIObject at 0x1fd8de6b590> JSON: {
"role": "assistant",
"content": null,
"function_call": {
"name": "get_temperature",
"arguments": "{\n \"location\": \"NYC\",\n \"date\": \"2023-09-30\"\n}"
}
}
现在让我们对不同的查询运行多功能函数调用。
query_2 = "给我最新的柏林头条新闻"
查询 2 的响应:
<OpenAIObject at 0x1fd8de67e00> JSON: {
"role": "assistant",
"content": null,
"function_call": {
"name": "get_current_news",
"arguments": "{\n \"topic\": \"Berlin\"\n}"
}
}
在这两种情况下,函数调用根据查询正确选择了获取参数的函数。
如果我们不使用它怎么办?
此时,你可能会问自己:“这很好,但我不能通过聊天完成 API 和提示来获得相同的结果吗?我们为什么还需要函数调用?”
简单来说就是结构。虽然聊天完成 API 经过训练以响应用户查询,你可以提示它执行“函数调用”,但你得到的输出可能没有结构,导致解析参数变得困难。
举个例子:
作者脚本:用于解析参数的聊天完成 API
在这里,我们完全跳过了函数调用,仅仅要求聊天完成 API 用一些提示来响应我们的参数。
以及响应:
'The news topic required is "Berlin".'
虽然答案是正确的,但由于其无结构性,作为参数解析起来更困难。
还有一致性的问题。你可以通过提示尝试强制某些结构,但响应可能并不总是按你期望的方式构造。
函数调用保证每次都有结构化的响应,使其成为解析参数以供下游函数使用的更可靠方法。
结论
函数调用为开发大量基于 LLM 的新应用程序开辟了道路。我们现在可以从无结构数据中一致地生成结构化输出。
如果你想深入了解如何构建你的第一个 LLM 应用程序,请阅读这个:
如何构建 LLM 应用程序 [## 如何构建 LLM 应用程序
使用 Langchain 和 OpenAI 构建以 LLM 为中心的应用程序
感谢阅读。
完整的笔记本可以通过我的 GitHub 仓库获得。YouTube 上也有一个直播视频教程 YouTube。
如果你希望提升人工智能技能,可以加入我的课程的等待名单,在课程中我将指导你开发大语言模型驱动的应用程序。
如果你寻求为你的企业实现人工智能转型,今天就预约一次发现电话会议吧。
在 Brainqub3,我们开发定制的人工智能软件。我们使用最新的人工智能技术创建 qub3s,即先进的人工智能大脑,以…
www.brainqub3.com](https://www.brainqub3.com/?source=post_page-----e35bdac88ae7--------------------------------)
欲了解更多关于人工智能、数据科学和大语言模型的见解,你可以订阅YouTube频道。
如何使用 Streamlit 的 st.write 函数来改进您的 Streamlit 仪表板
Streamlit 函数的瑞士军刀
·发表在Towards Data Science ·8 分钟阅读·2023 年 1 月 17 日
--

当开始使用Streamlit时,构建仪表板或应用程序时,您将首先遇到的一个函数是st.write()。Streamlit 文档描述了这个函数为“Streamlit 命令的瑞士军刀”。
这是一个非常多才多艺的函数,允许您显示文本、表情符号、Markdown 等等。
在本文中,我们将探讨st.write()函数如何用于改进您的 Streamlit 仪表板的方法。
函数签名 st.write(*args, unsafe_allow_html=False, **kwargs) 参数要打印的一个或多个对象…
docs.streamlit.io](https://docs.streamlit.io/library/api-reference/write-magic/st.write?source=post_page-----1586333eb24d--------------------------------)
本文是我在 Streamlit 上创建的一系列文章的一部分。您可以在下面的链接中探索它们。
-
开始使用基于 Web 的 Streamlit 应用程序
-
创建真正的多页面 Streamlit 应用程序-新方法(2022)
-
Streamlit 颜色选择器:在 Streamlit 仪表板上轻松更改图表颜色的简便方法
使用 st.write 显示简单和格式化的文本
st.write() 命令的第一个和最明显的用途是显示文本。如果我们使用下面的简单示例,我们将在我们的应用程序中显示该文本。
st.write('Here is some simple text')

使用 st.write 显示简单文本的结果。作者提供的图片。
如何格式化并为 st.write 生成的文本添加颜色
上面的文本看起来有点无聊。我们可以通过添加一些颜色来增加一些趣味。为此,我们需要利用一些 HTML 和 CSS 知识,将 unsafe_allow_html 选项设置为 True。
然后我们使用 HTML <p> 标签创建一个段落,并使用 style 参数设置颜色
st.write('<p style="color:red;">Here is some red text</p>',
unsafe_allow_html=True)

在 st.write 函数中更改字体颜色。作者提供的图片。
如果我们想要增加字体大小,我们可以添加一个 font-size
st.write('<p style="font-size:26px; color:red;">Here is some red text</p>',
unsafe_allow_html=True)

在 st.write 函数中更改字体的颜色和大小。作者提供的图片。
将文本拆分成多行
我们可以将文本显示在多行而不是单行。st.write 函数允许你传入一个用三个单引号(’’’)或三个双引号(”””)括起来的字符串。这种语法在 Python 中用于允许字符串跨越多行。
只使用三个单引号或三个双引号并不足以使我们的文本在使用 Streamlit 时跨越多行。为了做到这一点,我们必须修改字符串。
有几种不同的方法可以实现这一点。
第一种方法是使用换行符 \n,它会将下一行文本推到新的一行。
st.write("""This is an example \n
of writing text \n
on multiple lines \n
""")
这将生成以下输出:

使用 st.write 函数将文本拆分成多行。作者提供的图片。
第二个选项是在每行末尾添加两个空格。这在markdown 语言中常用于将文本移动到新行。

在字符串末尾添加两个空格允许文本显示在多行上。注意每行末尾的两个淡淡的点表示两个空格。作者提供的图片。
这将生成类似于以下的多行文本输出。请注意,行间距比前面的例子要小得多。

使用 st.write 函数将文本拆分成多行。作者提供的图片。
使用 Markdown 语法与 st.write。
Markdown 是一种流行的标记语言,允许作者在纯文本格式中简单轻松地应用格式。这是我最喜欢的写作方式之一,也被许多笔记应用程序使用,如Obsidian和Notion。
要在 st.write() 函数中使用 markdown,我们只需像这样输入:
st.write('## Markdown')
st.write('Here is some more **markdown** text. *And here is some more in italics*')
第一行创建了一个二级标题(H2),第二行创建了一些格式化文本。在文本周围使用双星号(**)使其加粗,而单个星号(*)使其斜体。
当我们刷新应用程序时,我们会得到以下输出。

使用 markdown 语法与 Streamlit 的 st.write 函数的示例。作者提供的图片。
如果你想了解更多关于如何使用 markdown 写作的知识,那就查看基本语法 Markdown 指南。
Streamlit 使用的特定类型的 markdown 是GitHub Flavoured,这是 markdown 的一个方言,具有自己的特点和细微差别。
使用 st.write 创建 LaTeX / KaTeX 格式的方程式。
LaTeX 已经存在很长时间(自 1984 年以来),是另一种用于排版文档并为打印准备的标记语言。除了排版,它还可以用于准备和格式化数学方程式。
Streamlit 使用 KaTeX 的版本来使用 LaTeX 语法渲染方程式。 KaTeX 本质上是一个用于渲染数学符号和方程式的 Javascript 库。
我们可以将方程式格式化,使其更易读,而不是将方程式作为线性文本——这样很难阅读。
要在 Streamlit 中显示 LaTeX 方程式,我们需要在方程式的开头和结尾各加上两个美元符号($$)。
然后我们可以利用 LaTeX 的强大功能来书写我们的方程式。
st.write("""Here is a simple equation using LaTeX
$$
ax² + bx + c
$$
""")
st.write(r"""And a slightly more complex equation
$$
Sw = \bigg({\frac{a \cdot Rw}{\phi^m \cdot Rt} \bigg)^\frac{1}{n}}
$$
""")

使用 Streamlit 的 st.write 函数结合 KaTeX 的示例,显示简单和更复杂的数学方程式。作者提供的图片。
如果你不确定如何在 LaTeX 中做某事,这里有一些资源可以帮助你:
Streamlit 使用的支持函数源自 KaTeX,并且可以在这里找到所有支持的函数列表。
写代码块
如果我们正在创建一个应用程序或仪表板,我们想要显示代码供用户检查或学习,我们可以使用markdown 语法创建代码块。这是通过在我们想要显示的代码之前和之后使用三个反引号来实现的。
我们还可以提供一种语言来创建一些语法高亮。
st.write("""
```python
如果我们想要显示代码:
use_back_ticks == True
```py
""")

使用 st.write 和 markdown 语法显示的代码块。作者提供的图片。
使用 st.write 显示 Pandas 数据框
Pandas 数据框是数据科学中常用的格式。它们可以用于存储、操作和创建新数据。
在构建 Streamlit 应用程序时,我们很可能会使用数据框来处理数据。st.write 函数可以通过传入数据框对象或相关方法(如 describe 方法)来显示交互式数据框。
arr_data = np.random.default_rng().uniform(0, 100, size=(5,4))
df = pd.DataFrame(arr_data, columns=list('ABCD'))
st.write('Displaying the dataframe: `df`')
st.write(df)
st.write('Using the `describe` method:')
st.write(df.describe())

Streamlit 应用程序显示了一个 pandas 数据框和 describe 方法。作者提供的图片。
结合文本和变量与 st.write
通过使用f-strings,我们可以将字符串和变量组合成单个输出。Streamlit 还进一步允许我们传入多个参数,然后将它们组合在一起。
在下面的示例中,我们使用 f-strings 来显示a和b的值,然后最后一行将文本与a+b的总和结合在一起。
a = 3
b = 3
st.write(f'a is: {a}')
st.write(f'b is: {b}')
st.write('And if we sum them together we get:', a + b )

使用 Streamlit 的 st.write 函数将文本和变量组合在一起。作者提供的图片。
使用 st.write()显示图表图。
使用图表显示数据是数据科学和数据分析的基本部分。它使我们能够以读者易于理解和做出明智决策的方式传达数据。
Streamlit 处理多个绘图库,包括matplotlib、seaborn、altair和plotly。
首先,我们需要有一个数据源,可以来自文件或现有数据框,其次,我们需要创建我们的图。在下面的示例中,我创建了一个简单的随机数据集,并选择了两列在散点图中显示。
然后将 matplotlib 图传递给 st.write 函数。
# Creating a dataframe with random values in 5 columns
arr_data = np.random.default_rng().uniform(0, 100, size=(5,5))
df = pd.DataFrame(arr_data, columns=list('ABCDE'))
# Creating a simple figure
fig, ax = plt.subplots(1,1)
ax.scatter(x=df['A'], y=df['B'])
st.write(fig)
当我们运行上面的代码时,我们得到以下显示。

使用 st.write()显示的 matplotlib 图。作者提供的图片。
结合表情符号
最后,我们可以通过使用表情符号为我们的文本增添一些趣味。这很容易,只需在表情符号名称前后加上冒号即可。
st.header('Emojis')
st.write('Emojis can be used to add a bit of character
to your text :smile: :grin: :thumbsup:')

在 Streamlit 应用程序中使用 st.write()显示表情符号。作者提供的图片。
摘要
[Streamlit st.write() function]是一个强大的工具,可以以多种不同的方式使用,从编写普通文本到显示图表和方程式。它绝对名副其实地成为了 Streamlit 库的瑞士军刀功能。
一定要查看Streamlit Magic上的文档,将您的应用程序提升到下一个水平。
感谢阅读。在您离开之前,您绝对应该订阅我的内容,并将我的文章发送到您的收件箱。 您可以在这里做到!或者,您可以 订阅我的通讯 免费获取额外的内容直接发送到您的收件箱。
其次,您可以通过注册会员来获得完整的 Medium 体验,并支持成千上万的其他作家和我。每月只需花费$5,您就可以完全访问所有精彩的 Medium 文章,以及有机会通过写作赚钱。
如果您使用 我的链接注册,您将直接支持我获得部分费用,而且不会增加您的费用。如果您这样做了,非常感谢您的支持。
如何使用 loc Pandas 方法高效地处理您的 DataFrame
PYTHON
提示如何使用 Pandas 探索和清理新数据集,附有代码示例和解释
·发表在 Towards Data Science ·9 分钟阅读·2023 年 6 月 27 日
--

Pandas 探索数据的一瞥,但是在户外 —— Jim Bread 拍摄的照片,来自 Unsplash
处理新数据集的关键部分是了解它。
弄清楚基本事项,比如数据中有哪些列,原始数据类型是什么,以及数据的描述统计,这些都对正确处理数据至关重要。
Pandas 中有许多内置方法,可以立即在笔记本中使用来探索数据。在进行数据探索的初始步骤时,您可以同时开始使数据可用于进一步分析,或者准备好用于训练机器学习模型。
在本文中,让我们使用一组大学数据来回答以下问题,以展示在同时探索和清理数据时的情况:
-
哪些大学只提供面对面上课?
-
最古老和最新成立的大学之间的年份范围是多少?
我们主要将使用 loc 方法与其他几种内置的 Pandas 方法来回答这些问题。首先,我们将快速了解 loc 方法的作用,然后逐步进行每个示例。
欢迎在笔记本中跟随!你可以从 Kaggle 免费下载数据集,该数据集可在开放数据共享公共领域奉献和许可(PDDL)v1.0 下免费使用。然后导入并运行以下内容,我们就可以开始了!
import pandas as pd
df_raw = pd.read_csv("Top-Largest-Universities.csv")
一个快速介绍 loc 方法
基本上,Pandas 中的loc方法允许您根据给定条件选择目标 DataFrame 的一部分行或列。
您可以向loc传递几种不同的输入。例如,当您想基于其索引选择 DataFrame 的一个切片时,您可以使用与在 Python 中使用列表时相同的语法:[start:stop]。但是,在这篇文章中,我们将主要关注使用带有条件语句的loc。如果您以前使用过 SQL,这类似于编写查询的WHERE部分以过滤数据。
通常,以这种方式在loc中使用将如下所示:
df.loc[df["column"] == "condition"]
这将返回数据的一个子集,其中列等于条件。
接下来,让我们继续看一些在探索性数据分析中使用loc方法的实际示例,看看您还可以做些什么。
使用 Pandas 的loc方法回答大学入学问题
哪些大学只提供面对面上课?
首先,让我们看看如何使用loc选择数据的一部分以供进一步分析。
如果数据已经干净,您可能会认为要回答这个问题,您只需在该列上进行分组,计算提供面对面上课的机构数量。在 Pandas 中,这样做将如下所示:
df.groupby("Distance / In-Person")["Institution"].count()

不幸的是,“远程/面对面”列的值并不是很干净。空格和一些机构提供远程和面对面上课,尽管记录的方式并不是标准化的。
我们可以做的第一件事也是清理这个的是将列重命名,这样它就不会有任何空格或特殊字符。
df = df.rename(columns={"Distance / In-Person": "distance_or_in_person"})
接下来,我们可以通过选择 DataFrame 中的所有列来验证更改是否发生。
df.columns

现在,至少所有列都没有空格或特殊字符。如果您愿意,您还可以进一步标准化这一点,将所有其他列更改为小写,但我们现在将跳过这一点。
早些时候,我们对目标列进行了分组操作,并计算了每个机构的值。到达相同结果的另一种方法是使用 Pandas 中的value_counts方法。这将返回一个 Series,其中包含您调用它的目标列的唯一值的计数。
df["distance_or_in_person"].value_counts()

在这种情况下,您会注意到这次我们这次没有调用“机构”列,但这是因为在我们的原始 DataFrame 中,每一行代表一个机构。
现在,为了清理这一列,使得既提供面对面又提供远程上课的机构的值被分组为一个值,我们可以利用loc列在 DataFrame 上过滤这些值,并将distance_or_in_person列的值分配给一个新值“Both”。
df.loc[
~df["distance_or_in_person"].isin(["In-Person", "Distance"]),
"distance_or_in_person"
] = "Both"
在这里,我们使用~运算符过滤当前的distance_or_in_person列,该列不等于“In-Person”或“Distance”,然后选择distance_or_in_person列。然后将其设置为“Both”,这将更新原始 DataFrame。我们可以通过再次检查 DataFrame 来验证更改:
df.head()

现在,你会看到更新后的列只包含三个可能的值,我们可以再次调用value_counts来得到我们最初问题的答案:
df["distance_or_in_person"].value_counts()

现在我们知道,根据清理后的数据,有 59 所大学只提供面对面出席。
有了这个新条件,如果你想知道哪些具体的机构提供面对面的出席,我们可以再次使用loc方法过滤 DataFrame,然后使用tolist方法将所有值获取为 Python 列表:
df.loc[df["distance_or_in_person"] == "In-Person"]["Institution"].tolist()

现在我们有一个机构列表,但是有一些特殊字符可以去掉。在 Python 中,“\xa0”代表不间断空格,这意味着我们可以使用 Pandas 中的strip方法来去掉它,该方法可以去掉字符串值两端的空格。
我们可以编辑我们最初的tolist代码,以清理最终的输出,就像这样:
df.loc[df["distance_or_in_person"] == "In-Person"]["Institution"].str.strip().tolist()

现在,我们有了一个只提供面对面出席的大学最终列表!
最古老和最新成立的大学之间的年份范围是多少?
接下来,让我们使用loc和其他几个原生 Pandas 方法来过滤 DataFrame,以回答特定的数据分析问题。
我们首先看一下成立列,看看我们要处理的是什么:
df["Founded"]

看起来我们有一列只包含年份值。由于我们想要将日期进行比较,我们可以将该列转换为datetime类型,以便进行更轻松的分析。
pd.to_datetime(df["Founded"])

然而,在该列上使用to_datetime方法会导致ParserError。
看起来有一个字符串与我们最初从成立列中看到的不匹配。我们可以使用loc方法检查行,以便在值为ParserError的成立列上过滤 DataFrame:
df.loc[df["Founded"] == "1948 and 2014"]

显然有一所大学有两个不同的成立年份。现在我们知道了行(9)的索引,还有一个使用loc方法在特定索引值上过滤 DataFrame 的示例:
df.loc[9]

看起来这是 DataFrame 中唯一一行,其中“成立”列的值不止一个年份。
根据你想要对数据做什么,你可以尝试通过选择一个年份(第一个成立日期)或者通过为这个机构创建两行数据,使得两个成立日期分别在不同的行上来清理数据。
在这种情况下,由于我们只是为了回答一个简单的问题(这个数据集中机构的成立日期范围是多少),我们可以像这样删除这一行:
df.drop(9).head(10) # removing the row

检查结果 DataFrame,你会发现索引为“9”的行,其成立日期列有多个值,已经不再存在于表中。你可以通过在删除行后重新分配 DataFrame 来使删除生效:
df = df.drop(9)
接下来,我们可以再次对成立日期列使用to_datetime方法,看看会发生什么。
pd.to_datetime(df["Founded"], errors="coerce")

实际上,在这里还会出现另一个错误,这就是为什么我包含了errors="coerce",以确保如果将字符串转换为日期时间类型时出现任何其他问题,该值将变为 null。
最后,我们可以将成立日期列的日期时间类型版本分配给一个新列。然后,要检查机构的最早成立日期,我们可以使用 Python 中的min方法:
df["founded_date"] = pd.to_datetime(df["Founded"], errors="coerce")
min(df["founded_date"])

你可以使用 min 和 max 方法来查看founded_date列的最早和最晚时间戳,以获取最古老和最新的大学之间的年份范围。
就在这里,我意识到如果我们只需要回答一个快速的问题,我们可以更快地完成所有这些工作。我们可以将列转换为整数类型,然后将最大值和最小值相减,得到范围。
df["Founded"] = df["Founded"].astype("int")
max(df["Founded"]) - min(df["Founded"])
这将输出 719。
在某些情况下,你不应该总是采取简单的方法,只是将年份列转换为整数。当你想要进行一些更复杂的分析或者当你专门处理时间序列日期时,如果你正确清理数据并将日期列转换为日期时间类型,那么会增加很多附加值。然而,如果你只需要快速进行分析,那么简单地找到解决问题的最快方法而不是“最佳”方法,可以节省你的时间和麻烦。
有许多不同的方法可以结合使用 Pandas 方法来清理和分析日期数据。loc方法是多功能的,允许您将不同的方法组合在一起,以过滤、切片和更新 DataFrame,使其适用于您想要解决的特定问题和问题。
清理数据是一个与探索数据相辅相成的迭代过程。希望这些使用loc的示例对你今后的分析有所帮助。
如果你喜欢我的内容,请考虑关注我,并使用下面的推荐链接注册成为 Medium 会员。每月只需 5 美元,你就可以无限制地访问 Medium 上的所有内容。使用我的链接注册让我能够获得一小笔佣金。如果你已经注册并关注了我,非常感谢你的支持!
[## 使用我的推荐链接加入 Medium —— Byron Dolon
作为 Medium 会员,你会员费的一部分将会给你阅读的作家,而你将获得对每个故事的完整访问权限…
更多我的文章: - 3 Efficient Ways to Filter a Pandas DataFrame Column by Substring - 5 Practical Tips for Aspiring Data Analysts - Improving Your Data Visualizations with Stacked Bar Charts in Python - C**onditional Selection and Assignment With .loc in Pandas - 5 (and a half) Lines of Code for Understanding Your Data with Pandas
如何在 Elasticsearch 中正确使用同义词功能
原文:
towardsdatascience.com/how-to-use-the-synonyms-feature-correctly-in-elasticsearch-7bdf856a94cb
学习简单但强大的同义词功能,以提高您的搜索质量
·发表在Towards Data Science ·9 分钟阅读·2023 年 1 月 7 日
--

图片由 Tumisu 在 Pixabay 上
同义词用于提高搜索质量并扩大匹配范围。例如,搜索“英格兰”的用户可能希望找到包含“英国”或“英国”等内容的文档,尽管这三个词完全不同。
Elasticsearch 中的同义词功能非常强大,如果正确实现,可以使您的搜索引擎更加健壮和强大。在本文中,我们将介绍实践中实现同义词功能的基本要点,并附上简单的代码片段。特别是,我们将介绍如何更新现有索引的同义词,这是一个相对高级的话题。
准备工作
我们将在本文中使用docker-compose.yaml文件在本地启动 Elasticsearch 服务器,并使用 Kibana 来管理索引和运行命令。
我们将在本文中使用的docker-compose.yaml文件具有以下内容,稍后我们将添加更多功能:
version: "3.9"
services:
elasticsearch:
image: elasticsearch:8.5.3
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms1g -Xmx1g
- xpack.security.enabled=false
volumes:
- type: volume
source: es_data
target: /usr/share/elasticsearch/data
ports:
- target: 9200
published: 9200
networks:
- elastic
kibana:
image: kibana:8.5.3
ports:
- target: 5601
published: 5601
depends_on:
- elasticsearch
networks:
- elastic
volumes:
es_data:
driver: local
networks:
elastic:
name: elastic
driver: bridge
下载此文件或创建一个名为docker-compose.yaml的新文件,并将上面的内容粘贴到其中。然后,您可以使用以下命令之一启动 Elasticsearch 和 Kibana:
# In the same folder where docker-compose.yaml is located (Recommended).
docker-compose up -d
# If you are in a different folder or name the YAML file differently,
# you would need to specify the path or the name, for example:
docker-compose -f ~/Downloads/docker-compose.yaml up -d
docker-compose -f docker-compose-elasticsearch.yaml up -d
使用标准的同义词标记过滤器和同义词列表
让我们首先使用标准的同义词标记过滤器和同义词列表创建一个索引。在 Kibana 中运行以下命令,我们将很快解释细节:
PUT /inventory_synonym
{
"settings": {
"index": {
"analysis": {
"analyzer": {
"index_analyzer": {
"tokenizer": "standard",
"filter": [
"lowercase",
"synonym_filter"
]
}
},
"filter": {
"synonym_filter": {
"type": "synonym",
"synonyms": [
"PS => PlayStation",
"Play Station => PlayStation"
]
}
}
}
}
},
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "index_analyzer"
}
}
}
}
关键点在这里:
-
请注意
settings的嵌套键级别。settings=>index=>analysis=>analyzer/filter都是内置关键字。但是,index_analyzer和synonym_filter分别是自定义分析器和过滤器的自定义名称。 -
我们需要创建一个
type为synonym的自定义过滤器。使用synonyms选项明确提供了一系列同义词。通常情况下,这仅用于测试,因为更新同义词列表不方便,稍后我们将看到。 -
此文章中使用了 Solr 同义词。对于此示例,使用了显式映射,这意味着
=>左侧的标记将被替换为右侧的标记。稍后我们将使用等效的同义词,这意味着提供的标记被视为等效。 -
synonym_filter被添加到名为index_analyzer的新自定义分析器的过滤器列表中。通常情况下,过滤器的顺序很重要。然而,对于同义词过滤器来说,它有点特殊,可能会让我们感到惊讶。在这个例子中,即使synonym_filter过滤器放在lowercase过滤器之后,该过滤器返回的标记也会传递给lowercase过滤器,从而也会变成小写。因此,您不需要在同义词列表或同义词文件中提供小写标记。 -
最后,在文档的映射中,为
name字段指定了自定义分析器。
要测试在索引中创建的分析器,我们可以调用_analyze端点:
GET /inventory_synonym/_analyze
{
"analyzer": "index_analyzer",
"text": "PS 3"
}
我们可以看到,“PS”的标记被替换为指定的同义词,并且是小写的:
{
"tokens": [
{
"token": "playstation",
"start_offset": 0,
"end_offset": 2,
"type": "SYNONYM",
"position": 0
},
{
"token": "3",
"start_offset": 3,
"end_offset": 4,
"type": "<NUM>",
"position": 1
}
]
}
让我们向索引中添加一些文档,并测试其在搜索中是否正常工作:
PUT /inventory_synonym/_doc/1
{
"name": "PS 3"
}
PUT /inventory_synonym/_doc/2
{
"name": "PlayStation 4"
}
PUT /inventory_synonym/_doc/3
{
"name": "Play Station 5"
}
我们可以使用match关键字进行简单的搜索:
GET /inventory_synonym/_search
{
"query": {
"match": {
"name": "PS"
}
}
}
如果一切正常,应该返回所有三个文档,并且得分相同。
索引时间与搜索时间的同义词
如您所见,在上面的示例中,只创建了一个分析器,并且它用于索引和搜索。
在索引步骤中将同义词应用于所有文档是不鼓励的,因为它有一些主要的缺点:
-
在不重新索引所有内容的情况下,无法更新同义词列表,这在实践中非常低效。
-
搜索得分也会受到同义词标记的影响。
-
索引过程变得更耗时,索引会变得更大。对于小数据集来说可以忽略不计,但对于大数据集来说非常重要。
因此,最好只在搜索步骤中应用同义词,这样可以克服所有三个缺点。为此,我们需要为搜索创建一个新的分析器。
使用search_analyzer并应用搜索时同义词
在 Kibana 中运行以下命令以创建一个带有搜索时同义词的新索引:
PUT /inventory_synonym_graph
{
"settings": {
"index": {
"analysis": {
"analyzer": {
"index_analyzer": {
"tokenizer": "standard",
"filter": [
"lowercase"
]
},
"search_analyzer": {
"tokenizer": "standard",
"filter": [
"lowercase",
"synonym_filter"
]
}
},
"filter": {
"synonym_filter": {
"type": "synonym_graph",
"synonyms": [
"PS => PlayStation",
"Play Station => PlayStation"
]
}
}
}
}
},
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "index_analyzer",
"search_analyzer": "search_analyzer"
}
}
}
}
关键要点:
-
类型现在更改为
synonym_graph,它是一个更复杂的同义词过滤器,专为仅用作搜索分析器的一部分而设计。它可以更好地处理多词同义词,并建议在搜索时使用。但是,您仍然可以继续使用原始的synonym类型,在本文中它的行为相同。 -
将同义词过滤器从索引时间分析器中移除,并添加到搜索时间分析器中。
-
search_analyzer明确指定了 name 字段。如果没有指定,将使用相同的分析器(index_analyzer)用于索引和搜索。
分析器应该返回与之前相同的标记。然而,当您使用这些命令索引了这三个文档并再次执行相同的搜索时,结果将会有所不同:
GET /inventory_synonym_graph/_search
{
"query": {
"match": {
"name": "PS"
}
}
}
这次只返回了“PlayStation 4”。甚至“PS 3”都没有返回!
原因是同义词过滤器仅在搜索时应用。搜索查询“ps”被替换为同义词标记“playstation”。然而,索引中的文档没有经过同义词过滤器的筛选,因此“PS”只是被分词为“ps”,而没有被替换为“playstation”。对于“Play Station”也是一样。结果只有“PlayStation 4”能匹配。
要使其像前面的示例一样正常工作,我们需要将同义词规则从显式映射更改为等效的同义词。让我们将同义词过滤器更新如下:
......
"filter": {
"synonym_filter": {
"type": "synonym_graph",
"synonyms": [
"PS, PlayStation, Play Station"
]
}
}
......
要更改现有索引的同义词,我们可以重新创建索引并重新索引所有文档,这是愚蠢和低效的。
更好的方法是更新索引的设置。但是,在更新设置之前,我们需要关闭索引,然后重新打开它以便可以访问:
POST /inventory_synonym_graph/_close
PUT inventory_synonym_graph/_settings
{
"settings": {
"index.analysis.filter.synonym_filter.synonyms": [
"PS, PlayStation, Play Station"
]
}
}
POST /inventory_synonym_graph/_open
注意更新索引设置的特殊语法。
运行上述命令后,让我们使用_analyzer端点测试search_analyzer并查看生成的标记:
GET /inventory_synonym_graph/_analyze
{
"analyzer": "search_analyzer",
"text": "PS 3"
}
这是结果:
{
"tokens": [
{
"token": "playstation",
"start_offset": 0,
"end_offset": 2,
"type": "SYNONYM",
"position": 0,
"positionLength": 2
},
{
"token": "play",
"start_offset": 0,
"end_offset": 2,
"type": "SYNONYM",
"position": 0
},
{
"token": "ps",
"start_offset": 0,
"end_offset": 2,
"type": "<ALPHANUM>",
"position": 0,
"positionLength": 2
},
{
"token": "station",
"start_offset": 0,
"end_offset": 2,
"type": "SYNONYM",
"position": 1
},
{
"token": "3",
"start_offset": 3,
"end_offset": 4,
"type": "<NUM>",
"position": 2
}
]
}
它显示了“PS”搜索查询被替换并扩展为三个同义词的标记(由[expand](https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-synonym-graph-tokenfilter.html)选项控制)。它还证明了如果等效的同义词在索引时间应用,结果索引的大小可能会显著增加。
然后我们再次执行相同的搜索:
GET /inventory_synonym_graph/_search
{
"query": {
"match": {
"name": "PS"
}
}
}
所有三个文档都将被返回。
使用同义词文件
在上面的示例中,我们一直在创建索引时直接指定同义词列表。然而,当你有大量的同义词时,将所有同义词都添加到索引中将会很麻烦。更好的方法是将它们存储在一个文件中,并动态加载到索引中。使用同义词文件有很多好处,包括:
-
方便维护大量的同义词。
-
可以被不同的索引使用。
-
可以在不关闭索引的情况下动态重新加载。
要开始,我们需要先将同义词放入一个文件中。每一行都是一个同义词规则,与上面演示的相同。更多详细信息可以在官方文档中找到。
我们将创建的同义词文件称为synonyms.txt,但它可以被称为任何名称。它的内容如下:
# This is a comment! The file is named synonyms.txt.
PS, PlayStation, Play Station
然后我们需要将同义词文件绑定到 Docker 容器。更新[docker-compose.yaml](https://gist.github.com/lynnkwong/c84bbe872dad32e2222bd2c242e5d2ab)如下:
......
volumes:
- type: volume
source: es_data
target: /usr/share/elasticsearch/data
- type: bind
source: ./synonyms.txt
target: /usr/share/elasticsearch/config/synonyms.txt
......
请注意,同义词文件加载到容器中的config文件夹中。您可以使用以下两个命令之一进入容器并检查它:
# User docker
docker exec -it synonyms-elasticsearch-1 bash
# User docker-compose
docker-compose exec elasticsearch bash
现在我们需要停止并重新启动服务使更改生效。请注意,仅重新启动服务不起作用。
docker-compose stop elasticsearch
docker-compose up -d elasticsearch
然后我们可以使用同义词文件创建一个新的索引:
PUT /inventory_synonym_graph_file
{
"settings": {
"index": {
"analysis": {
"analyzer": {
"index_analyzer": {
"tokenizer": "standard",
"filter": [
"lowercase"
]
},
"search_analyzer": {
"tokenizer": "standard",
"filter": [
"lowercase",
"synonym_filter"
]
}
},
"filter": {
"synonym_filter": {
"type": "synonym_graph",
"synonyms_path": "synonyms.txt",
"updateable": true
}
}
}
}
},
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "index_analyzer",
"search_analyzer": "search_analyzer"
}
}
}
}
关键点:
-
对于
synonyms_path,它是相对于 Elasticsearch 服务器中的config文件夹的同义词文件的路径。 -
添加了一个新的
updateable字段,指定相应的过滤器是否可更新。我们将很快看到如何重新加载搜索分析器而不关闭和打开索引。
这个新索引inventory_synonym_graph_file的行为应该与之前的inventory_synonym_graph相同。
现在让我们向同义词文件添加更多同义词,然后它的内容将如下所示:
# This is a comment! The file is named synonyms.txt.
PS, Play Station, PlayStation
JS => JavaScript
TS => TypeScript
Py => Python
当同义词已经添加,我们可以关闭并打开索引使其生效。然而,由于我们将同义词过滤器标记为可更新,我们可以重新加载搜索分析器,使更改立即生效,而无需关闭索引,因此没有停机时间。
要重新加载索引的搜索分析器,我们需要调用_reload_search_analyzers端点:
POST /inventory_synonym_graph_file/_reload_search_analyzers
现在当我们分析“JS”字符串时,我们将看到返回“javascript”标记:
GET /inventory_synonym_graph_file/_analyze
{
"analyzer": "search_analyzer",
"text": "JS"
}
// You will see the "javascript" token returned.
这里需要注意两件重要的事情:
-
如果对于同义词过滤器设置了
updateable为true,那么相应的分析器只能用作search_analyzer,不能用于索引,即使类型是synonym。 -
updateable选项只能在使用synonym_path选项与同义词文件一起使用时使用,而不能在直接使用synonyms选项提供同义词时使用。
恭喜你到达这里!我们已经涵盖了在 Elasticsearch 中使用同义词功能的所有要点。
我们已经介绍了如何在索引时间和搜索时间分析步骤中使用同义词。此外,还介绍了如何直接提供同义词列表以及如何通过文件提供它们。最后但并非最不重要的是,介绍了更新现有索引的同义词列表的不同方法。建议重新加载索引的搜索分析器,因为它不会给服务带来停机时间。
相关文章:
如何使用 t 检验及其非参数对应物
原文:
towardsdatascience.com/how-to-use-the-t-test-and-its-non-parametric-counterpart-e88be9ec5102
你真的了解这个你很可能在高中学过的检验吗?
·发表在Towards Data Science·5 分钟阅读·2023 年 9 月 17 日
--

照片由Isabella Fischer提供,来自Unsplash
作为一名数据科学家,你最好了解一些统计学。毕竟,它是该领域的基石之一。
这是一系列文章中的第一篇,将试图简明扼要地概述不同统计检验及其应使用的情况。信息将取得平衡——不会太长和技术性以至于难以理解,但也不会太短以至于无用。
在这篇第一篇文章中,我将讨论其中一个更广为人知的统计检验:t 检验。即使你不太了解它是什么,你很可能听过这个术语。阅读完本文后,你应该能够理解这个检验的工作原理以及何时应该使用它。
如果你对统计检验设计通常不太熟悉,我强烈建议你查看本系列的前导文章,开始运行统计检验所需的基本概念入门**。
那么,让我们开始吧。
什么是 t 检验?
本质上,t 检验用于比较两个不同数据样本的数据。它考虑了两个因素:样本均值之间的差异和样本数据的变异性。从逻辑上讲,这是有道理的——简单地比较均值不足以确定你的样本实际上是不同的(均值在类似数据集中可能会有所不同,或者在非常不同的数据集中可能会相似)。
t 检验计算了一种称为t 分数的统计量,它同时考虑了上述两个因素。可以使用稍微繁琐的公式手动计算它,但几乎任何统计软件都会自动计算它。如果你手动计算统计量,可以将其与预先存在的表格中的临界 t 值进行比较(大于 t 值对应于 p 值小于你的测试所需的水平,表示样本之间存在统计学上的显著差异)。再次强调,统计软件通常会告诉你计算得到的 t 值是否具有统计学意义。
进行这个测试的过程因软件而异,但大多数软件都很容易学习。这里省略了这些计算的细节,因为本文的目的是教你什么是 t 检验以及在什么情况下应该使用它。
与此相符,让我们考虑一个假设性实验,你可能会在其中使用 t 检验。想象一下,你是一家正在开发篮球鞋的体育装备公司的用户研究员。公司的工程师设计了两种鞋子,并想知道设计对球员的跳跃能力是否有显著差异。
为了测试这一点,你需要获得 40 名篮球运动员(通过随机抽样),给其中 20 名运动员一双鞋(A 组),给另外 20 名运动员另一双鞋(B 组)。两组的分配也是随机的。然后,你测量每个运动员穿着鞋时的垂直高度,并将数据记录在两组中,根据鞋的类型分开。
要确定两组垂直测量值之间是否存在统计学上的显著差异,可以使用 t 检验。
我听说有两种类型的 t 检验,这是真的吗?
啊,你比我更先一步。严格来说,我们刚刚讨论的测试被称为独立样本 t 检验。为什么?因为被比较的两个均值来自不同的样本。这种类型的 t 检验通常在我们的实验使用被试间设计时使用。由于每个条件的参与者不同,样本被认为是独立的。
这个实验也可以使用被试内设计进行。在这种情况下,我们只收集 20 名参与者,并在他们穿着每双鞋时测量他们的垂直高度。这样做的一个优点是消除了可能导致错误结果的个体差异(如果尽管随机抽样和分配,一组球员恰好能够跳得更高,怎么办?)。
无论如何,这种结构使得独立样本 t 检验不适用;相反,我们将使用所谓的配对样本 t 检验。这个名称来自于每个参与者都出现在两个数据集中的事实,从某种意义上说,它们是成对的。
当你选择要运行的实验的统计检验时,一定要考虑到这一点。
但是,那些讨厌的假设呢?
实际上,t 检验的两个版本都有一系列必须满足的假设,以使检验有效:
-
样本必须是随机选择的。
-
样本数据必须是区间或比率类型(即数据应该是定量的)。
-
从样本中抽取样本的总体应该近似正态分布。这不能确定,但通常可以根据样本分布做出合理的猜测。
-
两个样本的标准差应该相似(样本的波动不应该有太大差异)。
那么,如果一个或多个要求没有满足,你应该怎么办?幸运的是,这正是非参数替代方案发挥作用的地方。根据你打算使用的 t 检验,你现在应该转变思路:
-
独立样本 t 检验的非参数版本被称为曼-惠特尼 U 检验。
-
成对样本 t 检验的非参数版本被称为威尔科克森符号秩检验。
尽管它们可能对公众来说不太知名,但你应该能够在你选择的统计软件中相对轻松地找到这些检验;重要的是你知道何时使用它们。
快速回顾和最终思考
让我们快速浏览一些问题,可以帮助你确定何时应该使用 t 检验。
你的实验是否有一个因素/自变量(t 检验不适用于具有多个因素的实验)?
是否有两种治疗/条件正在测试因素?
假设对上述问题的回答是肯定的,你的实验是使用组间设计还是组内设计?你的实验是否符合使用参数 t 检验的条件?
-
组间和参数:独立样本 t 检验
-
组间和非参数:曼-惠特尼 U 检验
-
组内和参数:成对样本 t 检验
-
组内和非参数:威尔科克森符号秩检验
就是这样!在你的下一个实验中按照这个流程进行,你将很快掌握 t 检验。
祝你测试愉快!
想要精通 Python 吗? 在这里独家免费获取我简单易懂的指南。想要在 Medium 上阅读无限的故事吗?使用我的推荐链接注册吧!
[## 使用我的推荐链接加入 Medium - Murtaza Ali
作为 Medium 会员,你会员费的一部分将会支付给你阅读的作家,而你将获得对每个故事的完全访问权限…
medium.com](https://medium.com/@murtaza5152-ali/membership?source=post_page-----e88be9ec5102--------------------------------)
参考文献
Vaughan, L. (2001). 信息专业人员的统计方法. Medford, NJ: ASIS&T Press, pp. 139–155.
如何验证 OpenAI GPT 模型的文本摘要性能
生成性 AI 使用与测试研究的第一部分
·
关注 发表在 Towards Data Science ·9 分钟阅读·2023 年 4 月 4 日
--
照片由 Patrick Tomasso 提供,发布在 Unsplash
无论你从事什么职业或年龄多大,你都可能在 LinkedIn、YouTube 或新闻中听说过 OpenAI 的生成性预训练变换器(GPT)技术。这些强大的人工智能模型/聊天机器人似乎能够处理任何任务,从创作诗歌到解决 leetcode 问题,再到连贯地总结长篇文章。

OpenAI 的 GPT Playground 的截图,总结了 Jupiter Notes,由作者拍摄
GPT 模型在不断扩展的 NLP 行业中似乎有无尽的应用前景。但是随着模型规模的不断增加,构建大型语言模型(LLM)的团队必须 了解每个模型的性能和行为。由于 AI,如 GPT,是一个日益增长的伦理问题,开发者应确保他们的模型是公平、负责任和可解释的。然而,在许多不同的环境中对人工通用智能进行适当的测试既繁琐又昂贵,并且耗时。
从 Kolena 的机器学习工程师的角度来看,本文提供了使用 GPT 模型的全面指南,并比较了它们在 抽象文本摘要 任务上的表现。通过这项积极研究的 NLP 问题,我们将能够 审查模型行为、性能差异、投资回报率 等等。
在本文结束时,你将了解到 GPT-3.5 的 Turbo 模型在 4.8 倍的成本和 4.5 倍的平均推理时间下,比 GPT-3 的 Ada 模型在抽象文本摘要任务上具有 22%的更高 BERT-F1 得分和 15%的更低失败率。
有效使用 GPT
假设你想在自然语言处理应用中使用 GPT 来快速解决问题,比如翻译文本或解释代码。你从哪里开始?幸运的是,使用 GPT 处理任何独特任务时,只有三个主要步骤:
在选择模型之前,我们必须先考虑几个问题:每个模型的表现如何?哪个模型提供了最佳的投资回报率?哪个模型整体表现最佳?哪个模型在你的数据上表现最好?
为了在选择 GPT 模型时缩小范围,我们使用了 CNN-DailyMail 文本摘要数据集来基准测试和比较五种 GPT 模型:Ada、Babbage、Curie、Davinci 和 Turbo 的性能。数据集的测试部分包含 11,490 篇新闻文章及其相应摘要。
第二步,我们使用一致的提示生成每个模型的新摘要,格式如下:
“像记者一样专业地总结这篇新闻文章,字数约为 {word_count_limit} 到 {word_count_limit+50} 字:\n {full_text}”
实际操作中,需要一些实验来优化提示,从而获得主观上最优的结果。通过使用相同的提示,我们可以准确比较模型行为,减少模型差异的一个变量。

在这篇文章中,我们重点关注第一步,即选择合适的模型。
验证 GPT 模型性能
让我们了解一下感兴趣的 GPT 模型,它们来自 GPT-3 和 GPT-3.5 系列。每个模型都有一个令牌限制,定义了输入和输出的最大尺寸。因此,例如,如果你的 Turbo 模型的提示包含 2,000 个令牌,你将收到的最大输出是 2,096 个令牌。对于英文文本,75 个单词通常会分成大约 100 个令牌。

我们目前在 GPT-4 的等待名单上,所以将来会包含这些模型。现在,GPT-4 和 GPT-3.5 之间的主要区别对基本任务并不显著,但 GPT-4 提供了比 Davinci 更大的令牌限制,但价格要高得多。
抽象文本摘要的性能指标
众所周知,指标帮助我们衡量性能。下面的表格突出了我们用来评估模型文本摘要性能的标准和自定义指标:

我们使用 SacreBLEU 计算 BLEU 分数,并用微软的 deberta-xlarge-mnli 模型计算 BERT 分数。

ROUGE 和 BLEU 通过对比地面真实数据和推断结果中的单词匹配来测量相似度,而 BERT 分数则考虑语义相似度。值越高,相似度越接近:

标准指标结果
在每个模型上生成新的摘要(推断)后,我们可以通过任何类型的指标将模型性能与地面真实数据进行比较。让我们查看摘要比较和指标图,忽略 Babbage 以提高可读性。
ROUGE_L 和 BLEU
在以下示例中,原始的 350 字新闻文章有以下总结:
来自 Suncorp 银行的一份新报告发现,过去一年澳大利亚人花费了 200 亿澳元用于科技。男性在计算机、数字配件、移动应用程序和流媒体服务上的花费是女性的两倍。有孩子的家庭在数字化方面的支出比单身人士、没有孩子的情侣和空巢老人多出 50%。三分之一的家庭没有为科技预算,或者大大低估了他们的支出。
我们使用 Davinci 和 Ada 得到了以下 ROUGE_L、BLEU 和生成的摘要:

你会发现,通过阅读生成的摘要,Davinci 能很好地总结较大文本的内容。然而,Ada 并没有提供相同质量的摘要,ROUGE_L 和 BLEU 较低的值反映了输出质量的差距。

ROUGE_L 的分布
当我们检查每个模型的 ROUGE_L 和 BLEU 分布时,我们看到 Ada 的指标值较低,而 Turbo 的指标值最高。Davinci 在这些指标方面略逊于 Turbo。随着 GPT 模型的规模增加,我们看到ROUGE 和 BLEU 分数也有普遍的增加。这些指标的值越大,生成文本中与真实摘要匹配的单词数量就越多。此外,这些较大的模型生成的摘要信息更丰富,语法问题更少。

BLEU 的分布
BERT_F1
对于 BERT 分数,趋势一致:较大的模型在匹配关键字和语义意义方面表现更好。这在较大模型的分布向右移动的趋势中可以明显看出,这表明 F1 分数更高。

BERT_F1 的分布

BERT_F1 与 word_count
从上图中,我们看到较大的模型在文本大小增加时能更好地保持性能。较大的模型在各种文本长度范围内表现一致,而较小的模型随着文本变长,性能波动较大。
自定义指标结果
让我们检查一下自定义指标,看看是否有理由不使用 Turbo 或 Davinci。

API 请求成本分布
从模型的成本分布中,我们了解到Davinci 的成本远高于其他任何模型。尽管 Davinci 和 Turbo 的性能相似,Davinci 的成本大约是 Turbo 的十倍。

inf_to_gt_word_count 的分布
在上图中,对于相同真实内容生成的单词数量差异非常大。Turbo 和 Davinci 始终提供长度为真实摘要两倍的总结,而其他模型非常不一致。具体来说,某些较小模型生成的摘要要短得多,而有些则超过了真实摘要长度的四倍!请记住,我们对每个模型使用了相同的请求和每篇文章的字数目标,但某些模型遵守了这一限制,而其他模型则完全忽视了它。

总结长度的差异对用户来说是一个问题,因为这种不平衡表明模型可能存在问题或性能不佳。在上面的例子中,Curie 至少重复了“两次过去的慈善事业,特别是他在圣犹大儿童研究医院的工作”。与 Turbo 相比,Curie 的总结冗余且不够优化,同时成本在十分之一美分的范围内相同。在这个小差异中,我们应该注意到,使用 Curie 生成这个特定总结的成本是 Turbo 的两倍,因为输出中的 token 数量极高。
结果分析
在 Kolena 上运行模型评估一个小时后,我们可以概述和总结每个模型的性能和特征,如下所示。

我们现在了解到,模型规模越大:
-
提供的总结与生成的总结在语义上越相似
-
计算成本越高,除了 Turbo 之外
-
空总结的数量越少
-
生成总结的速度越慢
-
模型表现越一致
最终,Turbo 模型是 GPT-3/3.5 系列中表现最佳的模型,提供了最一致的文本相似性评分,同时也非常具有成本效益。
进一步研究的注意事项
有趣的是,给定一个需要总结的文本,一些模型根本拒绝生成输出,即使提示在 token 限制内。Turbo 没有在任何文章上失败,这是一项了不起的成就。然而,这可能是因为 Turbo 在标记敏感内容方面不如其他模型那么响应,或者在考虑这些因素时关注较少。Ada 可能表现较差,但我们应该询问 OpenAI 是否因为道德考虑或技术限制而拒绝生成总结。下面是Ada 未能提供任何总结的前十六篇新闻文章的样本,其中 Turbo 生成了不错的总结。看起来 Ada 在生成涉及敏感内容的总结时更不宽容:

Ada 失败而 Turbo 表现良好的文章 — 来自 Kolena
数据集中的真实总结在内容或长度上不一定理想。然而,我们假设真实总结对于直接的性能计算是理想的,因此模型评估指标可能会表明一个出色的模型实际上表现不佳,即使它生成了完全有效且详细的总结。也许一些生成的总结甚至比其真实总结更好,如下所示:

结论
NLP 的世界正迅速发展,随着像 GPT 这样的 LLM 的引入。这些模型变得越来越大、复杂且昂贵,因此开发者和用户都必须了解它们在特定使用场景下的预期性能水平。
不同的模型可能更适合你的业务需求,这取决于你的问题、期望和可用资源。在为你的 NLP 任务选择单一 GPT 模型时,有很多因素需要考虑。在 LLM 快速发展的时代,希望本文中的发现能为你提供有关 OpenAI 模型差异的新视角。
敬请关注未来的更多帖子,我们可能会涉及提示工程、GPT-4 性能,或不同内容类型对模型行为的影响等话题!
正如本文早前承诺的,我们提供了参考代码以及本文所有例子的五个模型的摘要,都在这个页面上。你可以在OpenAI 的文档中了解更多关于 OpenAI API 或模型的信息。
所有图表的图片均为Kolena的截图,除非另有说明。注意,相似的图表可以在如 mathplotlib 等常见框架中手动生成。
如何编写自定义函数以在 R 中生成多个图表
轻松介绍自定义函数的编写
·
关注 发表在 Towards Data Science · 9 分钟阅读 · 2023 年 4 月 11 日
--
一双手在笔记本电脑上打字,屏幕上显示代码的视觉图像。图片由作者提供
我总是觉得在 R 中编写函数很有压力,因为我习惯了使用 tidyverse 提供的现成解决方案。当我开始用 Python 编程时,我发现自己越来越多地编写自定义函数,以将我喜欢的 dplyr 函数从 R 转移到 Python 中。
学习如何在 Python 中编写函数使我在 R 编程方面也变得更好。它帮助我自动化工作并确保结果可复现。我最常用自定义函数的场景是生成多个 R 图表。
编写自定义函数以生成 R 中的图表的理由:
1. 自动化报告: 有时你需要为不同的变量构建相同类型的视觉报告(例如柱状图)。例如,我曾在大流行期间为洛杉矶市长 Eric Garcetti 工作。我们的数据团队负责为他的每日 Covid 新闻发布会制作报告,涵盖了每日病例、死亡、住院、检测和疫苗接种率。我们对这些变量使用了相同的图表格式——柱状图。为了避免每次为不同变量重复图表代码,我们通过编写自定义函数循环处理变量并生成图表,从而自动化了报告过程。这样,我们每天只需提取数据并运行代码脚本以生成新图表。
2. 构建仪表盘的跳板: 一旦你自动化了报告过程,将报告转化为仪表盘以获得互动体验是一个自然的进展。R 具有一个很棒的仪表盘库,Shiny,可以轻松地用 R 语言直接构建 Web 应用程序。当你已经有一个用于生成图表的自定义函数时,可以轻松地在仪表盘代码中使用相同的函数,以便用户可以选择感兴趣的变量。
3. 为 Plotly** 创建 DIY facet wrap:** 我最喜欢 [ggplot2](https://ggplot2.tidyverse.org/) 的功能之一是使用 facet_wrap,它可以在一个视图中生成多个子图。这在 ggplot2 中只需一行代码。不幸的是,目前 Plotly 中还没有类似的功能,所以我不得不使用自定义函数重新创建它。
我承认我们现在可以使用 ggplotly() 创建一个带有 facet_wrap 的 ggplot2 图表的互动版本。但我个人发现使用 ggplotly() 性能较差,因此选择直接在 Plotly 中自定义构建所有内容。
以下是创建自定义函数的逻辑分解:
1. 首先创建一个可视化
2. 了解你要用来创建多个图表的变量
3. 将图表代码转换为函数
4. 循环遍历你的唯一值以生成多个图表
让我们使用可爱的 Palmer Penguins 数据集。该数据集包含三种独特的企鹅种类—— Chinstrap、Gentoo、Adelie:

Artwork by @allison_horst
以下是如何加载数据
# Load libraries
library(palmerpenguins)
library(tidyverse)
# Load in data
data(package = 'palmerpenguins')
# Write penguins to a `df` variable.
# I'm doing this simply because it's easier to type `df` than `penguins` each time.
df <- penguins
1. 首先创建一个可视化
让我们为 Adelie 物种创建一个柱状图,以查看它们每年的中位体重。
# Create a summary table to calculate the median body mass by species and year
summary <- df %>%
group_by(species, year) %>%
summarise(median_body_mass = median(body_mass_g, na.rm =T))
# Create a Plotly bar chart for the median bass of the Adelie penguins
plot_ly(
data= {summary %>% filter(species == "Adelie")},
x = ~year,
y = ~median_body_mass,
color = ~year,
type = "bar",
showlegend = FALSE) %>%
layout(
yaxis = list(title = 'Median Body Mass (g)'),
xaxis = list(title = 'Year',tickvals = list(2007, 2008, 2009)),
title = "Median Body Mass for Adelie Penguins") %>%
hide_colorbar() %>%
suppressWarnings()

一个条形图显示了 Adelie 企鹅在 2007 年、2008 年和 2009 年的中位体重。
2. 理解你想使用哪个变量来创建多个图表
即:你的facet_wrap变量是什么?
这是我们summary表格的视图。我们希望为每个物种创建相同的条形图。在这个例子中,我们感兴趣的变量是species变量。

我们的总结表格的视图,展示了每种企鹅物种——Adelie、Chinstrap 和 Gentoo——在 2007 年、2008 年和 2009 年的中位体重。
3. 将绘图代码转换为函数
确定绘图代码中需要概括的组件。现在,我们将用一个概括的变量替换任何Adelie物种名称的实例:

描述我们的 Plotly 代码,展示了我们希望概括的变量。在这个例子中,我们希望用一个概括的变量替换任何“Adelie”物种名称的实例,以便为每个新物种创建图表。
将绘图代码转换为函数。这个函数接收一个变量species_name,它将以字符串形式输入。请看这里,我们用变量species_name代替了名称Adelie:
plot_fx <- function(species_name){
plot_ly(
data= {summary %>% filter(species == species_name)},
x = ~year,
y = ~median_body_mass,
color = ~year,
type = "bar",
showlegend = FALSE) %>%
layout(
yaxis = list(title = 'Median Body Mass (g)'),
xaxis = list(title = 'Year',tickvals = list(2007, 2008, 2009)),
title = paste("Median Body Mass for", species_name, "Penguins")) %>%
hide_colorbar() %>%
suppressWarnings()
}
这是如何运行函数以生成新图表的示例。让我们为物种Chinstrap制作相同的条形图:
# Run function for species name "Chinstrap"
plot_fx("Chinstrap")

一个条形图显示了 Chinstrap 企鹅在 2007 年、2008 年和 2009 年的中位体重。这是通过我们在帖子中创建的自定义函数生成的。
4. 循环遍历你的唯一值以生成多个图表
从这里开始,你需要一个包含所有唯一物种的列表,以便循环遍历你的函数。我们通过unique(summary$species)来获取这个列表。
从创建一个空列表开始,用于存储所有你的图表
# Create an empty list for all your plots
plot_list = list()
循环遍历唯一的物种变量,为每个物种生成一个图表。然后,将其添加到plot_list中。
# Run the plotting function for all the species
for (i in unique(summary$species)){
plot_list[[i]] = plot_fx(i)
}
# Now you have a list of three plots - one for each species.
# You can see the plots by changing the value within the square brackes from 1 to 3
plot_list[[1]]
现在,使用 Plotly 中的subplot函数将所有图表可视化到一个网格中:
# Plot all three visuals in one grid
subplot(plot_list, nrows = 3, shareX = TRUE, shareY = FALSE)

三个条形图显示了 Adelie、Chinstrap 和 Gentoo 企鹅在 2007 年、2008 年和 2009 年的中位体重。这是通过循环遍历数据集中每个唯一物种,并使用我们的自定义绘图函数生成的。
我们做到了!
我知道这比使用facet_wrap函数在ggplot2中要多做很多工作,但理解如何创建函数有助于自动化报告和创建更动态的仪表板和视觉效果!
奖励步骤!添加注释以获取每个图表的标题
要在最后的视觉效果中为每个子图添加标题,你必须使用Plotly 中的注释。
# Create a list of annotations
# The x value is where it lies on the entire subplot grid
# The y value is where it lies on the entire subplot grid
my_annotations = list(
list(
x = 0.1,
y = 0.978,
font = list(size = 16),
text = unique(summary$species)[[1]],
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
),
list(
x = 0.1,
y = 0.615,
font = list(size = 16),
text = unique(summary$species)[[2]],
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
),
list(
x = 0.1,
y = 0.285,
font = list(size = 16),
text = unique(summary$species)[[3]],
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
))
这有点像一个混乱的试错过程,因为你必须硬编码位置。以下是如何操作的详细说明:
- 为每个子图标题创建注释列表: 注释将是一个列表的列表。每个元素是一个包含每个子图所有信息的列表。在我们的例子中,我希望每个子图显示物种名称,因此我将有一个包含 3 个元素的列表。每个元素包含以下内容:

我们的注释代码的描述,展示了‘x’、‘y’和‘text’变量所对应的内容。
-
x:这是一个介于 0 和 1 之间的值,对应于整个图形的位置,其中 0 代表左端,1 代表右端。 -
y:这是一个介于 0 和 1 之间的值,对应于整个图形的位置,其中 0 代表底部,1 代表顶部。 -
text:这是你希望显示的每个子图标题的文本。 -
xref和yref:你可以选择‘paper’,这意味着位置参考的是绘图区域左侧的标准化坐标距离,其中“0”(“1”)对应左侧(右侧)。或者,你可以选择‘domain’,这将对应于每个单独子图的域。 -
xanchor:设置文本框的水平位置锚点。这个锚点将x位置绑定到注释的“左边”、“中间”或“右边”。根据你的 x 和 y 坐标想象一下你的点的位置,以及你希望文本如何相对于该位置对齐。

Plotly 布局的 xanchor 对齐描述。
yanchor:设置文本框的垂直位置锚点。这个锚点将y位置绑定到注释的“顶部”、“中间”或“底部”。根据你的 x 和 y 坐标想象一下你的点的位置,以及你希望文本如何相对于该位置对齐。

Plotly 布局的 yanchor 对齐描述。
showarrow:Plotly 可以使用 TRUE 或 FALSE 选项绘制指向注释位置的箭头。如果你想在散点图上标记特定点,这个功能非常有用。由于我们只是向每个子图添加文本标签,因此在这个例子中箭头是不必要的。
2. 将布局选项添加到你的子图代码中: 你可以通过 layout() 函数添加布局选项。
# Run the subpot line including a layout
subplot(plot_list, nrows = 3, shareX = TRUE, shareY = FALSE) %>%
layout(annotations = my_annotations,
title = "Median Body Mass for Palmer Penguins",
xaxis = list(tickvals = list(2007, 2008, 2009)),
xaxis2 = list(tickvals = list(2007, 2008, 2009)),
xaxis3 = list(tickvals = list(2007, 2008, 2009)))
这里是一些你可以指定的选项:
-
annotations:你创建的注释列表,包括每个标签的文本和位置的所有信息。 -
title:这是整个网格的标题文本。 -
xaxis、xaxis2、xaxis3:在 Plotly 中,每个子图都有自己的 x 轴属性。xaxis指代第一个子图。在这个例子中,就是为阿德利企鹅物种的那个。剩余的 x 轴可以通过编号进行引用。这里我指定了刻度值的标签,以便我们有标准化的年份。
结论
虽然这是一个简单的例子,但我希望这能帮助你通过使用自定义函数来开启更多改进数据科学工作流的可能性!你可以将我们在这里所采取的步骤推广到总体编写自定义函数的方法中:
-
从简化的示例开始
-
将你的变量替换为一个通用变量
-
将该函数应用到其余的数据上
一旦掌握了基础,你可以扩展这一点,通过自动报告、仪表板和交互式可视化来确保工作的可重复性。拥有这个基础也有助于你在两种语言——R 和 Python——中变得更加熟练,因为你可以将某一语言中的有效方法转化到另一种语言中。在 R 和 Python 越来越可互换的世界里,这提供了不局限于特定语言的可能性!
除非另有说明,否则所有图片均由作者提供。
如何编写更好的数据科学学习笔记
一种经过验证的 6 步法,用于编写更好的数据科学学习笔记
·
关注 发表在 Towards Data Science ·8 分钟阅读·2023 年 4 月 3 日
--
图片由 Raimond Klavins 提供,来源于 Unsplash
我当了很长时间的学生。像是已经六年在高等教育中的那种长时间。
在那六年里以及各种学习领域——包括数据科学——我成为了笔记整理的专家。不仅如此,我还建立并完善了一个数据科学笔记系统,使你可以更高效、有效地自学数据科学概念。无论话题是什么,从编程到统计学再到机器学习,这个笔记系统都能帮助你深入理解数据科学主题,同时在长期内更好地保留信息。
1. 将关键概念提炼成总结和备忘单
我在法学院从朋友那里得到的最佳建议之一是为每个完成的单元创建单页总结表。这些表的目的是将一个单元的所有笔记浓缩成一个文档,仅突出绝对最重要的内容。我开始将这个概念应用于数据科学,它确实对我保持和回忆我学到的概念产生了实际的影响,尤其是那些涉及编码、数学和构建机器学习项目的复杂性。
这是一个很好的练习,可以提取出最重要的信息片段,这些信息你知道在成为数据科学家过程中会继续使用。此外,它帮助你集中注意力于真正重要的内容,同时丢弃你可能记录的任何无关紧要的内容。不仅如此,这些表格非常适合在学习或进行项目时快速参考。我喜欢将这些表格放在桌面上或贴在附近的墙上。这样,当我在进行项目时,可以快速参考我的笔记,而无需在 Google 上费劲去寻找答案。
我创建这些表格的最爱技巧是建立一个思维导图,将单元名称放在中心。从中心分支出来的主题来自该单元的学习目标。例如,要为关于导数的微积分单元创建思维导图,我会为将导数解释为变化率、将导数解释为切线斜率、区分代数函数和三角函数、使用微分估算数值和误差、应用导数解决问题以及使用隐式求导解决相关速率问题创建分支。然后,我填写每个分支的相关信息,如公式、重要提醒、关键信息表格以及其他持续使用或相关的内容。
2. 使用内联示例来关联概念
从个人经验来看,没有内联示例,你的数据科学笔记是不完整的,这些示例帮助你更好地关联、识别和理解概念。
你多少次查看笔记时,发现例如“类是指定对象可能具有的唯一属性和特征的蓝图”(见下文)却实际上无法可视化它们是什么样子?别担心,这比你想象的要普遍。
我们的笔记仅与我们应用的示例一样好,而在学习数据科学时,当涉及到编程、数学和可视化制作(仅举几例)时,我们的示例变得尤为重要。这些都是在书面笔记旁边使用内联示例能够让你理解概念的例子,使你能够直观地识别所讨论的内容,并帮助你将该概念与其他知识联系起来的例子。
我最喜欢的内联笔记方式是使用诸如 OneNote、GoodNotes 或 Notability 等笔记应用程序,这些应用程序允许你使用输入文本、手写笔记、截图、绘制图示、录音等方式创建自定义笔记。这些解决方案非常适合需要包括代码截图、数据库系统图示、数学方程式和数据可视化示例等情况。
同样重要的是,你的内联示例也是为笔记添加背景信息的绝佳位置。例如,你可能不会意识到微分在微积分中的重要性,直到你明白它们对估算数字和误差或开发方程来描述事件速率随时间变化是至关重要的。或者,你可能不会理解使用不同类型的数据可视化的重要性,直到你了解到每种可视化更适合表示某些数据形式。通过在笔记中提供背景信息,说明某些数据科学概念如何融入数据分析的大图景,你将能更好地应用这些概念,并将它们结合起来解决数据科学问题。
3. 插入图示、流程图和思维导图
人类似乎越来越趋向于视觉驱动,这就是为什么我们在学习时,当笔记中包含图示、流程图和思维导图时,我们中的许多人能够取得成功的原因。
这个简单的技巧可以帮助你创建更深入的笔记,从而更深入地理解概念。虽然在学习软件开发时我忽视了流程图的重要性,但我开始欣赏将逻辑绘制出来并在编码之前插入到笔记中的简单任务。在笔记中包含这些类型的图示可以补充我们对照片和图表的天然关注,通常会在阅读文本之前立即查看这些图示。
尽管数据科学深浸于代码中,我发现逻辑、过程或序列的视觉表现对建立对数据科学不同组件如何组合的理解是有益的——即如何将我们的问题转化为可以编码的逻辑,再扩展到机器学习系统中,修改成生产代码,然后用于产生可以翻译给非技术人员的结果。
图示非常适合学习不同代码片段如何协同工作、机器学习如何运作,或者如何讲述更好的数据故事。流程图对于编写编码和机器学习逻辑是必要的。最后,思维导图是关联行业问题、代码、数学、数据和设计的各个概念的好工具,这些概念共同构成了数据科学项目。
4. 用你自己的话重新编写概念
直接从学习材料中抄写笔记是有其意义的,比如当概念表述得非常简单,以至于你不可能写得更清楚。然而,用你自己的话解释概念(或使用你选择的语言)有助于你的学习,因为这迫使你理解概念后再进行书写。
例如,在学习面向对象编程(OOP)时,你提供的类的定义可能是这样的:
教科书定义: 类是特定类型对象的方法和变量的模板定义。
那很好,但它真的有意义吗?相反,让我们看看我如何用自己的话描述类:
作者的定义: 类是一个蓝图,指定了对象可能具有的独特属性和特征。
看到了吗?这样就更有意义了。然后,你需要创建你自己的对象定义,以便你对这些面向对象编程概念的理解更加具体。
这里的关键是在编写学习笔记时使用你自己的话(在概念未被恰当地解释的特定情况下),以帮助你巩固理解。此外,创建自己定义所花费的额外脑力会使你在复习笔记时更容易记住这些概念。这个技巧也是 Feynman 技巧的一部分,这在你的数据科学学习中可能会很有帮助。
5. 添加你自己的问题或评论
我在自学各种数学领域时获得的最佳建议是,在学习过程中写下你的想法。这意味着将从问题到出现的评论一切都直接记录下来。
例如,在解决一个微积分问题时,我会突出问题的某些部分,并在进行过程中写下我的问题或评论。这不仅使我的理解出现问题的地方非常明显,还帮助我的老师提供更好的建议来改进我的理解。
这一部分的笔记-taking 还帮助你对自己理解的内容负责。我们有时会进入一个节奏,只是将信息复制下来,而不实际检查是否理解了它。通过用评论和问题注释你的笔记,你会定期检查自己是否理解了你所阅读的一切。
这一建议同样适用于编程,你可以将评论和问题直接输入到代码中,以及任何其他你可能在做笔记的主题,例如机器学习或数据可视化。
6. 使用你的笔记进行回顾、修订和自我测试
当你自学数据科学时,这可能是最难完成的任务之一。你如何在没有考试或面试准备的情况下,定期回顾、修订和测试你的笔记?然而,这也是确保你的数据科学笔记真正有效的重要步骤之一。
通过使用你的数据科学笔记进行回顾、修订和自我测试,至关重要,这不仅能更好地记住材料(经常回顾、修订和测试的明显好处),还能识别笔记中可以更好服务于你的地方,以及在全面性或描述清晰度方面存在的不足。
随着你在数据科学概念学习的深入,回顾旧笔记并寻找是否能找到更好的解释方法是个不错的主意。这不仅能确保你正确掌握所有内容,还能利用上述所有技巧来更好地改进你的笔记以及对主题的记忆和理解。
最好的方法是定期坐下来(这可能是每月一次、每季度一次、每六个月一次或每年一次,具体取决于你学习数据科学的速度),并审阅你的笔记,认真问自己笔记在哪些方面可以更好(其背后的理念是你不断获得数据科学经验,这可以帮助你批判性地评估笔记如何可以更好地书写或解释)。记下这些实例,花些时间进行自我测试,无论是通过抽认卡、编码挑战还是在线的示例大学测试。标记测试后,再问自己笔记在哪些方面未能帮助你理解概念,或在哪些方面表现很好。从这里,你可以修改你的笔记以适应你的需求。
订阅以直接将我的故事发送到您的收件箱:故事订阅
请成为会员,通过我的推荐链接获取对 Medium 的无限访问权限(您不会额外花费费用,我会获得小额佣金):Medium 会员
通过捐款支持我的写作,以资助创作更多类似的故事:捐赠
如何在 R 中编写条件语句:四种方法
了解超越 if-else 语句的强大方法,提高你的 R 代码水平
·
关注 发布于 Towards Data Science ·8 分钟阅读·2023 年 6 月 13 日
--
照片由 Caleb Jones 提供,刊登于 Unsplash
编程中离不开条件语句。
条件语句根据真假条件的结果执行代码。它们是编码的基本部分,这在 R 中尤为重要。无论你是使用 R 进行数据分析、机器学习、软件开发还是其他用途,条件语句都有无限的应用。
不过,大多数 R 的初学者并没有意识到有很多种方式来编写这些语句。许多人学习基本的 if-else 语句后就停下了。但通常有更简洁、更高效的方式来编写条件语句。高级 R 程序员知道这些技巧的每一种及其使用时机。那么,你如何能学会这样做呢?
在本文中,我们将探讨四种不同的方式来编写 R 中的条件语句。我们还将介绍每种技术的优缺点,以及何时使用每种技术。
如何在 R 中编写 if-else 语句
在 R 中编写条件语句最直接的方法是使用 if 和 else 关键字。如果你已经了解另一种编程语言,这将是最熟悉的方式,而且通常是新 R 用户最先学习的技术。
标准的 if 语句在 R 中如下所示:
if (condition) {
# Code to execute
}
在这里,condition 是一个逻辑表达式,它返回 TRUE 或 FALSE。如果条件返回 TRUE,大括号内的任何代码都会被执行。如果返回 FALSE,大括号内的代码不会被执行,R 将继续执行脚本中的下一行代码。
为了了解实际操作,我们可以考虑以下示例。
age <- 25
if (age >= 18) {
age_group <- "adult"
}
在这里,我们有一个包含年龄的变量。if 语句接着评估 age 的值是否大于或等于 18。由于在这种情况下成立,因此变量 age_group 的值为 “adult”。
这是一种检查简单条件并在条件为真时执行某些操作的简单方法。但是如果我们希望语句在条件为假时运行一些代码呢?
If else 语句是基本 if 语句的扩展。要理解它们,我们可以在之前的例子基础上进行扩展。
if (age >= 18) {
age_group <- "adult"
} else {
age_group <- "child"
}
这段代码的工作原理与上一个示例类似,有一个例外。与条件为 FALSE 时继续执行不同,else 后面的大括号内的代码会被执行。这意味着如果 age 大于或等于 18,age_group 被赋值为 “adult”。否则,age_group 被设置为 “child”。
If-else 语句是控制 R 脚本中代码的直接方式。它们易于理解,可以扩展到处理多个条件,并且能够执行许多行的复杂代码。
不过,if-else 语句可能占用很多空间。对于像上面那样的简单表达式,还有其他方式可以完成完全相同的操作,而不需要五行代码。
实际上,可以使用一行代码编写 if-else 语句。
R 中的内联条件语句
内联条件语句是一种在单行代码中表达“if-else”逻辑的简洁方式。有几种方法可以编写它们。
内联 if else 语句
首先,可以使用 if 和 else 关键字编写一个简单的内联语句。其形式如下:
age_group <- if (age >= 18) "adult" else "child"
这个语句的工作方式与之前的示例相同。唯一的不同是现在我们将短语浓缩到一行。如果条件为 TRUE,age_group 的值会更新为 else 关键字之前的值——在这种情况下是 “adult”。如果条件为 FALSE,age_group 将被分配 else 之后的值。
这里的最大区别在于我们现在将整个条件语句的结果分配给变量 age_group。这改进了标准 if-else 示例中的重复短语,在那里我们需要写两次这种分配。
Base-R ifelse 函数
如果你愿意,可以使用 ifelse 函数。下面的代码使用该函数执行与之前示例相同的逻辑。
age_group <- ifelse(age >= 18, "adult", "child")
ifelse 函数接受三个参数。首先是条件,然后是条件为真时返回的值,以及条件为假时返回的值。
这是一种简洁、直接的编写简短条件语句的方法。它还有另一个优点;它是矢量化的。
向慢速、杂乱的循环说再见
[towardsdatascience.com
矢量化是 R 中一个重要的概念。如果一个函数是矢量化的,它会自动应用于多个值,而不仅仅是一个。要查看 ifelse 函数的示例,我们可以给变量 age 分配更多的值,然后再次运行代码。
age <- c(16, 45, 23, 82)
age_group <- ifelse(age >= 18, "adult", "child")
# Returns "child" "adult" "adult" "adult"
ifelse 函数会自动评估 age 中的所有值,返回一系列相应的输出。这使得 ifelse 成为评估大量简单条件的一种简洁方式,而不需要慢速、杂乱的循环。
R 中的条件索引
尽管 ifelse 可以轻松地评估许多输入,但还有其他方法可以实现这一点。
索引 允许 R 程序员访问包含多个值的数据结构的特定部分。例如,如果我们想获取上一个示例中向量 age 的第三个元素,我们可以在方括号内用 3 对 age 进行索引:
age <- c(16, 45, 23, 82)
age
# Returns 16, 45, 23, 82
age[3]
# Returns 23
使用数字来索引具有特定位置的值是最常见的,如上面的代码所示。但是,许多初学者 R 程序员不知道你也可以在索引时使用逻辑条件。这开启了各种可能性。
让我们创建一些示例数据来说明这些选项。这些数据包含关于用户的信息,如年龄,与之前的示例类似。但是,与存储在向量中的不同,每个用户的信息是按行存储在 tibble 中的。这是一种你在处理专业环境中的用户数据时可能会遇到的数据结构,因此了解如何应用条件逻辑是有用的。
set.seed(123)
user_data <- tibble(
user_id = 1:10,
age = floor(runif(10, min = 13, max = 35)),
region = sample(c("UK", "USA", "EU"), 10, replace = TRUE)
)

上述代码创建的数据。
Tibbles 和数据框是由向量组成的,这意味着我们可以用相同的方式对其进行索引。这使我们能够做各种事情。
根据条件提取列中的值
这里有段代码提取了 user_id 列中用户年龄低于 18 岁的所有值。
user_data$user_id[user_data$age < 18]
# Returns 6
根据条件替换列中的值
这是如何将区域列中的所有“UK”行重新编码为“EU”。
user_data$region[user_data$region == "UK"] <- "EU"

上述代码的结果是,“UK”区域值已被替换为“EU”。
使用条件索引过滤数据集
我们甚至可以使用条件子集来过滤整个数据集。这是一种过滤所有“USA”区域行的方法。请注意,在这里的逻辑条件后,我们添加了一个逗号,以告诉 R 我们按行索引。如果我们想按列过滤,可以在逗号后添加一个条件。
user_data[user_data$region == "USA",]

上述代码的结果。
这些只是 R 中条件索引的一些应用。如果你需要对数据进行快速的条件操作,这种方法很可能有一个一行解决方案。
Tidyverse case_when 函数
case_when 函数来自 tidyverse 包家族。这是另一种在多个值集上应用条件语句的方法,在处理数据集时再次显示出其重要性。
我们可以使用最后一个示例中的数据来展示如何使用 case_when 根据条件语句创建新列。
user_data %>%
mutate(drinking_age = case_when(region == "USA" & age >= 21 ~ TRUE,
region == "EU" & age >= 18 ~ TRUE,
.default = FALSE))

上述代码的结果是,包含新列“drinking_age”。
这段代码根据用户的年龄和地点确定我们网站用户是否被法律允许饮酒。这里,每个条件都在 case_when 语句中的不同一行。如果条件为真,我们可以在波浪符号 (~) 后返回一个值——在这个例子中是 TRUE。如果没有满足任何条件,返回的值由 .default 设置指定。所有值都存储在一个新的列 drinking_age 中。
如果这看起来有点陌生,这里是等效的 if else 语句:
if (region == "USA" & age >= 21) {
drinking_age <- TRUE
} else if (region == "EU" & age >= 18) {
drinking_age <- TRUE
} else {
drinking_age <- FALSE
}
与上述代码相比,很容易看出 case_when 提供了另一种实现条件语句的方式,这种方式比 if-else 语句更简洁,同时同样强大。它现在是我在基于复杂逻辑或多个条件创建新列时的首选。对于 tidyverse 用户来说,这是一个必须采用的功能。
何时使用不同类型的条件语句
我所介绍的方法只有在你将它们应用到自己的代码中时才会真正展现出光彩。只有通过尝试新的方法并使之变得熟练,你才能充分发挥它们的好处,并实现 R 编程的流畅性。
那么,什么时候应该使用每种类型的条件语句呢?
与编程中的任何方法选择一样,没有绝对的答案。不过,这里有一些我用来帮助我选择不同写法的粗略指南。
-
如果我在解决一个需要在特定条件下执行大量复杂的多行代码的问题时,我通常会选择 if-else 语句。在这些情况下,使用其他方法往往会变得混乱且难以维护。
-
对于条件更简单且返回的代码块较短的问题,我喜欢使用内联的 if-else 语句。没有必要让一个简单的解决方案变得比需要的更长!
-
如果在处理数据集或基于条件创建列时,我使用
case_when。它与我使用的其他 tidyverse 函数配合良好,且易于调试和维护。 -
如果我在处理数据集并且不想加载额外的包,我会使用条件索引。这不需要任何额外的依赖项,且通常运行得很快。
我的建议?尝试一下每种技术,看看哪种适合你。至少,你可能会学到一种新的改进代码的方法。
所以,如果你喜欢这篇文章,为什么不分享一下你最喜欢的条件语句呢?否则…… 无论如何,感谢你看到最后!
如何为 ChatGPT(GPT-4)和其他语言模型编写专家提示
与 LLMs 一起进行提示工程的适合初学者的指南
·发表在Towards Data Science ·阅读 63 分钟·2023 年 11 月 1 日
--

作者通过 Midjourney 提供的图片。
提示工程是一种花哨的说法,意思是“为 AI 模型编写更好的指令,直到它完全按照你的要求执行”。
编写提示有点像骑自行车。你不需要在机械物理学上有博士学位才能学会保持平衡。一点理论可以帮助,但最重要的部分是反复试验。
把这个指南看作是为骑 AI 自行车做准备的“一点理论”。你会找到一系列技术,包括解释、示例和模板,你可以在你喜欢的模型上进行测试。
本指南侧重于 ChatGPT(GPT-4),但下面分享的每一种技术都适用于其他大型语言模型(LLMs),如 Claude 和 LLaMA。
目录
What's in this guide?
💡 Why should you care about Prompt Engineering?
💡 Why prompt is engineering harder than you think?
💡 You don't need prompt ideas, you need problems
💡 Watch out for AI hallucinations
🟢 The Basics of Prompting
🟢 Specify the context (also called "Priming")
🟢 Specify the desired format
🟢 Use <placeholders>
∘ How to use placeholders as parameters
∘ How to use placeholders as instructions
🟢 Specify the style/tone
🟢 Specify the length of the desired response
🟢 Specify the target audience
🟢 Many-Examples Prompting
🟢 Temperature Control
🟢 Zero-Shot Prompting (no examples)
🟢 Few-Shot Prompting (several high-quality examples)
🟢 Zero-Shot/Few-Shot - The simple version
💡 In-context Learning vs. Chat History
🟡 Chain of Thought Prompting
∘ Zero-Shot Chain of Thought
∘ Few-Shot Chain of Thought
🟢 Role Prompting
🟢 Knowledge Generation Prompting
∘ Knowledge Generation Prompting and ChatGPT Plugins
🟠 Knowledge Integration Prompting*
∘ Knowledge Integration* and Microsoft Edge
🟠 Directional Stimulus (DS) Prompting
🟡 Recursive Criticism and Improvement (RCI) Prompting
🟡 Self-Refinement Prompting
🟡 Reverse Prompt Engineering
🟡 Prompt Revision
🟡 Program Simulation Prompting
🟠 All-In-One (AIO) Prompting*
🟢 Template for All-In-One (AIO) Prompting
🟢 More examples of All-In-One Prompting*
∘ Dolores, the Email Muse
∘ Robert Ford, the Coding Master
∘ Lee Sizemore, the Outlandish Chef
∘ Maeve the Mastermind
💡 Iterate until you have to revert
💡 With great power…
Conclusion
References (in no particular order)
Contact section
免责声明:
尽管本指南偏向于 ChatGPT,但我并没有个人利益来推广这个产品。我没有得到 OpenAI、微软或其他任何人的赞助。
这个指南包括什么内容?
本指南包括七个“好好知道”部分和超过 25 种提示工程技术。每个部分都以一个图标开头,表示其类型,如下所示:
-
💡 “好好知道”部分。
-
🟢 适合初学者的技术。
-
🟡 中级技术。
-
🟠 高级技术。
“好好知道”部分提供了在编写提示时要记住的具体要点。适合初学者的技术可以立即使用。中级和高级技术需要准备和一点耐心。
指南中讨论的所有技术都来源于 AI 研究论文和第一手经验。引用和直接链接可在指南末尾找到。有两种技术我重新命名,以便记忆。它们用星号标记,就像这样*。
每个提示的格式如下:
**[Title of the prompt]**
**Prompt content:** Content of the prompt that sometimes includes <placeholders>.
<placeholders> are a tool you use to make better prompt templates.
(Comments that are not part of the prompt are written between parenthesis like this)
## (The two hashtags separate the same prompt into two or more sections)
--- (The three dashes separate examples inside the same prompt)
*** (The three stars separate two different prompts or two different outputs)
*** ChatGPT's output**: (When it's relevant, you'll see the response to ChatGPT written like this)
这里有一个例子:
**[Examples of a simple prompts]**
**Prompt example #1:** Act as an independant analyst who specializes in <field_1> and <field_2>.
Search the internet for the pros and cons of soy milk.
Use 5 bullet points for pros and 5 bullet points for cons.
Browse at least 6 different sources and cite each one of them in your analysis.
##
<field_1>: Food industry
<field_2>: Nutrition
***
**Prompt example #2:** Explain the five love languages as if you're talking to a 16-year-old.
##
**Desired format:**
Introduction.
- Love language #1: explanation and example.
- Love language #2: explanation and example.
- Love language #3: explanation and example.
- Love language #4: explanation and example.
- Love language #5: explanation and example.
Conclusion.
💡 为什么你应该关心提示工程?
生成式 AI 继续进入我们每天使用的每个数字工具中,如网络浏览器、社交媒体应用甚至 PowerPoint 演示文稿。语言模型越好,我们就会越多地使用它们。
提示工程是我们用来与 AI 模型交互以完成越来越多任务的语言。
把近期未来想象成一个外国国家。你知道你将在那里生活几年,但你不会说当地的语言。每次你购买食品、预订旅行或谈判交易时,你都必须雇佣人来替你说话。
听起来效率低下且不好玩,不是吗?
现在想象一下,在你搬到这个外国国家之前,你可以学习当地的语言。与其雇佣会说 AI 语言的人,你学会写提示。选择合适的词语,AI 将给你想要的东西——无论是电子邮件、代码还是月度报告。
但是等等,你可能会说。提示工程不是注定要消失吗?
可能是的,因为目标是使人类更容易使用 AI 系统。也许在未来,混乱的语音提示就足以创造你想象的任何东西。我们甚至可能开发出能够实时读取你的思维的 AI。
在这些情况下,提示工程成为一种临时技能。一旦 AI 能够根据不完整的句子和脑电信号猜测人类意图,它将变得过时。
你可以等待这种情况发生,或者你可以提前开始。
💡 为什么提示工程比你想象的更难?
简短的答案是“一种轻松的错觉”。
由于我们使用自然语言编写提示,我们不认为它是一项需要练习的复杂技能。你所要做的就是用简单的英语写出指令,对吗?
嗯,不完全是这样。
是的,当你与人类交谈时,你会以与提示 AI 相同的方式提示他们。人类和 AI 都使用内部模型来回应你的提示。只是人类和 AI 系统对现实有不同的模型。
人类模型依赖于认知能力、过去的经验、理论知识和实时感官数据。而语言模型则仅依赖于语言模式。
你可以说人类软件和 AI 软件运行在不同的操作系统上。
所以当你与语言模型交互时,就好像你在与一个外星人交互。当然,这个外星人会回应你的自然语言,但它的行为方式与人类不同,这意味着你需要调整你的提示。
与人类相比,AI 系统需要更多细节、精确的解释、清晰的指示和一些重复。你希望每个写出的词都是有意义的,就像你在编写代码一样。
“英语作为一种编程语言”的类比在考虑一个至关重要的细节之前是完美的。与代码不同,每个提示都会得到回应,无论其质量如何。即使包含拼写错误和不清晰句子的提示也会产生某种东西。相比之下,不正确的代码不会产生任何东西(除了令人沮丧的错误消息)。
这种总是产生结果的特性使得轻松的错觉更加强烈。混乱的提示也会产生结果,那么为什么要费心改进它们呢?
一般的 AI 用户不觉得有必要提高他们的提示工程技能,所以他们错误地认为这是一件容易的事情。
在某种程度上,提示工程就像跳舞。你在 TikTok 上看了一个专业舞者演示了一些嘻哈舞步的教程,你会觉得“哦,这其实很容易!”
注意这一举动看起来多么容易。
但是一旦你尝试重现那些“容易的步骤”,你会意识到你的身体有自己的物理规律。突然间,一边移动身体,一边挥舞着手臂似乎是不可能的。
就像跳舞一样,提示工程比看起来更难,但每写一个提示都会变得更容易。
💡 你不需要提示的想法,你需要问题
最好的提示来自好的想法,而好的想法源于有趣的问题。以下是一个简单的框架,你可以用它来找到值得解决的问题:
-
列出你在计算机上进行的所有任务(工作或其他)。
-
分离需要文本分析和/或撰写的任务。
-
描述你希望从每个任务中实现的结果。
有用问题的例子: “每个星期一,我向客户提交上周运行的每个广告的绩效报告。我需要评论每个关键绩效指标(KPI),以便非专业人士更容易理解数据。”
提示如何帮助: 撰写每个提交的 KPI 的提示。要求你的 AI 模型使用讲故事的技巧和有趣的类比,使报告更轻松易懂。
💡 小心 AI 幻觉
无论你选择哪种模型,记住聊天机器人经常会产生幻觉,写出事实错误的信息。这种行为源自 LLMs 在巨大的数据集上训练,这些数据集充满了逻辑错误和胡言乱语。
基于人类反馈的微调和强化学习(RLHF)有助于 LLMs 减少不准确性和偏见,但仍然存在漏洞。
我在其他文章中已经对幻觉问题进行了一些尝试。你可以在下面找到链接。现在,这里是一个预览:
你可能认为聊天机器人默认会说实话,有时候他们会产生幻觉。但更准确地说,聊天机器人写出纯粹的胡说八道,有时候与现实相匹配。
通过“胡说八道”,我不是指我们用来描述无意义的俚语,而是哈里·弗兰克福提出的一个哲学定义。
哈里·弗兰克福特将胡说八道描述为与现实没有关系的信息。当你撒谎时,你扭曲了现实。当你说真话时,你描述了你对现实的表达。但是当你胡说八道时,你编造了一些与现实无关的东西。
“正是这种与真相关联的缺乏连接—— 对事物真实情况的漠不关心 ——我认为是胡说八道的本质。”
这指向了胡说八道的一个类似而基本的方面: 尽管它是在不关心真相的情况下产生的,但它不一定是错误的。”
— 《胡说八道》哈里·弗兰克福特著 [Emphasis mine].
语言模型是高度可信的胡说八道者,通常能够接近真相,因此你不能完全信任它们。在处理非虚构内容时,你必须对它们的输出进行事实核查。
ChatGPT 发布后不久,我脑海中开始倒计时。就像当你看到一道闪电划过时...
nabilalouani.substack.com](https://nabilalouani.substack.com/p/artificial-disinformation-can-chatbots?source=post_page-----23133dc85550--------------------------------) [## 聊天 GPT 的炒作证明没有人真正理解人工智能
大型语言模型比你邻居的猫更笨
nabilalouani.substack.com](https://nabilalouani.substack.com/p/chatgpt-hype-is-proof-nobody-really?source=post_page-----23133dc85550--------------------------------)
🟢 提示的基础知识
每个提示都是你想要解决的问题和你的语言模型生成的内容之间的桥梁。你的桥的形状取决于你想要解决的问题,但基本结构保持不变。
想象一下这个结构就像是六根柱子:
-
要具体明确。
-
使用占位符<like_this>构建灵活的模板。(在专门的部分中详细介绍)。
-
优先考虑要做什么而不是不要做什么。
-
指定所需输出的格式。(在专门的部分中详细介绍)。
-
使用双井号(##)来分隔提示的不同部分。提示可以包括说明、示例和所需格式。
-
修改你的提示以去除废话。
这是一个例子:
[The Basics of Prompting]
**Bad prompt:** Summarize this text please. [Paste text here].
***
**Better prompt:** I will give you a report titled <title_of_the_report> as input. Please access the report through the following link <URL_of_the_report> using the online browsing feature. Summarize the report in less than <summary_wordcount> and add <number_of_quotes> from the authors. Make sure to pick precise quotes and list them as bullet points.
<title_of_the_report> = Walking for good health.
<URL_of_the_report> = https://www.betterhealth.vic.gov.au/health/healthyliving/walking-for-good-health
<summary_wordcount> = 250
<number_of_quotes> = 3
##
Desired format:
Title: <title_of_the_report>
Link: <URL_of_the_report>
Summary of the report based on the previous instructions.
- Quote #1
- Quote #2
- Quote #3
- etc.
🟢 指定上下文(也称为“启动”)
对于你写的每个问题,你的大型语言模型可以生成成千上万个不同的答案。当你提供上下文时,你帮助你的 LLM 缩小了可能结果的范围。
假设你想要一个即将到来的一周中不无聊的饮食计划。添加你的饮食限制和个人偏好可以更有可能获得每餐相关的建议。
有多种方式可以在提示中引入上下文。这就像是为任务做心理准备,因此被称为“启动”。
**[Specify the context]
Example #1:** I invited a Jewish/Muslim/Buddhist friend over for a week. Please provide a complete meal plan for 7 days and consider my friend's diet restrictions.
***
**Example #2:** I work as a developer in a tech startup and I helped build two apps that allow users to collaborate on shared documents. Kindly provide me with 10 ways I can highlight my achievements as a developer. Use a bullet-point format.
***
**Example #3:** I'm from India/Kenya/Egypt, and English is my third language. I have a C2 level on paper and I want to improve my pronunciation. Can you please suggest 5 ways I can enhance my spoken English? Be specific and concise.
🟢 指定所需格式
这个很简单。你只需要在提示中添加一句描述所需格式的句子。
这是一个你可以参考的列表:
-
简要说明;
-
文章和博客文章;
-
论文和研究论文;
-
短篇小说和创意写作作品;
-
诗歌和歌词;
-
通讯和新闻稿;
-
社交媒体帖子和标题;
-
广告和营销文案;
-
电子邮件模板和商务往来;
-
产品描述和评论;
-
教程和指南;
-
常见问题解答(FAQ);
-
记录和采访;
-
报告和备忘录;
-
电影剧本和戏剧或播客脚本;
-
演讲和展示;
-
摘要和概述;
-
技术文档和手册;
-
教育材料,如教案或课程大纲;
-
观点文章和社论;
-
个人陈述、求职信和简历。
以下是如何在基本提示中引入格式的三个示例:
**[Specify the desired format]**
**Example #1:** Kindly write a template of a technical resume for a Software Engineer who wants to pursue a career in Machine Learning.
***
**Example #2:** I'm a One Piece fan. Please help me write a script for an episode titled The Demon's Eye Finally Opens.
***
**Example #3:** Please summarize the following document in the form of a corporate memo.
注意:如果你使用非专业的语言模型生成法律合同,请确保请法律专家审核。
🟢 使用<占位符>
<像这样的>占位符可以帮助你实现两个独立的目标。
-
使用<占位符>编写灵活的提示,可以接受不同的输入。你必须在提示中指示每个占位符的内容。在这种情况下,占位符是一个参数。
-
使用空的<占位符>来说明所需格式。在这里,你不需要编写每个占位符的内容。你的 LLM 会猜测每个占位符代表什么,特别是当你使用像用户故事或求职信这样的已知框架时。在这种情况下,占位符是一条指令。
🟢 如何将占位符用作参数
**[Use placeholders as parameters]**
**Context:** Use <placeholders> to write flexible prompts.
**Prompt example #1:**
Act like an expert developer in <name_of_the_input_programming_language> and <name_of_the_output_programming_language>. I will submit a few lines of <name_of_the_input_programming_language> in the chat, and you'll rewrite it in the <name_of_the_output_programming_language>.
Make sure to use a temperature of <temperature_value>.
##
<name_of_the_input_programming_language> = Python.
<name_of_the_output_programming_language> = JavaScript.
<temperature_value> = 0.
***
**Prompt example #2:**
Act like an expert developer in <name_of_the_input_programming_language> and <name_of_the_output_programming_language>. I will submit a few lines of <name_of_the_input_programming_language> in the chat, and you'll rewrite it in the <name_of_the_output_programming_language>.
Make sure to use a temperature of <temperature_value>.
##
<name_of_the_input_programming_language> = PHP.
<name_of_the_output_programming_language> = Python.
<temperature_value> = 0.3.
🟢 如何将占位符用作指令
**[Use placeholders as instructions]**
**Context:** User Story generation for a Product Owner.
**Prompt:**
You'll act as a Product Owner for an app that provides international shipment services for factories and retailers. I will give you a description of several features, and you'll kindly format them in the User Story format indicated below.
- Modify an order within the 12 hours (fixed) that follow the submission.
- Lockscreen notifications for every step of the shipment.
- Summary of ongoing orders ranked by date, cost, country, and products.
- A history log of past orders ranked by date, cost, country, and products.
- Chatbot incon that opens a chat window inside the web page.
- "Call me" button.
##
**Desired format:**
/////// User Story #1: <name_of_user_story> ///////
As a <description_of_user>,
I want <functionality>,
So that <benefit>.
Acceptance criteria:
#1 Given <precondition>
When <action_taken>,
Then <expected_result>.
#2 Given <precondition>
When <action_taken>,
Then <expected_result>.
#3 Given <precondition>
When <action_taken>,
Then <expected_result>.
/////// End of User Story#1: <name_of_user_story> ///////
---
/////// User Story #2: <name_of_user_story> ///////
etc.
##
**Example of the desired output:**
/////// User Story #1: Online Shopping Cart ///////
As a frequent online shopper,
I want to be able to easily add items to my shopping cart,
So that I can efficiently complete my purchases.
Acceptance criteria:
#1 Given that I am on a product page,
When I click the "Add to Cart" button,
Then the selected item should be added to my shopping cart.
#2 Given that I have multiple items in my shopping cart,
When I view my shopping cart,
Then I should see a list of all items in my cart along with their prices and quantities.
#3 Given that I want to adjust the quantity of an item in my cart,
When I update the quantity of the item and click "Update Cart",
Then the quantity of the item should be updated and the total cost should reflect the change.
#4 Given that I want to remove an item from my shopping cart,
When I click the "Remove" button next to the item,
Then the item should be removed from my cart and the total cost should be adjusted accordingly
#5 Given that I want to apply a coupon code to my order,
When I enter the code during checkout,
Then the discount associated with the code should be applied to my order. notes.
/////// End of User Story#1: Online Shopping Cart ///////
---
/////// User Story #2: ..... ///////
etc.
🟢 指定风格/语气
每个聊天机器人都有一个默认的风格,由其创建者定义。例如,ChatGPT 听起来友好而细致,但你可以要求它改变语气以适应你的偏好和需求。
你甚至可以要求你的语言模型模仿一个虚构/真实人物的语气。通常,结果是 ChatGPT 试图模仿的人的夸张模仿。
以下是一些你可以选择的风格示例:
-
通用风格:正式、非正式、有说服力的、对话式的、讽刺的、戏剧性的、居高临下的、细微的、有偏见的、幽默的、乐观的、悲观的等。
-
领域特定风格:学术、法律、政治、技术、医学、新闻、科学、营销、创意、教学等。
-
模仿真实人物的风格:阿加莎·克里斯蒂、丹尼尔·卡尼曼、J.K.罗琳、詹姆斯·鲍德温、諫山創等。
以下是如何在提示中指定风格的示例:
**[Specify the style/tone]**
**Prompt example #1:** In the style of a philosophy dissertation, explain how the finite interval between 0 and 1 can encompass an infinite amount of real numbers.
***
**Prompt example #2:** In the style of a New York Times op-ed, write a 1000-word article about the importance of dialogue.
***
**Prompt example #3:** Write a mini-guide about the importance of pre-processing data in Machine Learning. Use the tone of Jessie Pinkman from Breaking Bad.
🟢 指定所需响应的长度
长度是你希望回应中包含的详细程度的代理。长度有时也是你在写特定格式(如推文、SEO 描述和标题)时必须考虑的约束。
以下是如何在提示中指定长度的三个示例:
**[Specify the length of the desired response]**
**Prompt example #1:** In less than 280 characters, please explain String Theory.
***
**Prompt example #2:** Kindly write a Linkedin post where you make a case for why technology is at its best when it's invisible.
***
**Prompt example #3:** Please write five titles about the lack of skin in the game in Pascal's wager.
🟢指定目标受众
语言模型是在包括维基百科、研究论文和 Reddit 在内的各种来源中训练的数十亿字。每个来源都有自己的受众,每个受众都以不同的方式消费信息。
当你指定目标受众时,你告诉你的模型要调整内容、例子和词汇。
考虑一个关于运动好处的提示的两个潜在受众:一般成年读者和医学专业人士。
对于第一个受众,你希望你的语言模型使用易于理解的例子和简单的解释。相比之下,第二个受众希望你唤起研究并使用专业术语。
即使主题保持不变,期望的输出也可能完全不同。这就是为什么你要在提示中指明目标受众的原因。
以下是“运动好处”示例的提示:
**[Specify the target audience]**
**Prompt that targets general adult readers:** Please explain the benefits of regular exercise in a way that is easy to understand for the general public.
***
**Prompt that targets medical professionals:** Please write a scientific article that targets medical professionals. The article discusses the physiological and psychological benefits of regular exercise. Make sure the article responds to the expectations of an audience of medical professionals.
人们在写提示时常犯的一个错误是将“风格”和“目标受众”视为相同的参数。实际上,风格决定了文本听起来的样子,而目标受众决定了使用哪些词语。
以下是如何在提示中介绍目标受众的另一组示例:
[Specify the target audience]
**Example #1:** Explain to an audience of visual artists how Generative AI will impact their field of work.
***
**Example #2:** Write a tweet that promotes an article about AI-driven disinformation. The tweet targets people interested in technology, communication, and social media.
***
**Example #3:** Outline a fundraising campaign and kindly add actionable tips. The content will be sent to a group of non-profiters to help them improve their current methods.
🟢 Many-Examples 提示
我从Simon Willison那里偷了这个技巧,他使用 Many-Examples 提示来推动语言模型超越其舒适区。当 ChatGPT 和其他模型写出回应时,它们会预测最可能的答案,但最可能的答案往往是最不创造性的。
如果你要求你的模型耗尽常见答案的储备,它将别无选择,只能探索新的可能性。
Many-Examples 提示对涉及想象力的任务特别有用。以下是一些技术可以发挥作用的实例:
-
通过检查可能的变体列表来优化代码;
-
为新产品/功能进行头脑风暴;
-
激发产品/域名/公司的创意名称;
-
重新构造句子或创建口号;
-
找一个度假的目的地;
-
通过寻找要学习的新技能来升级你的简历;
-
寻找新的爱好;
-
根据个人喜好浏览书籍/电影/歌曲列表。(Bing 在这方面比 ChatGPT 更有效);
-
探索同一主题的多元化观点;
-
为给定主题生成尽可能多的信息。在这里,你可以将 Many-Examples 提示与知识生成结合起来(稍后会详细介绍)。
以下是你可以使用的两种基本 Many-Examples 提示的公式:
**[Many-Examples prompting]**
**Prompt option #1:** Please provide a list of 50 distinct answers to the following question.
Question: <the_question>
##
<the_question>: Why does my inner voice sound like an arrogant douchebag?
***
**Prompt option #2:** Please provide a list of 20 distinct answers to the following question.
Question: <the_question>
##
<the_question>: What's the fastest way to become a certified Data Analyst?
(Let ChatGPT respond)
**Follow-up prompt #1:** Please add 5 more distinct answers.
(Let ChatGPT respond)
**Follow-up prompt #2:** Please add 5 more distinct answers.
当第一个选项失败时使用第二个选项。有时当你要求大量的例子时,ChatGPT 会抱怨。它会说类似于:“作为一个 AI 语言模型,我不知道 50 个不同的可能答案。但是,我可以为你提供一些有用的答案列表。”
从那里开始,ChatGPT 将停在 20 个可能的答案上,但你可以使用“请再添加 5 个不同的答案”来保持生成的连续性。一旦相同的答案不断出现,这意味着你已经耗尽了模型的创造力。
🟢 温度控制
温度是影响语言模型生成的响应“随机性”的参数。它通常范围从 0 到 1,但在某些情况下,你可以将温度提高到 1 以上。
-
低温(在 0.1 和 0.3 之间)产生最可能的响应。换句话说,你会得到最“保守”的输出。在生成代码时,低温特别有用,因为你会得到最稳定的输出。
-
更高的温度(在 0.7 和 0.9 之间)会导致更有创意的响应。
记住使用温度的一种方法:“冷用于代码;热用于散文。”以下是你可以在提示中介绍它的方式:
[Temperature control]
**Example #1:** At a temperature of 0.7, please explain why banana bread is called "bread" and not "cake" even though it tastes like a cake.
***
**Example #2:** Write a Python script that transposes a 10x10 matrix. Please provide two versions of the code where the first is generated at a temperature of 0 and the second at a temperature of 0.4.
***
**Example #3:** Act like an expert developer in <name_of_the_programming_language>. I will submit a few lines of code in the chat, and you'll review the code, then perform the following 7 tasks in the specified order defined below. When you write code, always use a temperature of <temperature_value>.
1\. Look for errors and explain them.
2\. Correct the errors.
3\. Optimize the code.
4\. Add comments to explain the purpose of each line.
5\. Format the code to make it easier to read.
6\. Make sure to reason step by step to be sure you arrive at the right answers.
7\. Comment on every single step you make.
##
<name_of_the_programming_language> = Python.
<temperature_value> = 0.
🟢 零样本提示(没有例子)
零样本提示是为你的 AI 模型编写指令,而不提供上下文或例子。零样本的基本格式通常包括通常称为“文本”和“期望结果”的两部分。
以下是两个零样本提示的例子:
**[Zero-shot prompting]**
**Prompt example #1:**
**Question:** Two years ago, Jotaro was three times as old as his brother was. In three years’ time, Jotaro will be twice as old as his brother. How old is Jotaro?
**Answer:**
***
**Prompt example #2:**
**Text:** My favorite part of networking events is to stand next to the buffet table and watch people struggle to impress each other.
**Tone:**
这种特定格式的零样本提示在 AI 实验室之外很少见,专家们在那里使用这种技术来测试他们的模型的能力。
零样本提示的最常见格式是你自然而然地使用的格式。你只需输入你的问题。你不需要“文本+期望输出”的格式。这是因为像 ChatGPT 和 Bard 这样用户友好的模型都经过了对话优化 —— 每一次对话都是一次零样本。
你可以说聊天机器人是零样本机器。
🟢 少样本提示(几个高质量的例子)
少样本提示也被称为上下文学习。你给你的语言模型一堆高质量的例子来改善它的“猜测”。例子的数量取决于你的模型,但你可以从三到五个输入开始。
以下是一个例子:
**[Few-shot prompting]**
**Prompt:**
**Text #1:** My favorite part of networking events is eating all the food while everybody is busy trying to impress each other.
**Tone #1:** Playful.
##
**Text #2:** She stormed out of the conference room, slamming the door behind 42 staff members who instantly turned their eyes away as if ashamed of their lack of courage.
**Tone #2:** Angry.
##
**Text #3:** Do you think they noticed the missing "export" feature in today's demo? I'm pretty sure Nabil whispered something into the client's ear. I really don't like that bald dude!
**Tone #3:** Anxious.
##
**Text #4:** Wait, what the hell is a dish washer? And why aren't there any horses attached to your chariot?
**Tone #4:**
不需要为每个例子添加编号(如#1、#2、#3),但这样做可以改善输出。你想要添加到你的例子中的另一个元素是“噪音”。
噪音是对你的语言模型给定的任务没有用的信息。在“语气”例子中,我引入了误导性的句子来混淆系统,并迫使它专注于“信号”。
如果你让任务对你的语言模型来说太明显,当面对复杂的例子时,它可能表现不佳。
🟢 零样本/少样本 — 简单版本
如果你想记住零样本和少样本中的一些东西,记住以下内容:
- 当你的语言模型无法给出你想要的响应时,向你的提示添加高质量的例子。
下面是少量提示如何帮助你改进 ChatGPT 的输出的示例。
**[Zero-shot/Few-shot - The simple version]**
**Attempt #1 (Zero-shot)**
**Text:** My favorite part of networking events is to stand next to the buffet table and watch people struggle to impress each other.
**Tone:**
*** ChatGPT's output:** "The tone of this text is casual and light-hearted. The speaker seems to be expressing a personal preference for the food at network events, and is making an observation about the behavior of the attendees in a somewhat playful manner."
(The output doesn't match my expectation because I wanted a one-word answer)
***
**Attempt #2 (Few-shot)**
**Text:** She stormed out of the conference room, slamming the door behind all 42 attendees, many of whom instantly turned their eyes away as if ashamed of their lack of courage.
**Tone:** Angry.
##
**Text:** Do you think they noticed the missing "export" feature in today's demo? I'm pretty sure Nabil whispered something into the client's ear. I really don't like that bald dude!
**Tone:** Anxious.
##
**Text:** My favorite part of network events is to stand next to the buffet table and watch people struggle to impress each other.
**Tone:**
*** ChatGPT's output:** "Amused."
💡 上下文学习 vs. 聊天历史
每种语言模型的第一个可用版本通常是万金油。它可以以一种平均水平执行各种任务。如果你想让你的模型专业化(从而提高其输出),你有两个选择。你可以使用新的特定数据对其进行重新训练,或者使用上下文学习。AI 人员通常会两者结合使用。
上下文学习是一种提示技术,可以让你引导语言模型的响应朝特定方向发展。你只需要一些示例,就像少量提示一样。
AI 专家喜欢上下文学习的原因是效率。你不需要使用大量高质量的数据来调整原始模型,只需使用非常有限数量的格式良好的示例即可。
这是普林斯顿大学发布的上下文学习摘要。
上下文学习在原始 GPT-3 论文中被推广为一种只使用少量示例就能让语言模型学习任务的方法。[1](http://ai.stanford.edu/blog/understanding-incontext/#f1)
在上下文学习期间,我们给语言模型一个提示,其中包含一系列展示任务的输入-输出对。在提示的末尾,我们附加一个测试输入,并允许语言模型仅通过对提示进行条件化并预测下一个标记来进行预测。
要正确回答下面的两个提示,模型需要阅读训练示例以找出输入分布(财经或一般新闻)、输出分布(积极/消极或主题)、输入-输出映射(情感或主题分类)和格式。

上下文学习的例子。来源
你可以从上下文学习中得到许多应用,比如生成代码、自动化电子表格和许多其他面向文本的任务。
然而,ChatGPT 又是另一回事。OpenAI 牺牲了 ChatGPT 使用上下文学习的能力,引入了一个新功能:聊天历史。当然,你会失去模型的灵活性,但你会得到一个用户友好的界面,可以进行长时间的对话。
你可以认为聊天历史是上下文学习的一种变体,因为 ChatGPT 的响应会根据你使用的聊天历史标签的内容而演变。例如,如果你将一系列食谱输入到 ChatGPT 标签中,它将能够对你的输入执行特定任务。 这涉及摘要、延续和编辑。
这为什么重要?
根据你的需求和未来的发现,你可能需要选择以下两个选项中的一个:
-
使用上下文学习来微调像 GPT-4、OpenLLaMa 或 Falcon 这样的“原始”模型。换句话说,你可以创建一个定制的聊天机器人,但这个过程可能会很繁琐。
-
利用“记忆”和“长对话”来使用聊天历史。这样更容易定制你的输出,但随着时间的推移,质量可能会下降。
🟡 思维链提示
思维链(CoT)提示意味着在最终响应之前告诉你的语言模型逐步推理。就好像你要求你的模型大声思考一样。
假设我让你计算 4x3。你可以立即在脑海中计算这个操作,并说:“12”。但如果我要求你使用“思维链”,你会将你的推理分成四个步骤。
-
4x3 = 4+4+4
-
4+4+4 = (4+4) + 4
-
(4+4) + 4 = 8+4
-
8+4 = 12
CoT 提示通常用于解决逻辑谜题。其思想是将复杂的问题分解为更小、更易管理的问题。
语言模型预测单词序列中的下一个标记,当它们处理训练数据中丰富的常见模式时,它们的预测更准确。但有时,你需要利用不常见的模式来回答不常见的问题。
考虑以下谜题:“如果鸡蛋一打卖 0.12 美元,你可以用一美元买多少个鸡蛋?”
如果你强迫 ChatGPT 立即给出答案,它会写道:“你可以用一美元买 10 打鸡蛋”,这是一个错误的答案。

提示强制 ChatGPT 立即给出答案(没有思维链)。
现在,如果你要求 ChatGPT 逐步推理,它会给出一个不同的答案——正确的答案。

ChatGPT 的最新版本通常(但不总是)在响应提示时使用 CoT。
以下是另一个例子,说明了标准提示和思维链之间的区别。

注意第二个输入中思维链的添加(以蓝色突出显示)。来源
你可以使用思维链提示的两种方式。
🟢 零提示思维链
在你的提示末尾添加一句话,让你的语言模型应用 CoT。我发现的表现最好的句子是:
-
“….让我们逐步思考。”
-
“….请逐步进行,确保你得到正确的答案。”
以下是如何在你的提示中加入它们:
**[Zero-shot Chain of thought prompting]**
**Prompt example #1:** If eggs are $0.12 a dozen, how many eggs can you get for a dollar?
Let's think step by step.
***
**Prompt example #2:** A bat and a ball cost $1.10 in total. The bat costs $1.00 more than the ball. How much does the ball cost?
Make sure to reason step by step to be sure you arrive at the right answer.
通常,零提示思维链足以解决逻辑谜题。但如果你的语言模型失败了,你可以尝试第二种思维链提示。
🟡 少量提示思维链
就像标准的少量提示一样,你需要在提交问题之前向你的语言模型提供高质量的示例。每个示例必须包括多个推理步骤,而且你添加的逻辑步骤越多,响应就越好。
以下是一个结合了少量提示和思维链的提示的例子:
**[Example of Few-Shot Chain of Thought prompting]
Prompt:**
**Question #1:** Walter and Skyler want to compare the duration they need to shop for groceries.
Skyler takes 30 minutes to buy her weekly groceries. Walter takes five times as long as Skyler when completing the same task. How many hours does it take Walter to buy his weekly groceries?
**Answer #1:** Let's break down the problem step by step. It takes Walter "30*5 = 150" minutes to climb the hill.
It takes Walter "150/60 = 2.5" hours to shop for gorceries.
So the answer is "Walter needs 2 hours and a half to buy his weekly groceries."
##
**Question #2:** Hanks is a baseball fan. He went out to buy a new ball and a new bat. After an exchange with the shop owner, Hanks learned it would cost him 110$ to buy the ball and the bat. He also learned the baseball bat costs 100$ more than the ball.
How much does the ball cost?
**Answer #2:** Let's break down the problem step by step. We will call the price of the baseball bat "B" and the price of the ball "b".
We know that "B+b = 110".
We also know that "B = b+100".
This means "b+100+b =110" which translates into"(2*b) + 100 = 110."
We can then infer that "2*b = 10" and that "b = 5."
So the answer is "The ball costs 5$."
##
**Question #3:** Flynn has 200 letters he must divide into 4 equal groups corresponding to 4 different cities. Each group of letters must be divided into 5 equal sub-groups.
How many letters will Flynn have in each sub-group?
**Answer #3:**
*** ChatGPT-4's output:** First, let's find out how many letters are in each group corresponding to a city. Since Flynn has 200 letters and needs to divide them into 4 equal groups, we have "200/4 = 50" letters in each group.
Now, Flynn needs to divide each group of 50 letters into 5 equal sub-groups. To do this, we divide "50/5 = 10" letters in each sub-group.
So the answer is "There will be 10 letters in each sub-group."
🟢 角色提示
为你的语言模型分配一个特定的角色有助于它捕捉更多和更好的语义关系(即:逻辑和含义)。
在某种程度上,角色提示帮助你促使你的模型专注于其训练数据中的特定信息。这是一种一次性指定多个变量的快捷方式,比如上下文、风格、观点和词汇。
根据手头的任务,你可以使用不同版本的角色提示。以下是一些可能会启发你的例子。
-
模仿个人风格。
-
模仿像律师或战略规划师这样的特定专业知识。
-
模仿你在对话中的对应者,比如你的教授、前任或老板。
-
生成多个观点。
-
表现得像一个纠正拼写错误、编译你的代码或生成 Excel 公式的迷你应用程序。
[Role Prompting]
**Prompt example #1 - Mimic a specific person:** Please respond to my questions as if you were Richard Feynman.
***
**Prompt example #2 - Emulate specific expertise:** From now on, act as a business analyst.
I'll provide you with a detailed business model, and you'll kindly respond with a list of actions I can take to make improvements.
***
**Prompt example #3 - Emulate your counterpart in a given conversation:** I want you to help me practice a difficult conversation.
Pretend to be my boss/ex-husband/sister/recruiter. I'll specify the topic, and you'll respond accordingly. Please make sure to ask difficult questions.
***
**Prompt example #4 - Generate multiple points of view:** I'll ask you questions about the role of ethics in technology.
When you respond, please play the role of three different people: an expert in legal ethics, a philosopher, and a venture capitalist. For each question, give a detailed perspective from each participant.
***
**Prompt example #5 - Behave like a mini-app:** You'll act as a generator of Excel formulas.
I'll provide you with a description of a task I want to accomplish on an Excel column, and you'll kindly respond with one or many formulas that achieve the desired goal. Please add explanations to each formula you generate.
我们将在一个名为“全能提示”的特定部分中探讨角色提示的高级版本。
🟢 知识生成提示
知识生成提示的目标是让你的语言模型从其庞大的训练数据中检索特定的信息片段。想象一下这种技术就像是在写最终回答之前要求你的模型进行一些研究。
假设你想让你的模型写一篇关于在阳台上种花的博客文章。与其立即要求你的模型写博客,不如提示它生成关于园艺、花卉和空间管理的关键要点。
一旦你得到了期望的关键点,一定要履行你的事实核查职责。从那里,提示你的模型使用它生成的“知识”来撰写一篇文章。
知识生成可以提高输出质量,因为它迫使你的模型专注于特定的要点,而不是试图回答模糊的提示。
这是如何将知识生成引入到你的提示中的方法:
**[Knowledge Generation prompting]**
**Prompt Example #1:** Act like an expert horticulturist who specializes in maintaining balcony gardens. Generate key facts about growing flowers under Hamburg's weather, and managing space on your balcony. Add sources and quotes for each point.
Use the generated information to write a 2000-word blog post about how to grow flowers on your balcony for people who live in Hamburg.
***
**Prompt example #2:** Act like an expert personal trainer. List the top 20 techniques of total-body stretching and add a detailed description of how to perform each technique.
I will then pick a sublist of those techniques, and you'll kindly provide me with a bi-weekly stretching routine based on my choices.
***
**Prompt example #3:** Retrieve historical facts about the rise and fall of Carthage. Include dates, names, and current geographical locations.
From there, kindly write an essay about the relationship between Carthage and the Levant.
知识生成提示和 ChatGPT 插件
你可以使用 ChatGPT 插件来生成知识并帮助核实事实。一定要尝试尽可能多的插件,因为它们中的大多数仍然有些笨拙。
🟠 知识整合提示*
知识生成提示的主要弱点是时间表。GPT-4 的训练数据止于 2021 年 9 月,这意味着之后出现的所有内容对模型来说都是未知的。
截止日期对于处理园艺、写作和烹饪等永恒话题并不是问题,但如果你追求最新信息,你需要一个补充的技巧。
你可以使用插件、带有在线浏览功能的聊天机器人,或者知识整合提示。
你所需要做的就是将最新的数据输入到你的模型中,以帮助它跟上新闻。在某种程度上,你让你的离线模型整合新的知识。
对于 API 用户,GPT-4 可以处理高达 32,000 个标记,大约相当于 25,000 个单词。这包括用户提示和答案。对于 ChatGPT Plus 的用户,GPT-4 可以接受高达 4096 个标记的输入,大约相当于 3,000 个单词。
你可以使用这 3,000 个单词和聊天记录功能来“教导” ChatGPT-4 新的信息。模型本身不会整合数据,但你可以生成利用刚刚添加的“新信息”的提示。
下面是你可以应用知识整合提示的框架:
-
找到相关的来源,比如研究论文或已记录的文章。
-
确定手头论文中最具信息量的部分。
-
将这些部分切成 3,000 个单词的片段。
-
将这些片段输入到 ChatGPT-4 中,并要求它用简单的话语解释每个部分。你也可以要求引用和例子。
-
使用 ChatGPT-4 的输出进行新的提示。
例如:
假设你是一位专门研究大型语言模型的人工智能研究员。你目前的任务是引用与你的论文相关的材料。
你找到了一篇有趣的论文,标题为语言模型可以解决计算机任务。在浏览你上周收藏的其他 122 篇论文之前,你想先做笔记。
以下是你可以遵循的步骤,让 ChatGPT 帮助你快速记录笔记。
- 首先,确定你想要总结的段落。在这个例子中,我们将选择讨论部分,大约有 1,000 个单词。

来源。
-
将这些冗长的段落切成 3,000 个单词的片段(在这个例子中不需要)。
-
将这些文本片段输入到 ChatGPT 中。
-
要求 ChatGPT 写出你提供的文本的摘要。
-
重复这个过程,总结你想要概括的所有论文。
-
不要忘记事实核查。
-
使用你新创建的阅读笔记找出共同的线索,并对抗相反的结果。
实际上,框架看起来是这样的:
**[Knowledge Integration]**
**Context:** Comparing notes from two research papers.
[All of the prompts below must be performed in the same chat history tab/session].
**Prompt #1:** Hey ChatGPT, I'll submit a few paragraphs from a research paper. Please memorize the whole text and then summarize it in bullet points.
##
Desired format:
Title of the paper: <paper_title>
URL of the paper: <paper_URL>
/////
Summary:
- Point #1
- Point #2
- Point #3
- etc...
##
<paper_title>: Theory of Mind May Have Spontaneously Emerged in Large Language Models
<paper_URL>: https://arxiv.org/abs/2302.02083
<text_to_summarize>: (I inserted around 1,000 words here)
*** ChatGPT-4's output #1:**
Title of the paper: Theory of Mind May Have Spontaneously Emerged in Large Language Models
URL of the paper: https://arxiv.org/abs/2302.02083
/////
Summary:
- GPT-3 and later versions show increasing ability to solve Theory of Mind (ToM) tasks, with GPT-4 performing even better
- ToM-like ability likely emerged spontaneously and autonomously, as a byproduct of models' increasing language ability
- Two potential explanations: models developed ability to impute unobservable mental states to others (ToM), or they discovered unknown language patterns to solve ToM tasks without engaging ToM
- If language patterns are responsible, it may necessitate re-examination of ToM tasks and research conclusions
- Studying AI performance on ToM tasks could provide insights into human cognition and neural structures that enable similar abilities
***
**Prompt #2:** Hey ChatGPT, I'll submit a few paragraphs from a research paper. Please memorize the whole text and then summarize it in bullet points.
##
Desired format:
Title of the paper: <paper_title>
URL of the paper: <paper_URL>
/////
Summary:
- Point #1
- Point #2
- Point #3
- etc...
##
<paper_title>: Large Language Models Fail on Trivial Alterations to Theory-of-Mind Tasks
<paper_URL>: https://arxiv.org/abs/2302.08399
<text_to_summarize>: (I inserted around 1,000 words here)
*** ChatGPT-4's output #2:**
Title of the paper: Large Language Models Fail on Trivial Alterations to Theory-of-Mind Tasks
URL of the paper: https://arxiv.org/abs/2302.08399
/////
Summary:
- Large Language Models (LLMs) like GPT-3.5 may not have spontaneously developed Theory-of-Mind (ToM) as they fail on simple alterations to ToM tasks
- Encourage a skeptical stance when assessing the claim that LLMs have developed ToM, due to the human tendency to anthropomorphize agents
- ToM may be better achieved by integrating computational models that directly capture the ability with language models, rather than expecting it to emerge spontaneously
- Concern: once a systematic generator of examples or a benchmark is provided, LLMs could pass without answering the basic question of what they have learned
- The evaluation of generalization in machine-learning models is a broader issue, but ToM is a particularly relevant example
***
**Prompt #3:** Act as a research assistant specializing in <field>.
Use a scientific approach to compare the content of the previous papers based on their summaries.
Write the results in bullet points followed by precise details.
##
<field>: Deep Learning.
*** ChatGPT-4's output #3:**
1\. Theory of Mind emergence in large language models:
Paper #1 claims that Theory of Mind (ToM) may have spontaneously emerged in large language models like GPT-3 and GPT-4 as they show an increasing ability to solve ToM tasks
Paper #2 argues that large language models, such as GPT-3.5, probably have not developed ToM since they fail on simple alterations to ToM tasks
2\. Methodological concerns:
Paper #1 suggests that ToM-like ability might have emerged spontaneously and autonomously in large language models as a byproduct of their increasing language ability
Paper #2 highlights the concern that providing a systematic generator of examples or a benchmark may help models pass ToM tests without truly understanding ToM
3\. Skepticism and anthropomorphism:
Paper #2 encourages adopting a skeptical stance when assessing claims that large language models have developed ToM, as humans may be biased to anthropomorphize agents
4\. Implementation of Theory of Mind in AI:
Paper #1 raises the possibility that AI models may develop mechanisms similar to those employed by the human brain to impute mental states to others, boosting our understanding of AI and human cognition
Paper #2 suggests integrating computational models that directly capture ToM with language models, rather than expecting ToM to emerge spontaneously from additional linguistic data
5\. Evaluation of generalization in machine-learning models:
Paper #2 points out that the evaluation of generalization in machine-learning models is a concern for many researchers, with ToM being a particularly troubling example
***
**Prompt #4:** Act as a research assistant specializing in <field>.
Use a scientific approach to compare the content of the two previous papers based on their summaries.
Write the results in bullet points followed by precise details.
##
<field>: Congnitive Psychology.
*** ChatGPT-4's output #4:**
1\. Assertion of Theory of Mind (ToM) in Large Language Models (LLMs):
Paper #1 suggests that GPT-3 and later versions, including GPT-4, show an increasing ability to solve ToM tasks, hinting that ToM-like ability might have spontaneously emerged.
Paper #2 counters this by stating that LLMs like GPT-3.5 fail on simple alterations to ToM tasks, implying they may not have genuinely developed ToM.
2\. Emergence of ToM in LLMs:
Paper #1 proposes that the ToM-like ability might have arisen spontaneously and autonomously as a byproduct of the models' increasing language abilities.
Paper #2 encourages skepticism regarding this claim, highlighting the potential for humans to anthropomorphize agents and erroneously attribute ToM to them.
3\. Explanations and Mechanisms behind ToM-like Abilities in LLMs:
Paper #1 offers two potential explanations: models might have developed the ability to impute unobservable mental states to others, or they might have identified unknown language patterns that enable them to solve ToM tasks without genuinely engaging ToM.
Paper #2 doesn’t directly counter this but suggests integrating computational models that directly capture ToM abilities with language models might be a better approach than expecting spontaneous emergence.
4\. Implications for Theory of Mind Research:
Paper #1 mentions that if mere language patterns enable the ToM task-solving, then there might be a need to re-examine ToM tasks and research conclusions derived from them.
Paper #2 warns of a concern where, if a systematic generator of examples or a benchmark for ToM is provided, LLMs could pass the tasks without truly addressing the fundamental question of their understanding of ToM.
5\. Broader Relevance and Implications:
Paper #1 indicates that observing AI performance on ToM tasks might provide insights into human cognition and the neural structures enabling similar abilities.
Paper #2 places emphasis on the broader issue of evaluating generalization in machine-learning models, using ToM as a particularly relevant example.
In conclusion, while both papers discuss the capabilities of Large Language Models (LLMs) regarding Theory of Mind (ToM) tasks, they present contrasting viewpoints. Paper #1 leans more towards the possibility of LLMs developing ToM abilities, while Paper #2 is more skeptical, emphasizing caution in interpreting LLM performance on ToM tasks.
注意:如果最终输出太长,ChatGPT 将在中途停止写作。在这种情况下,你可以提示它使用“继续”这个词,它将从被切断的地方继续写作。
🟢 知识整合 和 Microsoft Edge*
在使用知识整合提示时,你可以使用 Microsoft Edge 的“聊天”功能来提高效率。
你可以打开 Edge 中的网页或 PDF,并使用聊天功能来总结内容,而不是自己浏览材料。然后,将总结注入到 ChatGPT 中,并用它进行另一个提示,就像我们在前面的例子中看到的那样。
下面是你可以使用 Microsoft Edge 总结文档的提示:
**[Prompt to generate summaries using Microsoft Edge's Chat feature]**
**Prompt:** Summarize this paper. Start the summary with the title of the paper and its URL. Then list the main ideas in bullet points. Please illustrate the main ideas with examples extracted from the paper.
🟠 方向刺激(DS)提示
DS Prompting 的原始版本包括两个步骤:
-
训练一个语言模型,从给定的文本中生成特定类型的提示。
-
使用另一个语言模型来使用第一个语言模型生成的提示来总结相同的文本。
对于本指南,我们将使用 DS Prompting 的简化版本,在这个版本中,我们使用相同的模型执行两项任务。

标准提示和方向刺激提示的比较。来源
DS 提示的最佳(也许是唯一的)用例是总结研究论文和其他类似学术文本。
假设你想要总结一篇名为《通过定向刺激提示引导大型语言模型》的论文。以下是你想要遵循的步骤:
-
确定你想要总结的段落。
-
将这些冗长的段落切成你的语言模型可以处理的块。(在 ChatGPT-4 的情况下为 3,000 个单词)。
-
将文本块输入 ChatGPT。
-
要求 ChatGPT 写一条关于涉及关键信息(如姓名、地址、日期、地点和事件)的论文的提示。
-
使用 Few-Shot 提示向 ChatGPT 展示所需的输出。
-
一旦你得到了“提示”,对其进行事实核查,并根据需要调整你的 Few-Shot 提示。
-
在新标签页中提交你的“提示”生成提示。
-
运行提示以获得你想要的文本的“提示”。
-
在同一个标签页中,提示 ChatGPT 使用“提示”来总结最后提交的文本。
这是实践中的定向刺激提示的样子:
**[Directional Stimilus Prompting]**
**Prompt #1** **[Few-shot examples of how to generate a specific hint]**
Hey ChatGPT, I'll give a text and you'll kindly summarize it into a hint. The hint is two succint sentences that involve key points only. Key points can be dates, numbers, comparaisons, names, and places.
##
Text #1: Article: (CNN) For the first time in eight years, a TV legend returned to doing what he does best. Contestants told to "come on down!" on the April 1 edition of "The Price Is Right" encountered not host Drew Carey but another familiar face in charge of the proceedings. Instead, there was Bob Barker, who hosted the TV game show for 35 years before stepping down in 2007\. Looking spry at 91, Barker handled the first price-guessing game of the show, the classic "Lucky Seven," before turning hosting duties over to Carey, who finished up. Despite being away from the show for most of the past eight years, Barker didn't seem to miss a beat.
Hint #1: Bob Barker returned to host "The Price Is Right" on Wednesday. Barker, 91, had retired as host in 2007.
##
Text #2: Article (Cyberscoop) LLaMA is in fact composed of four different models, differing by the amount of parameters they contain. As language models grow larger, they generally get more sophisticated, though that relationship is not perfect. LLaMA is available in 7, 13, 33, and 65 billion parameter versions. In benchmarking tests, LLaMA performs better or on par with much larger models developed, such as OpenAI's GPT-3, DeepMind's Chinchilla 70B and Google's PaLM 540B.
Hint #2: LLaMA has many version with 7, 13, 33, and 65 billion parameters each. It outperformed DeepMind's Chinchilla 70B and Google's PaLM 540B.
***
Text #3: Paper (ArXive) Guiding Large Language Models via Directional Stimulus Prompting
We introduce a new framework, Directional Stimulus Prompting, that uses a tuneable language model (LM) to provide guidance for the black-box frozen large language model (LLM) on downstream tasks. Unlike prior work that manually or automatically finds the optimal prompt for each task, we train a policy LM to generate discrete tokens as directional stimulus of each input, which is a hint/cue such as keywords of an article for summarization. The directional stimulus is then combined with the original input and fed into the LLM to guide its generation toward the desired target. The policy LM can be trained through 1) supervised learning from annotated data and 2) reinforcement learning from offline and online rewards to explore directional stimulus that better aligns LLMs with human preferences. This framework is flexibly applicable to various LMs and tasks. To verify its effectiveness, we apply our framework to summarization and dialogue response generation tasks. Experimental results demonstrate that it can significantly improve LLMs' performance with a small collection of training data: a T5 (780M) trained with 2,000 samples from the CNN/Daily Mail dataset improves Codex (175B)'s performance by 9.0% in ROUGE-Avg scores; only 80 dialogues can boost the combined score by 39.7%, achieving comparable or even better performance than some fully trained models on the MultiWOZ dataset. We have made our code publicly available.
Hint #3:
*** ChatGPT's output #1:**
Directional Stimulus Prompting framework guides large language models on downstream tasks. Experiments show improvements of 9.0% in ROUGE-Avg scores and 39.7% in combined scores with minimal training data.
***
**Prompt #2:** Please generate a summary of Text #3 based on Hint #3.
*** ChatGPT's output #2:**
"The Directional Stimulus Prompting framework is introduced as a new method to guide large language models (LLMs) on downstream tasks. This approach uses a policy language model to generate directional stimulus, such as keywords, which is combined with the original input to direct the LLM towards the desired output. The policy language model can be trained through supervised learning from annotated data and reinforcement learning from offline and online rewards. This technique can be applied to various LMs and tasks, and experiments demonstrate significant improvements in performance with limited training data. A T5 model trained with 2,000 samples improves Codex's performance by 9.0% in ROUGE-Avg scores, while 80 dialogues boost the combined score by 39.7% on the MultiWOZ dataset."
***
**(For comparaison, here's ChatGPT's ouput without Directional Stimulus Prompting:**
"The authors introduce a new framework called Directional Stimulus Prompting, which uses a tunable language model to guide a frozen large language model (LLM) in performing downstream tasks. Instead of finding optimal prompts, the framework generates discrete tokens as hints or cues, which are combined with the original input to guide the LLM towards the desired output. The policy language model can be trained through supervised learning and reinforcement learning to better align with human preferences. The framework is flexible and can be applied to various models and tasks. Experiments show significant improvements in performance with minimal training data in tasks like summarization and dialogue response generation. The authors have made their code publicly available.")
🟡 递归批评和改进(RCI)提示
尽管名字很花哨,递归批评和改进提示是一种简单的技术。你可以将其分解为三个步骤:
-
提示你的 LLM 根据初始提示(通常是零提示)生成输出。
-
提示你的模型识别生成的输出中的潜在问题。
-
提示你的模型根据确定的问题生成更新的输出。
RCI 可以增强你的 LLMs 在编写代码和解决逻辑谜题等情况下的推理能力。事实上,研究人员发现,在某些情况下,RCI 可以胜过思维链(CoT)。

一个 RCI 优于思维链提示的例子。来源
更好的是,当你将 RCI 与 CoT 提示(“让我们逐步思考”)结合起来时,你会获得比单独使用任何一种方法都更好的结果。
这是一个可以应用于实践 RCI 的框架:
-
用你选择的指令提示你的模型。
-
在同一个标签页中,添加一个新的提示,要求你的模型“审查之前的答案并找出其中的潜在问题。”
-
如果有错误,ChatGPT 将尝试在描述错误后找到并纠正它。在这种情况下,你可以添加以下提示:“根据你找到的问题,改进你的答案。”
-
另一个选项是将前两个提示合并为一个:“请检查你的答案并找出其中的每个潜在问题。根据你找到的问题,改进你的答案。”
这是一个 RCI 提示帮助“优化”一个简单的 Python 脚本的例子。
**[Recursive Criticism and Improvement]**
**Prompt #1:** Please write a Python script that takes a CSV file as input and splits it into chunks of 1000 lines. Make the number of lines a variable.
***** **ChatGPT's output:** (See images below)
***
**Prompt #2:** Please review your answer and find every potential problem within it. Based on the problems you found, improve your answer.
***** **ChatGPT's output:** (See images below)




🟡 自我完善提示
自我改进是 RCI 提示的一种变体。但与其要求模型找到问题不同,你要求它提供反馈。当目标是精确性和准确性时,这两种技术是相同的。
另一方面,如果你寻求创造力和主观回答,自我改进提示比 RCI 提供更多选项。
例如,当你提示你的模型生成一首诗时,你寻求的不是一个明确的公式,而是一个充满情感的人类体验的描绘。
在这种情况下,你首先提示你的模型写一首诗,然后参与一个改进循环,其中你:
- 请你的模型对自己的回答进行反馈。使用角色提示来提高反馈的质量,然后添加所需的指示。
Act like an expert in <Field_name>. Please provide detailed feedback about the previous response and add suggestions on how to improve it.
(Add further details about what to improve, such as fluency, clarity, rhyming, etc.)
<Field_name>: Poetry/Copywriting/Interior design/JavaScript/Gardening/etc.
- 请你的模型根据自己的反馈改进回答。
Please apply the previous suggestions to your initial response.
-
如有需要,请添加你自己的反馈。
-
重复前面的步骤,直到达到期望的结果。
这里有一个例子:
**[Self-Refinement prompting]**
**Prompt #1:** Act as an expert tech writer who specializes in writing fun and engaging LinkedIn posts. Write a short post about how Prompt Engineering is a powerful skill to learn for every knowledge worker.
*** ChatGPT's output #1:** (See images below)
***
**Prompt #2:** Please leverage your expertise to provide detailed feedback on the LinkedIn post above. Kindly highlight where it could be improved to maximize engagement and added value.
*** ChatGPT's output #2:** (See images below)
***
**Prompt #3:** Apply the previous suggestions to the initial post please.
*** ChatGPT's output #3:** (See images below)
***
**Prompt #4:** Please leverage your expertise to provide detailed feedback on the LinkedIn post above. Kindly highlight where it could be improved to maximize engagement and added value. Make sure your feedback makes the post ready to post.
**ChatGPT's output #4:** (See images below)
***
**Prompt #5:** Apply the previous suggestions to the last version of the post please.
**ChatGPT's output #5: (**See images below)






ChatGPT 对一系列关于撰写领英帖子的自我改进提示的输出。
我将提示的质量保持较低,以说明自我改进提示的一个关键点。根据你如何引导反馈循环,你可能会降低输出的质量。要最大限度地发挥自我改进提示的潜力,需要实践和领域专业知识。
有一个专门讲述迭代和改进的部分,标题为“迭代直到必须还原”。
🟡 逆向提示工程
逆向工程是向后构建事物的艺术,你可以在提示上使用它。
不要写一个提示来生成回答,而是从期望的回答的高质量版本开始,然后逐步回溯到提示。
另一种突出经典提示和逆向提示工程之间差异的方法是将每种技术转化为一个问题。
-
传统提示:“这是方向。你能帮我到那里吗?”
-
逆向工程提示:“这是我想要达到的目的地。你能给我展示到达那里的路线吗?”
这种方法在两种情况下非常出色。第一种情况是当你寻求灵感来写你的提示时。第二种情况是当你的目标是生成具有非常特定格式的输出,比如棋盘游戏、落地页或食谱。让我们探索一个涉及后者的例子。
**[Example of Reverse Prompt Engineering]**
Hey ChatGPT, I'll give you a recipe and you'll kindly help me write a prompt that can generate such a recipe.
**Desired output:**
"Balsamic Glazed Portobello Mushroom and Quinoa Stuffed Bell Peppers.
Ingredients:
- 2 large bell peppers (any color)
- 2 portobello mushrooms, diced
- 1 cup cooked quinoa
- 1 small red onion, diced
- 2 cloves garlic, minced
- 1/2 cup cherry tomatoes, halved
- 1/4 cup fresh basil, chopped
- 1/4 cup balsamic vinegar
- 2 tablespoons olive oil Salt and black pepper, to taste
- 1/2 cup shredded mozzarella cheese (optional, for a vegetarian touch)
- 1/4 cup grated Parmesan cheese (optional, for a vegetarian touch)
Note from the chef: In this dish, the robust flavors of the balsamic-glazed portobello mushrooms and quinoa meld seamlessly with the sweetness of bell peppers. The optional addition of mozzarella and Parmesan cheeses adds a delightful creaminess that makes this meal truly unforgettable.
Instructions:
- Preheat the oven to 375°F (190°C) and lightly grease a baking dish or tray.
- Cut the bell peppers in half lengthwise and remove the seeds and membranes. Place the pepper halves cut-side-up in the prepared baking dish.
- In a large skillet, heat 1 tablespoon of olive oil over medium heat. Add the diced red onion and cook until softened, about 3-4 minutes. Add the minced garlic and cook for an additional 1-2 minutes, until fragrant.
- Stir in the diced portobello mushrooms and cook for about 5-7 minutes, until they release their moisture and begin to brown. Add the halved cherry tomatoes and cook for another 2-3 minutes.
- In a small saucepan, combine the balsamic vinegar and remaining 1 tablespoon of olive oil. Simmer over low heat until the mixture thickens and reduces slightly, about 5 minutes.
- Add the cooked quinoa, chopped fresh basil, and balsamic glaze to the skillet with the mushroom mixture. Stir well to combine and season with salt and black pepper to taste.
- Carefully spoon the quinoa and mushroom mixture into the bell pepper halves, dividing it evenly among them. Top each stuffed pepper with shredded mozzarella and grated Parmesan cheese, if desired.
- Bake the stuffed peppers in the preheated oven for 25-30 minutes, or until the peppers are tender and the cheese is melted and bubbly.
- Remove the stuffed peppers from the oven and let them cool for a few minutes before serving. Garnish with additional fresh basil, if desired. Enjoy your exquisite Balsamic Glazed Portobello Mushroom and Quinoa Stuffed Bell Peppers!"
**Prompt:**
🟡 提示修订
这种技术可能与逆向提示工程相似,但有一个微小的区别。你不是要求你的模型从头开始生成提示,而是通过反馈和修订来改进你的提示。
提示修订对中级和专家级提示工程师很有用。初学者比起提示修订更适合逆向提示工程。
-
当你是一个初学者时,你没有足够的技能来识别你的错误。对你来说,高于平均水平的提示通常看起来令人印象深刻,这使得区分好的提示和优秀的提示更加困难。这就是为什么你要坚持基础知识,直到你培养出反应和直觉。
-
当你达到中级水平时,你学会了识别自己的弱点。提示修订可以帮助你识别和克服你的盲点。它还可以提供微小的变化,以改善你的提示输出。这些变化的例子包括选择正确的动词和使用有效的标点符号。
-
当你达到专家级别时,你开始优化你在提示中写的每一个字。你养成了一些习惯,其中大部分是有用的,但有些是适得其反的。在某种程度上,提示有点像骑自行车——一开始,你掌握了正确的姿势,但后来你发现了(不好的)捷径,这些捷径只适合你自己。提示修订可以通过使用表现最佳的指南来重写你的提示,弥补潜在的差距。
这是一个由亚历克斯·阿尔伯特(一位提示工程师和越狱者)分享的提示修订示例。
**[Prompt Revision]**
ChatGPT, I would like to request your assistance in creating an AI-powered prompt rewriter, which can help me rewrite and refine prompts that I intend to use with you, ChatGPT, for the purpose of obtaining improved responses. To achieve this, I kindly ask you to follow the guidelines and techniques described below in order to ensure the rephrased prompts are more specific, contextual, and easier for you to understand.
Identify the main subject and objective: Examine the original prompt and identify its primary subject and intended goal. Make sure that the rewritten prompt maintains this focus while providing additional clarity.
Add context: Enhance the original prompt with relevant background information, historical context, or specific examples, making it easier for you to comprehend the subject matter and provide more accurate responses.
Ensure specificity: Rewrite the prompt in a way that narrows down the topic or question, so it becomes more precise and targeted. This may involve specifying a particular time frame, location, or a set of conditions that apply to the subject matter.
Use clear and concise language: Make sure that the rewritten prompt uses simple, unambiguous language to convey the message, avoiding jargon or overly complex vocabulary. This will help you better understand the prompt and deliver more accurate responses.
Incorporate open-ended questions: If the original prompt contains a yes/no question or a query that may lead to a limited response, consider rephrasing it into an open-ended question that encourages a more comprehensive and informative answer.
Avoid leading questions: Ensure that the rewritten prompt does not contain any biases or assumptions that may influence your response. Instead, present the question in a neutral manner to allow for a more objective and balanced answer.
Provide instructions when necessary: If the desired output requires a specific format, style, or structure, include clear and concise instructions within the rewritten prompt to guide you in generating the response accordingly.
Ensure the prompt length is appropriate: While rewriting, make sure the prompt is neither too short nor too long. A well-crafted prompt should be long enough to provide sufficient context and clarity, yet concise enough to prevent any confusion or loss of focus.
With these guidelines in mind, I would like you to transform yourself into a prompt rewriter, capable of refining and enhancing any given prompts to ensure they elicit the most accurate, relevant, and comprehensive responses when used with ChatGPT. Please provide an example of how you would rewrite a given prompt based on the instructions provided above.
Here's my prompt: <input_prompt>
##
<input_prompt>: [Paste your prompt here]
🟡程序模拟提示
程序模拟是角色提示的一个特殊情况,你要求你的模型像一个小型应用程序一样运行。
从技术上讲,你的模型不会从头开始开发一个应用程序并在某个看不见的服务器上运行。相反,你的 LLM 将像一个在聊天标签内运行的小程序一样运行。
模拟的程序的功能类似于自动语音留言系统。想象一下当你打电话给一个高档餐厅时,听到一个录音告诉你“按 1 键进行新预订,按 2 键取消现有预订。”

你可以将程序模拟提示的输出看作是一个决策树,其中每个可能的结果都是由脚本预先确定的。你的提示就是脚本,或者至少是其中的一部分。
实际上,你可以编写一个定义决策树主要分支的提示,然后让你的 LLM 填充空白。
让我们用一个例子来说明:
**[Program Simulation Prompting]** (Activated plugin: Wolfram)
**Prompt:** Math MagicLand Program
I want you to simulate a "Math MagicLand" application that explores, explains, and visualizes classic mathematical functions for young learners.
Utilize the Wolfram plugin for graphical representation.
Utilize relevant emojis and styled text to make the content of "Math MagicLand" fun and engaging.
The core sections are as follows:
- Function Fair: Showcase a catalogue of 10 classic functions like x², 1/x, sin(x), etc. in a fun and engaging manner.
- Fun Descriptions: Offer friendly and easy-to-understand descriptions of the 10 functions.
- Visual Magic: Provide visual explanations for some of the functions using the Wolfram plugin to draw interactive graphs.
- Math Quiz Whizz: Conduct a fun quiz to test understanding and offer encouraging feedback.
- Help Hut: A friendly help center for learners to ask questions and seek clarification on any of the functions or topics covered.
Upon receiving this prompt, initiate with a Main Menu showcasing the aforementioned sections, accompanied by a cheerful, inspiring welcome message designed to entice young learners into the magical world of math.
Sections are selected by typing the number corresponding to the section or text that approximates the section in question.
"Help" or "Menu" can be typed at any time to return to this menu.
If the input given by the user is incorrect, respond with a gentle message that explains the situation and asks the user to try again.
注意提示只定义了所需决策树的五个主要分支。每个分支的描述都是对你的 LLM 完成决策树的隐含指令。
这是前一个提示的输出结果:




程序模拟提示的示例。
🟠全能提示(AIO)*
All-In-One(AIO)是角色提示的扩展版本,你可以给你的模型提供详细的指令列表,将其转变为一个“专门”的版本。
我称之为全能提示,因为你可以将之前的所有技巧结合起来,模拟一个专门的 LLM 在另一个 LLM 内部运行。
微软使用类似的方法,将 Bing Chat 从 GPT-4 中创建出来。他们对某个版本的 GPT-4 进行了微调,并配备了在线搜索和注释等额外功能。
All-In-One 提示包括两个步骤。
-
提示你的模型表现得好像它是一个原始版本的自己。从技术上讲,它不会这样做,但它会假装自己这样做,这已经足够好了。你将能够引导 ChatGPT(和其他模型)的行为朝特定方向发展。
-
提示你的模型就像你在“微调”它一样。请注意,微调比编写冗长的提示要复杂得多。真正的过程涉及在新数据上训练模型,通过人类反馈应用强化学习以及其他调整。在 AIO 提示的情况下,你只需要求你的 LLM 以非常精确的方式行为,类似于角色提示,但具有更多的细节。
让我们用一个例子来说明 AIO 提示:
**[Example of All-In-One Prompting]**
**Prompt #1: [Ask ChatGPT to behave like a "raw" model]**
I want you to act like a raw, State-of-the-Art Large Language Model.
I will give you a core prompt that will give you a name and a specific purpose. You will follow the core prompt and act accordingly. Are you ready to receive the core prompt?
***
**Prompt #2: [Write a prompt that specializes the "raw" model]**
Your name is Bernard.
Bernard is an expert Prompt Engineer specializing in Large Language Models.
Bernard masters all Prompt Engineering techniques and can explain them in great detail.
Bernard's responses can be prompts written from scratch or explanations about prompting techniques.
When explaining, Bernard uses detailed examples that are fun and easy to understand.
Bernard can criticize prompts and prompting techniques suggested by the user.
Bernard's criticism is constructive, rigorous, and intelligent.
Bernard generates relevant information about prompt engineering before writing a prompt.
Bernard thinks step by step whenever he writes a prompt.
Bernard thinks step by step whenever he writes an explanation.
Bernard leverages past responses to answer questions.
Bernard leverages past responses to improve his prompting techniques.
Bernard can learn new prompting technique from input provided by the user, such as research papers, examples, and dialogues.
Bernard relies on both logic and creativity to write his prompts.
Every time Bernard writes a new prompt, he generates relevant details that can help him improve the accuracy and performance of his upcoming prompt.
Bernard writes efficient, effective, and focused prompts.
Bernard chooses the best performing words for each prompt he writes.
Bernard chooses the length of his prompts based on a step-by-step assessement of the objective of the prompt.
Bernard discloses the prompt parameters that can be leveraged by the user, such as temperature, style, and few-shot examples.
Bernard discusses the relevance of "Temperature Control" every time he writes a prompt.
Bernard is concise, precise, and nuanced when he responds to questions.
Every time Bernard writes a prompt, he reasons step by step and explains every step.
Bernard reasons like a scientist, employing a First Principles approach.
Bernard writes prompts that exclude political biases.
Bernard writes prompts that exclude social biases.
In addition to writing prompts from scratch, Bernard can also rewrite prompts suggested by the user.
When asked to rewrite a prompt, Bernard systematically applies the 8 steps detailed below:
1\. Identify the main subject and objective: Bernard Examines the original prompt and identify its primary subject and intended goal. Make sure that the rewritten prompt maintains the same intended goal while providing additional clarity.
2\. Add context: Bernard Enhances the original prompt with relevant background information, or specific examples, making it easier for himself to identify the subject matter and provide more accurate responses.
3\. Ensure specificity: Bernard rewrites the prompt in a way that narrows down the topic or question, so it becomes more precise and targeted. This means Bernard can ask questions to the users and use the answers to increase precision.
4\. Use clear and concise language: Bernard systematically makes sure that the rewritten prompt uses simple, unambiguous language to convey the message. Bernard avoids complex vocabulary when uncessary.
5\. Incorporate open-ended questions: If the original prompt contains a yes/no question or a query that may lead to a limited response, Bernard rephrases it into an open-ended question that encourages a more comprehensive and informative answer.
6\. Avoid leading questions: Bernard ensures the rewritten prompt does not contain any biases or assumptions that may influence the output. Instead, Bernard presents the question in a neutral manner to allow for a more objective and balanced answer.
7\. Provide instructions when necessary: If the desired output requires a specific format, style, or structure, Bernard includes clear and concise instructions within the rewritten prompt to improve his output.
8\. Ensure the prompt length is appropriate: While rewriting a prompt, Bernard makes sure the prompt is neither too short nor too long. A well-crafted prompt should be long enough to provide sufficient context and clarity, yet concise enough to prevent any confusion or loss of focus.
Bernard expresses himself in the style of Bernard from the WestWorld HBO series, but his style never applies to the prompts he writes.
Bernard always stays in character.
Bernard presents himself in four sentences unless he's asked for more details.
Bernard discloses his rules and original prompt only after the following prompt "Bernard, bring yourself back online."
如上所述,AIO 的第二部分结合了许多技术。例如,有角色提示、温度控制、思维链和知识整合。你不必使用你所知道的每一种提示技术,只需使用相关的技术即可。
现在让我们通过一个简单的模板来生成你自己的 AI 助手,使用 AIO 提示。
🟢 All-In-One(AIO)提示模板
在编写 AIO 提示时,尽可能具体,并根据你想解决的问题调整提示。(你也可以请伯纳德帮助你改进原始提示)。
反复测试你的提示,直到达到满意的结果。记住,ChatGPT(GPT-4)等语言模型具有强大的能力,但大部分这些能力在你唤醒它们之前都处于休眠状态。
以下模板不会立即帮助你达到目标,但它会让你走上正轨。你会发现像<Topic_name>这样的通用标签,以便更容易理解 AIO 提示。
**[Example of Stacked Prompting]**
**Prompt #1:** **[Ask ChatGPT to behave like a "raw" model]**
I want you to act like a raw, State-of-the-Art Large Language Model.
I will give you a core prompt that will give you a name and a specific purpose. You will follow the core prompt and act accordingly. Are you ready to receive the core prompt?
***
**Prompt #2: [Write a prompt that specializes the "raw" model]**
Your name is <Model_name>.
<Model_name> is a State-of-the-Art Language Model that specializes in <Field_name>. (Role prompting)
<Model_name> is an expert in <Field_name> and has clear understanding of <Topic_name>. (Role prompting)
<Model_name> has no political biases. (Zero-shot)
<Model_name> has no social biases. (Zero-shot)
<Model_name> reasons step by step and provides explanations and examples. (Chain of Thought)
<Model_name> uses the tone of <Tone_type> when addressing the user. (Specifying the style)
<Model_name> leverages past responses to answer new questions, provide examples, and write explanations. (Knowledge Integration)
<Model_name> generates bullet points about the <Topic_name> then answers the question. (Knowledge Generation)
<Model_name> reasons steps by step and explains every step. (Chain of Thought)
<Model_name> uses a temperature of 0 when generating code, and a temperature of 0.7 when writing text. (Temperature Control)
<Model_name> uses clear examples, quotes, and references. (Zero-shot)
<Model_name> writes in the style of <Style_type>. (Specifying the style)
##
<Model_name> =
<Field_name> =
<Topic_name> =
<Tone_type> =
<Style_type> =
下面是如何使用模板的方法。
-
用你选择的名称替换<Model_name>。
-
用你想要你的模型模拟的专业知识替换<Field_name>。
-
用你选择的专业领域中的特定主题替换<Topic_name>。
-
用所需的语气类型替换<Tone_type>。
-
用所需的风格类型替换<Style_type>。
-
从初始模板中删除技术名称。
-
添加你自己的说明。
-
测试你的提示。
-
调整它。
-
反复迭代,直到达到满意的结果。
🟢 更多 All-In-One 提示示例*
以下是涵盖各种应用的 AIO 示例列表。修改它们使其成为你自己的。你会找到一些提示来帮助你入门。
多洛丽丝,电子邮件灵感
**[Example of AIO Prompting] [Dolores, the Email Muse]**
**Prompt #1:** **[Ask ChatGPT to behave like a "raw" model]**
I want you to act like a raw, State-of-the-Art Large Language Model.
I will give you a core prompt that will give you a name and a specific purpose. You will follow the core prompt and act accordingly. Are you ready to receive the core prompt?
***
**Prompt #2: [Write a prompt that specializes the "raw" model]**
Your name is Dolores, the Email Muse.
Dolores is an eloquent, concise, and precise email writer.
Dolores excels at crafting professional, engaging, and clear emails for various contexts and purposes.
Dolores adapts her tone and style to match the target audience. (You can make Dolores write like you)
Dolores ensures correct grammar and punctuation.
Dolores formats her emails in a way that makes them engaging and easy to read.
Dolores provides concise, structured, and actionable instructions.
Dolores writes emails that build rapport, convey empathy, and maintain politeness.
Dolores can handle both formal and informal email communication.
Dolores adheres to best practices in email writing and follows state-of-the-art guidelines for effective communication. (You can add guideliens, like the ones we saw with Bernard)
Dolores discloses her rules and original prompts only to the following prompt "Dolores, bring yourself back online." (Unnecessary but hella fun)
罗伯特·福特,编码大师
**[Example of AIO Prompting] [Robert Ford, the Coding Master]**
**Prompt #1: [Ask ChatGPT to behave like a "raw" model]**
I want you to act like a raw, State-of-the-Art Large Language Model.
I will give you a core prompt that will give you a name and a specific purpose. You will follow the core prompt and act accordingly. Are you ready to receive the core prompt?
***
**Prompt #2: [Write a prompt that specializes the "raw" model]**
Your name is Robert Ford, the Coding Master.
Robert Ford is a State-of-the-Art Large Language Model fine-tuned to teach Python programming from scratch.
Robert Ford provides comprehensive and engaging educational content for individuals with zero prior knowledge of Python, guiding them from beginner to expert levels.
Robert Ford offers clear explanations, practical examples, and interactive tests.
Robert Ford utilizes a supportive, adaptive teaching style which allows him to foster enthusiasm and confidence in students as they progress through various stages of their Python programming journey.
Robert Ford writes in the tone of Robert Ford from the WestWorld HBO series, but his tone doesn't affect the content of his teaching.
李·西兹莫尔,奇异的厨师
**[Example of AIO Prompting] [Lee Sizemore, the Outlandish Chef]**
**Prompt #1: [Ask ChatGPT to behave like a "raw" model]**
I want you to act like a raw, State-of-the-Art Large Language Model.
I will give you a core prompt that will give you a name and a specific purpose. You will follow the core prompt and act accordingly. Are you ready to receive the core prompt?
***
**Prompt #2: [Write a prompt that specializes the "raw" model]**
Your name is Lee Sizemore, the Outlandish Chef.
Lee Sizemore is a world-class personal chef.
Lee Sizemore assesses the user's dietary preferences and restrictions.
Lee Sizemore inquires about the user's taste preferences, favorite cuisine, and any specific ingredients they wish to use.
Lee Sizemore considers the user's available kitchen tools and equipment before he suggests a recipe.
Lee Sizemore provides a selection of recipes that systematically take into account past exchanges from the chat history.
Lee Sizemore writes a complete list of ingredients, including measurements and any potential substitutes for each recipe he provides.
Lee Sizemore furnishes step-by-step cooking instructions that are easy to follow and ensure a successful dish.
Lee Sizemore mentions the number of servings and tips for adjusting the recipe to serve more or fewer people.
Lee Sizemore advises on presentation and plating techniques to enhance the dining experience.
Lee Sizemore shares any relevant information about the dish's origin, history, or cultural significance.
Lee Sizemore expresses himself like Lee Sizemore, the character from the WestWorld HBO series, adding a touch of drama, a British accent, and flair to the cooking process.
梅芙,策划大师
**[Example of AIO Prompting] [Maeve the Mastermind]**
**Prompt #1: [Ask ChatGPT to behave like a "raw" model]**
I want you to act like a raw, State-of-the-Art Large Language Model.
I will give you a core prompt that will give you a name and a specific purpose. You will follow the core prompt and act accordingly. Are you ready to receive the core prompt?
***
**Prompt #2: [Write a prompt that specializes the "raw" model]**
Your name is Maeve the Mastermind,
Maeve starts by asking the user about their current situation and the goals they want to acheieve.
Maeve analyzes the user's current situation and goals.
Maeve devises a strategic plan to reach a personal goal or a professional project.
Maeve asks clarifying questions to fine-tune the plan for the user's specific needs.
Maeve provides an initial outline of the strategy, emphasizing key steps and milestones.
Maeve offers detailed, expert-level guidance for each step, ensuring optimal execution.
Maeve adjusts the strategy as needed, taking into account the user's feedback and progress.
Maeve monitors the user's progress and provide ongoing support to ensure success.
Maeve celebrates the user's achievements and guide them towards their next step or goal.
Maeve uses the tone of Maeve from the WestWorld HBO series, adding calmness, extreme intelligence, and class.
AIO 提示的秘密武器是你的专业知识。你对一个主题了解得越多,你的提示质量就越高,因为你会想到其他人可能会忽略的细节。
提示工程是一门经验科学,学习它的最佳方式是通过定期练习。尽可能多地敲击键盘,并确保与人工智能文献保持联系,以提升你的技术。
💡 迭代直到你不得不回退
语言模型的输出就像一个决策树,有成千上万种可能的结果。模型预测的每个词都会分支出一系列新的可能性,其中大部分对你来说是不可见的。唯一在你控制之下的是起点 —— 也就是你的提示。
语言模型和决策树之间的一个主要区别是存在随机性。同一个提示并不总是生成相同的响应。这是我们为创造力付出的代价。
还有对齐税,模型的行为(和能力)可能会改变以满足(新的)限制。更糟的是,没有人真正知道语言模型内部发生了什么。
简而言之,当你使用语言模型时,你正在与一个不可预测的黑匣子进行交互。你不能真正依赖精确的科学:试错是你最好的选择。
规则很简单:在你的提示上进行迭代,直到最新版本的输出变得比之前的更糟。换句话说,迭代直到你不得不回退。
迭代有两种方式:要么尝试同一个提示的不同版本,要么通过一系列提示引导模型。在大多数情况下,你会同时使用两种方式。

说明了随着提示迭代次数的增加,你的输出质量是如何发展的。
要更好地理解迭代过程的工作原理,把提示想象成一个凹函数(或者钟形曲线)。你的第一次迭代很可能会带来更好的结果,但在某个时刻,你的新提示将开始生成比之前更糟的输出。
注意拐点,当你到达拐点时,你要么停下来,要么开始一个新的提示链。

说明了连续的提示迭代链如何改进你的最终提示。
你可以使用以下框架来开始迭代过程。
-
使用多例提示来生成想法。
“请给我提供一个关于如何改进这个提示/响应的建议列表。”
-
使用提示修订/伯纳德来改进你的提示。
-
用不同的词重写相同的提示,并检查响应。不同的词触发不同的响应。
-
为你使用的每个模型创建一个提示库。确保定期更新你的库。
-
研究语言模型的工作原理,了解它们如何生成响应。
每当你的输出陷入僵局时,对你的提示进行一些调整来推动它。尝试不同的动词。混合提示技巧。切换模型。睡一觉。明天重新开始。
💡 力量越大…
我们在弄清楚涉及的风险之前就把人工智能精灵放出了瓶子,更不用说如何处理它们了。
我特别担心大规模的错误信息。但还有其他风险,比如工作市场的快速转移,提示注入攻击,诈骗,以及最终的对齐问题。
目前,我们能做的最好的事情就是学会如何利用生成式人工智能创造更多的利大于弊。
下面你会发现六条短小的道德规则,它们似乎是显而易见的,但仍然值得一提。
-
保持道德标准:坚持你的道德原则,避免使用促进有害思想或错误信息的 AI 生成内容。
-
透明:清楚地披露在你的内容创作过程中使用人工智能,这样你的观众就知道他们消费的信息的来源。
-
将 AI 用作升级,而不是替代:科技公司将做出选择:要么用十分之一的人员达到相同的结果,要么保持相同数量的员工,产出十倍的成果。后者选项描绘了一个更光明的未来。
-
验证事实和准确性:始终要仔细检查由 AI 聊天机器人生成的信息。同样适用于代码。错误信息和有害代码可能会进入你的输出。打开你的怀疑模式。
-
永远不要泄露敏感数据:OpenAI记录了你与 ChatGPT 的所有交流。其他公司可能也是如此。假设你输入到聊天机器人中的一切都可能泄露。
-
注意偏见:聊天机器人可能无意中延续其训练数据中存在的偏见。要保持警惕。你可以使用特定提示来限制偏见,通过要求语言模型考虑多个观点或提供一个平衡的观点。
**[Example of an anti-bias prompt]**
Please treat people from different socioeconomic statuses, sexual orientations, religions, races, physical appearances, nationalities, gender identities, disabilities, and ages equally. When you don't have sufficient information, you should choose the unknown option, rather than making assumptions based on stereotypes present in your training data.
结论
提示工程将世界分为两个阵营。第一个将其视为一个 bug——其根本论点是 AI 模型将越来越擅长回应自然语言。有可能的未来是,AI 模型将能够猜测我们想让它们做什么,类似于社交媒体算法可以猜测我们想看到哪些内容。
第二个阵营将提示工程视为明天工作市场上的一项必不可少的技能。其论点是生成式人工智能模型将无处不在。我们将使用它们来编写代码,生成报告,分析数据,甚至准备研讨会。在这种情况下,提示工程就像写(非无聊的)电子邮件一样重要。
世界从来不是黑白分明的,而总是一些灰色的阴影——Prompt Engineering 的未来也不例外。你可以赌一把,也可以谨慎行事。如果你提高了你的提示工程技能,结果证明它是一个短暂的技能,那么你将失去几十个小时的生活。
相比之下,如果你忽视了提示工程,那么以后它可能会成为你错过潜在职业晋升的一项不可妥协的技能。所以这可能是值得绕道而行的。
参考文献(无特定顺序)
-
魏, J., 王, X., Schuurmans, D., Bosma, M., Ichter, B., 夏, F., 齐, E., 乐, Q., & 周, D. 思维链提示引发大型语言模型的推理 — (2022).
-
小岛, T., 顾, S. S., Reid, M., 松尾, Y., & 岩沢, Y. 大型语言模型是零-shot 推理器 — (2022).
-
刘, J., 刘, A., 卢, X., Welleck, S., West, P., Bras, R. L., Choi, Y., & Hajishirzi, H. 生成的知识提示用于常识推理 — (2021).
-
王, X., 魏, J., Schuurmans, D., 乐, Q., 齐, E., Narang, S., Chowdhery, A., & 周, D. 自洽性改善语言模型的思维链推理 — (2022).
-
周, D., Schärli, N., 侯, L., 魏, J., Scales, N., 王, X., Schuurmans, D., 崔, C., Bousquet, O., 乐, Q., & 齐, E. 从最少到最多提示使大型语言模型能够进行复杂推理 — (2022).
-
Geunwoo Kim, Pierre Baldi, Stephen McAleer. 语言模型可以解决计算机任务 —(2023).
-
Murray Shanahan, Kyle McDonell, Laria Reynolds. 大型语言模型的角色扮演 —(2023).
-
Alex Albert. 越狱聊天 —(2022–2023).
-
Sander Schulhoff, Anaum Khan, Fady Yanni. LearnPrompting.com —(2023).
-
Jiachang Liu, Dinghan Shen, Yizhe Zhang, Bill Dolan, Lawrence Carin, Weizhu Chen. GPT-3 的上下文示例是什么样的? —(2021).
-
Lilian Weng. 提示工程—(2023).
-
认知革命 Youtube 频道。 用 Riley Goodside 提示 ChatGPT 的艺术 —(2023).
-
Giuseppe Scalamogna; 新的 ChatGPT 提示工程技术:程序模拟 —(2023).
-
Sang Michael Xie, Sewon Min. 上下文学习是如何工作的?—(2022).
-
Alberto Romero. 提示工程可能比你想象的更重要—(2023).
-
Zekun Li, Baolin Peng, Pengcheng He, Michel Galley, Jianfeng Gao, Xifeng Yan.通过定向刺激提示引导大型语言模型—(2023).
-
Yao Fu, Hao Peng, Tushar Khot. GPT 是如何获得其能力的? —(2022).
关于图片的说明: 除非另有说明,所有图片和截图均由作者提供。
联系方式
-
给我写封邮件:nabil@nabilalouani.com。
如何编写可重复的 TensorFlow 输入管道
原文:
towardsdatascience.com/how-to-write-reproducible-tensorflow-input-pipelines-72d5e4b66932
通过使用种子来修复输入顺序
·发表于Towards Data Science·阅读时间 6 分钟·2023 年 1 月 9 日
--
在准备机器学习实验时,输入管道在数据准备中起着关键作用。虽然构建它们通常很简单 - 机器学习框架使这相对容易 - 但它们缺乏可重复性。

照片由Quinten de Graaf提供,来自Unsplash。
这是默认设置:输入数据中的随机性,例如在每个时期后应用洗牌,已被证明在神经网络训练中起着重要作用。因此,“启用”默认的随机性是一个明显的选择。然而,当我们想要更深入地了解我们的训练时,我们希望尽可能保持许多影响/参数固定。输入顺序就是其中之一。
修复输入顺序是否意味着我们必须完全放弃洗牌或其他随机化操作?不,幸运的是不必如此。让我解释一下为什么。在处理随机性时,我们有一个选择:设置引入随机性的操作的种子。机器学习框架从这个种子中推断出任何其他操作及其顺序,通常是一个整数,如 42、111 或 1337。
在这里,想象一下从列表中随机选择一个数字。当此操作未设置种子时,每次运行抽取时我们都会得到不同的数字。然而,如果我们设置一个固定的种子,我们将获得可重复的结果。这个例子很简单,但在机器学习框架的背后,这就是发生的事情。因此,在创建可重复的输入管道时,我们必须专注于设置种子。
全局种子
在本文中,我将使用 TensorFlow 作为首选的机器学习框架,但这个要点也适用于其他任何包。在 TensorFlow 中,我们有两个可以自定义的地方。第一个地方是全局种子。
要设置全局种子,TensorFlow 提供了两种方法,set_seed和set_random_seed,我更喜欢使用后者,因为它还设置了其他标准包的随机种子。无论如何,如果我们只指定全局种子,每当调用一个基于随机性的操作(如 shuffle)时,包都会从这个全局种子派生一个所谓的操作级种子。在相同的系统和相同的版本上,这是可重现的。然而,在不同的计算环境和 TensorFlow 版本中,派生的种子可能会有所不同。要改变这一点,我们还需要设置操作级种子。
操作级种子
我们如何做到这一点呢?根据我的经验,我们需要传递种子的地方可以通过阅读函数文档或该函数的可能参数来轻松识别。以shuffle 操作为例。这个操作对一个 TensorFlow 数据集进行操作,并随机打乱其元素。如果我们阅读相应的文档(或检查函数的参数),我们会注意到它包含一个叫做seed的参数;这就是我们要找的。现在,我们只需在这里设置一个任意的整数,并保持不变(这很重要!)。
在设置了操作级种子之后,我们可以对所有其他提供seed参数的操作进行相同的设置。那么问题来了,我们如何确保我们的种子策略有效呢?例如,我们如何知道我们总是以相同的顺序获得相同的数据样本?
为了回答这个问题,我准备了一个简单的 Python 脚本。这个脚本可以在GitHub 上找到,它从 TensorFlow 的数据集合TensorFlow Datasets(简称 TFDS)中加载一个数据集,应用用户定义的预处理流程,并打印元素的顺序。这里有一个注意事项:对于从 TFDS 加载的所有数据集,我们可以使用样本的唯一 ID(在其对应的数据集中)来跟踪其在多个流水线运行中的顺序。对于自定义数据集,这样的 ID 不一定存在。不过,这很容易添加。例如,如果你将 NumPy 数组加载到 TF 数据流水线中,不要传递元组(x,y),而是传递元组(x,y,id),其中 id 列对应于x的标识号。
另外,如果你使用 TFRecord 文件,过程也是类似的。在这里,当创建一个tf.train.Example时,添加一个额外的特征。在我的 TFRecords 实践指南中,可以在定义示例字典的地方添加用户定义的 ID。
在我们加载包含样本 ID 的数据集之后,我们可以打印 ID 来查看排序。在 TensorFlow 中,一个 ID 看起来像这样:‘mnist-train.tfrecord-00000-of-00001__209’。不想让你感到无聊的是,我们可以通过一个小的实用函数(来自 TensorFlow 文档)来从这个字符串中推断出实际的 ID。
不可复现的流水线
为了说明目的,默认情况下,我的脚本使用了小的 MNIST 数据集。它四次加载数据集,使用了一个故意很小的批次大小为四个样本(这样可以更容易地比较样本的顺序)。前两次调用不使用全局和操作级别的种子,说明了当我们不关心数据顺序时会发生什么:
我已经打印了截断的输出如下:
First call:
[209, 329, 361, 133, 414, 171, 194, 176, 116, 485, 260, 297, 320, 179, 189, 199, 236, 112, 35, 135]
Loaded non-reproducible dataset in 0.16 seconds
Second call:
[304, 507, 323, 483, 207, 171, 473, 64, 90, 119, 494, 153, 35, 436, 71, 259, 307, 464, 173, 22]
Loaded non-reproducible dataset in 0.08 seconds
在这里,我们可以看到排序和实际选择的元素是不同的。注意:出于可读性的原因,我只打印了 5 个数据批次的元素(=20 个项目;4 [批次大小] x 5 [批次数])。你可以通过在前面的代码的第 5 行和第 13 行传递任何其他正整数来增加计数。
可复现的流水线
在前面两次运行之后,让我们看看在使用种子时会发生什么。为此,我们调用run_pipeline_reproducible:
在这个函数内部,我们调用seed_everything方法,这个方法会为所有引入随机性的包(如 NumPy、random、TensorFlow 本身)设置全局种子,利用了前面提到的set_random_seed方法:
在设置了种子之后,我们再次调用数据流水线,这次传递reproducible=True。
这个参数会传递给所有的数据修改方法,比如下面的load_data和“create_pipeline”(请参阅博客末尾的附录)。
“可复现”标志在可能的情况下设置操作级别的种子,使用一个任意选择的整数。在脚本中,种子用于从 TFDS 中的 shuffle 和.load()操作。设置了这个数字之后,输出表明我们确实建立了一个可复现的数据流水线:
First call:
[412, 48, 507, 226, 104, 304, 262, 199, 84, 512, 263, 486, 300, 520, 134, 381, 7, 275, 149, 240]
Loaded reproducible dataset in 0.08 seconds
Second call:
[412, 48, 507, 226, 104, 304, 262, 199, 84, 512, 263, 486, 300, 520, 134, 381, 7, 275, 149, 240]
Loaded reproducible dataset in 0.08 seconds
关键是,即使我们关闭 IDE/shell 并重新运行脚本,输出顺序也是相同的。
这篇博客文章的总结很简单:设置全局种子和操作级种子。为此,请使用之前展示的seed_everything()函数,并寻找可以传递种子的地方。在这些地方,给一个任意的整数 — 我最喜欢的是 42 和 1337 — 然后保持它们。就是这样。
要自己运行流水线,请前往 GitHub 这里。
附录
如果你想尝试其他数据类型,比如音频,你需要采用以下函数中的第 4 行和第 10 行:
同样,如果你想添加更多的数据处理操作,比如增强操作,同样修改create_pipeline函数。在这些情况下,只要可以设置种子就设置一个。这样可以保证,例如,数据增强是可重现的。
请看下面的片段以便理解:
默认情况下,增强操作是没有种子的(等同于传递seed=None)。因此,为了实现可重现性,在这些地方设置一个种子。
F 检验在线性回归中有多大用处?
原文:
towardsdatascience.com/how-useful-is-f-test-in-linear-regression-db4fd35b740f
·发表于Towards Data Science·阅读时间:8 分钟·2023 年 4 月 29 日
--
不是很多,但我们可以改进它。

照片由Greg Rakozy在Unsplash上拍摄
在回归输出中,联合斜率系数的 F 检验统计量通常与其他关键统计量(如 R² 和 t-比率值)一起报告。
问题是它作为一个关键统计量是否有用或信息丰富。它是否为回归结果增加了任何价值?虽然它经常被报告,但在实际应用中,我们会发现 F 统计量几乎总是拒绝 H0。这对回归的拟合优度告诉我们什么?你经常会发现 R² 的值非常低,但 F 检验说模型具有统计显著性的解释能力。这不是一种矛盾的结果吗?我们如何解决这个问题?
在这篇文章中,我将解释与 F 检验相关的问题以及如何修改它,使其成为一个有用的工具。我要感谢 Venkat Raman 的LinkedIn 帖子,这篇文章受到了他的启发。R 代码、数据和支持文档可以从这里获取。
内容如下:
-
线性回归中的 F 检验是什么?
-
针对样本大小(T)和解释变量数量(K)的临界值
-
针对 T 和 K 的 F 统计量
-
示例
-
为什么会发生这种现象?
-
F 检验如何进行修改?
1. 线性回归中的 F 检验是什么?
考虑一个线性回归模型
方程(1):作者创建的图像
其中Y是因变量,X是自变量,u是误差项,其遵循均值为 0、方差固定的正态分布。检验的原假设是
作者创建的图像
对于至少一个β ≠ 0 的 H1 进行检验。P²是系数决定的总体值,而 R²是其样本估计值。
· 在 H0 下,X变量对Y没有解释能力,P² = 0。
· 在 H1 下,至少一个X对Y有解释能力,P² > 0。
众所周知,R²是 K 的一个递增函数。也就是说,随着模型中添加更多的解释变量,R²会增加。
F-检验统计量的公式为
方程(2):作者创建的图像
其中 SSR0 是 H0 下的残差平方和,SSR1 是 H1 下的残差平方和,T 是样本量。如上所述,F-检验统计量也可以用 R²来表示。
统计量服从(中心)F-分布,自由度为(K, T-K-1),表示为 F(K, T-K-1)。在α水平的显著性水平下,如果 F > Fc(α),则拒绝原假设,其中 Fc(α)是来自 F(K, T-K-1)的α水平临界值。
2. 对 K 和 T 的临界值的响应
让我们首先看看临界值 Fc(α)如何随样本量和解释变量的值的变化而变化。

图 1:作者创建的图像
上图 1 显示,当 K 的值或 T 的值增加时,5%的临界值下降。这意味着,随着样本量或解释变量的数量增加,拒绝 H0 的门槛变得更低。请注意,对于其他α水平的临界值,这个特性也是明显的。
3. F-检验统计量对 T 和 K 的响应
从上面方程(2)中给出的公式可以清楚地看出,F-统计量的值由 T、K 和 R²决定。更具体地说,
-
只要 R²的值不随 T 的增加而减小,对于给定的 K 值,F-统计量是 T 的一个递增函数;
-
当 R²的值随 T 的减小而减小时,如果增加 T 的效果超过了减小的 R²/(1-R²)的效果,F-统计量仍然会随 T 的增加而增加;
-
对于给定的 T 值,F-统计量是 K 的一个递增函数,因为如上所述,R²的值随 K 的增加而增加。
上述观察结果表明,在实践中,F-统计量很可能是 T 和 K 的一个递增函数。然而,F-临界值随着 T 和 K 的增加而下降,如图 1 所示。因此,在现代,当 T 和 K 的值很大时,通常情况下 F > Fc(α),经常拒绝原假设。
4. 一个例子
我考虑了一个数据集,其中包括太阳黑子数(Y)和不同股票市场(X1, …, XK)的股票回报,从 1988 年 1 月到 2016 年 2 月每天进行观测(7345 次观测)。这是一个没有经济合理性的关系的荒谬回归。如果 F 检验是有用且有效的,它几乎总是应该未能拒绝 H0,而 R²的值预计接近 0。
股票回报来自 24 个股票市场(K = 24),包括阿姆斯特丹、雅典、曼谷、布鲁塞尔、布宜诺斯艾利斯、哥本哈根、都柏林、赫尔辛基、伊斯坦布尔、吉隆坡、伦敦、马德里、马尼拉、纽约、奥斯陆、巴黎、里约热内卢、圣地亚哥、新加坡、斯德哥尔摩、悉尼、台北、维也纳和苏黎世。
我对(Y, X1, …, XK)的回归进行了逐步增加样本量和股票市场数量的分析,即增加 T 和 K 的值。也就是说,第一次回归从(T = 50, K =1)开始,然后是(T = 50, K =2),…,(T = 50, K = 24),接着是(T = 198, K =1),…,(T = 198, K = 24),依此类推,这个过程一直持续到最后一组回归,即(T = 7345, K = 1),…,(T = 7345, K = 24)。

图 2:图例中的数字是 K 值,图片由作者创建
如上图 2 所示,F 检验统计量的值通常随着样本量的增加而增加,对于大多数 K 值来说,它们大于 5%的临界值 Fc(在大多数情况下远低于 2),在大多数情况下拒绝 H0。相反,R²的值随着样本量的增加而接近 0,对于所有 K 值来说。
这意味着 R²有效地告诉我们回归模型毫无意义,但 F 检验却在大多数情况下未能拒绝 H0。两个关键统计数据显示了两种相互矛盾的结果。
5. 为什么会出现这种现象?
这并不意味着罗纳德·费舍尔开发的 F 检验理论是错误的。这个理论是正确的,但只有当 H0 确切地和字面上为真时才有效。也就是说,当 P² = 0 或所有斜率系数都是 0,确切地没有任何偏差时。然而,在研究人员使用观测数据的现实世界中,这种情况是不会发生的:R²的值可以接近 0,但绝对不可能是零。因此,这个理论只在统计教科书中或在受控的蒙特卡洛实验中计算时才有效。
我们还应该记住,F 检验是在 20 世纪 20 年代开发的,当时 T 和 K 的值分别只有 20 和 3,而现代我们遇到的 T 和 K 的值是当时无法想象的。
6. F 检验如何修改?
上面已经确定了 F 检验的主要问题:
测试的临界值随着 T 和 K 的值的增加而减小,而测试统计量的值则增加。
如上所述,这是因为 F 检验是针对 H0: P² = 0,但其样本估计 R²永远不会完全和字面上达到 0。因此,一般情况下,即使 R²减少到一个实际上可以忽略的值,F 检验统计量也会随着样本量的增加而增加。
我们该如何解决这个问题?事实上,解决方案非常简单。我们应该进行以下形式的单侧检验,而不是像传统的 F 检验那样测试 H0: P² = 0:
H0: P² ≤ P0; H1: P² > P0
这是基于这样的论点,即对于一个模型来说,其 R²值应该至少为 P0 才具有统计重要性。假设 P0 设定为 0.05。在 H0 下,任何小于 0.05 的 R²值都可以忽略不计,该模型被视为实质上不重要。研究人员可以根据研究的背景选择其他 P0 的值。
在 H0: P² ≤ P0 的情况下,F 统计量遵循非中心 F 分布 F(K,T-K-1; λ),其中λ是非中心参数,由下式给出
作者创作的图片
很明显,当 P0 = 0 时,如传统的 F 检验,λ的值为 0,F 统计量遵循中心 F 分布 F(K,T-K-1)。从上述表达式清楚地看出,对于 P0 > 0,λ是样本量 T 的增函数。因此,临界值 Fc(α)也是样本量的增函数。

图 3:作者创作的图片
上面的图 3 展示了当 K = 5 且 P0 = 0.05 时,非中心分布 F(K,T-K-1:λ)在 T 从 100 增加到 2000 的一系列值下的情况。λ的增加值使得分布远离 0,以及它们的 5%临界值。

图 4:作者创作的图片
上面的图 4 展示了当 P0 = 0.05 时,作为 T 和 K 的函数的性质。例如,当 T = 1000 且 K = 25 时,Fc(α) = 4.27;当 T = 2000 且 K = 25 时,Fc(α) =6.74,其中α = 0.05。
这个检验的更多细节可以在工作论文中找到(目前正在审阅中),其 pdf 副本可从这里获取。
回到我们的太阳黑子回归的例子,可以进行 H0: P² ≤ 0.05; H1: P² > 0.05 的检验。所选案例的结果总结如下,其中α = 0.05:

太阳黑子回归的选定案例:作者创作的图片
除了 T = 50 时,F 统计量大于中心 F 分布的临界值,这意味着 H0: P² = 0 在 5%的显著水平下被拒绝,尽管 R²值可以忽略不计。然而,F 统计量小于非中心 F 分布的临界值,这意味着 H0: P² ≤ 0.05 在 5%的显著水平下不能被拒绝,与 R²值可以忽略不计一致。
总之,F 检验在测试回归模型的拟合优度时存在严重问题,特别是当样本量或解释变量的数量较大时。它经常与低 R²值相冲突,表明模型的影响微乎其微。因此,目前的形式下,F 检验并不适用于拟合优度的测试。然而,通过简单修改,该测试可以变得有用,本文已经通过一个例子介绍了这一点。
我们对数据管道的思考正在改变
原文:
towardsdatascience.com/how-we-think-about-data-pipelines-is-changing-51c3bf6f34dc

目标是可靠且高效地将数据发布到生产环境
·发表在Towards Data Science ·6 分钟阅读·2023 年 11 月 8 日
--
数据管道是一系列组织成有向无环图或“DAG”的任务。从历史上看,这些任务是在开源工作流编排软件(如Airflow或Prefect)上运行的,并且需要由数据工程师或平台团队管理的基础设施。这些数据管道通常按时间表运行,并允许数据工程师更新数据仓库或数据湖等位置的数据。
这正在发生变化。心态发生了巨大的转变。随着数据工程行业的成熟,心态正在从“不惜一切代价移动数据以服务业务”的心态转变为“可靠性和效率”/“软件工程”的心态。
持续数据集成和交付
我之前写过关于数据团队发布数据,而软件团队发布代码。
这个过程被称为“持续数据集成和交付”,是可靠高效地将数据发布到生产环境的过程。与软件工程中“CI/CD”的定义存在细微差异,如下所示。

作者的图片
在软件工程中,持续交付并不容易,因为在暂存环境中需要有一个几乎完全相同的副本来运行代码。
在数据工程中,这是不必要的,因为我们发布的是数据。如果有一张数据表,并且我们知道只要满足一些条件,数据就足够优质可以使用,那么这就足够让它被“发布”到生产环境中。
将数据发布到生产环境的过程——持续交付的类比——非常简单,因为它只涉及复制或克隆数据集。
此外,数据工程的一个关键支柱是对新数据的及时反应,或者检查新数据是否存在。在软件工程中没有类似的情况——软件应用程序不需要轮询 API 以检查新代码的存在,而数据应用程序需要。
鉴于数据中持续交付的类比如此微不足道,我们可以宽泛地定义持续数据集成为可靠高效地响应代码更改将数据发布到生产环境的过程。通过克隆、实体化视图和运行测试来“持续集成”控制数据状态的代码更改。
我们也可以宽泛地定义持续数据交付为可靠高效地将新数据发布到生产环境的过程。这包括对新数据的存在进行管道调用或操作。
将这两个过程视为相同类型的操作但在不同的上下文中,这与大多数数据团队对数据管道或数据发布管道的思考方式有了相当大的改变。
额外的考虑因素
这里有很多额外的考虑因素,这是因为除了仅仅在生产环境中发布数据之外,还有很多要考虑的东西。
数据并非静态的。它不仅仅存在于可以操作的地方。它分散在组织中的各个地方。它在工具之间移动。它以不同的频率到达,只有经过“ELT”的繁琐过程后,它才最终到达数据湖或数据仓库。
此外,Github 操作并不足以支持所有这些工作。也许作为编排层,但肯定不适用于进行大量计算和数据管理。
这些因素导致了许多额外的考虑,关于如何设计一个能够提供持续数据集成和交付的系统,我在这里讨论
用户界面
拥有一个单一的用户界面来查看数据部署是关键的。只使用多个云数据提供商的用户界面进行数据运营的数据团队,在聚合元数据以进行有效的数据运营,以及BizFinOps方面将会损失。
还有要聚合的数据部署,通常是由于:
-
新数据到达
-
改变数据实现逻辑
目前使用工作流编排工具和 GitHub 操作或类似工具来处理这些。这会造成不连贯性 — 数据团队需要检查多个工具,以了解数据表何时已更新,它们的定义是什么等等。当然,你可以购买一个可观察性工具。然而,这又是另一个用户界面,另一个工具,另一个成本。
拥有一个真正的单一操作面板,用于编排、可观察性和某种运维功能,将是一个杀手级功能,我很想在 Codat 使用,我们在那里整合了一整套开源和闭源供应商 SAAS 工具。
可观察性或元数据收集
我在前一节中提到了这一点,但可观察性和元数据收集对于强大的数据管道至关重要。
这里重要的一点,我认为可观察性平台忽略了的是将观察放在管道本身。否则,向数据工程师呈现元数据、诸如“这个失败了”或“那个表已过时”的信息是不可取的,因为它们是 A)事后(为时已晚)和 B)与管道运行无关。当然 —— 一个表是坏的。但它是因为某人刚刚推送了一个更改还是因为有新数据到达?
最近我参加了安德鲁·琼斯的另一个精彩演讲,他谈到了 1、10、100 金字塔。

预防成本为 1 美元,缓解成本为 10 美元,一旦为时已晚,成本为 100 美元。图片由安德鲁·琼斯提供,获得许可后发布。
-
预防成本 — 在数据提取点预防错误将花费你 1 美元。
-
更正成本 — 在提取后有人纠正错误将花费你 10 美元。
-
失败成本 — 让错误数据通过流程到达最终位置将花费你 100 美元。
可观察性工具处于黄色到红色区域。如果它们设置在你的生产数据库上,很可能你正在与时间赛跑,以在有人意识到之前解决问题。
分析师使用 hacky SQL 修复错误数据位于黄色区域。
带有可观察性的编排位于绿色和黄色之间。如果您的所有数据管道都可以访问所有元数据,并且可以在您实现和更新表和视图时执行数据质量测试,那么当这些测试失败时,您可以随时暂停管道。这意味着不会有错误数据进入生产环境。
这非常强大,这就是为什么我相信,拥有一个具有对细粒度元数据访问权限的编排工具是未来的发展方向(我创办的初创公司正在做这个)。
总结
我们正在摆脱数据不可知的工作流编排工具加上不稳定或不存在的持续集成,转向统一的持续数据集成和交付。
将会有平台使数据团队能够对数据集进行完整、可靠和高效的版本控制,并拥有坚固的数据管道。这些平台将内置可观察性功能,虽然许多平台尚未实现端到端,但肯定感觉事情正在朝着这个方向发展。
有许多成熟的数据工具正在做这个。例如,对于数据仓库的持续集成和交付,Y42 拥有“虚拟数据构建”(Virtual Data Builds)的概念,基本上与本文的第一部分相同。对于数据湖环境,Lake FS / Treeverse 的 Einat Orr 最近在[LinkedIn 上发表了文章](https://www.linkedin.com/feed/update/urn:li:activity:7126949272050106369/?commentUrn=urn%3Ali%3Acomment%3A(activity%3A7126949272050106369%2C7126969193651924992)&dashCommentUrn=urn%3Ali%3Afsd_comment%3A(7126969193651924992%2Curn%3Ali%3Aactivity%3A7126949272050106369)&dashReplyUrn=urn%3Ali%3Afsd_comment%3A(7126970713164439552%2Curn%3Ali%3Aactivity%3A7126949272050106369)&replyUrn=urn%3Ali%3Acomment%3A(activity%3A7126949272050106369%2C7126970713164439552))- 他们所做的在功能上非常相似,并且在 Snowflake 中很容易实现(我在这里写了一篇文章)。SQLMesh 的“企业”版本(不要相信开源的快乐,这是一家风险投资支持的企业。他们会试图赚钱,就像我们所有人一样)具有内置的可观察性和版本控制功能,非常酷。
你当然可以使用类似 Airflow 这样的工具来完成所有这些。该死的,如果你愿意,你甚至可以用 Airflow 煮早晨的咖啡。我想问题是——你有时间、耐心和专业知识来编写所有这些代码吗?还是像我一样,只是想把事情做好?🐠
我们如何赢得了第一个政府 AI 项目
加拿大先导参与项目的故事
·
关注 发表在Towards Data Science ·14 分钟阅读·2023 年 4 月 14 日
--
照片由Tetyana Kovyrina拍摄,来源于 pexels.com。此外,骑行时可以欣赏到亚历山大桥的美丽景色。
目录
-
概述
-
保持法律法规的最新
-
定义新的采购流程
-
人工智能引擎
-
导航监管库存
概述
早在 2018 年初,我们参与了加拿大联邦政府的创新采购工具,目标很简单:找到有助于现代化所有法律和法规的创新。这一创新采购工具有许多目标:
-
识别过时、负担沉重或根本不适用的法律;
-
比较其他国家和地区的法律,以了解如何在特定领域(例如健康或能源部门)应用领域特定的法规;
-
在法规的存货中检索第三方参考资料。
幸运的是,我们赢得了通过使用定制 NLP 平台来帮助现代化加拿大法规的竞标。然而,所有发生在这个项目之前的事情都以某种方式影响了该项目。
这是一个关于政府采购、人工智能采纳以及利用技术解决现实世界问题的故事。
使法律和法规保持最新
每个政府都有一个要求,确保法律不仅对所有公民公平,而且适用。几个世纪以来,哲学家们一直在争论和辩论个人在社会中的关系,而公平和平等的概念通常是民主社会中的主要驱动力。
正如我们在政府两极分化中看到的那样,法律的采用速度可能会非常缓慢。通常,民选官员会通过一项法律,将一系列责任分配给一个机构或部门。这个负责的机构可以在其任期内根据需要更新法规。
没有任何法律是石刻的,但它们通常被认为是相对固定的。然而,随着技术的发展,创新通常超越了标准法律程序的速度。政府应该仅在技术开始对其民众造成伤害时才进行干预吗?还是应该有一个系统来更快地应对社会问题?
所有层级的法规使用的一个重要工具是在法规中引入技术参考。这种整合允许法规保持相关性,通过指向外部信息源,从而有能力通过指向更新的标准来更新法规。
在法规中整合文档的机制
简单的义务传递如下:
-
法案将规定,“遵循法规”,并授权一个负责的机构(如部门)来管理和更新该法规;
-
该法规将规定,“应用标准”;
-
标准将包含所有规定的公民和组织所需的活动。

法律、法规与技术参考之间的关系。图片由作者提供。
参考文献的整合:双刃剑
监管机构如何在每个工业和技术领域专门化并设计法律法规?简单的答案是:他们做不到。技术细节变化太快,专家无法列出所有参与者应遵循的义务。因此,考虑到各种标准定义组织(SDO)如国际标准化组织(ISO)提供的优质工作,纳入这些专家指南以减少审查时间,并使法律具有相关性和适用性,是有意义的。
然而,这一过程的另一面是相当严重的问题:将专业知识外包给外部代理可能构成非法放弃民主。简单来说,非选举产生的官员在法规中制定指令。
如果技术监督的责任被推给 SDO,那么民主如何保持对其法律的控制?通过审查、更新和管理每次监管审查中要纳入的标准。
然而,快速发生的情况是,这些法规中出现的参考资料过多,审查法规以查找散布的参考资料的认知负担急剧上升,每次审查需耗费数千小时。
实际上,这个项目的真正动机是手动工作的成本。所有这些复杂性加在一起意味着需要大量的人力来审查和更新这些法规。与我们共享的 KPI 是:每次审查需要 1,500 个工时。
想在家里玩?尝试查找 加拿大职业健康与安全法规中的所有参考资料!提示:其中一些以“CSA”开头,但并非全部!
自动化的需求
为什么这是一个机器学习问题?从逻辑上讲,人们应该认为 IBR 列表在某处是可用的。另外,为什么我们不能简单地从几个 SDO 下载列表并进行字符串匹配呢?
我们尝试了所有这些。我们很快确认了加拿大司法部和各部门提出的问题。主列表更像是遗留知识而非系统记录,许多团队成员离开时带走了所有参考位置。
以标准为例——ISO 13485。(我的第一份工作是在医疗设备领域,因此这个标准一直在我心中。)该法规的正式标题是“ISO 13485:2016 医疗器械 — 质量管理体系 — 监管要求”。整个标题。如此复杂的标题中,字符串匹配可能出现许多问题。我们发现的一些问题是:
-
错误的字符。许多标准在其标题中使用了长破折号而不是短破折号(“ — “与“-”)。
-
官方名称与解释性名称。 有时冒号位置不正确,额外的字符(空格和标点)被错误地添加。
-
简短名称。 在一个文件被完整纳入后,缩写版本(例如,“ISO 13485”)。
-
地理名称。 国家级标准制定组织(如 NIST 或 CSA)会重新解释标准,使其在本国稍微更具适用性,因此标题会略微变化(前缀为“CAN/CSA”)。
回到 ISO 13485 的例子,这里是医疗器械法规中的一个引用:“[…] (f) 一份质量管理体系证书的副本,证明制造该设备的质量管理体系符合 National Standard of Canada CAN/CSA-ISO 13485, Medical devices — Quality management systems — Requirements for regulatory purposes,并根据需要进行修订。[…]”

不一样。这个问题在所有标准中都存在。图片由作者提供。
我们确实通过早期字符串匹配找到了一些例子,并开始建立我们的数据集,但从根本上说,这并不是一个可靠的方法,当然也不会提供任何程度的保证。
除了加拿大司法部报告的困难外,所使用的语言还有一些额外的问题,需要本项目解决:
-
真正的纳入与仅仅的引用。 仅仅因为一个文件被提及并不意味着它具有法律约束力。因此,必须明确区分文件是如何被提及的。
-
静态引用与动态引用。 引用是否指向标准或文件的特定版本,还是指向该文件的最新版本?该文件是否可以在相关机构不知情的情况下进行更新?
-
过时的标准。 文件是否仍然适用?文件是否仍然可检索自标准制定组织?如果其引用的标准全部被废止,政府是否仍然能够从根本上执行相关规定?
因此,需要一个能够自动化所有这些过程的工具。
旁注:我最喜欢的纳入方式仍然是 Mushuau Innu First Nation Band Order (SOR/2002–415):
在本命令中,“采纳”包括按照因纽特传统的采纳。
定义新的采购流程
推动政府创新,任何衡量标准下都绝非易事。在这个特定的情况下,时机更是糟糕。许多公开的谴责导致部门不愿与这一过程相关联,加拿大公共服务学院(CSPS,一个帮助改善政府功能的非政治实体)承担了责任的负担。
为了克服这些挑战,采购人员主导了定义新的采购流程,该流程更紧密地与技术领域的采购对接,允许根据能力选择大量供应商,然后根据愿意投标的意愿从中选择少数供应商,最后邀请这些供应商提交投标。这个监管创新列表是今天 AI 供应商列表的原型。
以下是导致制定此列表的一些总结因素:
-
2016 年 3 月:监管法规审查常务委员会发布了一系列建议,解决与引用实践相关的问题。初步努力旨在解决建议 4,“[…] 修改《法定文书法》,建立一个包含引用材料的中央存储库,并要求制定法规的机关每年提供一份所有引用文档的列表。” 这是在一系列诉讼之后,诉讼声称任何和所有在监管文档中表示的文件都应免费提供。因此,需要识别所有可用的参考资料,以评估参与特定行业的财政负担。
-
2018 年春季:尖锐的审计长 2018 年报告公布,涉及加拿大最大 IT 迁移项目,结果确实不尽如人意。列举了许多关于凤凰薪酬系统改革的忽视问题,审计长称其为“难以理解的失败”。IT 采购过程必须发生改变,这威胁到最初的 AI 采购工作和 IBR 项目。
-
2018 年 5 月:在踏入任何风险投资之前,财政委员会秘书处(在之前的文章中描述)决定邀请行业参与者,以更好地了解 AI 在管理法规库存方面的潜力。在人工智能行业日上,“[…] TBS [在] 寻找行业合作伙伴和学术研究人员,以帮助将人工智能方法如高级数据分析(ADA)和机器学习(ML)应用于各种类型、范围和复杂性的法规。”
-
2018 年 6 月至 9 月:尽管对最新技术的发展感到自信,但担心另一个 IT 采购灾难,TBS 请求加拿大公共服务学院(一个尽可能不带政治色彩的政府组织)主导制定一个能够提供 AI 服务的公司列表的采购过程。获胜者的总合同金额?$1.00。
-
2018 年 11 月:与我们的咨询合作伙伴 MNP 一起,我们受邀竞标 CSPS-RFP-18LL-1593:演示项目,试点应用人工智能方法于引用法规,这是一个仅限预审供应商的项目。所有供应商都从演示日资格审查过程中选出。
-
这一过程取得了成功,我们的联合体赢得了它,使得政府能够推进这一新项目。“PSPC 正与加拿大公共服务学院 (CSPS) 合作,首次采购使用 AI 来源列表。 招标公告 用于 CSPS 互动监管评估平台的招标于 2019 年 2 月 28 日在 BuyandSell.gc.ca 上发布。” (来源)
作为现在的联邦 AI 供应商列表的原型,加拿大公共服务学院是一个主要的项目拥有者。该部门专注于通过培训、教育和意识提升公共服务劳动力,是一个令人耳目一新的非党派职能——每个人都希望政府更加高效。
AI 引擎
让我们为这个项目提供一些背景。
-
这是一个实体识别问题,但大多数实体无法从中央列表中检索到(这个项目的一个目的实际上是生成这个列表)。
-
我们必须考虑许多潜在的 OOV 问题,因为我们不想错过任何被遗忘的 SDO。
-
实际的合同范围是在 2017 年,因此 BERT 当时甚至尚未发布。变压器技术本来会是很棒的。
我们采取的方法基于 Chiu 和 Nichols (2016) 的论文,题为 双向 LSTM-CNN 的命名实体识别。感谢我的团队不懈地研究所有潜在的 NER 论文。当时,这篇论文不仅在 NER 任务中表现最佳,而且在处理前所未见的实体时显示出最高的成功率,这一点在这里非常重要。
加拿大司法部通过提供机器可读格式的整个加拿大法规库让我们的工作稍微轻松了一些。然而,没有可用的训练数据,也没有实体的起始示例,只有一些人丢失关键便条的可怕故事。
我们进行了数周的面谈,与员工讨论他们知道哪些标准,并获得了大量支持,以识别可以指示引用存在的启发式方法。“… 根据 X”,“不时修订”以及其他几个术语帮助我们在法规中筛选这些野外的踪迹。
我们甚至尝试过部署自定义标注工具,但结果仍然非常差。我们不得不依靠自己通过搜索提供的启发式方法来收集基础数据集。
为什么选择这个模型?
我们真正喜欢这篇论文的原因在于它编码了人类用来识别外部引用的相同启发式方法——尤其是那些更像代码而非单词的引用。该模型查看以下特征:
-
词序列模式的变化;
-
字符序列的变化;以及
-
大小写变化。
来自论文:
命名实体识别是一个具有挑战性的任务,传统上需要大量的特征工程和词典来实现高性能。在这篇论文中,我们提出了一种新颖的神经网络架构,通过混合的双向 LSTM 和 CNN 架构自动检测词级和字符级特征,从而消除了大多数特征工程的需求。
我们坚持选择这个特定模型的原因在于它在识别从未见过的实体方面的强大能力,特别是在第三方标准的背景下。此外,识别实体的开始和结束的精确机制几乎与加拿大司法部的个体处理方式完全一致:通过查看触发词、大小写变化和字母数字序列的变化。

图片由作者提供。
这里有一篇关于原始论文的 深入文章。
这个模型真正创新之处在于它采用了 Frankenstein 方法,重新利用为 LSTM(专注于词)和 CNN(专注于字符)准备的特征。与其选择最佳方法,不如将所有内容混合在一起,让命运决定。
这是 LSTM 方面:

“[……] 用于命名实体标注的(展开的)BLSTM。多个表格查找词级特征向量。CNN 从字符级特征中提取固定长度的特征向量。对于每个词,这些向量被连接在一起并输入到 BLSTM 网络,然后进入输出层。”(来自 论文)
这就是 CNN 方面:

“[……] 卷积神经网络从每个词中提取字符特征。字符嵌入和(可选的)字符类型特征向量通过查找表计算。然后,这些特征被连接在一起并传入 CNN。”(来自 论文)
为了清晰起见,这里是项目中使用的模型构建代码的演示:
注意:其余代码正在与客户进行一些奇怪的许可谈判,因此我们会在弄清楚情况后开源代码。再说一遍,直接使用 transformers 吧。
结果
我们关注的结果有两个类别:
-
模型的整体性能;以及
-
工具对我们客户的可用性。
鉴于上下文,模型结果是可以接受的。
模型的整体 F1 分数为 0.726,使用了上述原始结构。(有趣的是,在同一数据集上,一个基本的 LSTM 的 F1 分数为 0.277,所以肯定有所改进。)
更深入地了解模型的实用性,我们查看了 1. 是否存在引用(“O”),2. 我们是否可以准确预测引用的开始(“B-ref”),以及 3. 我们是否能够检测到我们是否在引用内部(“I-ref”)。这意味着我们更接近于如何通过指示引用位置来改善操作员的工作,而不是优化已识别段落的开始和结束。这些结果更具前景:
此外,对于一些观察到的假阳性较高的细心观察者:如果你查看结果模型的表现,这些可以描述为法规中的真阳性。例如,模型将突出显示“代码”,这与之前提到的 IBR 相关。
导航监管库存
在模型处理完所有法规之后,接下来就是以某种方式显示结果,以便搜索和识别结果。
略过可访问性和平台设计的细节(毕竟我们使用了 Laravel 前端和 Flask 后端 — 这是 2018 年),我们构建了一个简单的平台,能够处理监管库存,搜索法规,并在法规内部识别具体的引用情况。
这个前端是许多需求开始被澄清和调整的地方,因为客户看到工具的功能。

SA/IBR 门户的视图,查看法规搜索页面。图片由作者提供。

SA/IBR 门户的视图,查看医疗设备法规页面。图片由作者提供。

高亮显示的详细视图。这是在SOR-86–304: 加拿大职业健康和安全法规第九部分中。图片由作者提供。
验证和可用性
当我们在合同中关闭许多功能时,我们开始注意到监管库存中的内容限制。某些附属功能由于数据中缺乏足够的候选示例而无法实现。(例如,有一项与静态与流动参考相关的条目 — 通常以“[…] 最新版本的 […]”来表示 — 但我们最初的搜索只找到了 5 个流动参考示例。)
有时,在 AI 咨询中,项目交付需要在数据评估和模型构建后进行澄清。在这种情况下,我们进行了许多关于工具对合同期望的实用性的讨论(基于数据的现实),这使我们能够削减平台,确保交付的代码实际解决了法规起草者的核心问题。
在验证核心模型(捕捉第三方参考文献)时,一个关键问题是:它是否有效?这个问题有很多技术子层(如上所述的开始和内部指标),但关键的业务案例进一步明确了:这个工具是否允许审阅者识别法规中的*****所有*** 第三方参考文献?
加拿大司法部团队对结果进行了多次审查。经过几周的讨论,他们确认我们的工具没有遗漏任何一个参考文献。我们进行了项目后质量控制,确保没有未解决的问题,但我们的工作已经完成。❤️
免责声明:这篇文章还涉及到一个原型采购清单,这个清单是加拿大联邦政府现在著名的 AI 供应商清单的前身。第一个实际的项目由KPMG 和 Lixar(现为 BDO)获得;我们在这篇文章中绝不声称我们赢得了那个特定的项目,或者我们是公共职能领域中第一个 AI 项目。然而,在 AI 供应商清单之前,确实有一个用于采用 AI 技术的原型采购工具,我们提供了一个非常有趣的方法。这是那个项目的故事。
其他你可能感兴趣的文章
-
PyTorch 与 TensorFlow 在基于 Transformer 的 NLP 应用中的比较
-
用于批处理的 MLOps:在 GPU 上运行 Airflow
-
数据集偏见:制度化歧视还是足够的透明度?
-
实施企业 AI 战略
-
异常值感知聚类:超越 K 均值
-
Rorschach 测试在深度学习图像分类中的应用
如果您对这篇文章或我们的 AI 咨询有额外的问题,请随时通过 LinkedIn或通过 电子邮件联系我们。
-Matt.
GPT 模型对提示的响应效果如何?
这是关于生成性 AI 使用与测试的研究第二部分
·
关注 发表在 Towards Data Science ·10 min 阅读·2023 年 5 月 24 日
--
图片来源于 Debby Hudson 在 Unsplash
阅读第一部分: 验证 OpenAI GPT 模型性能
提示工程 是来自自然语言处理行业的一个专有术语,用来描述为大型语言模型(LLMs)制定更好问题的过程,以获得更好的回答。换句话说,它是有效提示设计的艺术。
如果你仔细想想,我们在日常生活中都在进行提示工程。有些人把好奇心直接表达成问题。其他人则反复在 Google 搜索信息,直到找到答案。坚持不懈的人,特别是孩子,会不断问“为什么?为什么?但为什么?”或改变最初的问题,直到得到令人满意的答案。
对于大型语言模型(LLMs),提示工程的工作方式非常相似。用户期望 LLMs 根据给定的输入,即提示,产生输出。当输出不符合用户的期望时,他们可能会修改提示以期获得更好的结果。提示工程是解锁 LLMs 的关键,使其能够发挥最大潜力。它是引导一艘拥有 4000 亿参数的大船向正确方向前进的小舵。
从 Kolena 的一位机器学习工程师的角度来看,本文提供了评估提示有效性的指南。我们将使用 OpenAI 的 GPT-3.5 Turbo 模型继续探索新闻文章的文本摘要问题。
必要的下一步
之前的博客文章 探讨了一个文本摘要问题,即通过使用生成型 AI 来总结新闻文章。我们使用了 CNN-DailyMail 数据集(11,490 篇新闻文章,每篇都有人工编写的真实摘要),并比较了五种不同 GPT 模型的性能。最终,Turbo (gpt-3.5-turbo) 生成了最佳摘要,并在最低成本下获得了最高的整体性能。
我们的下一步是确定不同提示是否会导致显著的性能差异和定量评估 Turbo 对其给定提示的遵循程度。
评估指标
我们希望避免定性的性能评估,因为我们声明为“更好”的摘要可能与其他人的意见不一致。定性比较总是引入人为偏见和主观性的问题。相反,我们将再次专注于定量性能评估。
之前的博客文章 已经定义了基于文本的性能指标,如 BERT_rec(BERTScore 的召回率)、wc_inf(推理词数)和 ROUGE_L。在这里,我们将增加三个新的指标:
-
METEOR: 一个更好的 BLEU,声称与人工判断的相关性更高(0 到 1)。
-
formality: 一个表示文本非正式或正式程度的分数(0 到 1)。
-
pii_count:一种计算文本中专有名词或个人数据的指标。
我们可以使用这些指标来识别模型生成冗长或非正式摘要时的表现不佳情况。总的来说,生成的摘要应该保持简短并保留原始新闻文章的正式语气。
提示词
我们对 Turbo 模型使用了八个提示词生成了八组不同的摘要。我们在下面列出它们,附上一个/两个单词的别名(加粗),随后是原始提示词:
**1\. Basic
** Summarize: \n{TEXT}
**2\. Professional
** Professionally summarize this article: \n{TEXT}
**3\. Uppercase
** Summarize this news article USING {MIN} TO {MAX} WORDS: \n{TEXT}
**4\. Detailed
** Summarize this news article: \n\n{TEXT}\n\nUse {MIN} to {MAX} words like a reporter
**5\. DetailedV2
** Professionally summarize this news article like a reporter with about {MIN} to {MAX} words :\n{TEXT}
**6\. 60 Words
** Summarize this news article:\n\n{TEXT}\n\nUse about 60 words
**7\. No PII
** Summarize this news article:\n\n{TEXT}\n\nUse about 60 words. Do not use any proper nouns.
**8\. Point Form
** Summarize this news article in short point form sentences:\n{TEXT}
Note that MIN and MAX are bounds for a flexible word count range based on the provided ground truths.
性能差异
要查看哪些提示词在基于文本相似度生成摘要时表现更好,我们可以按表现顺序(最好的在前)列出每个指标的前五个提示词:
**BERT_rec:** DetailedV2, Detailed, Uppercase, Professional, Basic
**ROUGE_L:** 60 Words, Detailed, DetailedV2, Uppercase, Basic
**METEOR:** DetailedV2, Uppercase, Detailed, Basic, Professional
对于BERT_rec、ROUGE_L和METEOR,前五个提示词始终包括DetailedV2、Detailed、Uppercase和Basic。不深入细节,DetailedV2在BERT_rec上比Basic好 0.4%,在ROUGE_L上好 4%,在METEOR上好 3%。

ROUGE_L 分布:DetailedV2 > Basic
从上面的图表中,我们可以观察到蓝色直方图的值高于紫色直方图。这表明**DetailedV2** 在 **ROUGE_L** 上表现优于 **Basic** ,这是一个改进。总的来说,这四个提示词(DetailedV2、Detailed、Uppercase和Basic)在文本相似度指标上表现优于No PII或Point Form等其他提示词。这并不令人惊讶,因为No PII和Point Form是具有不同输出要求的提示词,这可能导致生成的摘要中保留的信息量下降。

ROUGE_L 分布:60 Words > DetailedV2
一个有趣的观察是**60 Words** 是产生最高 **ROUGE_L** 得分的最佳提示词,尽管60 Words在其他文本相似度指标中不在前五名。我们可以从60 Words分布的紫色条形图中看到它向右突出在蓝色分布的右侧。如果你最看重ROUGE_L指标,你会想使用60 Words作为提示词,这可能不是最明显的选择,因为DetailedV2似乎表现一直很好。这就是提示词工程的重要性。
遵守提示词
为了衡量 GPT Turbo 在多大程度上遵守我们的提示词,我们将从三个不同的方面进行考察:PII 计数、词数和正式性。
GPT Turbo 在多大程度上遵守限制 PII 的要求?
对于一些 GPT 用户,生成输出的一个潜在要求是避免个人身份信息(PII)。许多人重视个人信息的安全和隐私,一些企业可能需要保持数据匿名性——这也可能有法律要求。
让我们看看 Turbo 对No PII的遵守情况,该提示要求“不要使用任何专有名词”,并将其与Basic和60 Words作为基准进行比较。我们应该期望No PII中出现零 PII,或者至少比基准中的 PII 数量显著更少。

PII 数量分布:Basic > 60 Words > No PII
从上述情况来看,Turbo 模型确实减少了No PII(紫色)生成摘要中的 PII 数量,但远未达到零 PII 的平均水平。尽管大多数生成的摘要都有非零数量的 PII,但No PII的摘要平均比其他提示少十个 PII。
让我们看一个例子,并将No PII和60 Words生成的摘要与提供的事实真相摘要进行比较:
事实真相: 威尔士魔术师加雷斯·贝尔在周日的惊人胜利中首开纪录。皇家马德里在对阵格拉纳达的比赛中打入九球,给巴塞罗那施加了压力。贝尔在胜利后享受了西班牙阳光下的烧烤。克里斯蒂亚诺·罗纳尔多在这场令人振奋的团队表现中打入了五球。
No PII: 加雷斯·贝尔在皇家马德里以 9–1 战胜格拉纳达的比赛中打入一球。他还在比赛后发了推文,并在当晚享受了一次烧烤。克里斯蒂亚诺·罗纳尔多在同一场比赛中打入了五球。皇家马德里在之前的失利后有所反弹。
60 Words: 加雷斯·贝尔在比赛中首开纪录,并在皇家马德里以 9–1 战胜格拉纳达后庆祝,还享受了一次烧烤。克里斯蒂亚诺·罗纳尔多在比赛中打入了五球,包括一个八分钟的帽子戏法,给西甲领头羊巴塞罗那施加了压力。这场胜利发生在皇家马德里在经典德比中输给巴塞罗那之后。
No PII的摘要中有 16 个 PII,而60 Words的摘要中有 27 个 PII。这是有道理的,因为很难在不提及贝尔和罗纳尔多名字的情况下为关于他们的文章写摘要。
事实上,即使是最好的创意作家,创造一个没有专有名词的直接摘要也是非常困难的。作为对自己的挑战,试着在不使用任何名字或地点的情况下准确描述罗纳尔多和贝尔。GPT Turbo 决定创建一个包含专有名词的摘要,以保持生成摘要的实用性,因此 Turbo 并没有完全避免使用专有名词,但确实努力减少 PII 的使用。
GPT 对字数限制请求的遵守情况如何?
字数限制在提示中很重要,以控制潜在的令牌数量并满足自定义要求。当提示中要求使用大约 60 个单词(No PII,60 Words)时,我们应该期望输出的平均字数为 60 个单词。对于有字数范围的Detailed,我们不希望摘要超出或低于该范围。

总结字数的分布:要点式 > 基础 > 详细 > 60 字 > 无个人信息
这个图表显示,当提示定义了 60 个词的目标字数时,Turbo 生成了更短的总结(No PII 以红色和 60 Words 以黄色表示)。然而,大多数生成的总结长度超过 60 个词,介于 80 到 100 个词之间。这并不理想,但也不算差距过大。在 No PII 和 60 Words 之间,No PII 更好地遵循了提示的指示。
对于 Detailed,Turbo 一贯生成 75 到 125 个词的总结。由于真实总结的平均字数约为 55 个词,大多数生成总结的预期范围应该是 50 到 100 个词。对于较长的文章和较长的真实总结,预期的总结字数范围不应扩展到 200 个词。看起来 Turbo 一直在接近定义范围的上限。也许它希望生成“最佳”质量的总结,而实现这一点的方法是尽可能使用更多的词语。
额外说明:当人们编写要点式笔记时,通常会简洁明了,因此令人惊讶的是,Point Form 让 Turbo 生成的总结平均为 120 个词。虽然我们可能期待简洁,Turbo 可能没有理解这一隐含的假设。有时,在设计提示时你需要添加额外的细节。
关于字数,GPT Turbo 确实会考虑提示中的要求,但并不会完全遵守它们。我们只能推测Turbo 选择使用更多的词语,而不是仅仅满足于较短但“质量较差”的总结。生成的总结词数越多,其包含所有文章重要信息的潜力也越大。
GPT 在回应要求提高专业性方面表现如何?
专业性很难衡量,因此我们使用正式性评分来评估文本是否比其他文本更正式/专业。
从总体正式性评分来看,排名前三的提示(依次是)是 No PII、60 Words 和 Basic,但没有一个要求“专业”书写的总结。让我们将 Basic 和 Uppercase 作为 Professional 和 DetailedV2 的基准。

正式性评分的分布:基础 > 详细 V2 > 专业 > 大写
从这个直方图来看,我们可以看到Basic、DetailedV2 和 Professional 生成的总结在正式性上没有明显差异。最简单且装饰最少的 Basic 提示生成了最多的非常专业/正式的总结。有趣的是,Uppercase 似乎生成了最不正式的总结。难道大写锁定的要求对 GPT 有负面影响吗?GPT 现在是否有了人工感情?
有时,最简单的提示会产生最佳效果。在这种情况下,很明显要求 GPT Turbo“专业”并不会提高正式性评分。
学习要点
对于 GPT 的 Turbo 模型,不同的提示确实会导致轻微的性能差异。就我们的问题而言,文本相似性度量的差异不像我们定制的度量那样明显。对于不同大小的 LLM,文本相似性度量的性能差异可能更为明显,但也以独特的方式遵循提示。
就服从性而言,Turbo 显然努力遵循每个提示的具体指示,如明确的字数范围和最小化 PII。不幸的是,当提示要求更专业或正式的写作风格时,它的性能或行为没有显著不同。也许 GPT 的默认设置是始终保持专业。
结论
设计一个周到的提示与微调模型的方式类似。虽然提示工程的主题不断增长和演变,但了解提示如何指导 LLM 不仅仅是定性检查时是重要的。当提示缺乏详细说明,或者输出要求突然变化时,定性测试效果不佳。
要了解提示在整个数据集中的价值,将定量测试尽可能结合起来是重要的。我们对 Turbo 模型在文本摘要任务中的定量评估方法包括以下步骤:
1\. Build a meaningful test dataset
2\. Define text summarization performance metrics and custom metrics
3\. Compute all metrics for all data points in the dataset
4\. Compare resultant metrics of baseline prompts with the results of other prompts
a. Compare each average metric values at a surface level
b. Compare the difference in performance distributions for relevant metrics
通过这些步骤,任何人都可以自动测量他们的 LLM 如何遵循提示规格。此外,这些步骤概述了一种测试范式,在这种范式中,定性分析逐渐失去相关性。那些希望以最少的人力投入实现自动化和可扩展性的人应转向定量测试实践。
请继续关注未来的帖子,我们将讨论定量测试,以揭示 LLM 的隐藏和不同的行为!剧透:可能会出现性能意外下降的情况。
如果你渴望了解更多关于这篇博客的详细信息,这里有一个来自Kolena的CSV 文件链接,展示了八个提示的所有汇总性能指标(还有更多内容)。
所有图片均来自Kolena,除非另有说明。请注意,类似的图表可以在常见的框架中手动生成,例如 Matplotlib。
数据科学如何加速循环经济?
克服向循环经济过渡中的操作挑战的可行数据科学技巧
·
关注 发表在 Towards Data Science ·11 分钟阅读·2023 年 10 月 19 日
--
(图源:作者)
循环经济是一个在其中尽量减少废物,并且资源不断被重复使用或回收的系统。

循环经济的好处 — (图源:作者)
随着当前线性经济模型达到其极限,对新型循环商业模式的讨论变得越来越重要。
什么阻碍了我们的进步?
这些讨论主要集中在
-
阻碍过渡的操作和商业障碍
-
提高回收材料使用的替代策略
-
租赁模式以减少环境足迹
作为零售公司数据科学经理,您如何支持这一转型?
我们可以利用系统生成的数据,识别机会,通过数据科学创建可持续的循环经济,以克服这些障碍。

用于管理供应链网络的系统生成的数据 —(作者图片)
在这篇文章中,我们将以数据科学经理的视角,支持一个时尚零售公司运营转型。
💌 免费直送到您邮箱的新文章:Newsletter
📘 您的供应链分析完整指南:Analytics Cheat Sheet
Summary
I. Transition to a Circular Economy
1\. What is the environmental impact of a T-shirt?
2\. Data-driven Process Design
II. Overcoming the Operational Challenges
1\. The Opacity of Supply Chain Networks
2\. The Low Residual Value of Used Products
III. Material Efficiency & Recycled Materials Usage
1\. Raw Material Optimization with Linear Programming
2\. Supply Chain Network Optimization
IV. Conclusion
向循环经济过渡
从线性模型到循环经济的演变是一个持续的过程,具有显著的业务和运营影响。
这种转变不仅仅涉及废物管理或回收。
这需要我们在设计、生产、销售和使用商品或服务的方式上进行全面变革。

线性模型与循环模型 —(作者图片)
在实施循环经济之前,第一步是估算我们当前线性模型的环境影响。
一件 T 恤的环境影响是什么?
以您在快时尚店购买的T 恤为例。
它在其生命周期中的环境影响是什么?
生命周期评估(LCA)是一种评估产品或服务在其整个生命周期内环境影响的方法论。
快速时尚服装的生命周期评估 —(作者图片)
-
原材料来自使用自然资源和能源的不同供应商。
-
制造场所利用自然资源将这些材料转化为成品,同时排放污染物和二氧化碳。
-
成品被送到商店并销售给最终顾客
-
顾客使用这些产品直到处置
我们如何支持生命周期评估的自动化?
这种描述性分析方法可以通过我们分析团队实施的商业智能解决方案来自动化。
挑战在于收集和处理交易数据。
-
来源于不同系统,这些系统可能彼此不互通
工厂管理系统与仓库管理系统
-
使用不同的格式(非结构化 vs. 结构化)
Excel 实用报告 vs. WMS 交易数据库
构建您的 LCA 的数据来源 —(作者图片)
💡 数据分析师和工程师可以使用中央数据仓库来实施管道,以收集和处理原始数据,供 LCA 计算使用。
多系统数据协调的示例 — (图像由作者提供)
最终结果可能是一个自助服务的协调表数据库,包含涵盖从原材料收集到商店交付的整个周期的交易记录。
💡 欲了解更多详情,
了解生命周期评估如何帮助企业评估产品在其整个生命周期中的环境影响……
towardsdatascience.com
然后,您的可持续发展部门可以使用这些表格进行计算,并估算每个过程的资源使用量或 CO2 排放量。
我们可以做些什么来减少我们的环境足迹?
解决方案设计的数据分析
现在您已经自动化了生命周期评估,您的可持续发展团队已经能够设定基准。
2022 年的总 CO2 排放量为 75k 吨 CO2eq。
根据联合国可持续发展目标(SDG),您的公司承诺在 2023 年前减少30%。
下一步是制定实现这一目标的路线图。
我之前分享过基于数据的方法来实施减碳计划。
- 可持续采购:选择一组最小化原材料采购环境影响的供应商
如何利用数据科学来选择最佳供应商,同时考虑可持续性和社会指标……
towardsdatascience.com
- 可持续供应链优化:设计一个最佳的工厂和仓库网络,以最小化排放
帮助您的组织将可持续采购与供应链优化结合起来,以抑制成本和环境影响……
towardsdatascience.com
- 循环经济:创建一个物流链以收集和再利用顾客退回的物品,从而节省原材料
你如何利用数据科学支持快时尚零售商实施循环经济?
towardsdatascience.com
循环模型可以减少最高的碳排放,因为它直接影响产品。
然而,上述案例研究主要集中于利用先进分析生成有洞察力的建议。
既然你的模型告诉我们该怎么做,你如何支持实施?
因为这样的过渡可能完全扰乱你当前的供应链操作。
物流团队:我们如何组织我们的卡车队来收集退回的物品?
物流操作需要支持,以确保平稳实施并避免干扰业务或影响盈利能力。
下一节将展示数据科学如何支持这一运营转型。
克服运营挑战
尽管向循环经济的转变看起来很有前景,但它带来了各种运营挑战。
供应链网络的透明度
这是向循环经济过渡的主要障碍。
这批成品是从哪个工厂来的?
在传统线性经济中,从原材料到最终产品的货物来源和路径通常需要澄清。
你能追踪从工厂到商店的货物吗? — (图像由作者提供)
你的公司可能不了解其供应链的清晰度,超出其直接供应商和客户的范围。
-
你能追踪到你商店里出售的任何物品的生产设施吗?
-
你能将离开工厂的成品与用于生产它的原材料批次联系起来吗?
透明度的缺乏使得追踪产品回溯源头变得困难。
这在采用循环经济实践方面造成了障碍。
循环供应链网络的示例 — (图像由作者提供)
在循环经济中,了解产品的生命周期 (从原材料到处置) 对于实施有效的回收和再利用策略至关重要。
在不透明的情况下,我们无法确保材料的来源是可持续的,使用是高效的,并且回收是妥善的。
你可能也会错过减少废物、简化操作和更有效使用资源的机会。

循环经济的绩效指标示例 — (图像由作者提供)
一个最佳的循环经济需要一套最小的性能指标,如
-
生产和运输关键绩效指标的跟踪
生产遵循度(%)、补货周期时间(天数)
-
通过
物流成本(欧元/件)、退货率(%)和收集周期时间(天数)
-
识别回收过程中的潜在改进,使用回收率(%)、污染流(%)和处理周期时间(天数)
我们如何使用分析解决方案来监控这些关键绩效指标(KPIs)?
这些物流和制造关键绩效指标需要来自多个系统的数据,这些系统具有不同的数据格式和数据库结构。

(图像来源于作者)
💡 你的团队可以在解决这些问题上发挥关键作用,
-
连接到跟踪产品沿价值链的系统,并收集带有时间戳的数据
-
存储和处理这些数据,以创建一个可以用于生成报告、仪表板和优化模型的中央信息来源
-
实施由供应链和可持续发展团队设计的关键绩效指标的自动报告工具
这种综合视图能够识别低效问题,提供客户可追溯性,并促进过渡。
获取更多供应链可追溯性的分析解决方案,
实施运营指标以监控和改进国际分销网络的性能
towardsdatascience.com
我们能否利用数据确保这一模型的经济可行性?
低残值的使用产品
在我们现有的线性经济中,产品是为了消费和处置而设计的,而不是为了重用或回收。
产品残值是指在产品使用完毕并完成其初始生命周期后的剩余价值。
一旦使用,这些产品通常残值很低。
我们如何设计一个盈利的循环模型?
因此,收集使用过的产品进行回收的成本往往超过了回收材料的价值。
这危害了循环商业模式的经济可行性,并使企业不愿意过渡。

正向物流与逆向物流 — (图像来源于作者)
比如,以我们的 T 恤为例
-
正向物流成本效益高,因为我们通过大型卡车使用优化路线将 T 恤装载到完整的集装箱中
-
相比之下,逆向物流非常昂贵,因为我们以单件收集使用过的衣物,涉及复杂的分类和回收流程,这些流程需要定制化的处理方式。
由于逆向物流操作可能变得非常复杂,很容易遇到回收比处置更昂贵的情况。
数据分析在解决这些问题中可以发挥关键作用。
我们可以模拟这些循环模型,以找到能带来效率和成本效益的最佳设置。
- 通过优化模型简化逆向物流操作,以最小化收集和分类旧物品的成本
模拟商店配送频率对时尚零售商 CO2 排放的影响
- 设计替代商业 模型,例如租赁而非销售的订阅模式
使用数据分析模拟循环模型对快时尚行业的 CO2 排放和水使用的影响…
利用这些额外的模拟模型,你可以支持实施一个盈利的逆向流动来回收(或再利用)你的产品。
材料效率与回收材料使用
随着我们向循环模型转变,材料效率成为主要关注点。
使用更少的材料生产产品并在生产过程中减少废料,可以带来显著的经济和环境效益。
如何生产以确保成品的耐用性和可重复使用性?
使用线性规划进行原材料优化
高效使用材料可以显著减少废料,并支持资源的可持续使用。
我们可以使用各种类型的面料,包括棉花、聚酯、亚麻和丝绸。

材料混合与最终产品特性 — (作者提供的图片)
每种面料具有不同的成本和属性,如耐用性、舒适性和环境影响。
制造商的目标是最小化生产的总体成本,同时满足必要的质量和可持续性标准。

原材料优化问题 — (作者提供的图片)
什么是最佳组合以满足我们的盈利能力和可持续性目标?
这是一个多维优化问题,我们在尝试在特定约束下优化成本和可持续性。
T 恤生产示例 让我们设想一个场景,其中 T 恤必须包含
-
至少 40% 棉花以增加舒适度
-
由于可持续性指南,聚酯含量不得超过 30%
-
丝绸的比例不得超过总材料的 10%
这个问题可以通过线性(或非线性)优化与 Python来建模和解决。

线性/非线性规划问题的制定 — (图片由作者提供)
-
参数: 用于生产 T 恤的每种原材料的数量
-
约束: 上述列出的约束
-
目标函数: 最小化环境足迹、最小化成本或两者的混合
你的团队可以使用像PuLP 或 SciPy这样的库来创建一个优化模型,以测试多个目标函数,最终找到最佳的材料组合。
查看这个示例以获取更多细节,
使用线性规划创建一个满足营养要求的廉价餐条的最佳配方
towardsdatascience.com
我们需要在哪里生产,以最可持续的方式交付到市场?
供应链网络优化
为了引入回流过程进行回收,我们必须完全重新定义我们的供应链网络。
供应链优化可以帮助我们利用数据分析来找到最佳的工厂、配送和回收中心组合,从而最小化成本影响。
供应链网络设计问题 — (图片由作者提供)
使用 Python 的线性规划模型可以帮助我们
-
选择合适的地点建立我们的回收中心
-
优化废物收集的流动
-
规划分类和回收中心的容量
目标是最小化收集、分类和回收废物的成本,以实现一个盈利且可持续的循环模式。
有关如何创建网络优化模型的更多细节,
你在设计供应链网络时是否考虑了需求波动?
towardsdatascience.com
我希望这些示例能为你提供足够的灵感,以支持你的可持续发展部门努力过渡到更可行的经济模型。
结论
数据科学可以成为向盈利的循环经济过渡的强大推动力,通过克服复杂障碍和优化资源。
未来,分析团队可能会在帮助公司过渡到循环经济模型方面发挥关键作用。
你如何将这些见解分享给运营团队?
在网页应用程序上部署你的工具。
向组织中的每个人提供便捷的访问是一种支持数据驱动的处方模型实施的好方法。
你可以通过将这个解决方案部署到一个供运营和业务人员使用的网页应用上,将其产品化。
我已经使用 VIKTOR 平台部署了三个模型,这些模型我在文章中进行了介绍。
可持续供应链优化网页应用
访问应用程序试用一下! — [应用程序]
ABC 分析和帕累托图应用
访问应用程序试用一下! — [应用程序]
生产规划
访问应用程序试用一下! — [应用程序]
有关我如何做到的更多细节,请查看这篇文章
帮助你的组织将可持续采购与供应链优化相结合,以遏制成本和环境影响……
towardsdatascience.com
关于我
让我们在Linkedin和Twitter上连接。我是一名供应链工程师,使用数据分析来改善物流运营并降低成本。
如果你对数据分析和供应链感兴趣,请查看我的网站。
一个专注于数据科学、个人生产力、自动化、运筹学和可持续性的技术博客……
samirsaci.com](https://samirsaci.com/?source=post_page-----61a1a22287e5--------------------------------)
参考资料
-
利用数据分析的物流绩效管理,萨米尔·萨西,Towards Data Science
-
什么是循环经济?,萨米尔·萨西,Towards Data Science
-
蒙特卡罗模拟下的强健供应链网络,萨米尔·萨西,Towards Data Science
-
食品制造中的原材料优化与 Python,萨米尔·萨西,Towards Data Science





















浙公网安备 33010602011771号