TowardsDataScience-博客中文翻译-2022-二十二-

TowardsDataScience 博客中文翻译 2022(二十二)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

在图像中查找聚类

原文:https://towardsdatascience.com/finding-clusters-in-an-image-96fe5eefc784

不仅仅是在表格数据中查找聚类

Alex Shuper 在 Unsplash 上拍摄的照片

我们也很容易在数据的行和列中找到聚类。但是如何在图像中找到集群呢?让我用最近在卡塔尔举办的 2022 年世界杯的一张图片来说明这个问题。

这里展示的是我在巴西对塞尔维亚的比赛中拍摄的照片。这张照片是在理查森的惊人进球之前拍摄的。

自己拍的 2022 世界杯足球赛巴西 vs 塞尔维亚的照片(图片由作者提供)

你能在照片中观察到任何集群吗?视觉上,您可以找到一群人,如下所示。

2022 年世界杯足球赛巴西对塞尔维亚的照片,显示了集群,由本人拍摄(图片由作者提供)

现在让我告诉你如何自动识别集群的技术步骤。

1.检测图像中的对象

第一步是检测图像中的所有对象。检测什么对象将基于聚类的目的。这里我们的目标是检测人群。检测照片中的人物可以在 YOLOv5 上完成。使用 YOLOv5 进行物体检测的结果如下所示。

用 YOLOv5 进行物体检测(图片由作者提供)

YOLOv5 还提供了额外的统计数据,如对象检测的数量。在这张照片中,有 17 个物体(人)。下面是一段代码

import yolov5
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
import numpy as np
import pandas as pd

# load pretrained model
model = yolov5.load('yolov5s.pt')

# set model parameters
model.conf = 0.25  # NMS confidence threshold
model.iou = 0.45  # NMS IoU threshold
model.agnostic = False  # NMS class-agnostic
model.multi_label = False  # NMS multiple labels per box
model.max_det = 1000  # maximum number of detections per image

# set image
img = 'brazil_vs_serbian.png'

# inference with test time augmentation
results = model(img, augment=True)

# parse results
predictions = results.pred[0]
boxes = predictions[:, :4] # x1, y1, x2, y2
scores = predictions[:, 4]
categories = predictions[:, 5]

2.识别物体的 X 和 Y 位置

下一步是识别物体的 X 和 Y 位置。YOLOv5 结果存储图像中每个对象的位置。位置存储为 xmin、ymin、xmax、ymax。我们可以取 xmin 和 xmax,以及 ymin 和 ymax 的平均值,求出物体的 x,y 位置。

对象的坐标(图片由作者提供)

可以将坐标转换为数据框。有一个调整很重要,它与反转 Y 位置有关。通常,图像以 Y 轴反转的方式存储。这意味着 Y=0 在图像的顶部。在计算过程中,我们可以通过减去图像的高度(这里是 2253)来还原位置。高度也可以自动确定,但是为了简化代码片段,我直接输入了高度。

df = results.pandas().xyxy[0]
df['x'] = (df['xmin'] + df['xmax'])/2.0 
df['y'] = (2253-(df['ymin'] + df['ymax'])/2.0) #2253 is image height

结果在这里显示为数据帧。

位置为 x,y 的数据框(图片由作者提供)

3.使用基于密度的聚类来查找聚类。

现在我们已经准备好寻找星团了。找到图像中的聚类意味着找到与图像的其他部分相比有更多对象的区域。本质上,这意味着根据图像中对象的密度来寻找聚类。一种有效的算法是基于密度的聚类(DBSCAN)。下面显示的是散点图中绘制的结果。

DSBSCAN 结果(图片由作者提供)

DBSCAN 算法将绿点识别为密集区域的一部分。下面是 DBSCAN 算法的代码片段。

#Density clustering
X = StandardScaler().fit_transform(df[['x','y']])
clustering = DBSCAN(eps=0.5, min_samples=5).fit(X)
df['c'] = clustering.labels_

4.分析结果

现在让我们对比照片分析散点图上的结果。

分析结果(图片由作者提供)

瞧啊。散点图看起来像是足球比赛的可视化数字表示!

结论

在图像中寻找聚类是一项有趣的技术,也有许多用例。在博客中,您看到了体育分析的一个用例。此外,还有各种其他用例,如识别公共空间中的过度拥挤或识别购物者在购物中心花费更多时间的区域。

希望你喜欢这个博客!

用我的推荐链接加入 Medium

https://pranay-dave9.medium.com/membership

订阅每当我发布一个新的故事时,请及时通知我。

https://pranay-dave9.medium.com/subscribe

额外资源

网站(全球资讯网的主机站)

你可以访问我的网站,这是一个学习数据科学的无代码平台。【https://experiencedatascience.com】T5T6

Youtube 频道

这是我的 YouTube 频道
https://www.youtube.com/c/DataScienceDemonstrated的链接

在 Python 中查找两个纬度和经度之间的距离

原文:https://towardsdatascience.com/finding-distance-between-two-latitudes-and-longitudes-in-python-43e92d6829ff

地理数据

在 Python 中查找两个纬度和经度之间的距离

地理坐标的特征工程距离

为模型准备数据时,有时查找两个位置之间的距离可能会很有用。这篇文章展示了如何在 Python 中从两个地点的纬度和经度找到两个地点之间的最短球面距离和旅行距离。

照片由 Unsplash 上的 Dariusz Sankowski 拍摄

🌐地理坐标

我们可以根据地理坐标定位地球上的任何地方。一个位置的地理坐标由它的纬度和经度位置组成。

作者图片

📍纬度

纬度是北极和南极之间垂直位置的量度。假想的水平纬线称为纬线赤道是一条特殊的纬线,位于 0°纬度,介于南北极之间。

作者图片

📍经度

经度是水平位置的度量。假想的垂直经线称为经线本初子午线是一条特殊的子午线,位于 0°经度。谈到时区,经度也很重要。

在本初子午线的对面,反子午线位于经度 180 度。

纬线像一个环,而经线像半个环。

📦设置

我们将导入这些库,并在澳大利亚墨尔本设置两个示例位置坐标:

import numpy as np
import pandas as pd
from math import radians, cos, sin, asin, acos, sqrt, pifrom geopy import distance
from geopy.geocoders import Nominatim
import osmnx as ox
import networkx as nxlat1, lon1 = -37.82120, 144.96441 # location 1
lat2, lon2 = -37.88465,  145.08727 # location 2

安装 [osmnx](https://osmnx.readthedocs.io/en/stable/#installation)可能比较繁琐。按照本教程设置环境的一个简单方法是使用 [Google 合作实验室](https://research.google.com › colaboratory):首先,创建一个新的笔记本;二、用!pip install osmnx安装库;第三,重启:从顶部菜单进入运行时>重启运行时>环境准备就绪!

🔵最短球面距离

地球的赤道半径是 6378 公里,极地半径是 6356 公里,所以地球不是一个完美的球体。然而,假设地球是球形的,使我们能够容易地找到近似的距离,这在某些应用中是令人满意的。在本节中,我们将使用哈弗辛公式从地理坐标中找出两个位置之间的球面距离。让我们先熟悉一下哈弗辛函数

哈弗辛函数如下:

使用哈弗辛公式可以计算出圆心角的哈弗辛,等于球面距离除以球面半径:

我们可以使用哈弗辛函数的第一个定义来转换这个公式,并重新排列它,使d在左边:

现在,是时候把这个翻译成 Python 代码了。有两点需要强调:首先,纬度和经度是以度为单位的,所以在我们将它们代入公式之前,我们必须将它们转换成弧度。其次,我们将使用全球平均值 6371 公里作为球形地球的半径。

def calculate_spherical_distance(lat1, lon1, lat2, lon2, r=6371):
    # Convert degrees to radians
    coordinates = lat1, lon1, lat2, lon2
    # radians(c) is same as c*pi/180
    phi1, lambda1, phi2, lambda2 = [
        radians(c) for c in coordinates
    ]  

    # Apply the haversine formula
    a = (np.square(sin((phi2-phi1)/2)) + cos(phi1) * cos(phi2) * 
         np.square(sin((lambda2-lambda1)/2)))
    d = 2*r*asin(np.sqrt(a))
    return dprint(f"{calculate_spherical_distance(lat1, lon1, lat2, lon2):.4f} km")

或者,我们可以使用带余弦的哈弗辛函数的第二个定义,并重新排列等式来表示d:

这可以用 Python 表达如下:

def calculate_spherical_distance(lat1, lon1, lat2, lon2, r=6371):
    # Convert degrees to radians
    coordinates = lat1, lon1, lat2, lon2
    phi1, lambda1, phi2, lambda2 = [
        radians(c) for c in coordinates
    ]

    # Apply the haversine formula
    d = r*acos(cos(phi2-phi1) - cos(phi1) * cos(phi2) *
              (1-cos(lambda2-lambda1)))
    return dprint(f"{calculate_spherical_distance(lat1, lon1, lat2, lon2):.4f} km")

更实际的是,我们可以使用geopy包在一行代码中获得球面距离:

print(f"{distance.great_circle((lat1, lon1), (lat2, lon2)).km:.4f} km")

另外,用geopy包很容易找到其他距离。例如,我们可以这样基于椭球地球假设得到距离:distance.distance((lat1, lon1), (lat2, lon2)).km。有不同的椭球模型可用,前面的函数使用了WGS-84模型,这里有一个替代语法:distance.geodesic((lat1, lon1), (lat2, lon2), ellipsoid=’WGS-84').km。如果你想了解更多关于这个图书馆的信息,请查看它在计算距离上的资源。

🚗最短旅行距离

在这一节中,我们将看看如何使用 OpenStreetMap[OSMnx](https://github.com/gboeing/osmnx)包找到最短的旅行距离。我们将从绘制城市网络图开始:

mel_graph = ox.graph_from_place(
    'Melbourne, Australia', network_type='drive', simplify=True
)
ox.plot_graph(mel_graph)

这段代码可能需要一段时间来运行。我们用network_type='drive'来获取行驶距离。其他网络类型也可用。例如,如果我们在步行距离之后,那么我们将代码调整为network_type='walk'

现在,我们可以使用图表找到行驶距离:

orig_node = ox.distance.nearest_nodes(mel_graph, lon1, lat1)
target_node = ox.distance.nearest_nodes(mel_graph, lon2, lat2)
nx.shortest_path_length(G=mel_graph, source=orig_node, target=target_node, weight='length')

从位置 1 到位置 2 的最短行驶距离为 15,086.094 米。值得注意的是,从位置 2 到位置 1 的距离不一定与从位置 1 到位置 2 的距离相同。

我们可以创建一个计算距离的函数:

def calculate_driving_distance(lat1, lon1, lat2, lon2):
    # Get city and country name
    geolocator = Nominatim(user_agent="geoapiExercises")
    location = geolocator.reverse(f"{lat1}, {lon1}")
    address = location.raw['address']
    area = f"{address['city']}, {address['country']}" # Get graph for the city
    graph = ox.graph_from_place(area, network_type='drive', 
                                simplify=True) # Find shortest driving distance
    orig_node = ox.distance.nearest_nodes(graph, lon1, lat1)
    target_node = ox.distance.nearest_nodes(graph, lon2, lat2)
    length = nx.shortest_path_length(G=graph, source=orig_node, 
                                     target=target_node, weight='length')
    return length / 1000 # convert from m to kmsprint(f"{calculate_driving_distance(lat1, lon1, lat2, lon2):.2f} km")

就是这样!如果你想了解更多关于这个库的信息,请查看 OSMnx 用户参考OSMnx 示例

照片由粘土堤Unsplash 上拍摄

您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果您使用 我的推荐链接成为会员,您的一部分会费将直接用于支持我。

谢谢你看我的帖子。如果你感兴趣, 以下是我的一些帖子的链接:
◼️ 用这些技巧充实你的 Jupyter 笔记本
◼️ 用这些技巧整理你的 Jupyter 笔记本
◼️ 有用的 IPython 魔法命令
◼️Python 虚拟数据科学环境简介
◼️git 数据科学简介
◼️ 你会发现有用的 python 中的简单数据可视化
seaborn(python)
◼️️给熊猫用户的 5 个建议
◼️️ 在熊猫中编写 5 个常见的 SQL 查询

再见🏃💨

使用无监督学习寻找平面布置图上的“m”细节

原文:https://towardsdatascience.com/finding-m²-details-on-a-floorplan-using-unsupervised-learning-f8f8891befa9

使用 opencv、pytesseract、scikit-learn 和 pandas

斯文·米克在 Unsplash 上的照片

由于可以在搜索中应用个性化过滤器,在网上浏览一处房产会带来无数好处。然而,有一个非常重要的过滤器被遗漏了(至少对于像[ 1 ,[ 2 ]这样的英国房产搜索网站来说是这样的):房产的平方英尺分割。

有很多情况下,我会发现一个伟大的财产(大小,位置,价格等。)直到我看到它的平面图细节。如果我们可以根据房间的平方米来过滤属性结果,那将会节省我们很多时间。因此,我决定做一个实验,看看如何提取给定房地产广告平面图上的所有“m”细节。

介绍

在这篇文章中,我阅读了一个在线平面图图像并对其进行了处理,然后解释了为什么我使用无监督学习算法将相关的文本块分组在一起,演示了进一步的清理并测试了几个不同平面图图像的结果。 在结论部分 ,我提到了我遇到的局限性和任何改进的空间(隐喻性的)。

步骤:

第一步:读取并处理输入的平面图图像(opencv)
第二步:检测字符及其位置(pytesserac)
第三步:聚类字符(scikit-learn)
第四步:处理聚类(熊猫)
测试:

导入库

我们需要以下内容:

from matplotlib import pyplot as plt
import cv2
import numpy as npimport pandas as pd
pd.set_option('display.max_rows', 500)
pd.set_option('display.max_columns', 500)
pd.set_option('display.max_colwidth', 500)%matplotlib inline
plt.rcParams["figure.figsize"] = (40,20)import pytesseract
pytesseract.pytesseract.tesseract_cmd = r'/usr/local/bin/tesseract/'from sklearn.preprocessing import StandardScaler
from sklearn.cluster import DBSCAN

步骤 1:读取和处理输入图像

我们可以很容易地阅读如下平面图图像:

img = cv2.imread('Dummy Floorplanner Input Image.png')plt.imshow(img)

按作者输入图像

我们正在处理输入图像,这样,当输入到 pytesseract 时,它可以揭示更多关于文本块的信息。请阅读下文了解更多关于阈值的信息。

https://pyimagesearch.com/2021/04/28/opencv-thresholding-cv2-threshold/

经过处理后,我们的输入图像如下所示:

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
plt.imshow(gray)
plt.imshow(blur)
plt.imshow(thresh)

按作者分类的图像(灰色、模糊、阈值)

第二步:检测字符及其位置

Pytesseract 从左上到右下开始读取图像像素(就像看书一样)。因此,如果一个房间的信息与另一个房间的信息在同一水平层上,那么两个房间的文本将被连接到一个房间,并且没有逻辑方法来正确解析完整的字符串,如下所示(餐厅和卧室的名称和尺寸被连接)。

print(pytesseract.image_to_string(thresh))

作者图片

幸运的是,还有另一个 pytesseract 函数(image_to_boxes)可以检测每个字符及其在图像上的位置。

df_img = pd.DataFrame([x.split(' ') for x in pytesseract.image_to_boxes(thresh).split('\n')],                     columns=['char', 'left', 'top', 'width',        'height', 'other'])df_img = df_img[ ~ df_img['left'].isnull()]# dropping whitespace characters like
# [',' '.' '/' '~' '"' "'" ':' '°' '-' '|' '=' '%' '”']df_img = df_img[ ~ df_img['char'].str.contains(r'[^\w\s]')].reset_index(drop=True)df_img[['left', 'top', 'width', 'height']] = df_img[['left', 'top', 'width', 'height']].astype(int)df_img.head(20)

作者图片

让我们根据这些字符的 x-y 坐标来绘制它们。

fig, ax = plt.subplots()ax.scatter(df_img['left'].tolist(), df_img['top'].tolist())for i, txt in enumerate(df_img['char'].tolist()):
    ax.annotate(txt, (df_img['left'].tolist()[i],
                      df_img['top'].tolist()[i]), 

                textcoords='data', 

                fontsize=28)

作者图片

基本上,我们需要对近距离内的数据点(字符)进行分组。

第三步:聚类字符

如果需要,请阅读下面的帖子来了解 DBSCAN 是如何工作的。

使用 DBSCAN,我们能够将相关的字符块组合在一起。当该聚类算法预测值为-1 时,该数据点被视为异常值。

X = StandardScaler().fit_transform(df_img[['left', 'top']].values)db = DBSCAN(eps=0.19, min_samples=10)db.fit(X)y_pred = db.fit_predict(X)
plt.figure(figsize=(10,6))
plt.scatter(X[:,0], X[:,1],c=y_pred, cmap='Paired')
plt.title("Clusters determined by DBSCAN")df_img['cluster'] = pd.Series(y_pred)
df_img.groupby(['cluster'])['char'].apply(lambda x: ' '.join(x)).reset_index()

作者提供的图片

从上面的图像中我们可以看到,聚类算法在对平面图图像上的字符进行分组时工作得非常好。然而,它仍然需要一些进一步的清洁和处理。

步骤 4:处理集群

通常,在平面图上,我们有位置的类型(卧室、厨房、花园等。),其尺寸以米为单位,最后以英尺为单位。有了这些信息,我们可以假设我们第一次看到一个聚类的数字是我们可以连接文本的最低水平。换句话说,在一个群中,如果在一个数字第一次出现的级别之下有一个数字,这可能是一个平方英尺信息。让我们从数据框中删除所有平方英尺(不是米)的行。

df_cc = df_img.copy().reset_index(drop=True)for cluster_no in df_cc['cluster'].unique():

    index_char_top_list = []

    # if the data point is not an outlier
    if cluster_no!=-1:

        index_char_top_list = [(index, char, top) for index, char, top in 

            zip(df_cc[(df_cc['cluster']==cluster_no)].index, 
                df_cc[(df_cc['cluster']==cluster_no)]['char'].values, 
                df_cc[(df_cc['cluster']==cluster_no)]['top'].values)ifchar.isdigit()] if index_char_top_list:

        df_cc = df_cc[~ ((df_cc['cluster']==cluster_no) & (df_cc['top'] <= ( index_char_top_list[0][2] - 5 ))) ]df_img.shape[0], df_cc.shape[0]
# (149 rows went down to 104 rows)

让我们创建一个函数,它可以将一串数字解析成宽度、长度维度。

def dimension_splitter(input_text):

    input_text_len = len(input_text) if input_text_len%2==0:
        split_text_by = int(input_text_len/2)
    else:
        split_text_by = int(input_text_len/2+0.5)

    dim1 = input_text[:split_text_by] dim2 = input_text[split_text_by:]

    dim1 = float('{}.{}'.format(dim1[:-2], dim1[-2:])) dim2 = float('{}.{}'.format(dim2[:-2], dim2[-2:])) return dim1, dim2

让我们对清理后的聚类数据框进行分组,并引入维度列。

df_cc_grouped = df_cc.groupby(['cluster'])['char'].apply(lambda x: ' '.join(x)).reset_index(name='text')df_cc_grouped['text_digits'] = df_cc_grouped.apply(lambda x: ''.join([y for y in x['text'] if y.isdigit()]), axis=1)df_cc_grouped['text_digits_len'] = df_cc_grouped.apply(lambda x: len([y for y in x['text'] if y.isdigit()]), axis=1)df_cc_grouped = df_cc_grouped[(df_cc_grouped['cluster']!=-1) & 
                              (df_cc_grouped['text_digits_len']>=5)].reset_index(drop=True)df_cc_grouped['room'] = df_cc_grouped.apply(

    lambda x: x['text'][:[x.isdigit() for x in x['text']].index(True)].strip()

                                                      , axis=1)df_cc_grouped['length'] = df_cc_grouped.apply(lambda x: dimension_splitter(x['text_digits'])[0]

                                                      , axis=1)df_cc_grouped['width'] = df_cc_grouped.apply(lambda x: dimension_splitter(x['text_digits'])[1]

                                                     , axis=1)df_cc_grouped['surface'] = np.round(df_cc_grouped['length'] * df_cc_grouped['width'], 2)df_cc_grouped

作者提供的图片

我们可以正确地提取所有的平方米信息。让我们看看我们的逻辑是否适用于其他图像。

试验

在第一项测试中,在检测步骤中,pytesseract 误读了“接待室”中的一些字母,并在检测到 Utility 之前多加了一个“a”。然而,在此之后,无监督学习如预期那样工作,并正确地揭示了所有信息。

img_test1 = cv2.imread('Dummy Floorplanner Test 1.png')plt.imshow(img_test1)df_cc_grouped_test1

作者提供的图片

执行第 2 号测试时没有出现任何问题。

img_test2 = cv2.imread('Dummy Floorplanner Test 2.png')plt.imshow(img_test2)df_cc_grouped_test2

作者提供的图片

尽管平面布局不同(纵向或横向),我们能够在大多数地方提取平方米信息。

结论

在这篇文章中,我演示了如何使用 OCR 库(pytesseract)来检测房地产平面图上的字母和数字,然后使用无监督学习算法(DBSCAN)来收集相关信息,以找到每个房间的平方米信息。

如果 pytesseract 可以正确地检测字符,我们可以毫无问题地找到房产的平方米分割。我们利用了这样一个事实,即英国的房产平面图具有几乎相同的结构:(1)房间的类型,下面的尺寸(2)米,下面的尺寸(3)英尺。但是,该脚本需要进一步开发,以处理具有不同结构的平面图图像,或者对缺失/未检测到的数据进行一些科学猜测。

感谢阅读!

通过分析寻找适合商业地产的产品市场

原文:https://towardsdatascience.com/finding-product-market-fit-for-commercial-space-with-analytics-5930434a9494

为房地产营销和分析的直觉增加数据

照片由 Unsplash 上的伊斯特弗莱、马库斯拍摄

本文代表我个人的想法、观点和分析,与任何其他组织无关。

为租户匹配商业空间对双方来说都是一项具有挑战性的工作。房东通常会对企业做出不确定的长期承诺,反过来,企业也会承担在租赁期内支付租金的风险。

一个空间的价值通常取决于它的位置。在许多地区,你可以开车或步行去感受一个地区。你还看到其他什么企业在运营?谁倾向于走在街上?虽然作为一个开始很有帮助,但这是一个有限的时间点样本,并且错过了更大的画面——该领域与其他选项相比如何?

历史上,数据被汇总到共同认可的地理位置进行分析,作为展示位置的一种手段。我们直观地了解一个城市或邮政编码。不过,这些地区很大,对于决定长期租赁的租户来说,对周围地区更有信心有助于做出更好的决定。

今天,我们可以超越传统的邮政编码或城市界限,但仍然可以开发直观的数据工具来帮助企业做出这些关键决策。我们可以问类似这样的问题:“有多少人住在离一处房产 15 分钟步行路程的范围内?他们的人口构成是怎样的?”或“附近还有哪些其他类型的企业?”

在阅读之后(或之前)尝试一个交互式仪表盘。

具有多种方法和时间选项的仪表板示例

我很幸运生活在一个快速发展和成长的地区。每天,我都会路过处于不同发展阶段的建筑。我注意到一栋建筑的开发商在 LinkedIn 上发布了一个帖子,寻找商业租户,以占据一个混合用途开发项目的底层。

北卡罗来纳州罗利市中心附近新开发项目的物业营销

我想知道——谁最适合这份工作?我大致了解这个领域,但是我可以引入哪些数据来揭示新的机会或见解呢?

免责声明:这不考虑许可限制、空间成本、建筑限制或其他相关信息。这只是一个分析框架,用于确定一处房产对潜在租户的价值。

邻里

以下是一些关于格伦伍德南区的基本信息:

  • 它位于罗利市中心附近,大型豪华公寓建筑发展迅速
  • 它最近的名声是作为一个聚会社区,俱乐部和酒吧散布在这个小区域
  • 考虑到最近的发展、许可申请和价格上涨,未来 3-5 年可能会转向更高档的区域

除了高端办公空间和众多的酒吧和餐厅之外,它非常适合步行,步行即可到达多所大学。

房产

新开发项目位于 Glenwood South 的北端,交通繁忙。它位于住宅和商业混合区的正中央,附近步行即可到达大学、高中和小学,此外还有高速公路入口(在下图中用黄色标出;不完全是高速公路,但也不是普通的街道)。

来自谷歌地图的带有注释的酒店位置图片

鉴于该物业的位置和周边地区,我的观点是,它将从步行交通中受益最多,其次(尽管仍然很有价值)是位于高能见度街道上。我怀疑——像许多城市地区一样——将会有有限的停车位,限制了在特定时间可以停靠的汽车数量。

所以,知道了以上内容,这让我们不禁要问…有多少人住在离该地点步行的距离内,他们的人口构成是什么?该位置周围还有哪些企业可能会让某类租户受益或受到抑制?

分析

这就是有趣的地方——至少如果你是像我一样的分析呆子。

让我们假设在一个城市地区,某人将步行 20 分钟到达一个位置。这是地图上 20 分钟步行半径的样子。给定这个半径,我们对该区域发生的活动了解多少?

距离该位置约 20 分钟的步行半径;作者图片

数据显示,在这段步行时间内,大约有 10,500 人和 25,000 个工作岗位。考虑到数据报告和今天之间的时间差,我估计这两个数字今天会更高。这些数字明显高于县基准,表明这是一个密集的混合使用区(“生活-工作-娱乐”,俗话说)。

在那些生活在 20 分钟步行范围内的人中,大约 50%在 18 岁至 34 岁之间(相比之下,该县为 24%),40%的工作是办公室工作(相比之下,该县为 30%),这意味着“年轻专业人士”可能是人口的一个适当特征。

在人口和办公室工作之间,每天有足够多的人在 20 分钟的步行距离内填满当地的 NHL 竞技场

该地区非常适合步行,满分为 100 分,得分约为 85 分,典型家庭收入约为 73,000 美元。家庭收入低于县平均水平,然而,典型的家庭规模也更小,这表明人均可支配资金更高。

估计的覆盖区域数据,距离酒店大约 20 分钟的步行时间

在步行范围内大约有 100 家有执照的餐馆,其中大部分在南边,因为北边是住宅区。

步行半径范围内获得许可的餐饮机构的大致位置;作者图片

步行区内约有 16 个被视为“豪华”的公寓区——更不用说额外的公寓大楼了——还有同一个街区的多块土地,作为奖励,它们可能在租约有效期内开发。

最后,来自北卡罗来纳州的汽车交通数据表明,在给定的一天,大约有 11,500 辆汽车经过这个地方。这是 2020 年的数据,受到 COVID 的影响,因此,我的观点是,今天这一数据已经大幅增加。

这意味着什么

因此,综上所述,我们可以说这个地区非常适合步行和城市化,周边人口年轻、富裕且不断增长。在汽车交通、当地人口和附近的工作岗位之间,它可能有一个每天至少 40,000 人的可寻址市场(或者,用当地人可以欣赏的术语来说,足以填满附近 UNC 教堂山的篮球体育场两倍)。

最高价值的解决方案是一个企业将受益于(1)周围地区的人口和工人;(2)在(非常)邻近的小学接送孩子的家长,以及(3)在附近步行距离内上高中的 2000 多名学生。

哪些类型的企业可以从这一目标受众和市场规模中受益?

银行在寻找市场份额的过程中一直在这个三角地带进行着激烈的竞争,因此资本相对较低的租户(相比之下,比如说,必须建造一个商业厨房)可能是一家寻找高知名度分支机构的银行。他们可以通过步行,加上路过的道路交通带来的隐性品牌效应,接触到年轻、高收入的客户群。

高端健身精品店可能会有机会,但另一个需要考虑的问题是,所有的豪华公寓楼都将有健身房,一些写字楼也将有健身房,因此潜在市场可能会比步行距离内的人口/员工的预期要小。

酒吧或餐厅/快速服务餐厅(QSR)是一种可能性,尽管如地图所示,许多已经存在,更多的正在计划中,作为新开发的一部分,在附近步行即可到达,因此它需要是一个独特的概念,尚未在更广泛的区域内捕获。

像 CVS 或 Walgreens 这样的药店可能会根据空间的大小而发挥作用,因为它们都有大约一英里远的位置,这在城市环境中可能会错过希望立即方便的人的市场机会。

我要提到的最后一个想法是类似于 Amazon Go 商店的东西,这无疑是一个很长的机会/非常低的可能性,但我认为具有很高的价值。它的价值跨越年龄组,它将受益于混合使用驱动交通通过。该位置位于步行交通的中心,可用于取包裹或用作微型配送中心。除了所讨论的步行可达性之外,一个企业还可以通过 10 分钟的车程到达约 120,000 人,释放出更多的价值。它的快速进出模式将有利于(可能的)低可用停车位。

这只是现代数据工具和资源可用能力的一个示例。我们选择步行时间,因为它与位置相关,但任何驾驶时间或径向距离(即 2 英里的圆圈)也是选项。餐厅被引用是因为它们是空间的常见用途,但如果医疗保健客户是目标受众,它也可以很容易地被牙科诊所或医生取代。

如今,数据可以为商业房地产代理商带来的本地市场知识和专业知识增加一层,从而为决策带来更多的说服力。找到正确的位置从来都不容易,但是增加相关的、有针对性的信息的数据访问可以显著增加决策的价值。

试试附带的仪表盘

对这类材料和分析感兴趣? 在 LinkedIn 上联系我,或者在 jordan@jordanbean.com 给我发邮件。

使用 TF-IDF 和 Python 查找相关文章

原文:https://towardsdatascience.com/finding-related-articles-with-tf-idf-and-python-d6e1cd10f735

如何找到与 TF-IDF 相关的文章?用 Python 实现 TF-IDF 算法。

马丁·范·登·霍维尔在 Unsplash 上的照片

正如我在关于 总结文字 的文章中提到的,我收集了过去十年的新闻文章。总结完这些文章后,我面临的下一个挑战是找到相关的文章。如果我有一篇文章,我如何找到与这篇文章相关的文章?立即导致问题什么是相关文章

经过初步研究,选择 TF-IDF 作为算法。这是一个复杂性有限的旧算法,因此可以创建一个实现,并从中学习如何找到类似的文章。通常,有更好的实现可用,例如在 scikit-learn 中,但是我更喜欢为了学习而构建自己的实现。请注意,完全实现需要一些时间。

TF-IDF 算法

那么,什么是 TF-IDF 呢?我们分解缩写的时候,说的是词频——逆文档频,一口。术语频率是指特定术语(词)在文本中出现的相对次数;

词频(图片由作者提供)

文档 d 中的 t 项的 TF 等于文档 d 中出现的 t 项的频率除以文档 d 中所有t’项的频率之和。示例:

'The green man is walking around the green house'
TF(man) = 1 / 9 = 0.11
TF(green) = 2 / 9 = 0.22

这是对一个单词在文档中的重要性的度量,根据文档的长度进行校正。

IDF 是对单词在整个语料库(分析的所有文档的集合)中的重要性的度量。这个想法是,如果一个单词出现在很多文档中,它不会增加很多信息。标准功能是

反向文档频率(作者图片)

IDF 的计算方法是将文档数( N )除以一个术语出现的文档总数,并取其对数值。如果一个术语在 10.000 个文档中出现 10 次,则 IDF 等于 3。在同一组文档中出现 100 次的术语的 IDF 值为 2。当一个术语出现在所有文档中时,IDF 值等于 0.0。对数值用于减少 IDF 可能具有的大范围值。

最后,一项的 TF-IDF 值等于 TF 乘以 IDF:

TF-IDF 公式(图片由作者提供)

上面的公式是 TF 和 IDF 的标准公式。更多变种可以在 TF-IDF 的维基百科页面找到。

为所有文档的语料库中的每个单词计算 TF-IDF 值,包括术语出现次数为 0 的文档。作为例子,下面两句话

0: 'the man walked around the green house',
1: 'the children sat around the fire'

产生以下 TF-IDF 值:

计算值(图片由作者提供)

对于所有唯一的单词,为每个句子计算 TF-IDF 值。在以下情况下,该值为 0.0

  • 由于 TF 为 0.0,特定单词不会出现在句子中(例如,第二句中的“green”)
  • 一个特定的词出现在所有的句子中,例如“大约”,因为 1 的对数值等于 0.0。在这种情况下,所有句子的 TF-IDF 值都是 0.0

计算距离

既然计算了 TF-IDF 值,那么可以通过计算这些值之间的余弦相似度来确定两个文档之间的距离。这是通过使用每个单词的 TF-IDF 值(上面数据帧中的一行)作为向量来完成的。

余弦距离是 N 维空间中从原点到两点的直线之间的角度差。在二维空间中形象化是相对简单的。上面的例子是 9 维的,超出了我的想象。在二维空间中:

余弦差的定义(图片由作者提供)

余弦差可以使用欧几里得点积公式计算:

计算余弦差(图片由作者提供)

它是两个向量的点积除以向量长度的乘积。当两个向量平行时,余弦差为 1.0,当它们正交时,余弦差为 0.0。

该功能需要应用于语料库中的所有文档组合。从 A 到 B 的差等于从 B 到 A 的差,从 A 到 A 的差是 1.0。下表显示了距离矩阵在对角线上的镜像:

差 A-B 等于 B-A(图片由作者提供)

完成所有这些计算后,是时候开始实现了!

实施 TF-IDF

干得好,你挺过了理论背景!但是现在的问题是如何实现这个功能。目标是计算几个文档之间的距离。反过来,我们需要 TF-IDF 来计算数据,我们需要 TF 和 IDF 来计算 TF-IDF。

计算 TF 和 IDF 需要对文档进行一些处理。我们需要 TF 的每个文档中每个单词的出现次数和文档中单词的总数,以及 IDF 的每个单词出现在文档中的数量和文档的数量。这些都需要相同的通用文档解析,因此增加了一个预处理步骤:

class DocumentDistance:
    distancespre = {}

    def add_documents(self, docs: typing.List[str],
                      ) -> None:
        """
        Calculate the distance between the documents
        :param documents: list of documents
        :return: None
        """
        word_occ_per_doc, doc_lens, doc_per_word = self.pre_proces_data(docs)

        # Calculate TF values
        tfs = []
        for i in range(len(word_occ_per_doc)):
            tfs.append(self.compute_tf(word_occ_per_doc[i], doc_lens[i]))

        # Calculate IDF values
        idfs = self.compute_idf(doc_per_word, len(docs))

        # Calculate TF-IDF values
        tfidfs = []
        for i in range(len(tfs)):
            tfidfs.append(self.compute_tfidf(tfs[i], idfs))

        # Calculate distances
        self.calculate_distances(tfidfs)

定义了一个类,它将所有文档之间的距离存储为一个二维数组。这个数组将由calculate_distances方法填充。但是首先,我们将创建 pre_process_data 方法,该方法将解析列表中的所有文档,并返回每个文档的单词出现次数、文档长度以及每个单词在文档中出现的次数:

 from nltk import word_tokenize

  class DocumentDistance:

      ...

      def pre_proces_data(self,
                        documents: typing.List[str]
                        ) -> (typing.Dict[int, typing.Dict[str, int]], 
                             typing.Dict[int, int], 
                             typing.Dict[str, int]):
        """
        Pre proces the documents
        Translate a dictionary of ID's and sentences to two dictionaries:
        - bag_of_words: dictionary with IDs and a list with the words in the text
        - word_occurences: dictionary with IDs and per document word counts
        :param documents:
        :return: dictionary with word count per document, dictionary with sentence lengths
        """
        # 1\. Tokenize sentences and determine the complete set of unique words
        bag_of_words = []
        unique_words = set()
        for doc in documents:
            doc = self.clean_text(doc)
            words = word_tokenize(doc)
            words = self.clean_word_list(words)
            bag_of_words.append(words)
            unique_words = unique_words.union(set(words))
        # 2\. Determine word occurences in each sentence for all words
        word_occurences = []
        sentence_lengths = []
        for words in bag_of_words:
            now = dict.fromkeys(unique_words, 0)
            for word in words:
                now[word] += 1
            word_occurences.append(now)
            sentence_lengths.append(len(words))

        # 3\. Count documents per word
        doc_count_per_word = dict.fromkeys(word_occurences[0], 0)
        # Travese all documents and words
        # If a word is present in a document, the doc_count_per_word value of
        # the word is increased
        for document in word_occurences():
            for word, val in document.items():
                if val > 0:
                    doc_count_per_word[word] += 1

        return word_occurences, sentence_lengths, doc_count_per_word

第一部分通过在 NLTK 包的word_tokenize部分的帮助下对文档进行标记,将文档分割成单词。在标记之前,会进行一些文档清理,比如将文档转换成小写(稍后讨论)。在clean_word_list method中清理词表,目前为空。每个文档的令牌列表存储在bag_of_words变量中,这是一个每个文档有一个条目的列表,包含一个令牌列表。在这个循环中,创建了一组所有唯一的单词。这个集合unique_words包含了语料库中出现的所有唯一单词。

步骤 2 为所有文档确定所有唯一单词的出现次数。对于每个文档(在bag of words上的循环),为所有唯一的单词(dict.fromkeys(…))创建一个值为 0(零)的字典。然后,代码遍历文档中的所有单词,并将字典值增加 1(一)。该词典被添加到word_occurences中,为所有文档创建一个词典列表,其字数和文档长度存储在sentence_lengths中。

最后一步,步骤 3,统计每个单词在文档中出现的次数。首先,创建一个列表doc_count_per word,其中每个唯一单词出现 0 次。当文档中特定单词的单词计数大于零时,该单词存在于文档中。

预处理的结果是:

 Documents:
'the man walked around the green house'
'the children sat around the fire'

word_occurences:
{
 0: {'green': 1, 'fire': 0, 'house': 1, 'sat': 0, 'around': 1, 'man': 1, 
     'the': 2, 'children': 0, 'walked': 1}, 
 1: {'green': 0, 'fire': 1, 'house': 0, 'sat': 1, 'around': 1, 'man': 0, 
     'the': 2, 'children': 1, 'walked': 0}
}

sentence_lengths:
[7, 6]

doc_count_per_word:
{'around': 2, 'green': 1, 'house': 1, 'walked': 1, 'sat': 1, 
 'children': 1, 'man': 1, 'fire': 1, 'the': 2}

有了这些数据集,可以相对直接地计算 TF 和 IDF:

 def compute_tf(self,
                   wordcount: typing.Dict[str, int],
                   words: typing.List[str]
                   ) -> typing.Dict[str, float]:
        """
        Calculates the Term Frequency (TF)
        :param wordcount: dictionary with mapping from word to count
        :param words: list of words in the sentence
        :return: dictionary mapping word to its frequency
        """
        tf_dict = {}
        sentencelength = len(wordcount)
        for word, count in wordcount.items():
            tf_dict[word] = float(count) / sentencelength
        return tf_dict

    def compute_idf(self,
                    doc_count_per_word: typing.List[typing.Dict[str, int]],
                    no_documents: int
                    ) -> typing.Dict[str, int]:
        """
        Calculates the inverse data frequency (IDF)
        :param doc_count_per_word: dictionary with all documents. A document is a dictionary of TF
        :param no_documents: number of documents
        :return: IDF value for all words
        """
        idf_dict = {}
        for word, val in doc_count_per_word.items():
            idf_dict[word] = math.log(float(no_documents) / val)
        return idf_dict

     def compute_tfidf(self,
                       tfs: typing.Dict[str, float],
                       idfs: typing.Dict[str, float]
                       ) -> typing.Dict[str, float]:
        """
        Calculte the TF-IDF score for all words for a document
        :param tfs: TFS value per word
        :param idfs: Dictionary with the IDF value for all words
        :return: TF-IDF values for all words
        """
        tfidf = {}
        for word, val in tfs.items():
            tfidf[word] = val * idfs[word]
        return tfidf

对每个文档调用compute_tf方法。每个单词的 TF 值是通过将出现次数除以句子长度来计算的(计算结果被强制转换为浮点类型)。

使用每个单词的文档计数和文档总数来调用compute_idf。讨论的公式适用于这些值。

最后,通过将 TF 值乘以相应的 IDF 值来计算每个句子每个单词的 TF-IDF 值。

tfs:
[{'sat': 0.00, 'green': 0.14, 'walked': 0.14, 'the': 0.28, 
  'around': 0.14, 'children': 0.00, 'fire': 0.00, 'man': 0.14, 
  'house': 0.14}, 
 {'sat': 0.16, 'green': 0.00, 'walked': 0.00, 'the': 0.33, 
  'around': 0.16, 'children': 0.16, 'fire': 0.16, 'man': 0.00, 
  'house': 0.00}]

idfs:
{'sat': 0.69, 'green': 0.69, 'walked': 0.69, 'the': 0.00, 
 'around': 0.00, 'children': 0.69, 'fire': 0.69, 'man': 0.69, 
 'house': 0.69}

tfidfs:
[{'sat': 0.00, 'green': 0.09, 'walked': 0.09, 'the': 0.00,
  'around': 0.00, 'children': 0.00, 'fire': 0.00, 'man': 0.09,
  'house': 0.09}, 
 {'sat': 0.11, 'green': 0.00, 'walked': 0.00, 'the': 0.00, 
  'around': 0.00, 'children': 0.11, 'fire': 0.11, 'man': 0.00,
  'house': 0.0}
]

现在我们有了 TF-IDFS 值,就可以计算不同文档之间的距离了:

 def normalize(self,
                  vector: typing.Dict[str, float]
                  ) -> float:
        """
        Normalize the dictionary entries (first level)
        :param tfidfs: dictiory of dictionarys
        :return: dictionary with normalized values
        """
        sumsq = 0
        for i in range(len(vector)):
            sumsq += pow(vector[i], 2)
        return math.sqrt(sumsq)

def calculate_distances(self,
                        tfidfs: typing.List[typing.Dict[str, float]]
                        ) -> None:
    """
    Calculate the distances between all elements in tfidfs
    :param tfidfs: The dictionary of dictionaries
    :return: None
    """
    vectors = []
    # Extract arrays of numbers
    for tfidf in tfidfs:
        vectors.append(list(tfidf.values()))

    self.distances = [[0.0] * len(vectors) for _ in range(len(vectors))]
    for key_1 in range(len(vectors)):
        for key_2 in range(len(vectors)):
            distance = np.dot(vectors[key_1], vectors[key_2]) / \
                       (self.normalize(vectors[key_1])* self.normalize(vectors[key_2]))
            self.distances[key_1][key_2] = distance

如本文第一部分所述,两个向量之间的余弦距离是通过将向量的点积除以归一化向量的积来计算的。

为了计算点积,使用了来自 numpy 库dot方法。点积通过对成对相乘的值求和来计算:

计算矩阵的点积(图片由作者提供)

向量的标准化值等于原点和向量所标识的点之间的直线长度。这等于每个维度的平方和的平方根:

标准化矢量(图片由作者提供)

矢量的归一化是通过normalize()方法实现的。所有矢量组合之间的距离用calculate_distances()方法计算。所有距离都存储在该类的distances属性中。在计算距离之前,该变量被初始化为 0.0 值的 N x N 矩阵。

一个例子:

Sentences:
'the man walked around the green house'
'the children sat around the fire'
'a man set a green house on fire'

Distances:
[1.00, 0.28, 0.11] 
[0.28, 1.00, 0.03] 
[0.11, 0.03, 1.00]

请注意,值为 1.0 时距离最小,值为 0.0 时距离最大。由于两个句子没有共同的单词,所以它们的距离是 0.0。一句话和它本身的距离是 1.0。

性能改进

有了由三个文档组成的语料库,每个文档由一个小句子组成,距离可以快速计算出来。当文档变成文章并且数量增加到数百时,这个时间增加得很快。下图显示了独特词的数量与新闻文章数量的函数关系。

独特单词的数量(图片由作者提供)

当处理 1000 篇文章时,唯一单词的数量增加到几乎 15000 个。这意味着描述文档的每个向量有 15000 个条目。一个点乘需要 2.25 亿次乘法和 2.25 亿次加法。每矢量。归一化一个向量也是 2.25 亿次乘法(计算平方),2.25 亿次加法和一个平方根。因此,两个向量之间的距离计算是 7.75 亿次乘法,7.75 亿次加法,1 次平方根,1 次除法和 1 次加法。所有这 100 万次来填充整个距离数组。可以想象这需要一些时间…

那么如何才能减少工作量呢?黄金法则是尽可能少花时间。所以让我们看看我们能做些什么来优化。

距离计算

在第一部分中已经提到,距离矩阵包含重复值。A 到 B 的距离等于 B 到 A 的距离,我们只需要计算一次,分两个地方加到矩阵里。

其次,矩阵的对角线是 1.0,因为 A 和 A 之间的距离总是 1.0。这里不需要计算。这两步将使计算的距离减半。

第三,对于每次计算,所有向量都被归一化。这是多余的。我们可以预先计算所有的归一化,并将其存储在数组中以备再次使用。这将把距离计算减少 2/3,对于每个矢量组合,只需要计算点。

这些改进将所需时间减少了 85%!

def normalize(self,
              tfidfs: typing.List[typing.Dict[str, float]]
              ) -> typing.Dict[int, float]:
    """
    Normalize the dictionary entries (first level)
    :param tfidfs: dictiory of dictionarys
    :return: dictionary with normalized values
    """
    norms = []
    for i in range(len(tfidfs)):
        vals = list(tfidfs[i].values())
        sumsq = 0
        for i in range(len(vals)):
            sumsq += pow(vals[i], 2)
        norms.append(math.sqrt(sumsq))
    return norms

def calculate_distances(self,
                        tfidfs: typing.List[typing.Dict[str, float]]
                        ) -> None:
    """
    Calculate the distances between all elements in tfidfs
    :param tfidfs: The dictionary of dictionaries
    :return: None
    """
    norms = self.normalize(tfidfs)
    vectors = []
    # Extract arrays of numbers
    for tfidf in tfidfs:
        vectors.append(list(tfidf.values()))

    self.distances = [[1.0] * len(vectors) for _ in range(len(vectors))]
    for key_1 in range(len(vectors)):
        for key_2 in range(key_1 + 1, len(vectors)):
            distance = np.dot(vectors[key_1], vectors[key_2]) / (norms[key_1] * norms[key_2])
            self.distances[key_1][key_2] = distance
            self.distances[key_2][key_1] = distance

对代码的一些小的改变导致计算时间减少了 85%。但是有更多的可能性

减少向量大小

距离计算是代码中最昂贵的部分。上述更改大大减少了计算时间。但是还有一个方面会极大地影响性能,那就是文档向量的大小。对于 1.000 篇文章,每个文档向量是 15.000 个元素。如果我们能够移除这个向量大小,计算时间将会受益。

那么我们能做些什么来减少向量的大小呢?我们如何找到可以去除的没有影响的元素?查看 IDF 函数,它显示停止字对距离计算没有影响。它们出现在每个文档中,因此所有向量中的 IDF 值和 TF-IDF 将为 0.0。将所有这些零相乘仍然需要时间,所以第一步是从唯一单词列表中删除停用词。

NLTK 工具包中提供了每种语言的停用词。工具包提供了停用词列表,可以从唯一的词中过滤掉这些停用词。

第二,只在一个文档中出现的单词对于所有其他文档总是乘以零。我们可以安全地从向量中移除这个单词。

最后,最后一步是使用词干。单词列表将包含“house”和“houses”等单词。通过将这些合并到 house,同样的意思,我们可以进一步减少单词列表并提高整个算法的质量。它将不再把“house”和“houses”视为不同的词,而是一个词。NLTK 提供了几个词干分析器,支持多种语言,其中使用了 雪球词干分析器

词干化也减少了所有动词的基数。像‘like’,‘liking’,‘liked’等词都将被简化为‘like’这个词干。

方法reduce_word_list已经被引入(但为空),所以现在我们可以用它来应用这些规则:

from nltk.stem.snowball import SnowballStemmer
from nltk.corpus import stopwords

stop_words = set(stopwords.words("dutch"))
stemmer = SnowballStemmer("dutch")

def clean_word_list(self, words: typing.List[str]) -> typing.List[str]:
    """
    Clean a worlist
    :param words: Original wordlist
    :return: Cleaned wordlist
    """
    words = [x for x in words if x not in self.stop_words and 
                                 len(x) > 2 and not x.isnumeric()]
    words = [self.stemmer.stem(plural) for plural in words]
    return list(set(words))

第二个规则是去除只存在于一个文档中的单词,通过在第三个步骤之后增加第四个步骤,该规则被应用于预处理功能的修改版本中:

 # 4\. Drop words appearing in one or all document
        words_to_drop = []
        no_docs = int(len(documents) * .90)
        for word, cnt in doc_count_per_word.items():
            if cnt == 1 or cnt > no_docs:
                words_to_drop.append(word)
        for word in words_to_drop:
            doc_count_per_word.pop(word)
            for sent in word_occurences:
                sent.pop(word)

第一个循环收集出现一次或总是被收集的单词。在这个循环中不可能修改单词列表,因为它会改变用于迭代的集合。第二步需要从文档计数器和每个文档中删除( pop )多余的文字。这是一个相对昂贵的操作,但向量大小的减少是值得的。

对所有这些向量缩减步骤的效果感到好奇吗?让我们看看:

独特单词的数量(图片由作者提供)

蓝线是唯一单词的原始数量。灰线是减少的字数。因此,在 1.000 篇文章中,字数从 15.000 减少到 5.000。每个向量乘法减少 66%。

代码用 cProfile 进行分析,结果用 snake_viz 可视化。原始代码的执行时间:

剖析原始代码(作者截图)

添加文档需要 31.2 秒,其中 29.6 秒用于计算距离,0.4 秒用于预处理数据。

29.6 calculate_distance(...)
 0.6 compute_tf(...)
 0.4 pre_proces_data(...)
 0.4 compute_tfidf(...)
 0.1 compute_idf(...)

优化版本的执行时间:

剖析优化版本(作者截图)

添加文档所需的时间现在为 5.39 秒(was 31.2),其中 4.32 秒(was 29.6)用于计算距离,0.9 秒用于处理数据(was 0.4)。

所以预处理需要双倍的时间,增加了 0.5 秒的执行时间,但是距离计算只需要原来时间的 14%。额外的预处理时间很容易收回。

 4.2 calculate_distance(...)
 0.9 pre_proces_data(...)
 0.1 compute_tf(...)
 0.0 compute_tfidf(...)
 0.0 compute_idf(...)

开心吗?

对结果满意吗?老实说,没有。我真的很喜欢实现所有计算文档距离的逻辑。看到可能的性能改进是令人满意的。所以从算法开发的角度来看,我很高兴。

但是应用该算法并没有产生很好的效果。例如,关于寒冷天气的新闻文章似乎与一篇关于伊朗妇女权利的文章更相关,而不是与一篇关于有足够的天然冰可以滑冰的文章更相关。我以为会是另一种情况…

这并不像是浪费了几个小时的快乐编程时间,但是一定会进行一些额外的研究来找到更好的实现。尽管如此,我还是希望您学到了一些关于用 Python 实现算法和寻找提高性能的方法的知识。

最后的话

一如既往,完整的代码可以在我的 Github 上找到。在这里你可以找到本文中讨论的实现,以及一个扩展版本,支持多种语言TF 和 IDF 的多种实现,如 TF-IDF 的维基百科页面上所讨论的。

我希望你喜欢这篇文章。要获得更多灵感,请查看我的其他文章:

如果你喜欢这个故事,请点击关注按钮!

免责声明:本文包含的观点和看法仅归作者所有。

用交互式混淆矩阵和折线图寻找不平衡分类的最佳分类阈值

原文:https://towardsdatascience.com/finding-the-best-classification-threshold-for-imbalanced-classifications-with-interactive-plots-7d65828dda38

使用 binclass-tools amazing Python 包将您对二进制分类问题的分析提升到一个新的水平

作者图片

即将训练分类模型的数据科学家经常发现自己在分析产生的混淆矩阵以查看模型的性能是否令人满意。让我们仔细看看这是怎么回事。

引入混淆矩阵

术语“混淆”是指一个观察值可能被模型正确或错误地预测,然后在分类中被混淆。如果我们考虑一个二元分类模型(其可能的结果只有两个,例如,),该模型的混淆矩阵是一个矩阵,该矩阵组织了为测试数据集的目标变量(作为模型的输入给出)获得的与该变量在数据集中呈现的真值相关的预测输出。通过这种方式,可以识别正确预测的数量和错误预测的数量(假阳性假阴性)。将问题中感兴趣的类别定义为(例如,交易是欺诈性的,因此是正类别),假正是那些预测为真(预测的正类别),但实际上不是(假预测)的观察;假阴性则是那些观测值被预测为假(负类预测),而实际上不是(假预测)。理解了这些定义,你就可以自己推导出真阳性真阴性的含义。

模型结果的这种排列允许理解模型的性能是否如预期的那样。通常,二元模型的混淆矩阵如下所示:

图 1-二元分类的混淆矩阵示例(作者图片)

一旦确定了混淆矩阵的 4 个象限的值,就有可能确定测量模型性能的定量和定性指标,如参考文献所示(见参考文献 1)。1).

乍看起来,混淆矩阵的四个象限的值对于被分析的模型是不变的。然而,在幕后,这些值是基于一个非常具体的假设计算出来的。

注意分类阈值

二元分类模型主要为目标变量中的每个类返回相似概率得分,该得分提供了一种度量方法,用于衡量针对该观察值获得的预测为正类的可能性。如果您使用 Python 来训练分类模型,我们讨论的分数通常是通过 Scikit-learn predict_proba函数获得的。该分数具有以下属性:

  • 它是一个介于 0 和 1 之间的实数
  • 将把一个观察关联到正面类的分值和把同一观察关联到负面类的分值相加,我们得到 1

任何学过一点数学的人都会认识到,这些属性与概率度量的定义是相同的。然而,我们的分数并不是一个真正的概率度量,即使通过应用一种称为 模型校准 的变换,有可能使我们的分数近似于一个概率度量。也就是说,我们之前讨论的关于混淆矩阵值的假设如下:

通常,考虑到如果得分大于或等于 0.5,则预测是正类,如果得分小于 0.5,则预测是负类,来计算与二元分类模型相关联的混淆矩阵所示的 TP、TN、FP、FN 的值

前一语句的 0.5 值被称为分类阈值,可以在 0 和 1 之间变化。为了理解阈值的用途,假设您已经训练了一个二进制分类模型来检测房屋火灾中的烟雾。您的模型将在安装于厨房的真实烟雾探测器中实现。现在假设测试产品的实验室将探测器安装在烤肉的炉子附近。如果传感器阈值设置得较低,在烤肉时,探测器很可能会报告火灾(假阳性)。为了使探测器更可靠,实验室技术人员应该在几次实验后将阈值增加到被认为适合识别真实火灾的值。

改变阈值,我们去修改预测的类。例如,使用阈值 0.5(得分 0.54 大于阈值 0.5,因此观察结果为真)分类为的观察结果,如果阈值变为 0.6(得分 0.54 小于阈值 0.6,因此为假),则变为。因此,通过改变阈值,我们前面提到的所有定量和定性指标(例如,精度召回)也会发生变化。所以,很明显:

分类器性能随着阈值的变化而变化

因此,您应该很好地理解,使用默认值为 0.5 的阈值的分类器可能会导致低质量的结果,尤其是在处理不平衡数据集时,因为不平衡数据的概率分布往往偏向多数类。

在这一点上,一旦模型已经被训练,问题是:“好的,现在我应该为我的模型使用什么阈值?”。通常适用于任何复杂决策情况的坏消息是:

不存在适用于所有情况的最佳阈值。这取决于要满足的业务需求。

在我们详细讨论阈值调整的不同情况之前,让我们快速提醒一下最常用于不平衡分类的指标,它们是更复杂的训练和测量模型。

不平衡分类的评价指标

让我们回想一下下面一些对初学者有用的基本概念,以便能够测量不平衡二进制分类的性能。当所分析的情况不同时,同样的概念将有助于解释最佳阈值。

精确度-召回率的权衡

假设您需要训练一个二进制分类模型来检测欺诈性信用卡交易。这是一个典型的不平衡问题,对于这个问题,感兴趣类别(欺诈交易=正类别)中的标签数量远远小于负类别中的标签数量。在这种情况下,为了评估分类器,经常考虑的度量是精度召回(在平衡问题的情况下,经常考虑灵敏度特异性)。

现在,需要提出的问题是:“将健康交易归类为欺诈更严重,还是将欺诈交易归类为健康更严重?”。如果对一个健康的事务强制进行检查,那么您的成本就是检查所需的时间。另一方面,如果欺诈性交易被错误分类为健康的,欺诈的成本显然高于前一种情况。

被错误分类为健康(负面类别)的欺诈交易(正面类别)属于假阴性 (FN)集合。当 FNs 减少时,一个增加的指标(保持 TPs 数量不变)就是回忆。事实上,看看回忆的定义,我们有:

图 2 —回忆的定义(作者提供的图片)

从上面的公式可以明显看出,如果我们想通过使 fn 的数量趋向于 0 来最小化 fn 的数量,那么召回的值将趋向于 TP/TP = 1。

所以,首先要做的是将阈值移动到使回忆最大化的值 1。不幸的是,人们会发现,要使回忆趋于 1,必须将阈值移动到非常接近 0。但这将意味着把几乎所有的交易都归类为欺诈!实际上,召回公式中不存在的假阳性(FPs)的数量不成比例地增长,并且分类器在所有被预测为欺诈的观察结果中辨别实际上是欺诈的观察结果的能力被取消。换句话说,名为 Precision 的指标值急剧下降到零,这可以从下面定义它的公式中看出:

图 3——精度的定义(图片由作者提供)

从上式可以看出,如果 FPs 趋向于比 TP 大得多的数 N,精度就会趋向于 TP/N~0

你所了解的是著名的精确-召回权衡,它包括以下内容:

不可能同时拥有高精度和高召回率,除非你处理的是一个完美的模型(FP = FN = 0)。对于不太完美的模型,如果提高精度,就会降低召回率,反之亦然。

下面显示了一个示例模型的精度-召回曲线,展示了上述权衡:

图 4——精确召回曲线(图片由作者提供)

为了兼顾精确度和召回率,我们可以考虑由两者的调和平均值给出的度量,也称为F1-得分:

图 5—F1 的定义(图片由作者提供)

如果您希望对其中一个定义指标给予更多权重,您可以通过引入“不平衡”β系数来推广该公式:

图 6-F-beta 的定义(作者提供的图片)

例如,如果您认为召回率是精确度的两倍,那么您可以将β设置为 2。反过来,如果你相信精度比召回率重要 2,那么β就会。

回到我们的欺诈交易问题,已经确定召回比精确度重要得多,您可以考虑β > 1 并优化阈值,以便 F 分数取最大值。

从精确度、召回率和 F 值到马修斯相关系数

有一个重要的事实需要考虑。精度和召回率(以及 F 值,F 值是两者的函数)将正面类视为感兴趣的类,回答以下问题:

"在所有肯定预测的例子(FP + TP)中,有多少肯定检测(TP)是正确的?"(精度)

“在所有实际的正面例子(FN + TP)中,我们能够识别多少正面例子(TP)?”(回忆)

如果你仔细看上面的定义,真否定 (TN)从来不会出现。如果感兴趣的类(较罕见的类)被标记为正的(大多数情况下都是这样),这就不是问题。例如,在这种情况下,F1 分数仍然是不平衡分类的有效指标。事实上,如果模型没有正确地预测负类,则不正确的预测将馈入 FPs。因此,精度的值将减少,因为它被定义为 TP/(FP+TP),因此 F1-score 的值也将减少。总之,在这种情况下,较少的负类正确预测了较低的 F1 分数。

有时可能发生的情况是,感兴趣的类没有被标记为正面的,而是被标记为负面的。在这种情况下,F1 值会误导对问题的正确分析。有关更多详细信息,请查看参考资料(参考。7).

因此,需要引入一种新的度量,该度量也将 TNs 考虑在内,并且不管正类分配如何都是稳定的。

那些研究过一些统计学的人肯定遇到过克拉默相关系数 ( 克拉默相关系数 V ),它测量两个分类变量之间的关联强度。1975 年,Brian W. Matthews 引入了一个相关系数,它是应用于 2x2 混淆矩阵的 cramér V 的一个特例。我们正在讨论的马修斯相关系数 (MCC)定义如下:

图 7 —马修斯相关系数的定义(图片由作者提供)

与通常的相关系数一样,MCC 也取[-1,1]范围内的值。在分类的情况下,值-1 表示模型预测正类和负类的方式与它们在目标变量中呈现的实际值完全相反。如果 MCC 取值为 0,则模型随机预测正类和负类。在值为 1 的情况下,模型是完美的。此外,如果正类被重命名为负类,MCC 是不变的,反之亦然。MCC 的主要特点如下:

只有当模型能够正确预测大多数正面观察值和大多数负面观察值时,MCC 才是唯一得分高的指标。

在快速回顾了不平衡分类问题(处理起来最复杂)中最常用的指标之后,我们可以回到文章的主题,即如何在这些情况下设置阈值。

为您的模型选择正确的阈值

确定阈值的“最佳值”意味着找到使特定目标函数最大化或最小化的值,该值衡量模型的良好性,并适合要解决的业务问题。归根结底,这是一个优化问题。

基本上,有两种优化阈值的方法:

  • 基于特定指标的优化
  • 基于成本的优化

在第一种情况下,选择感兴趣的度量(例如,F2 分数,或 MCC)并识别所选度量指示模型的最大性能的阈值(意味着必须根据其性质最大化或最小化度量)。

另一方面,在第二种情况下,使用所谓的成本矩阵,由此成本可以与混淆矩阵的每个类别相关联。这样,通过最小化与每个类别相关联的成本的总和来给出最佳阈值。

一旦确定了感兴趣的目标函数,不管它是度量还是成本,也不管它是需要最大化还是最小化,都有可能通过操作所谓的阈值移动来找到最佳阈值:

  1. 将阈值从 0 变化到 1,步长例如为 0.01,记录每个阈值的目标函数值。
  2. 选择使函数最大化(或最小化)的阈值。

然而,以这种方式识别的值将严格依赖于训练数据集,并且它可能不是阈值的最佳值的良好估计。

最近开发了一种通用方法,该方法总是基于从训练数据集获得的目标函数值,称为 GHOST ( G 一般化 THreshOLDSHIFTing 过程)。总之,它使用通过分层随机抽样提取的训练数据集的 N 个子集,以便它们保持类别分布。然后,对于每个子集,它应用由每个阈值给出的目标函数。这样,对于每个阈值,将有由 N 个子集给出的目标函数的 N 个值。此时,它会计算上述值的每个阈值的中值,因此您只能获得一个与阈值相关的“稳定”值。您可以在参考资料中找到更多详细信息(参考。8).

使用 binclass-tools Python 包控制一切

现在,深入分析的所有方面的总体情况都更清楚了,您很好地理解了主要困难之一是随着阈值的变化,要有手头上最重要的指标的视图。只有这样,您才能够根据所选择的业务标准,快速了解模型的性能何时接近期望的性能。

正是这种需求促使我开发了一个新的 Python 包,其中包含一些用于这种类型分析的有用工具。我们正在谈论令人惊奇的 binclass-tools 软件包:

图 8—bin class-tools 包的徽标(图片由作者提供)

让我们看看这个包提供的一些最重要的工具。

交互混淆矩阵

软件包中最有趣的工具之一是交互式混淆矩阵,这是一个交互式图表,允许您查看二进制分类的最重要指标如何随着阈值的变化而变化,包括与矩阵中的类别相关联的任何金额和成本:

图 9 —交互式混淆矩阵的实例

图 9 中可以看出,阈值与范围从 0 到 1 的滑块相关联,具有用户选择的步长(在本例中为 0.02)。随着滑块的移动,一些显示的测量和指标会相应地改变。

剧情分为两部分:

  • 底部是混淆矩阵,它突出显示了属于 TP、TN、FP、FN 类别的观察数量以及与单个观察相关的数量或成本的可能度量。这是因为通过对从训练数据集的一列中获取的单个观察值进行求和来查看与每个类别相关联的总量如何变化可能会很有趣。类似地,通过对每个观察的平均成本或每个观察的成本列表中的值求和,来查看与每个类别相关联的总成本如何变化可能是有趣的。这些值显然会随着阈值的变化而变化,每个值旁边都显示了它在总数中所占的百分比值
  • 最上面是三张桌子。左边的第一个包含所有依赖于阈值的度量,因此将随着载玻片的移动而变化(例如,准确度、F1 分数等。).另一方面,中间的表包含所有那些随着阈值变化而不变的指标(例如 ROC AUC 或 PR AUC)。只有在调用生成图的函数时指定了右边的第三个也是最后一个表,并且该表包含相对于左边的列中显示的指标而言最佳的所有阈值时,该表才会出现。目前可以优化的指标有 Cohen's kappa、Matthews 相关系数、ROC 曲线、F1-得分、F2-得分、f 0.5-得分和成本。在 1.1.0 版之前,最佳阈值的计算是通过 GHOST 完成的(如前一节所述)。从版本 1.1.0 开始,图中显示的最佳阈值只是与给定数据集的已实施指标的最佳值相关联的阈值(即:最大化 F 分数、MCC 和 Kappa 以及最小化成本的阈值)。实现 GHOST 方法的函数在包中仍然可用,并可用于获得通常优化给定指标的阈值(最佳实践:使用训练集进行优化,并对新数据使用这些阈值)。

使用交互混淆矩阵分析二元分类器的预测的便利性是毋庸置疑的。同样,显示阈值变化时金额或成本趋势的图表也是有用的。

交互式混乱折线图

从通过交互式混淆矩阵完成的分析中,分析师可能不仅对看到一个值感兴趣,而且对可视化与混淆矩阵的每个类别相关联的可能数量或成本的趋势感兴趣,因为阈值改变。这就是为什么 binclass-tools 包也允许你绘制交互混淆线图:

图 10 —交互式混淆折线图的实例

您可以从图 10 中观察到,除了代表所选阈值下的金额/成本的 4 个图中的每一个图都有一个点之外,还有黑色的“菱形”表示金额和成本曲线互换的第一个阈值。曲线交换点也可以多于一个。

如果分析师希望关注混淆矩阵中任何类别组合的总金额或成本值,还有另一个非常有用的图。

交互式金额-成本折线图

假设您想帮助一家公司的团队使用欺诈检测分类器分析可能的欺诈。假设分类器将欺诈类检测为阳性,分析师可能会提出以下观点:

  • 如果模型检测到 TPs,则交易金额从可能的欺诈中“节省”出来,因此可以认为是公司的收益。
  • 所有被分类为良好但实际上是欺诈的观察结果(因此 FNs)实际上都是与相关交易总额相等的损失。因此,它们是成本。
  • 此外,团队必须对模型预测为欺诈但实际上不是欺诈的所有交易(FPs)进行检查,这也是成本,尽管在数量上小于与欺诈相关的成本。在这些情况下,通常考虑每次检查的固定成本。

如果现在分析师想要比较他认为的收益(TPs 数量)的表现和他认为的损失(FNs 数量+每 FP 的固定成本)的表现,他可以通过交互式数量-成本折线图来完成:

图 11 —交互式金额-成本折线图示例

同样在该图中,黑色“菱形”表示发生数量和成本曲线交换的第一个阈值。

在这一点上,基于分类器的损益分析的需求可以很容易地使用上述图来满足。

酷!但是我在哪里可以找到这个伟大的包呢?

一段时间以来,我一直在考虑将函数放在一个库中,以便对二进制分类器提供的结果进行交互式分析。由于我没有时间,Python(或 R)中的实现总是阻塞步骤。自从我得到一位同事的承诺,帮助我开发上述功能后,实现这个项目的可能性变得更加现实。这就是为什么所有这些想法的实现都要归功于 Greta Villa 的 Python 专业知识。

也就是说,我决定在 GitHub 上以开源的形式提供这个项目,原因有两个:

  • 我想与整个数据科学家社区分享一个工具,它可以使二元分类器的分析更加容易。
  • 我依靠社区可能的帮助来建议新的特性,改进现有的代码,或者帮助我们开发软件包的未来版本。

binclass-tools 包在 PyPI 上发布,所以您只需要下面一行代码就可以将它安装到您的 Python 环境中:

pip install binclass-tools

有关如何使用软件包中包含的功能的更多详细信息,请参考 GitHub 页面:

https://github.com/lucazav/binclass-tools

还添加了交互式 ROC 和精确回忆图,您可以在本文中读到:

[## 走向数据科学

binclass-tools Python 包中的 ROC 和 PR 曲线、概率分布和密度图](/roc-and-pr-curves-probabilities-distribution-and-density-plots-now-in-binclass-tools-python-9351681a3803)

我们还将添加包装器,以简化未来版本中校准模型的操作。

欢迎对该包的任何反馈!

参考

  1. 如何确定分类模型的质量和正确性?第 2 部分—定量质量指标
  2. 什么是平衡和不平衡数据集?
  3. 不平衡分类评价指标巡礼
  4. 真实世界用例中的精度-召回权衡
  5. 马修斯相关系数(MCC)在二分类评估中相对于 F1 分数和准确度的优势
  6. 在二进制分类评估中,马修斯相关系数(MCC)比科恩的 Kappa 和 Brier
    分数
    更具信息量
  7. 马修斯相关系数:何时使用,何时避免
  8. GHOST:调整决策阈值处理机器学习中的不平衡数据

用机器学习寻找最好的开瓶器

原文:https://towardsdatascience.com/finding-the-best-wordle-opener-with-machine-learning-ce81331c5759

我是如何几乎和 3blue1brown 一样得出相同的开头词的

芬恩·哈克肖在 Unsplash 上拍摄的照片

如果你正在看这篇文章,那么你可能听说过流行的网络游戏“Wordle”。这是一个非常容易上瘾、分裂家庭的脑筋急转弯,最近被《T4 时报》收购。这个游戏的目的是以尽可能少的尝试来猜测每日单词,通常是 5 个字母的单词。当你尝试不同的单词时,游戏会提供反馈,表明猜测的质量;灰色方块表示字母不在单词中,黄色方块表示字母在单词中但位置错误,绿色方块表示字母在单词中且位置正确。

为了在这个游戏中获得持续的成功,玩家必须进行初步猜测,以揭示仅与 5 个字母单词的一小部分相关的模式。也就是说,每次猜测都需要排除尽可能多的潜在答案,只给玩家留下几个词来选择下一次猜测。因此,自然的问题是“最好的第一个猜测是什么”?

许多人试图用数学和计算机模拟来回答这个问题。在我看来,最佳答案是由 Grant Sanderson(又名 3blue1brown)在一个视频中发现的,他在视频中使用了来自信息论的概念来寻找平均来说揭示了关于目标词的最多信息的开头词。

在这篇文章中,我将采用不同的方法,利用无监督学习来寻找最佳的 Wordle 开启器。我将首先展示如何创建一个数据集,以数字形式表示 5 个字母单词的分布,然后我将展示一个机器学习模型如何帮助我们找到与所有可能的 Wordle 解决方案“最相似”的世界。这篇文章的所有代码可以在这里找到。尽情享受吧!

属国

对于这个项目,我们需要在 Python 中进行以下导入:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import string
import re
from collections import Counter
from tqdm import tqdm
from pyod.models.copod import COPOD

数据

和任何机器学习问题一样,我们需要一些数据来学习。截至今天,Wordle 将接受 12,972 个单词作为猜测。然而,在这些被接受的猜测中,只有 2315 个是解字。为了一致,我将把 12,972 个被接受的猜测称为“被接受的单词”,把 2,315 个解称为“解单词”。

让我们从查看“接受的单词”的字母频率开始我们的分析。我们首先从文本文件中读入单词:

def read_word_list(file_name:str):
    result = []
    with open(file_name) as fp:
        result.extend([word.strip() for word in fp.readlines()])
    return result# Read in word lists
possible_words_list = read_word_list("possible_answers.txt")
accepted_words_list = read_word_list("accepted_words.txt")

以下是“已接受单词”的列表:

前 8 个“接受词”。图片作者。

以及“可能的单词”列表:

前 8 个“可能的单词”。图片作者。

然后,我们可以计算每个字母在所有“接受的单词”中的频率——这也可以理解为每个字母包含在单词中的比例。

# Create a letter occurrence dictionary 
words_string = ''.join(accepted_words_list)
letter_counts = dict(Counter(words_string))# Create letter frequency dictionary
letter_frequencies = {k:v/len(accepted_words_list) for k,v in letter_counts.items()}# Create letter frequency DataFrame
letter_frequencies = pd.DataFrame({'Letter':list(letter_frequencies.keys()),                      'Frequency':list(letter_frequencies.values())}).sort_values('Frequency',                                                                                  ascending=False)

字母频率。图片作者。

这个柱状图让我们了解了“已接受单词”列表中每个字母的常见程度。例如,它告诉我们,字母“s”出现在超过 50%的“公认单词”中。

最初,人们可能会尝试使用原始频率作为一种好的初步猜测的方式。例如,我们最初的猜测可能是“soare ”,因为“soare”包含了最常出现的前 5 个字母。这样做的问题是,它没有考虑字母的相对顺序。以“soare”为例,一个单词包含一个“o”后跟一个“a”的情况非常罕见。事实上,这种情况只出现在 2315 个“可能单词”中的 23 个中:

包含“oa”的 23 个“可能单词”。图片作者。

因此,使用“soare”作为第一个猜测最有可能得到一堆黄色方块。因此,我们需要创建一个数据集,它不仅要捕获字母的常见程度,还要捕获字母在单词中的排序方式。让我们先看看这个数据集是什么样子,以获得一些直觉,然后看看如何创建它。

单词编码数据集。图片作者。

这个数据集中的每一行代表一个“接受的单词”,每一列告诉我们字母 i 是否在单词中的位置 j我们来看第一个词,“aahed”,作为例子。假设我们将每个单词看作一个包含 5 个元素的数组。那么我们将用下面的数组表示“aahed ”:

“aahed”数组表示。图片作者。

数据集中的每一行都代表这个数组编码:

“aahed”的编码。图片作者。

列“a0”的值为 1,表示字母“a”位于单词的第 0 个位置(或第一个方块)。列“a1”的值为 1,表示字母“a”位于单词的第一个位置(或第二个方块)。列“d4”的值为 1,表示字母“d”位于单词的第四个位置(或第五个方块)。这个模式对单词中的每个字母都是如此。

因为字母表中有 26 个字母,而我们只考虑 5 个字母单词,所以数据集包含 26*5 = 130 列。每一列代表单词中的一个字母和一个位置,可以认为是一个 伯努利 随机变量,因为它只能取{0,1}中的值。

我们可以用下面的代码创建这个数据集:

# Create letter/position occurrence matrix
letter_pos_freq = pd.DataFrame()# For each word in the list of accepted words
for word in tqdm(accepted_words_list):

    # Convert the word to it letter-position format
    word_and_pos = ''.join([f"{letter}{pos}" for pos, letter in enumerate(word)])

    # Create letter-position counter dictionary
    letter_pos_counter = {}
    for wp in letter_freq_cols:
        letter_pos_counter[wp] = len(re.findall(wp, word_and_pos)) tmp_pos_freq = pd.DataFrame(letter_pos_counter, index=[word])
    letter_pos_freq = pd.concat([letter_pos_freq, tmp_pos_freq]) 

这个数据集允许我们表示字母频率和字母排序。通过将我们的数据视为来自具有相依成分的多元伯努利分布,我们可以计算出我们想要的任何概率。例如,单词中第一个字母是“a”的概率由以下代码给出:

图片作者。

我们还可以看看更复杂的概率,比如假设前三个字母是“s”、“h”和“a”,那么第四个字母是“r”的概率:

图片作者。

那么我们如何利用这个数据集找到最佳的开头词呢?继续读下去就知道了(悬念)。

基于 Copula 的孤立点检测模型

对于这个项目,我们将利用最常用于异常检测的最先进的无监督学习算法。如果你想知道算法背后的理论,请查阅的论文或者这篇文章。COPOD 算法的核心是试图通过逼近累积分布函数(CDF)来模拟数据集的潜在概率分布。然后,它可以使用近似的 CDF 为数据集中的每个观察值分配“异常值分数”,较高的分数表示该观察值位于分布的较低密度区域(更有可能是异常值)。

COPOD 算法。来源:https://arxiv.org/pdf/2009.09463.pdf

我们不使用 COPOD 来查找离群值,而是使用它来查找内嵌值。也就是说,我们要找到位于所有单词分布最可能位置的单词。想象一下,我们可以以某种方式将每个单词表示为一个数字,这个数字编码了字母出现的频率以及它们通常是如何排序的。然后,假设这个分布是对称的,我们可能会选择最接近分布平均值的世界作为我们的初始猜测。

图片作者。

粗略地说,这就是我们试图通过将 COPOD 算法应用于我们的数据集来实现的。

模型——实施和结果

让我们将 COPOD 算法应用于我们的数据集,并查看它认为哪个单词具有与单词分布的“核心”最接近的结构。为简单起见,我们将只考虑“可能的单词”:

letter_pos_freq_possible = letter_pos_freq[letter_pos_freq.index.isin(possible_words_list)]# Drop columns with all zeros
for col in letter_pos_freq_possible.columns:

    if letter_pos_freq_possible[col].sum() == 0:
        letter_pos_freq_possible.drop(col, axis=1, inplace=True)
        letter_pos_freq.drop(col, axis=1, inplace=True)

现在,我们所要做的就是实例化并拟合 COPOD 模型,将其应用于我们的数据集,并按离群值对单词进行排序:

# Fit COPOD model
copod_model = COPOD(contamination=0.01)
copod_model.fit(letter_pos_freq_possible)# Add outlier scores to dataset and sort
letter_pos_freq_possible['score'] = copod_model.decision_scores_
letter_pos_freq_possible.sort_values('score',inplace=True)letter_pos_freq_possible['rank'] = range(1,len(letter_pos_freq_possible)+1)

以下是 COPOD 算法认为最接近单词分布“核心”的前 10 个单词:

COPOD 十大开场白。图片作者。

你有它!根据问题被框定的方式,科波德认为‘圣人’是最好的开场。请注意,Grant Sanderson 推荐的开场词“crane”在 2315 个“可能单词”中排名第五。这可能表明 COPOD 算法已经完成了一些类似于在“3blue1brown”上进行的信息论模拟的事情。

最后的想法

在本文中,我们学习了如何创建一个数据集来充分表示 Wordle 中使用的 5 个字母单词的结构。然后,我们使用无监督学习算法来寻找与所有其他单词结构最相似的单词。请对本实验的思考过程提供反馈——我很想听到创建单词结构数据的更好方法或更好的算法的想法。感谢阅读!

喜欢我的文章?给我买杯咖啡:【https://www.buymeacoffee.com/HarrisonfhU】

寻找 Python 集合的交集:一个实际用例

原文:https://towardsdatascience.com/finding-the-intersection-of-python-sets-a-practical-use-case-f892fef5ff83

如何使用 Python 的 Set?购物篮分析的交集()方法

图片由 Pixabay 提供

Python set是一个无序的、不可改变的*和唯一的元素列表,这些元素放在花括号内,用逗号分隔。注意,当我们说它是‘不可改变的’时,我们的意思是一旦一个set被创建,我们不能改变一个项目,但我们仍然可以删除项目或添加新项目。

python 最流行的set操作之一是寻找两个或更多集合的交集,使用 Python 的set.intersection()方法可以轻松完成。例如,假设您有三个集合,如下所示:

set1={'Storage','Phones','Binders','Accessories','Envelopes'}
set2={'Phones','Envelopes','Binders','Copiers','Tables'}
set3={'Paper','Supplies','Binders','Accessories','Envelopes'}

要找到集合 1 和集合 2 的交集,您可以:

s = set1.intersection(set2)

要找到这三个集合的交集,您可以执行以下任一操作(注意,在第二个代码片段中,我们通过用星号*操作符打开列表,将所有集合作为参数传递给set.intersection()):

s = set1.intersection(set2,set3)
or:
s = set.intersection(*[set1,set2,set3])

作者图片

够简单!现在让我们看看如何将set交集应用到一个实际用例中,在这个用例中,我们需要使用set.intersection()方法和 python for循环对一百多对集合进行交集运算。我们将使用流行的 Tableau 样本超市数据集并运行一个简单的购物篮分析,使用set.intersection()方法找出哪些产品倾向于从超市一起购买!

一个实际的用例:购物篮分析

关于数据

你可以从这里下载超市数据样本。这个免费的公共数据集包含超市的产品、销售、顾客购买历史等信息。从 2014 年到 2017 年。我们有兴趣了解哪些产品子类别可能会一起购买。让我们首先将数据导入熊猫数据框架。

作者图片

对于每个产品子类别(例如,书架),我们可以通过为该子类别创建一个set来获得该产品子类别中所有唯一的订单 id。然后,对于任何一对产品子类别(例如,书架和椅子),我们可以将这两个集合相交,并查看两个集合中存在多少个订单 id。这两个集合之间的重叠越多,就越能表明这两个产品子类别倾向于一起购买。

我们将对所有可能的产品子类别对进行set交集运算,并计算每对的重叠百分比。让我们看看它是如何工作的。

创建所有可能的产品子类别对

首先,让我们使用下面的代码创建一个所有可能的产品子类别对的列表。 itertools.combinations(iterable,r)方法允许我们创建任意数量的产品子类别的组合,由参数r指定。例如,如果您想要创建 3 个子类别的组合,您可以简单地将第 2 行中的r的值更改为 3。

作者图片

又快又简单!看起来我们总共有 136 个产品子类别对要分析。如果我们要查看 3 个子类别的组合,那么总共会有 680 种组合!

创建集合和集合交集

接下来,让我们使用 python for循环为每一对创建两个集合,并用set.intersection()方法使这两个集合相交,如下面的代码所示。注意,在第 6 行中,我们首先使用set()方法为每一对子类别创建了两个集合。然后,我们使用set.intersection(*[set1,set2])来相交这两个集合,就像我在这篇文章前面展示的那样。

对于for循环中的每次迭代,我们将我们的结果——每对中唯一订单 id 的数量、每对中重叠订单 id 的数量以及订单对名称添加到我们在第 1–3 行创建的三个列表中。

计算重叠百分比

最后,让我们把所有东西放在一起,计算每对订单 id 的重叠百分比,看看哪些产品子类别倾向于一起购买。

作者图片

这就对了。通过不到 20 行代码,我们能够完成各种产品组合的市场购物篮分析,并深入了解客户的购买模式和产品分组。您可以轻松地将这一概念和方法扩展到其他类似的用例:客户喜欢一起下载的应用程序、客户喜欢一起访问的网站、客户喜欢一起购买的书籍等。

感谢阅读。我希望你喜欢这篇文章,并且学到了一些关于 python 的新知识!

数据源:

本文中使用的数据集是一个免费的、公开的样本数据集,可以从 Tableau 公共资源页面下载。该数据是由 Tableau 提供的样本数据集,可用于学习和教育目的。

你可以通过这个推荐链接注册 Medium 会员(每月 5 美元)来获得我的作品和 Medium 的其他内容。通过这个链接注册,我将收到你的会员费的一部分,不需要你额外付费。谢谢大家!

使用 BigQuery Firebase 数据查找高级用户

原文:https://towardsdatascience.com/finding-your-power-users-using-bigquery-firebase-data-ad0e16e0ddea

了解用户旅程,无需投资第三方工具

Solen Feyissa 在 Unsplash 上拍摄的照片

如果你使用 Firebase 和 BigQuery,你可以从 iOS 原生应用中挖掘点击流数据,并将其转化为关于用户旅程的可操作见解。你不需要任何第三方应用程序,你可以定制你的深度分析。

未开发的点击流数据金矿

如果你正在为一个原生的 iOS 应用程序使用 Firebase,那么每当用户登陆一个应用程序屏幕时就会触发一个事件。这个事件是 screen_view,并且你的屏幕被正确命名,你可以提取一个很好的屏幕名称序列,这将让你了解你的应用程序上的用户旅程。

Google Analytics 跟踪屏幕转换,并将当前屏幕的信息附加到事件中,使您能够跟踪用户参与度或每个屏幕的用户行为等指标。大部分数据收集是自动进行的,但您也可以手动记录屏幕视图。

使用一些 SQL 操作和 BI 软件,您可以轻松了解:

  • 最常访问和最少访问的屏幕;
  • 从一个屏幕到另一个屏幕最频繁和最不频繁的转换:你可能会发现你的团队忘记存在的令人惊讶的转换;
  • 退出屏幕:你可以了解大多数用户存在于 app 的什么地方,再加上过渡分析,你可以看到退出前发生了什么;
  • 群体行为:一些用户会转变,一些不会——你将能够看到旅程中是否有任何差异,并围绕什么使你成为超级用户建立假设;
  • 用户路径和用户达到应用旅程里程碑所需的时间:你的用户达到预期的转化事件需要多长时间?

虽然您肯定可以使用第三方工具跟踪用户的旅程,但访问原始数据也有好处——并不是所有的第三方工具都能够提供这一点。作为一个喜欢接触数据的人,当我看到我可以操作的数据时,我会很兴奋。

丰富您的应用旅程数据

如果您可以在同一个地方访问来自多个数据源的数据,那么您将拥有巨大的优势。丰富数据的 3 种主要方式:

  • 使用您的客户数据:这将需要一个唯一的客户标识符,当用户浏览应用程序时,可以合法地收集该标识符(更多信息见下文);
  • 交易数据和反馈数据:交付一个产品需要多长时间,NPS 是什么,客户是否被保留,等等。;
  • 从 Firebase 收集的其他事件:用户是 AB 测试的一部分吗,他们使用哪个应用程序版本,他们在哪个国家,等等。其他点击流事件,如产品视图、点击和其他互动、横幅互动等。,也可以用来丰富您的屏幕过渡数据。

最后,分析和可视化都在您的掌控之中。您想要会话级转换吗?给你。3 天归因?让它发生。想要排除 AB 测试中的用户吗?你能做到的。

iOS15 和用户追踪的噩梦

值得注意的是,在苹果公司未经用户明确同意限制广告客户 id 收集后,对于 Firebase 数据来说,这意味着为否认跟踪的用户返回空的 IDFA 字符串。然而,在 Firebase 中,你仍然与user_pseudo_id保持一致。在某些情况下,它比 IDFA 更稳定,这是一个优势。

许多第三方工具从 IDFAs 转向使用概率模型来近似用户身份。这很讽刺,但确实有效。

分析数据

我将列出我最喜欢的三种挖掘这些数据的方法。

分析这些数据最简单的方法是建立一个转移矩阵,其中在行中有一个屏幕,在列中有一个屏幕——下一个屏幕或一个空屏幕以防退出。非常类似于马尔可夫链中使用的一个概念,但是构造起来更加自由。这种情况下的值将是从一行中的屏幕移动到一列中的屏幕的用户的百分比,或者是每行总转换的百分比。它将突出显示用户进行的最常见的转换,以及最常见的屏幕退出——屏幕名称在一列中为空。使用LEADLAG窗口函数以及几乎任何可视化工具,这种分析都相当容易。

例如,在下面的快速模型中,蓝色单元格是主页、(产品)详细信息、购物车屏幕的前 3 个转换。用户最常跳转到产品列表,四处搜索。但是一旦用户进入详细信息页面,他们最有可能去搜索或另一个详细信息页面。为什么会这样?你能让 PDP 搜索对用户更方便吗?为什么用户会从主页跳过搜索?令人担忧的是,大多数用户从购物车转到产品详情页面,而不是结账,这甚至不在图片中。原因是什么?我们如何改善用户行为?

作者图片

这将是一个良好的开端,为了提高水平,您可以将用户在其会话或滚动窗口中查看的所有屏幕汇总起来,并比较有多少用户参与了您感兴趣的活动。如果是电商,往往是电商购买。你的假设可能听起来像这样“在会话期间查看评论屏幕的用户显示出更高的转化率”。在转换分析的基础上,这将给你建立用户行为假设的基础。

可视化用户旅程的最常见方式之一是使用流程图,它允许超越一个过渡的是 路径浏览器 也称为桑基图。Google 在 GA4 中引入了一个路径浏览器,看起来很有前途。您可以选择您想要考虑的事件以及屏幕名称。DIY 版本将需要一点 Python 技能(或任何其他语言)来绘制这样的图表,并考虑经常发生的重复屏幕转换。Plotly 是我最喜欢的可视化软件包之一,我认为它没有得到足够的信任,但它令人难以置信。

下面的模型路径显示了一个相当典型的电子商务流程,从首页到列表再到细节,然而,部分首页流量直接进入搜索。大多数搜索流量都指向产品列表页面,但其中很大一部分会将用户直接带到产品详情页面。

作者图片

陈楚翔写了一篇关于 超级用户曲线 的文章,如果你有兴趣,我强烈推荐。一旦你知道在哪里寻找转换信号,看看有多少用户在一个给定的时间段内做这个动作:一天,一个月,两个星期,等等。在最初的博客文章中,Andrew 建议将“一个月的总活跃天数”显示为 X 轴,并使用条形图显示相应桶中的用户百分比。它非常类似于一个直方图的概念,通过显示有多少用户使用你的功能特性,可以告诉你很多关于你的“用户质量”的信息。您可以围绕它跳舞,开发您自己的表示,例如“用户访问产品列表页面的次数”,等等。如果你能通过流量来源和媒介来监控用户构成的变化,那就更好了。

第三方工具经常使用向日葵图来可视化用户路径。它们玩起来很有趣,但当一些屏幕比其他屏幕被访问得更频繁时,或者当两个或更多屏幕之间频繁切换时,它会很快失控。例如,在一个电子商务应用程序中,您可以预期产品列表页面是最常访问的页面,并且会在产品列表页面和产品详细信息页面之间进行大量切换。这些数据并不总是受到重视,可能很难分析。

尽管如此,如果你的应用有任何显著的流量,点击流数据可能会变大,这可以通过聚合来解决。

它告诉你什么

总的来说,了解转换用户使用哪些功能将让您深入了解高级用户的行为。你可以开始构建你的假设,并在数据深度挖掘、用户访谈或实验中测试它们。

除此之外,这种方法将帮助你理解另一面——用户旅途中的干扰和退出点。

在此基础上,你可以开始开发用户质量框架——这对产品工作以及你的营销和 CRM 策略都有好处。

它没有什么

小心避免因果关系-相关性因果关系-相关性陷阱。相关性或关联并不意味着因果关系。换句话说,如果你发现你的超级用户——转化率最高的用户——比其他人更频繁地访问某个特定的屏幕,这并不意味着引导更多的用户访问这个屏幕就一定会带来更多的转化率。虽然这不是不可能的,但是您的高级用户可能会因为他们的内在动机而更倾向于使用某个特定的功能。因此,你需要实验,做用户研究和数据深度挖掘来理解行为。

资源

https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type=gtag#view_item_list https://andrewchen.com/power-user-curve/ https://cloud.google.com/bigquery/docs/reference/standard-sql/navigation_functions https://plotly.com/python/sankey-diagram/

基准测试流失预测方法的发现

原文:https://towardsdatascience.com/findings-from-benchmarking-churn-prediction-methods-95940683523d

通过比较广泛使用的客户流失预测方法获得的见解

Unsplash 上由 Robert Bye 拍摄的照片。

T 他的文章展示了我上一篇(技术性更强)文章中的结果和发现,我在文章中解释了如何构建一个通用管道来对流失预测方法进行基准测试。这篇文章背后的动机是来自 Geiler 等人(2022) 的一篇论文,该论文对不同的流失预测方法进行了基准测试。

TL;博士;医生

  • 根据我的上一篇文章,常见的客户流失方法在五个免费可用的客户流失数据集上进行了基准测试。
  • 由于样本量小(只有五个数据集),这些发现可能不具有代表性,但可以为您提供关于在您的下一个流失预测项目中考虑哪些方法的想法。
  • 在处理高等级不平衡时,采样方法并不总能提高你的模型性能
  • 根据您偏好的误差度量,最佳方法是没有采样(PR AUC)的软投票分类器模型(逻辑回归+ XGB +随机森林)或具有过采样 (SMOTE)和随机过采样 (RND)方法(F2 得分)的逻辑回归

回顾和方法

在讨论结果之前,我想给你一个快速回顾所使用的管道和方法。图 1 显示了所使用的基准测试管道的整体结构,从通过预清理步骤加载数据开始,到实际的基准测试和结果的最终可视化。

图一。基准测试流程概述(图片由作者提供)。

“动态部分”(绿色)表示不同的方法或机器学习模型和采样方法的组合,它们“动态地”附加到流水线的静态部分(估算器、缩放器和编码器)。

🛢数据集

对于这篇文章,我只使用了免费使用的客户流失数据集,并且有一个“真实的”流失率。我所理解的“现实流失率”是 20%或更少的高等级不平衡。如果你的数据有 40%的流失率,你应该考虑你是否真的想做流失率预测或更好地分析你的商业模式,因为几乎一半的客户正在离开。

下表 1 总结了所使用的数据集。

表 1。所用数据集的总结(图片由作者提供)。

前两行显示预清理步骤后每个数据集的行数或观察值,以及相关的流失率。以下各行显示了每个数据集的列数或特征数及其数据类型数。

所述的预清洁步骤移除立柱

  • 丢失超过 20%的值
  • 仅常量值
  • 像唯一标识符(例如,用户 id 或地址)

除了这些规则,我还删除了 KDD 数据集中包含 1000 多个不同类别值的列。否则,在应用一次热编码步骤时,我会遇到一个维数灾难问题。

本文使用的数据集如下:

  • ACM KDD 杯— 2009 ( kdd ) :来自法国电信公司 Orange 的营销数据库,用于预测客户更换提供商的倾向( CC0:公共领域)。它是具有最多观察和特征的数据集。
  • IBM HR Analytics 员工流失&绩效( ibm_hr ) :由 IBM 数据科学家创建的虚构数据集,其中包含导致员工流失的因素(数据库内容许可证(DbCL) )。
  • 2020 年客户流失预测( ccp ) :流失数据(基于与真实世界相似的说法人工得出)。数据也是 R 包(GPL(>= 2 license)的一部分。
  • 葡萄牙银行营销数据集( prt_bank ) :一家葡萄牙银行的电话直销营销活动数据集( CC BY 4.0 )。
  • 报纸流失 ( news ): 关于报纸订阅用户的数据集( CC0:公共域)。

⚖️阶级不平衡

在现实生活中,处理接近 50%的类平衡是非常罕见的。特别是在客户流失预测领域,人们通常不得不处理高度不平衡的问题(流失者占少数)。imblearn 包提供了一系列不同的方法来处理这个问题。使用了以下几种(及其组合):

📦模型

投票分类器使用软投票,这意味着它们的结果是它们使用的模型预测的平均值。

⏱️误差度量

使用了以下误差指标:

有许多关于“正确”误差度量的讨论。首先,没有银弹。选择正确的错误度量标准不仅取决于您的用例优先级(例如,您在假阴性或假阳性上的成本更高吗?)还取决于您的数据(例如,处理强烈的类别不平衡)以及您是否想要预测类别(与阈值相关)或概率(与阈值无关)。因此,更有意义的是考虑几个指标来更清楚地了解你的模型的性能。

F1 分数同等对待精度和召回。然而,在客户流失预测的情况下,我们通常会有更高的假阴性成本(获取客户的成本通常远高于留住客户的成本)。 F2 分数在召回上增加了更高的权重,在等式的精度部分增加了更低的权重。********

例如,如果我们将分类器的默认阈值(0.5)更改为 0.34,因为我们认为这是一个更好的阈值,我们的 F2 分数召回度量更改它们的值。阈值独立指标是 ROC AUCPR AUC 。对于不平衡数据集,Saito 和 Rehmsmeier (2015)和 Czakon (2022)更喜欢 PR AUC 而不是 ROC AUC。

人们也可以认为 PR AUC 是为每个回忆阈值计算的精确度分数的平均值(Czakon,2022)。***

🔬交叉验证

为了计算分数,我使用了重复分层 k 倍交叉验证,其中 n_repeats 和 n_splits = 5。这种方法通常在处理不平衡数据集时使用。在每个折叠中,每个目标类的样本百分比大致相同。

结果

结果可以通过使用像箱线图或可视化表格这样的图表来解释。我将在下面使用两者,从 F2 开始。下面的图 2 显示了通过对所有五个数据集使用不同的采样方法得到的每个模型的 F2 分数。平均值由绿色三角形标记表示。

图二。对所有数据集使用不同采样方法的每个模型的 F2 分数的箱线图(图片由作者提供)。

我们可以从方框图中看到,与深度学习模型(gev_nnffnn)相比,基于树的模型(rflgbxgb)总体上显示出更广泛的分布。我们还可以观察到,当使用混合采样方法(h_SMOTE_RNDh_SMOTE_Tomekh_SMOTE_NCR)时,我们的线性模型(lr)的分布很小,与其他方法相比,其性能相当好。

下表(表 2)显示了每种方法的平均 F2 分数。

表二。对所有数据集使用不同采样方法的每个模型的平均 F2 值(图片由作者提供)。

该表可以按行读取。粗体显示的 F2 分数是相应模型采样方法的最高(最好)分数。粗体和 绿色高亮的单元格是具有最佳综合得分的方法。当使用 SMOTE+RND 过采样方法时,逻辑回归(lr)模型得到了总体最高的 F2 分数

如前所述,F2 分数取决于阈值。一个独立于阈值的指标是 PR AUC 指标,如下图所示(图 3)。

图 3。对所有数据集使用不同采样方法的每个模型的 PR AUC 分数的箱线图(图片由作者提供)。

我们可以看到,基本上高斯朴素贝叶斯(gnb)显示了最大的扩散,而投票分类器方法(lr_xgb_rflr_xgb_rf_ffnn)显示了最小的扩散。通过查看表格(表 3),我们可以看到,最好的方法是没有抽样方法的投票分类器模型。

表 3。在所有数据集上使用不同取样方法的每个模型的平均 PR AUC 值(图片由作者提供)。

在我们的特定情况下,使用逻辑回归、XGB 分类器、随机森林(lr_xgb_rf)的软投票分类器比使用前馈神经网络(lr_xgb_rf_ffnn)的软投票分类器表现稍好。

结论

在这篇文章中,我们使用了五个免费的客户流失数据集对不同的方法进行了基准测试。需要指出的是,少量的数据集可能没有足够的代表性来得出可靠的结论。

然而,我希望这篇文章能让你知道在你的下一个客户流失预测项目中,你可以尝试哪些方法(例如,没有抽样方法的软分类器)。模型的代码和更详细的解释可以在我的上一篇文章中找到。

如果您对提到的其他指标的值感兴趣,请参见下面的附录。

来源

盖勒,l .,阿费尔特,s .,纳迪夫,m .,2022。流失预测的机器学习方法综述。Int J Data Sci Anal。https://doi.org/10.1007/s41060-022-00312-5**

****蒙赫达莱,l,蒙赫达莱,t,刘,K.H,2020。 GEV-NN: 针对二元分类中类别不平衡问题的深度神经网络架构。基于知识的系统。【https://doi.org/10.1016/j.knosys.2020.105534 ****

j .布朗利(2020 年 2 月)。机器学习 Fbeta-Measure 的温和介绍。机器学习掌握。https://machine learning mastery . com/fbeta-measure-for-machine-learning/**

Czakon,J. (2022 年 7 月 21 日)。 F1 评分 vs ROC AUC vs 准确性 vs PR AUC:应该选择哪种评价指标?Neptune . ai . 2022 年 9 月 18 日检索,来自https://neptune.ai/blog/f1-score-accuracy-roc-auc-pr-auc

Saito 和 m . rehms Meier(2015 年 3 月 4 日)。在不平衡数据集上评估二元分类器时,精确召回图比 ROC 图提供的信息更多。PLOS 一号,10(3),e0118432。https://doi.org/10.1371/journal.pone.0118432**

附录

表 A1。F1 宏观评分(图片由作者提供)。

表 A2。F2 分数(图片由作者提供)。

表 A3。Lift 评分(图片由作者提供)。

表 A4。精度(图片由作者提供)。

表 A5。回忆(图片由作者提供)。

表 A6。ROC AUC(图片由作者提供)。

微调变压器模型,用于回答有关自定义数据的问题

原文:https://towardsdatascience.com/fine-tune-transformer-models-for-question-answering-on-custom-data-513eaac37a80

关于在自定义数据上微调拥抱脸 RoBERTa QA 模型并获得显著性能提升的教程

问答精选|塞犍陀·维维克

问答和变形金刚

伯特是 2019 年风靡全球的变形金刚模型。通过屏蔽单词并训练模型基于上下文预测这些屏蔽单词,在未标记的数据上训练 BERT。伯特后来在多项任务上进行微调,并在许多特定的语言任务上取得了艺术级的表现。特别是,BERT 对来自 SQUAD 数据集的 100k+问题答案对进行了微调,这些问题由维基百科文章中提出的问题组成,其中每个问题的答案都是相应段落中的一段文本,或 span

来自 https://arxiv.org/abs/1810.04805的伯特变压器架构

不久后发布的 RoBERTa 模型通过修改关键超参数和改进训练建立在 BERT 的基础上。我们感兴趣的模型是 deepset 发布的 huggingface 上的微调过的 RoBERTA 模型,它在上个月被下载了 100 多万次。

例如,让我们使用 SubjQA 数据集的数据—包含来自 6 个不同领域的评论的 10,000 个问题:书籍、电影、杂货、电子产品、猫途鹰(即酒店)和餐馆。

https://github.com/megagonlabs/SubjQA

特别是因为我正在说明微调的力量,我将从电影评论中产生的问题和答案。这些可以方便地分为两个 csv 文件,分别用于培训(train.csv)和测试(test.csv)。

预处理数据

有 4 个主要列— id、问题、上下文和答案。

微调预处理后的 subjQA 数据集|塞犍陀·维韦克

在这种情况下,Id 可以是一个虚拟索引,上下文指的是要从中提取问题的文本块。我发现这里的答案不仅指答案,还需要包含起始字符索引。例如这里:

用于微调的典型问答格式|塞犍陀·维韦克

一旦你有了这种格式的熊猫数据框架,无论 QA 数据集是什么,其他步骤都是相同的——基本上是将数据预处理成 HuggingFace 模型训练器的格式。我将链接到下面脚本的笔记本。

模特培训

我唯一真正改变的是这里的纪元数量。微调模型需要几分钟时间。其他耗时的步骤包括加载模型和推送到 HuggingFace hub。

模型训练超参数|塞犍陀·维维克

时代的损失|塞犍陀·维维克

模型评估和部署

用于评估问答模型性能的最常见指标是 F1 分数。关于验证数据,旧模型

罗伯塔基于 SubjQA 数据集的模型指标|塞犍陀·维韦克

罗伯塔微调 SubjQA 数据集的模型指标|塞犍陀·维韦克

如您所见,微调将性能提高了 50%以上——这是一个巨大的性能提升!

要部署模型,您只需运行:

trainer.push_to_hub(commit_message=”Training complete”)

模型推理

现在是激动人心的部分——将模型部署到 huggingfacehub 后,您就可以从浏览器访问它了!

https://huggingface.co/skandavivek2/roberta-finetuned-subjqa-movies_2

也可以在笔记本中调用如下:

# Replace this with your own checkpoint 
model_checkpoint2 = “skandavivek2/roberta-finetuned-subjqa-movies_2”
question_answerer = pipeline(“question-answering”, model=model_checkpoint2)

作为健全性检查——您可以从训练数据集中的一个条目中查看微调后的转换器是否获得了正确的结果。

微调问答管道|塞犍陀·维维克

基地问答管道|塞犍陀·维维克

外卖食品

一般来说,NLP 和 AI 的世界在不断发展,似乎每个月都有新的突破。在过去的几周里,OpenAI 的 ChatGPT 风靡了全世界。然而,麦肯锡最近的一份报告显示,即使是基于人工智能的公司,也只有 11%在积极使用变压器。

虽然聊天功能和创造性的人工智能正在形成巨大的里程碑,但我相信,在信息提取和问题回答等任务中使用变形金刚,可以通过提供前所未有的快速准确的结果,为行业带来立竿见影的好处。

拥抱脸降低了进入门槛——现在不仅可以创建自己的微调变压器模型,而且它们使部署和可伸缩性更容易。

您可以在这个 GitHub repo 中找到代码:

https://github.com/skandavivek/transformerQA-finetuning

更新:https://www.answerchatai.com/—我们使用生成式 AI 回答问题并从自定义文本中提取关键知识的 QA 引擎现已上线!回答特定领域的问题 3 个简单的步骤!

  1. 上传网址或粘贴文字,点击搜索按钮
  2. 针对上下文提出问题并点击查询
  3. 得到你的答案!

放心使用,在评论里告诉我你的反馈和任何建议!

参考文献:

  1. https://hugging face . co/docs/transformers/tasks/问答
  2. https://github.com/megagonlabs/SubjQA/tree/master/SubjQA

如果你还不是中会员,想支持我这样的作家,可以通过我的推荐链接随意报名:https://skanda-vivek.medium.com/membership

获取每周数据透视 订阅此处

用于文本分类的微调 BERT

原文:https://towardsdatascience.com/fine-tuning-bert-for-text-classification-54e7df642894

Python 的分步教程

意大利贝加莫皮佐科诺。图片作者。

目录

  1. 简介
  2. 环境设置
  3. 数据集
  4. 预处理
  5. 数据分割
  6. 列车
  7. 预测
  8. 结论
  9. 参考文献

1.介绍

BERT****(BI directionalEn coderRpresentations fromTtransformers)是一个基于 transformers 的机器学习模型,即能够学习单词之间上下文关系的注意力组件。

自然语言处理(NLP)社区可以(至少)以两种方式利用像 BERT 这样强大的工具:

  1. 基于特征的方法
    1.1 下载预训练的 BERT 模型。
    1.2 用 BERT 把自然语言句子变成向量表示。
    1.3 将预先训练好的矢量表示输入到模型中,用于下游任务(如文本分类)。
  2. 执行微调 2.1 下载一个预先训练好的 BERT 模型。
    2.2 更新下游任务的模型权重。

在本帖中,我们将遵循二进制文本分类示例的微调方法。我们将分享可以在谷歌实验室上轻松复制和执行的代码片段。

2.环境设置

虽然这不是必要的,但训练过程将受益于 GPU 的可用性。在 Colab 中,我们可以通过选择Runtime > Change runtime type启用 GPU** 。**

然后,我们安装拥抱 Face⁴ transformers库如下:

!pip install transformers

我们导入所需的依赖项:

3.资料组

我们使用公共的垃圾短信收集数据集⁶.UCI 机器学习库中的这些数据由一个文本文件组成,其中包含一组被标记为垃圾短信的文本文件。来自 Colab 笔记本:

  • 将数据集下载为 zip 文件夹:
!wget 'https://archive.ics.uci.edu/ml/machine-learning-databases/00228/smsspamcollection.zip'
  • 打开文件夹:
!unzip -o smsspamcollection.zip
  • 检查数据文件的前几行:
!head -10 SMSSpamCollection

图片作者。

  • 对于每一行,标签后跟一个制表符和原始文本消息。我们选择处理该文件以得到一个pandas.DataFrame 对象,因为这是数据科学实验中的一个常见起点:
file_path = '/content/SMSSpamCollection'
df = pd.DataFrame({'label':int(), 'text':str()}, index = [])
with open(file_path) as f:
  for line in f.readlines():
    split = line.split('\t')
    df = df.append({'label': 1 if split[0] == 'spam' else 0,
                    'text': split[1]},
                    ignore_index = True)
df.head()

图片作者。

  • 我们提取文本和标签值:
text = df.text.values
labels = df.label.values

4.预处理

我们需要在将文本源提供给 BERT 之前对其进行预处理。为此,我们下载了BertTokenizer:

tokenizer = BertTokenizer.from_pretrained(
    'bert-base-uncased',
    do_lower_case = True
    )

让我们观察一下记号赋予器如何将一个随机句子分割成单词级记号,并将它们映射到 BERT 词汇表中它们各自的 id:

图片作者。

BERT 需要以下预处理步骤:

  1. 添加特殊记号** :
    • [CLS]:每句话首(ID 101 )
    • [SEP]:每句话尾(ID 102)**
  2. 使的句子具有相同的长度:
    ——这是通过填充来实现的,即向较短的序列添加方便的值,以匹配期望的长度。较长的序列被截断。
    -填充([PAD])令牌具有 ID 0
    -允许的最大序列长度为 512 个令牌。
  3. 创建一个注意力屏蔽:
    -0/1 列表,指示模型在学习它们的上下文表示时是否应该考虑记号。我们期望[PAD]令牌具有值0

该过程可以表示如下:

图片作者。

我们可以通过使用tokenizer.encode_plus ⁷方法来执行所有需要的步骤。当被调用时,它返回一个带有以下字段的transformers.tokenization.tokenization-utils_base.BatchEncoding对象:

  • input_ids:令牌 id 列表。
  • token_type_ids:令牌类型 id 列表。
  • attention_mask:0/1 列表,指示模型应该考虑哪些令牌(return_attention_mask = True)。

当我们选择max_length = 32时,较长的句子将被截断,而较短的句子将用[PAD]标记(id: 0)填充,直到它们达到期望的长度。

注意:使用tokenizer.encode_plus方法的想法(以及它的代码)是从这篇文章中借用的:克里斯·麦考密克和尼克·瑞恩的《PyTorch⁸的伯特微调教程。**

我们可以观察文本样本的标记 id,并识别特殊标记[CLS][SEP]的存在,以及达到期望的max_length的填充[PAD]:

token_id[6]

图片作者。

我们还可以通过检查令牌、它们的 id 和随机文本样本的注意掩码来验证tokenizer.encode_plus的输出,如下所示:

图片作者。

注意 : BERT 是一种具有绝对位置嵌入的模型,因此通常建议将输入填充在右边(序列的结尾)而不是左边(序列的开头)。在我们的例子中,tokenizer.encode_plus负责所需的预处理。

5.数据分割

我们将数据集分为训练集(80%)和验证集(20%),并将它们包装在一个torch.utils.data.DataLoader对象周围。凭借其直观的语法,DataLoader在给定的数据集上提供了一个 iterable。

有关DataLoader的更多信息,请点击此处:

  • 数据集&数据加载器— Pytorch 教程
  • 数据加载器文档

6.火车

现在是微调任务的时候了:

  • 根据 BERT 论文中的建议选择超参数:

最佳超参数值因任务而异,但我们发现以下可能值范围适用于所有任务:

-批量:16 个,32 个

-学习率(Adam): 5e-5,3e-5,2e-5

-历元数:2、3、4

  • 定义一些函数来评估培训过程中的验证指标(准确度、精密度、召回率和特异性):

图片作者。

  • 下载transformers.BertForSequenceClassification,这是一个 BERT 模型,在汇集的输出之上有一个用于句子分类(或回归)的线性层:

注意:最好在有 GPU 的情况下运行本笔记本。为了在 CPU 上执行它,我们应该在上面的代码片段中注释model.cuda()以避免运行时错误。

  • 执行培训程序:

训练日志。图片作者。

7.预测

在训练过程之后,在测试集上评估模型的性能是一个很好的实践。出于这个例子的目的,我们简单地预测一个新文本样本的类别(火腿垃圾邮件):

8.结论

在本文中,我们对分类任务的 BERT 进行了微调。我们分享了可以在 Google Colab (或其他环境)上轻松复制和执行的代码片段。

深度学习框架已经配备了像 BERT 这样流行的 NLP 转换器的实现。 TensorFlowPyTorch 提供了一组预先训练好的模型和直观的 API,以方便它们的采用和执行微调任务。此外,像拥抱脸** ⁴这样的人工智能社区使得访问大型模型中枢和简单的界面成为可能。**

最后,我们分享一些有用的资源,从中可以找到更多与该主题相关的示例和信息:

  • TensorFlow 教程:微调 BERT 模型
  • tensor flow Hub 上的 BERT 模型
  • PyTorch 变形金刚⁴
  • 拥抱脸:变形金刚笔记本⁵
  • 拥抱脸:模特枢纽⁶
  • 伯特与 PyTorch⁸微调教程:借用了本帖中tokenizer.encode_plus的用法。
  • 第一次使用 BERT 的视觉指南,⁷,作者 Jay Alammar。

在之前的帖子⁸中,我们也在一个多类文本分类任务中使用了 BERT 和 TensorFlow

9.参考

[1]德夫林,雅各布;张明伟;李,肯顿;图塔诺娃、克里斯蒂娜、伯特:用于语言理解的深度双向变压器的预训练,2018、arXiv:1810.04805 v2

[2]阿希什·瓦斯瓦尼、诺姆·沙泽尔、尼基·帕尔马、雅各布·乌兹科雷特、利永·琼斯、艾丹·戈麦斯、卢卡兹·凯泽、伊利亚·波洛舒欣,“关注是你所需要的全部”,2017 年, arXiv:1706.03762

https://colab.research.google.com/

https://huggingface.co/

https://archive.ics.uci.edu/ml/datasets/sms+spam+collection

[6]https://archive.ics.uci.edu/ml/index.php

[7]https://hugging face . co/docs/transformers/v 4 . 18 . 0/en/internal/token ization _ utils # transformers。pretrainedtokenizerbase . encode _ plus

[8]克里斯·麦考密克和尼克·瑞安。(2019 年 7 月 22 日)。使用 PyTorch 的 BERT 微调教程。从 http://www.mccormickml.com取回

[9]https://py torch . org/tutorials/beginner/basics/data _ tutorial . html #使用数据加载器准备培训数据

[10]https://py torch . org/docs/stable/data . html # torch . utils . data . data loader

[11]https://hugging face . co/transformers/v 3 . 0 . 2/model _ doc/Bert . html # bertforsequenceclassification

[12]https://www . tensor flow . org/text/tutorials/classify _ text _ with _ Bert

https://tfhub.dev/s?q=bert

https://pytorch.org/hub/huggingface_pytorch-transformers/

https://huggingface.co/docs/transformers/notebooks

https://huggingface.co/models

[17]https://jalammar . github . io/a-visual-guide-to-use-Bert-for-first-time/

[18]https://towards data science . com/multi-label-text-class ification-using-Bert-and-tensor flow-D2 e88d 8 f 488d

自然语言处理中领域适应的微调

原文:https://towardsdatascience.com/fine-tuning-for-domain-adaptation-in-nlp-c47def356fd6

卢卡·布拉沃在 Unsplash 上的照片

创建您的自定义模型并上传到拥抱脸

简介

通常,当我们想要解决一个 NLP 问题时,我们会使用预先训练好的语言模型,显然会小心地选择最合适的模型,这些模型已经针对我们感兴趣的语言进行了微调。
例如,如果我正在进行一个基于意大利语的项目,我将使用诸如dbmdz/Bert-base-Italian-xxl-caseddbmdz/Bert-base-Italian-xxl-uncased这样的模型。
这些语言模型通常在通用文本上工作得很好,但当我们在特定领域使用它们时却常常不太合适,例如,如果我们在有其特殊语言的医学或科学领域使用它们。
为此,我们需要应用域适配
领域适应,是指我们对新数据集上的预训练模型进行微调,它会给出更适合该数据集的预测。

微调是什么意思?

NLP 中的微调指的是使用您自己的自定义数据重新训练预训练语言模型的过程。作为微调过程的结果,原始模型的权重被更新,以考虑领域数据和您感兴趣的任务的特征。

作者图片

在我们的例子中,我们将使用屏蔽语言模型任务(MLM) 进行微调。换句话说,我们的数据集将不具有前缀标签,但是对于每个句子,一些单词将被隐藏(被屏蔽),并且模型将不得不猜测哪些是隐藏的单词。

资料组

我们将用于此目的的数据是公开的,可以在 kaggle 的链接中找到。这个数据集包含大约 13k 条新闻。我们只对评论的内容感兴趣,所以你只需要使用文本列。在本文中,我不会描述从 kaggle 下载数据集并提取 csv 文件的过程,如果您有问题,可以阅读我在 TDS 上发布的其他文章。

亲自动手

让我们先导入我们需要的所有库。

进口

让我们定义模型训练所需的超参数。(如果你有足够的计算能力,可以随便玩!)

超参数

先说资料准备。加载您的 csv 文件,分割它并将其转换为数据集对象。

数据准备

现在你必须选择你的分层模型和标记器。我喜欢用 distilbert,因为它更轻,训练更快。

开始模型和标记器

为了给模型提供信息,我们需要对数据集进行标记。

现在我们可以训练我们的模型了。DataCollatorForLanguageModeling是一个允许我们非常容易地在掩蔽语言任务上训练模型的函数。

训练并保存您的自定义模型

困惑评估

你创建的定制模型真的比源模型好吗?要了解是否有改进,我们可以计算模型的困惑度!如果你对这个指标感兴趣,请阅读这篇文章

原始模型困惑

定制模型困惑

希望你注意到了模型困惑的改善!

在拥抱脸上发布您的自定义模型!

如果您在个人数据集或您自己创建的特定数据集上训练您的模型,您的模型可能对其他人有用。只需几行代码就可以上传到你的拥抱脸账户!

首先创建一个拥抱脸的个人帐户,然后运行以下命令。

上传你的模型到拥抱脸

搞定了。现在你的模型在拥抱脸,任何人都可以下载和使用它!感谢大家的贡献!

结论

如果你也喜欢创建你自己的自定义语言模型并把它发布在 Hugging Face 上,请继续创建新的模型并让社区可以使用它们。
如果你想看看我在太空物品(遥感器、卫星……)方面训练过的模型,这就是:

https://huggingface.co/Chramer/remote-sensing-distilbert-cased

结束了

作者:马赛洛·波利蒂

LinkedinTwitter

检测语音数据中的情感:使用 Huggingface 微调 HuBERT

原文:https://towardsdatascience.com/fine-tuning-hubert-for-emotion-recognition-in-custom-audio-data-using-huggingface-c2d516b41cd8

深度学习—音频的自然语言处理

构建定制的数据加载器、实验日志、改进指标的技巧以及 GitHub repo,如果您愿意的话

为什么是音频数据?

与文本和计算机视觉任务的 NLP 相比,音频数据的 NLP 没有得到足够的重视。是时候改变了!

工作

情绪识别——识别语音是否表现出愤怒快乐悲伤厌恶惊讶中性情绪。

注意:一旦我们完成教程,你应该能够重用任何音频分类任务的代码。

资料组

对于本教程,我们将使用 Kaggle 上公开可用的 Crema-D 数据集。(非常感谢大卫·库珀·切尼收集了这个令人敬畏的数据集)。所以请点击链接上的下载按钮。您应该看到包含 Crema-D 音频文件的 archive.zip 开始下载。它包含 7k+音频文件,格式为.wav

注意:请随意使用您收集的任何音频数据,而不是 CremaD 数据集。

如果你想跟随这个教程,这里有 GitHub repo。

https://github.com/V-Sher/Audio-Classification-HF/tree/main/src

拥抱脸库和教练 API

正如标题中提到的,我们将使用拥抱人脸库来训练模型。特别是,我们将使用它的训练器类 API。

为什么是培训师?为什么不用 PyTorch 写一个标准的训练循环呢?

下面是标准样板代码在 Pytorch 中的样子:

摘自我给 PyTorch 的入门教程

相比之下,训练器大大简化了编写训练循环的复杂性,使得训练可以在一行中完成:

trainer.train()

除了支持基本的训练循环,它还允许在多个 GPU/TPU 上进行分布式训练、回调(例如提前停止)、评估测试集的结果等。所有这些都可以通过在初始化培训师课程时简单地设置几个参数来实现。

如果不是因为任何事情,我觉得用 Trainer 代替香草 PyTorch 肯定会导致一个更有组织,看起来更干净的代码库。

让我们开始吧…

装置

虽然是可选的,但我强烈建议通过创建和激活一个新的虚拟环境来开始本教程,在这个环境中我们可以完成所有的pip install ...

python -m venv audio_env
source activate audio_env/bin/activate

正在加载数据集

与任何数据建模任务一样,我们首先需要使用数据集库加载数据集(我们将把它传递给培训师类)。

pip install datasets

鉴于我们正在使用自定义数据集(与该库附带的预安装数据集相反),我们需要首先编写一个加载脚本(让我们称之为crema.py),以教练可接受的格式加载数据集。

在之前的一篇文章中,我已经讲述了如何创建这个脚本(非常详细)。(我强烈建议通过它来了解configcache_dirdata_dir等的用法。在下面的代码片段中)。数据集中的每个示例都有两个特征:filelabel

dataset_config = {
  "LOADING_SCRIPT_FILES": os.path.join(PROJECT_ROOT, "**crema.py**"),
  "CONFIG_NAME": "clean",
  "DATA_DIR": os.path.join(PROJECT_ROOT, "data/archive.zip"),
  "CACHE_DIR": os.path.join(PROJECT_ROOT, "cache_crema"),
}ds = load_dataset(
  dataset_config["LOADING_SCRIPT_FILES"],
  dataset_config["CONFIG_NAME"],
  *data_dir*=dataset_config["DATA_DIR"],
  *cache_dir*=dataset_config["CACHE_DIR"]
)print(ds)********* OUTPUT ********DatasetDict({
    train: Dataset({
        features: ['**file**', '**label**'],
        num_rows: 7442
    })
})

P.S:虽然我们为 CremaD 数据集创建了一个 *datasets.Dataset* 对象(传递给 Trainer 类),但它不一定必须是这样。我们也可以定义并使用 *torch.utils.data.Dataset* (类似于我们在 教程中创建的 CSVDataset)。

编写模型培训脚本

Github repo 中的目录结构:

Audio-Classification-Medium  
│
└───src
│   │
│   └───data
│       │   crema.py
│   
└───data
|   │   archive.zip 
|
└───scripts
    │   audio_train.py

让我们开始写我们的audio_train.py剧本吧。

装置

实验跟踪(可选)

我们使用权重&偏差进行实验跟踪,因此请确保您已经创建了一个帐户,然后根据您的详细信息更新USERWANDB_PROJECT

加载特征提取器

问题:从广义上讲,什么是特征提取器?
答:特征提取器是一个负责为模型准备输入特征的类。例如,在图像的情况下,这可以包括裁剪图像、填充,或者在音频的情况下,这可以包括将原始音频转换成频谱特征、应用归一化、填充等。

图像数据的特征提取器示例:

从变形金刚导入 ViTFeatureExtractor
vit _ extractor = ViTFeatureExtractor()
print(vit _ extractor)

ViTFeatureExtractor {
" do _ normalize ":true,
"do_resize": true,
" feature _ extractor _ type ":" ViTFeatureExtractor ",
"image_mean": [0.5,0.5,0.5],
"image_std": [0.5,0.5,0.5],
"resample": 2,
"size": 224
}

更具体地说,我们将使用[Wav2Vec2FeatureExtractor](https://huggingface.co/docs/transformers/model_doc/wav2vec2#transformers.Wav2Vec2FeatureExtractor)。这是从sequence feature extractor派生的一个类,它是一个通用的语音识别特征提取类,由 Huggingface 提供。

使用wav2 vec 2 feature extractor有三种方法:

  • 选项 1 —使用默认值。
from transformers import Wav2Vec2FeatureExtractorfeature_extractor = Wav2Vec2FeatureExtractor()
print(feature_extractor)**** OUTPUT ****
Wav2Vec2FeatureExtractor {
   "do_normalize": true,
   "feature_extractor_type": "Wav2Vec2FeatureExtractor",
   "feature_size": 1,
   "padding_side": "right",
   "padding_value": 0.0,
   "return_attention_mask": false,
   "sampling_rate": 16000 
}
  • 选项 2-修改任何Wav2Vec2FeatureExtractor 参数来创建您的自定义特征提取器。
from transformers import Wav2Vec2FeatureExtractorfeature_extractor = Wav2Vec2FeatureExtractor(
          ***sampling_rate*=24000,
          *truncation*=True**
)
print(feature_extractor)**** OUTPUT ****
Wav2Vec2FeatureExtractor {
   "do_normalize": true,
   "feature_extractor_type": "Wav2Vec2FeatureExtractor",
   "feature_size": 1,
   "padding_side": "right",
   "padding_value": 0.0,
   "return_attention_mask": false,
 **"sampling_rate": 24000,
   "truncation": true**
}

选项 3:因为我们不寻求任何定制,我们可以使用from_pretrained()方法来加载预训练模型的默认特征提取器参数(通常存储在名为preprocessor_config.json的文件中)。由于我们将使用facebook/hubert-base-ls960作为我们的基础模型,我们可以获得它的特征提取器参数(可用于视觉检查这里preprocessor_config.json)。

from transformers import Wav2Vec2FeatureExtractor**model = "facebook/hubert-base-ls960"**
feature_extractor = Wav2Vec2FeatureExtractor**.from_pretrained(model)**print(feature_extractor)*** OUTPUT ***Wav2Vec2FeatureExtractor {
   "do_normalize": true,
   "feature_extractor_type": "Wav2Vec2FeatureExtractor",
   "feature_size": 1,
   "padding_side": "right",
   "padding_value": 0,
   "return_attention_mask": false,
   "sampling_rate": 16000
 }

为了查看特征提取器的运行情况,让我们将一个虚拟音频文件作为raw_speech输入到 Wav2Vec2FeatureExtractor:

model_id = "facebook/hubert-base-ls960"
feature_extractor = Wav2Vec2FeatureExtractor.from_pretrained(model_id)audio_file = "dummy1.wav"
audio_array = librosa.load(audio_file, *sr*=16000, *mono*=False)[0]input = *feature_extractor*(
       **raw_speech=audio_array**,
       *sampling_rate*=16000,
       *padding*=True,
      *return_tensors*="pt"
)print(input)
print(input.shape)
print(audio_array.shape)***** OUTPUT ******
{'input_values': tensor([[-0.0003, -0.0003, -0.0003,  ...,  0.0006, -0.0003, -0.0003]])}torch.Size([1, 36409])(36409,)

需要注意的几件事:

  • 特征提取器的输出是一个包含input_values的字典。它的值只是应用于audio_array标准化,即来自librosa库的输出。事实上,input.input_valuesaudio_array的形状是一样的。
  • 当调用特征提取器时,确保您使用的sampling_rate与基础模型用于其训练数据集的sampling_rate相同。我们使用这个 facebook 模型进行训练,它的模型卡明确声明以 16Khz 采样语音输入。
  • return_tensors可以分别为 PyTorch 张量、TensorFlow 对象和 NumPy 数组取值“pt”、“tf”和“np”。
  • padding在单个音频文件的情况下没有太大意义,但当我们进行批处理时,它有意义,因为它填充较短的音频(带有额外的 0 或-1)以具有与最长音频相同的长度。以下是用不同长度填充音频文件的示例:
audio_file_1 = "dummy1.wav"
audio_file_2 = "dummy2.wav"audio_array_1 = librosa.load(audio_file_1, *sr*=16000, *mono*=False)[0]
audio_array_2 = librosa.load(audio_file_2, *sr*=16000, *mono*=False)[0]input_with_one_audio = *feature_extractor*(
       **audio_array_1**,
       *sampling_rate*=16000,
       *padding*=True,
      *return_tensors*="pt"
)input_with_two_audio = *feature_extractor*(
       **[audio_array_1, audio_array_2]**,
       *sampling_rate*=16000,
       *padding*=True,
      *return_tensors*="pt"
)print(input_with_one_audio.input_values.shape)
print(input_with_two_audios.input_values.shape)***** OUTPUT ****
torch.Size([1, 36409])
torch.Size([2, 37371])

既然我们知道特征提取器模型的输出可以根据输入音频的不同而改变形状,那么在将一批输入推送到模型进行训练之前,填充为什么重要就变得很清楚了。当处理批次时,我们可以(a)将所有音频填充到训练集中最长音频的长度,或者(b)将所有音频截断到最大长度。(a)的问题是,我们不必要地增加了存储这些额外填充值的内存开销。(b)的问题是,由于截断,可能会丢失一些信息。

有一个更好的选择——使用数据整理器在模型训练期间应用动态填充。我们将很快看到他们的行动!

在构建批次(用于训练)时,数据整理器可以只对特定批次的输入进行预处理(比如填充)。

分类的加载基础模型

如前所述,我们将使用脸书的休伯特模型来分类音频。如果你对休伯特的内部工作方式感兴趣,可以看看这篇由乔纳森·Bgn 撰写的关于休伯特的很棒的入门教程。

裸露的 HubertModel 是 24 个 transformer 编码器层的堆栈,并为这 24 层中的每一层输出原始隐藏状态(顶部没有任何特定的头用于分类)。

bare_model = HubertModel.from_pretrained("facebook/hubert-large-ls960-ft")last_hidden_state = bare_model(input.input_values).last_hidden_state
print(last_hidden_state.shape)*** OUTPUT ***torch.Size([1, 113, 1024]) *# the hidden size i.e. 113 can vary depending on audio*

我们需要在这个裸模型之上的某种分类头,它可以将最后一个隐藏层的输出馈送到一个线性层,最终输出 6 个值(6 个情感类别中的每一个)。这正是Hubert for sequence classification所做的事情。它的顶部有一个分类头,用于音频分类等任务。

然而,与上面解释的特征提取器配置类似,如果您从预训练模型中获得 HubertForSequenceClassification 的默认配置,您会注意到,由于其默认配置的定义方式,它仅适用于二进制分类任务。

model_path = ""facebook/hubert-large-ls960-ft""hubert_model = HubertForSequenceClassification.from_pretrained(model_path)hubert_model_config = hubert_model.configprint("Num of labels:", hubert_model_config.num_labels)**** OUTPUT ******Num of labels: 2**

对于我们的 6 类分类,我们需要使用 PretrainedConfig 更新要传递给 Hubert 模型的配置(查看部分— 参数进行微调)。

需要注意的几件事:

  • 在第 5 行,from_pretrained()facebook/hubert-base-ls960加载模型架构模型权重(即所有 24 个变压器层+线性分类器的权重)。
    注意:如果你简单地做了 *hubert_model = HubertForSequenceClassification()* ,变换编码器和分类器权重被随机初始化
  • ignore_mismatched_sizes参数设置为True很重要,因为如果没有它,你会因为尺寸不匹配而得到一个错误(见下图)——作为facebook/hubert-base-ls960一部分的分类器权重具有形状(2, classifier_proj_size),而根据我们新定义的配置,我们的权重应该具有形状(6, classifier_proj_size)。鉴于我们无论如何都要从头重新训练线性分类器层,我们可以选择忽略不匹配的大小。

分类器大小不匹配导致的错误

冻结图层进行微调

根据一般经验,如果训练预训练模型的基础数据集与您正在使用的数据集有显著不同,最好取消冻结并重新训练顶层的几个层。

首先,我们解冻顶部两个编码器层(最接近分类头)的权重,同时保持所有其他层的权重冻结。为了冻结/解冻权重,我们将param.require_grad设置为假/真,其中param指的是模型参数。

在模型训练期间解冻权重意味着这些权重将照常更新,以便它们可以达到手头任务的最佳值。

注意:虽然在训练过程的最开始解冻许多层看起来很直观,但这不是我推荐的。我实际上是通过冻结所有层并只训练分类器头来开始我的实验的。因为结果很差(这点不奇怪),我解冻两层恢复训练。

正在加载数据集

使用我们的定制加载脚本crema.py,我们现在可以使用load_dataset()方法从数据集库中加载我们的数据集。

接下来,我们使用map()将数据集中的所有原始音频(以.wav格式)转换成数组。

一般来说,map对数据集中的所有行/样本重复应用一个函数。

这里,函数(定义为一个 lambda 函数)接受一个参数x(对应于数据集中的一行),并使用librosa.load()将该行中的音频文件转换为一个数组。如上所述,确保采样率(sr)合适。

注意:如果你在这个阶段做 *print(ds)* ,你会注意到数据集中的三个特征:

*print(ds)******* OUTPUT *******DatasetDict({
    train: Dataset({
        features: ['file', 'label', 'array'],
        num_rows: 7442
    })
})*

一旦我们生成了数组,我们将再次使用map——这一次使用辅助函数prepare_dataset()来准备输入。

prepare_dataset是一个助手函数,它将处理函数应用于数据集中的每个示例(或者一组示例,也就是batch)。更具体地说,该函数做两件事— (1)读取出现在batch["array"]的音频数组,并使用上面讨论的feature_extractor从中提取特征,并将其存储为名为input_values的新特征—(除了filelabelsarray)以及(2)创建名为labels的新特征,其值与batch["label"]相同。

问题:你可能想知道对于每个例子都有 *label* *labels* 有什么意义,尤其是当它们有相同的值时。
原因:训练器 API 会寻找 *labels* 的列名,默认情况下,所以我们只是乐于助人。如果你愿意,你甚至可以在这一步删除https://huggingface.co/docs/datasets/process#remove*和其他* *label* 列,或者更好,在创建加载脚本时将特征命名为“标签”。

如果你仔细观察,就会发现不像前面的map用例,lambda 函数只需要一个输入参数,prepare_dataset()需要两个参数。

记住:每当我们需要向map内部的函数传递多个参数时,我们必须将fn_kwargs参数传递给map。这个参数是一个字典,包含所有要传递给函数的参数。

基于其函数定义,我们需要两个参数用于prepare_dataset() — (a)数据集中的行和(b)特征提取器——因此我们必须如下使用fn_kwargs:

接下来,我们将使用class_encode_column()将所有字符串标签转换成 ids,1,2,3,4,5,6)。

最后,我们通过使用train_test_split()引入训练-测试-验证分割。我们需要以这种方式分割两次,以获得三个不重叠的数据集,所有这些数据集在下面的步骤 8 中合并成一个单独的DatasetDict

让训练开始吧…

所有的细节都准备好了,我们现在准备开始使用训练者职业进行训练。

首先,我们需要指定训练参数——这包括时期的数量、批量大小、存储训练模型的目录、实验记录等。

需要考虑的事情很少:

  • 梯度累积步骤在你想在训练期间推动更大批量但你的记忆有限的情况下非常有用。设置gradient_accumulation_steps=4允许我们在每 4 步后更新权重—在每一步中,batch_size=32个样本被处理,它们的梯度被累加。只有在 4 个步骤之后,当积累了足够的梯度时,权重才会得到更新。

其次,除了指定训练和评估数据集之外,我们用这些训练参数实例化训练器类。

需要考虑的事情很少:

  • 在第 5 行,我们使用了一个data_collator。我们在本教程开始时简要讨论了这种动态填充输入音频数组的方法。数据排序器初始化如下:
*# DEFINE DATA COLLATOR - TO PAD TRAINING BATCHES DYNAMICALLYdata_collator = DataCollatorCTCWithPadding(
            processor=feature_extractor,
            padding=True
)*

DataCollatorCTCWithPadding是改编自本教程的 dataclass。我强烈推荐快速阅读教程中的设置教练部分,以了解这个课程发生了什么。

不涉及太多细节,这个类中的__call__方法负责准备接收到的输入。它从数据集中获取一批示例(记住每个示例有 4 个特征— filelabelslabelarrayinput_values),并返回同一批示例,但使用processor.pad将填充应用到input_values。还有,批量中的labels都转换成 Pytorch 张量。

  • 在第 8 行,我们定义了compute_metrics(),这是一种告诉培训师在评估期间必须计算哪些指标(准确度、精确度、f1、召回率等)的方式。它将评估预测(eval_pred)作为输入,并使用metric.compute(predictions=.., references=...)比较实际标签和预测标签。同样,compute_metrics()的样板代码是从改编而来的。

注:如果您想发挥创意并显示自定义指标(例如,绝对差异 btw 实际值和预测值的日志),您可以修改 *compute_metrics()* 。在这样做之前你只需要知道 *eval_pred* 返回的是什么。这可以通过在实际训练模型之前在您的评估/测试数据集上运行 *trainer.predict* 来提前发现。在我们的例子中,它返回实际的标签和预测(即 logits 在其上应用 [*argmax*](https://en.wikipedia.org/wiki/Arg_max) 函数以获得预测的类):

*trainer = Trainer(model=..., args=...,...)
output = trainer.predict(ds["test"])print(output)**** OUTPUT *****
PredictionOutput(
predictions=array([
       [ 0.0331, -0.0193, -0.98767, 0.0229, 0.01693, -0.0745],
       [-0.0445,  0.0020, 0.13196, 0.2219, 0.94693, -0.0614],
        .
        .
        .
], dtype=float32),
label_ids=array([0, 5, ......]),
metrics={'test_loss': 1.780486822128296, 'test_accuracy': 0.0, 'test_runtime': 1.6074, 'test_samples_per_second': 1.244, 'test_steps_per_second': 0.622}
)*

现在实际的训练只有一行代码

快速迂回:第 4 行包含从一个检查点继续训练的命令。但是首先,什么是检查点?

在训练过程中,教练将创建模型重量的快照,并将它们存储在TrainingArguments(output_dir="results")中定义的output_dir中。这些文件夹通常被命名为checkpoint-XXXX,包含模型权重、训练参数等。

检查站

您可以分别使用[save_strategy](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.TrainingArguments.save_strategy)[save_steps](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.TrainingArguments.save_steps)指定何时多久创建这些检查点。默认情况下,每 500 步后会保存检查点(save_steps=500)。我提到这一点的原因是因为我不知道这些默认值,并且在一次培训会议(运行了 7 个小时)中,我看到在输出目录中没有创建任何检查点。这是我正在使用的配置:

  • 训练样本:6697
  • 次数= 5
  • 批量= 32
  • 梯度累积步长= 4

经过一小时又一小时的调试,我发现在我的例子中总共只有 260 步,而默认的保存只发生在第 500 步之后。🤦作为TrainingArguments()的一部分,‍♀设置save_steps = 100为我解决了这个问题。

在底部,您可以找到优化步骤的总数

注意:如果您想知道如何计算总步数(即本例中的 260 步):

总训练批量 =批量梯度累积步长= 324 = 128* 总优化步长 =(训练样本/总训练批量)历数=(6697/128)*5 ≈ 260。**

对测试集进行预测并记录结果

需要考虑的事情很少:

  • 要记录权重和偏差的任何额外指标/变量,我们可以使用wandb.log()。例如,在第 4 行,我们记录了测试集的准确性。
  • 默认情况下,wandb不记录训练过的模型,所以它只在训练完成后在本地机器上可用。为了显式地存储模型工件,我们需要使用带有policy="end"wandb.save(),这意味着只有当运行结束时才同步文件。

结果和反思

使用不同超参数组合的所有不同模型运行的结果都记录在我的重量和偏差仪表板上这里

WandB 仪表板

查看学习曲线,看起来我们最近的运行( faithful-planet-28 测试精度= 68%——考虑到只花了 4 个小时的训练,还不算太坏)可能会受益于额外的时期,因为 train 和 eval 损失仍在减少,尚未稳定下来(或者更糟,开始发散)。根据这是否可行,可能需要解冻更多的编码器层。

学习曲线

一些反思和学习:

  • 如果我们正在增加纪元,可能值得考虑提前通过回调停止训练。详见栈溢出讨论。
*# TO IMPLEMENT EARLY STOPPINGtrainer = Trainer(
  callbacks=[EarlyStoppingCallback(early_stopping_patience = 10)]
)*
  • 除了 Cuda 之外,Trainer 最近还增加了对新 Mac M1 GPU 的支持(只需设置args = TrainingArguments(use_mps_device=True))。如果您正在与他们一起工作,请注意,有些人报告了指标的下降(这是一个已知的错误—参见这个问题)。

结论

希望你现在对使用变形金刚库微调深度学习模型更有信心了。如果你真的推进这个项目,请与我和更广泛的社区分享你的结果(和提高准确性的步骤)。

与任何 ML 项目一样,关注负责任的 AI 开发对于评估未来工作的影响至关重要。考虑到最近的工作表明情绪检测方法可能有内在的性别/种族偏见,并可能造成现实世界的伤害,这变得更加重要。此外,如果您正在处理敏感的音频数据(比如包含信用卡详细信息的客户支持电话),请应用脱敏技术来保护个人身份信息、敏感的个人数据或业务数据。

一如既往,如果有更简单的方法来做/解释本文中提到的一些事情,一定要让我知道。一般来说,避免不请自来的破坏性/垃圾/敌意评论!

直到下一次✨

我喜欢写循序渐进的初学者指南、操作指南、ML/AI 中使用的解码术语等。如果你想完全访问我的所有文章(以及其他媒体上的文章),那么你可以使用 我的链接**这里 注册。**

** **

微调用于发票识别的 LayoutLM v2

原文:https://towardsdatascience.com/fine-tuning-layoutlm-v2-for-invoice-recognition-91bf2546b19e

从注释到训练和推理

作者图片:用于发票识别的 LayoutLMV2

介绍

自从我写了上一篇关于利用 layoutLM transformer 模型进行发票识别的文章"微调 Transformer 模型"之后,微软发布了一个新的 layoutLM v2 transformer 模型,与第一个 layoutLM 模型相比,在性能上有了显著的提高。在本教程中,我将一步一步地演示如何从数据注释到模型训练和推理来微调发票上的 layoutLM V2。

Google Colab 上提供了训练和推理脚本。

培训脚本:

https://colab.research.google.com/drive/18gi-OUCv8S_1ttD6AoHD9hCUO_0j35-4?usp=sharing

推理脚本:

https://colab.research.google.com/drive/1G6fBdP6xHhuT1GdJA1Ntjomx_Um8XMhO?usp=sharing

布局 V2 模型

与第一个 layoutLM 版本不同,layoutLM v2 在 Transformer 体系结构的第一个输入层中集成了可视功能、文本和位置嵌入,如下所示。这使得模型能够学习视觉和文本信息之间的跨模态交互,在单个多模态框架中文本、布局和图像之间的交互。下面是摘要中的一个片段:“实验结果表明,LayoutLMv2 大幅优于 LayoutLM,并在多种下游视觉丰富的文档理解任务上取得了新的最新成果,包括 FUNSD (0.7895 → 0.8420)、CORD (0.9493 → 0.9601)、SROIE (0.9524 → 0.9781)、Kleister-NDA (0.8340 → 0.8520)、RVL

更多信息请参考原文

布局 LMV2 架构(图片来自徐等人,2022 )

注释

对于本教程,我们已经使用ubai 文本注释工具注释了总共 220 张发票。 UBIAI OCR 注释允许以常规或手写形式直接在原生 pdf、扫描文档或图像 PNG 和 JPG 上进行注释。我们最近增加了对 20 多种语言的支持,包括阿拉伯语和希伯来语等。

作者图片:UBIAI 多语言 OCR 注释

以下是关于如何使用该工具注释 pdf 和图像的精彩概述:

UBIAI 教程由 Karndeep Singh

除了带标签的文本偏移量和边界框,我们还需要导出每个带注释的文档的图像。使用 UBIAI 可以很容易地做到这一点,因为它将所有注释和每个文档的图像一起导出到一个 ZIP 文件中。

作者图片:注释数据 JSON 输出

数据预处理:

UBIAI 导出 ZIP 文件后,我们将文件上传到 google drive 文件夹。我们将使用 google colab 进行模型训练和推理。

  • 第一步是打开一个 google colab,连接你的 google drive 并安装 transfromers 和 detectron2 软件包:
from google.colab import drivedrive.mount('/content/drive')!pip install -q git+https://github.com/huggingface/transformers.git!pip install -q datasets seqeval!python -m pip install -q 'git+https://github.com/facebookresearch/detectron2.git'
  • 为了简化数据预处理和模型训练步骤,我们创建了 preprocess.py 和 train.py 文件,其中包含启动训练所需的所有代码。从 github 克隆文件:
! rm -r layoutlmv2_fine_tuning! git clone -b main [https://github.com/walidamamou/layoutlmV2.git](https://github.com/walidamamou/layoutlmV2.git)
  • 接下来,我们需要解压缩导出的数据集,并将所有文件放在一个文件夹中:
IOB_DATA_PATH = "/content/drive/MyDrive/LayoutLM_data/Invoice_Project_XhDtcXH.zip"! cd /content/! rm -r data! mkdir data! cp "$IOB_DATA_PATH" data/dataset.zip! cd data && unzip -q dataset && rm dataset.zip! cd ..

微调 LayoutLM v2 模型:

我们几乎准备好启动培训,我们只需要指定几个超参数来配置我们的模型和模型输出的路径。你当然可以改变这些变量来得到最好的结果。对于本教程,我们使用 33%的测试规模,批量= 4,学习率= 5e-5 和 50 个时期。

#!/bin/bash#preprocessing argsTEST_SIZE = 0.333DATA_OUTPUT_PATH = "/content/"#training argsPROCESSED_DATA_PATH = DATA_OUTPUT_PATHMODEL_OUTPUT_PATH = "/content/layoutlmv2-finetuned"TRAIN_BATCH_SIZE = 4VALID_BATCH_SIZE = 2LEARNING_RATE = 5e-5EPOCHS = 50

我们现在准备启动模型,只需运行:

! python3 layoutlmv2_fine_tuning/train.py --epochs $EPOCHS \--train_batch_size $TRAIN_BATCH_SIZE \--eval_batch_size $VALID_BATCH_SIZE \--learning_rate $LEARNING_RATE \--output_dir $MODEL_OUTPUT_PATH \--data_dir $PROCESSED_DATA_PATH

训练完成后,精确度、召回率和 F1 分数将显示如下。我们获得了 0.75 的 F1 分数和 0.96 的准确度,这对于注释 220 张发票来说是不错的分数。

作者图片:LayoutLMV2 分数

与 layoutLM V2 的推论:

我们现在准备在一张新的看不见的发票上测试我们新训练的模型。对于这一步,我们将使用 Google 的 Tesseract 对文档进行 OCR,并使用 layoutLM V2 从发票中提取实体。

让我们安装 pytesseract 库:

## install tesseract OCR Engine! sudo apt install tesseract-ocr! sudo apt install libtesseract-dev## install pytesseract , please click restart runtime button in the cell output and move forward in the notebook! pip install pytesseract## install model requirements!pip install -q git+https://github.com/huggingface/transformers.git!pip install -q torch==1.8.0+cu101 torchvision==0.9.0+cu101 -f https://download.pytorch.org/whl/torch_stable.html!python -m pip install -q 'git+https://github.com/facebookresearch/detectron2.git'

接下来,我们将使用文件 layoutlmv2Inference.py(先前克隆的),它将处理 OCRd 发票并应用模型来获得预测。

最后,指定您的模型路径、图像路径、输出路径,并运行如下所示的推理脚本:

model_path = "/content/drive/MyDrive/LayoutLM v2 Model/layoutlmv2-finetuned.pth"imag_path = "/content/invoice_eng.PNG"output_path = "/content/Inference_output2"! python3 layoutlmv2Inference.py "$model_path" "$imag_path" "$output_path"

一旦推断完成,您将在图像上找到覆盖的预测,以及一个包含所有标签、文本和推断中的偏移的 JSON 文件)output2 文件夹。让我们看看模型预测:

图片作者:LayoutLMV2 预测

以下是 JSON 文件的一个示例:

作者图片:JSON 输出

该模型能够预测大多数实体,如卖方,日期,发票号码和总额,但错误地预测“单价”为 TTC_ID。这表明我们需要注释更多类型的发票,这样我们的模型就可以学习一般化。

结论

总之,我们已经展示了如何从注释开始到训练和推理来微调发票上的 layoutLM V2 的逐步教程。该模型可以在任何其他半结构化文档上进行微调,如驾照、合同、政府文档、财务文档等。

如果你想尝试 UBIAI 的 OCR 注释功能,只需免费注册即可开始注释。

在推特上关注我们 @UBIAI5订阅这里

页(page 的缩写)s:写完这篇文章后,一个关于培训 layoutlmV3 的新教程已经发布,如果你想了解更多,请点击这个链接

微调用于发票处理的 LayoutLM v3

原文:https://towardsdatascience.com/fine-tuning-layoutlm-v3-for-invoice-processing-e64f8d2c87cf

并将其性能与 LayoutLMv2 进行比较

来自 Pixabay 的图片由 Sabine van Erp

文档理解是文档处理和抽取的第一步,也是最重要的一步。它是从非结构化或半结构化文档中提取信息并将其转换为结构化形式的过程。然后,这种结构化表示可用于支持各种下游任务,如信息检索、摘要、分类等。理解文档有许多不同的方法,但它们都有一个共同的目标:创建文档内容的结构化表示,以便用于进一步处理。

对于发票、收据或合同等半结构化文档,随着 LayoutLM v1 和 v2 的开发,微软的 layoutLM 模型显示出了巨大的前景。如需深入教程,请参考我之前的两篇文章“微调变压器模型用于发票识别”和“微调 LayoutLM v2 用于发票识别”。

在本教程中,我们将微调微软最新的 LayoutLM v3,类似于我以前的教程,我们将比较其性能与 layoutLM v2 模型。

LayoutLM v3

LayoutLM v3 相对于其前身的主要优势是多模态转换器架构,它以统一的方式结合了文本和图像嵌入。不是依赖 CNN 来进行图像嵌入,文档图像被表示为图像块的线性投影,然后图像块被线性嵌入并与文本标记对齐,如下所示。这种方法的主要优点是所需参数的减少和总体计算量的降低。

布局 LM v3 架构。来源

作者表明,“LayoutLMv3 不仅在以文本为中心的任务(包括表单理解、收据理解和文档视觉问答)中,而且在以图像为中心的任务(如文档图像分类和文档布局分析)中,都实现了一流的性能”。

微调 LayoutLM v3

与我的上一篇文章类似,我们将使用 220 个带注释的发票的相同数据集来微调 layoutLM v3 模型。为了执行注释,我们使用了 UBIAI 文本注释工具,因为它支持 OCR 解析、原生 PDF/图像注释以及以与 LayoutLM 模型兼容的正确格式导出,而无需任何后期处理。此外,可以在 UBIAI 平台中对 layouLM 模型进行微调,并使用它自动标记您的数据,这可以节省大量手动注释时间。

以下是关于如何使用该工具注释 pdf 和图像的精彩概述:

Karndeep Singh 提供的 UBIAI 教程

UBIAI 导出注释文件后,我们将它上传到 google drive 文件夹。我们将使用 google colab 进行模型训练和推理。

可以在下面的 google colab 中访问训练和推理脚本:

训练:

https://colab.research.google.com/drive/1TuPQ1HdhMYjfQI9VRV3FMk7bpPYD4g6w?usp=sharing

推论:

https://colab.research.google.com/drive/1YDTKuIg4mkrCBz1bzqabL27_UzF2Z82F?usp=sharing

  • 第一步是打开一个 google colab,连接你的 google drive,从 huggingface 安装变形金刚包。请注意,与 layoutLMv2 不同,我们没有使用 detectron 2 包来微调实体提取模型。但是,对于布局检测(超出了本文的范围),需要 detectorn 2 包:
from google.colab import drivedrive.mount('/content/drive')!pip install -q git+https://github.com/huggingface/transformers.git! pip install -q git+https://github.com/huggingface/datasets.git "dill<0.3.5" seqeval
  • 接下来,提取 preprocess.py 脚本来处理从 UBIAI 导出的 ZIP 文件:
! rm -r layoutlmv3FineTuning! git clone -b main [https://github.com/UBIAI/layoutlmv3FineTuning.git](https://github.com/UBIAI/layoutlmv3FineTuning.git)#!/bin/bashIOB_DATA_PATH = "/content/drive/MyDrive/LayoutLM_data/Invoice_Project_mkWSi4Z.zip"! cd /content/! rm -r data! mkdir data! cp "$IOB_DATA_PATH" data/dataset.zip! cd data && unzip -q dataset && rm dataset.zip! cd ..
  • 运行预处理脚本:
#!/bin/bash#preprocessing argsTEST_SIZE = 0.33DATA_OUTPUT_PATH = "/content/"! python3 layoutlmv3FineTuning/preprocess.py --valid_size $TEST_SIZE --output_path $DATA_OUTPUT_PATH
  • 加载数据集后处理:
from datasets import load_metricfrom transformers import TrainingArguments, Trainerfrom transformers import LayoutLMv3ForTokenClassification,AutoProcessorfrom transformers.data.data_collator import default_data_collatorimport torch# load datasetsfrom datasets import load_from_disktrain_dataset = load_from_disk(f'/content/train_split')eval_dataset = load_from_disk(f'/content/eval_split')label_list = train_dataset.features["labels"].feature.names
num_labels = len(label_list)
label2id, id2label = dict(), dict()
for i, label in enumerate(label_list):
    label2id[label] = i
    id2label[i] = label
  • 定义一些评估指标:
metric = load_metric("seqeval")
import numpy as npreturn_entity_level_metrics = Falsedef compute_metrics(p):
    predictions, labels = p
    predictions = np.argmax(predictions, axis=2)# Remove ignored index (special tokens)
    true_predictions = [
        [label_list[p] for (p, l) in zip(prediction, label) if l != -100]
        for prediction, label in zip(predictions, labels)
    ]
    true_labels = [
        [label_list[l] for (p, l) in zip(prediction, label) if l != -100]
        for prediction, label in zip(predictions, labels)
    ]results = metric.compute(predictions=true_predictions, references=true_labels,zero_division='0')
    if return_entity_level_metrics:
        # Unpack nested dictionaries
        final_results = {}
        for key, value in results.items():
            if isinstance(value, dict):
                for n, v in value.items():
                    final_results[f"{key}_{n}"] = v
            else:
                final_results[key] = value
        return final_results
    else:
        return {
            "precision": results["overall_precision"],
            "recall": results["overall_recall"],
            "f1": results["overall_f1"],
            "accuracy": results["overall_accuracy"],
        }
  • 加载、训练和评估模型:
model = LayoutLMv3ForTokenClassification.from_pretrained("microsoft/layoutlmv3-base",
                                                         id2label=id2label,
                                                         label2id=label2id)processor = AutoProcessor.from_pretrained("microsoft/layoutlmv3-base", apply_ocr=False)NUM_TRAIN_EPOCHS = 50PER_DEVICE_TRAIN_BATCH_SIZE = 1PER_DEVICE_EVAL_BATCH_SIZE = 1LEARNING_RATE = 4e-5training_args = TrainingArguments(output_dir="test",
                                  # max_steps=1500,
                                  num_train_epochs=NUM_TRAIN_EPOCHS,
                                  logging_strategy="epoch",
                                  save_total_limit=1,
                                  per_device_train_batch_size=PER_DEVICE_TRAIN_BATCH_SIZE,
                                  per_device_eval_batch_size=PER_DEVICE_EVAL_BATCH_SIZE,
                                  learning_rate=LEARNING_RATE,
                                  evaluation_strategy="epoch",
                                  save_strategy="epoch",
                                  # eval_steps=100,
                                  load_best_model_at_end=True,
                                  metric_for_best_model="f1")# Initialize our Trainer
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset,
    tokenizer=processor,
    data_collator=default_data_collator,
    compute_metrics=compute_metrics,
)trainer.train()
trainer.evaluate()

训练完成后,对测试数据集进行评估。下面是评测后的模型评分:

{'epoch': 50.0,
 'eval_accuracy': 0.9521988527724665,
 'eval_f1': 0.6913439635535308,
 'eval_loss': 0.41490793228149414,
 'eval_precision': 0.6362683438155137,
 'eval_recall': 0.756857855361596,
 'eval_runtime': 9.7501,
 'eval_samples_per_second': 9.846,
 'eval_steps_per_second': 9.846}

该模型能够实现 0.69 的 F1 分数、0.75 的召回率和 0.63 的精确度。

让我们对不属于训练数据集的新发票运行模型。

使用 LayoutLM v3 进行推理

为了运行推理,我们将使用 Tesseract 对发票进行 OCR,并将信息提供给我们训练好的模型来运行预测。为了简化这个过程,我们创建了一个定制的脚本,只有几行代码,让您可以接收 OCR 输出并使用模型运行预测。

  • 第一步,让我们导入一些重要的库并加载模型:
#drive mountfrom google.colab import drivedrive.mount('/content/drive')## install Hugging face Transformers library to load Layoutlmv3 Preprocessor!pip install -q git+https://github.com/huggingface/transformers.git## install tesseract OCR Engine! sudo apt install tesseract-ocr! sudo apt install libtesseract-dev## install pytesseract , please click restart runtime button in the cell output and move forward in the notebook! pip install pytesseract# ! rm -r layoutlmv3FineTuning! git clone https://github.com/salmenhsairi/layoutlmv3FineTuning.gitimport osimport torchimport warningsfrom PIL import Imagewarnings.filterwarnings('ignore')# move all inference images from /content to 'images' folder
os.makedirs('/content/images',exist_ok=True)
for image in os.listdir():
  try:
    img = Image.open(f'{os.curdir}/{image}')
    os.system(f'mv "{image}" "images/{image}"')
  except:
    pass# defining inference parametersmodel_path = "/content/drive/MyDrive/LayoutLM_data/layoutlmv3.pth" # path to Layoutlmv3 modelimag_path = "/content/images" # images folder
# if inference model is pth then convert it to pre-trained format
if model_path.endswith('.pth'):
  layoutlmv3_model = torch.load(model_path)
  model_path = '/content/pre_trained_layoutlmv3'
  layoutlmv3_model.save_pretrained(model_path)
  • 我们现在准备使用模型运行预测
# Call inference module! python3 /content/layoutlmv3FineTuning/run_inference.py --model_path "$model_path" --images_path $imag_path

作者图片:输出 LayoutLM v3

有了 220 张带注释的发票,该模型能够正确预测卖家姓名、日期、发票号码和总价(TTC)!

如果我们仔细观察,我们会发现它犯了一个错误,将笔记本电脑的总价作为发票总价。鉴于模型的得分,这并不奇怪,并暗示需要更多的训练数据。

LayoutLM v2 与 LayoutLM v3 的比较

除了计算量较小之外,layoutLM V3 与 v2 相比是否提供了性能提升?为了回答这个问题,我们比较了同一张发票的两种模型输出。下面是我上一篇文章中显示的 layoutLM v2 输出:

作者图片:输出 LayoutLM v2

我们观察到几个区别:

  • v3 模型能够正确地检测大多数密钥,而 v2 无法预测 invoice_ID、Invoice number_ID 和 Total_ID
  • v2 模型错误地将总价$1,445.00 标记为 MONTANT_HT(在法语中表示税前总价),而 v3 模型正确地预测了总价。
  • 这两种型号都犯了一个错误,将笔记本电脑的价格标为总价。

基于这个例子,layoutLM V3 总体上表现出了更好的性能,但是我们需要在更大的数据集上进行测试来证实这一观察结果。

结论

通过开源 layoutLM 模型,微软正在引领供应链、医疗保健、金融、银行等许多行业的数字化转型。

在这个循序渐进的教程中,我们展示了如何在一个特定的用例(发票数据提取)上微调 layoutLM V3。然后,我们将其性能与 layoutLM V2 进行了比较,发现性能略有提升,但仍需在更大的数据集上进行验证。

基于性能和计算增益,我强烈推荐利用新的 layoutLM v3。

如果您有兴趣以最高效和最简化的方式创建自己的训练数据集,请不要犹豫,免费尝试 UBIAI OCR 注释功能此处

在推特上关注我们 @UBIAI5订阅这里

用于发票识别的微调无 OCR 甜甜圈模型

原文:https://towardsdatascience.com/fine-tuning-ocr-free-donut-model-for-invoice-recognition-46e22dc5cff1

并将它的性能与 layoutLM 进行比较

图片来自 Envanto

介绍

智能文档处理(IDP)是自动理解文档内容和结构的能力。对于任何需要处理大量文档的组织来说,这都是一项至关重要的功能,例如客户服务、索赔处理或法规遵从性。然而,国内流离失所者不是一项微不足道的任务。即使对于最常见的文档类型,如发票或简历,现有的各种格式和布局也会使 IDP 软件很难准确解释内容。

当前的文档理解模型(如 layoutLM)通常需要进行 OCR 处理,以便在处理文档之前从文档中提取文本。虽然 OCR 是一种从文档中提取文本的有效方法,但它也不是没有挑战。OCR 准确性会受到一些因素的影响,如原始文档的质量、使用的字体和文本的清晰度。此外,OCR 速度慢且计算量大,这又增加了一层复杂性。这使得很难实现 IDP 所需的高精度。为了克服这些挑战,需要新的 IDP 方法来准确地解释文档,而不需要 OCR。

进入 Donut,它代表DocumenTUunderstandingTtransformer,这是一款无 OCR 的变压器模型,根据原始论文的说法,它在精度方面甚至击败了 layoutLM 模型。

在本教程中,我们将微调新的发票提取 Donut 模型,并将其性能与最新的 layoutLM v3 进行比较。我们开始吧!

作为参考,下面是用于微调甜甜圈模型的 google colab 脚本:

https://colab.research.google.com/drive/16iPnVD68oMnCqxHcLaq9qn9zkRaIGeab?usp=sharing#scrollTo=h072rVoFMNYb

环形建筑

那么,该模型如何能够提取文本并理解图像,而不需要任何 OCR 处理呢?甜甜圈架构是基于一个视觉编码器和一个文本解码器。视觉编码器将视觉特征 x∈R H×W×C 作为输入输入到一组嵌入{zi |zi∈R d,1≤i≤n}中,其中 n 是特征图大小或图像块的数量,d 是编码器的潜在向量的维数。作者使用 Swin 变压器作为编码器,因为根据他们的初步研究,它显示出最佳性能。文本解码器是一个 BART transformer 模型,它将输入特征映射到一系列子词标记中。

Donut 使用教师强制策略模型,该模型在输入中使用基本事实,而不是输出。该模型基于提示生成一系列令牌,提示取决于我们希望实现的任务类型,如分类、问答和解析。例如,如果我们希望提取文档的类别,我们将把图像嵌入和任务类型一起提供给解码器,模型将输出对应于文档类型的文本序列。如果我们对问答感兴趣,我们将输入问题“choco mochi 的价格是多少”,模型将输出答案。输出序列然后被转换成一个 JSON 文件。更多信息,请参考原文

甜甜圈建筑。来源

发票标签

在本教程中,我们将对使用 UBIAI 文本注释工具标记的 220 张发票的模型进行微调,类似于我以前关于微调 layoutLM 模型的文章。下面是一个示例,展示了从 UBIAI 导出的带标签数据集的格式。

作者图片:UBIAI OCR 注释功能

UBIAI 支持 OCR 解析、原生 PDF/图像注释和以正确格式导出。您可以在 UBIAI 平台中对 layouLM 模型进行微调,并使用它自动标记您的数据,这可以节省大量手动注释时间。

微调甜甜圈

第一步是导入所需的包,并从 Github 克隆 Donut repo。

from PIL import Imageimport torch!git clone https://github.com/clovaai/donut.git!cd donut && pip install .from donut import DonutModelimport jsonimport shutil

接下来,我们需要从 UBIAI 导出的 JSON 文件中提取标签并解析图像名称。我们将标注数据集的路径和处理过的文件夹(替换为您自己的路径)。

ubiai_data_folder = "/content/drive/MyDrive/Colab Notebooks/UBIAI_dataset"ubiai_ocr_results = "/content/drive/MyDrive/Colab Notebooks/UBIAI_dataset/ocr.json"processed_dataset_folder = "/content/drive/MyDrive/Colab Notebooks/UBIAI_dataset/processed_dataset"with open(ubiai_ocr_results) as f: data = json.load(f)#Extract labels from the JSON file
all_labels = list()
for j in data:
  all_labels += list(j['annotation'][cc]['label'] for cc in range(len(j['annotation'])))all_labels = set(all_labels)
all_labels#Setup image path
images_metadata = list()
images_path = list()for obs in data:ground_truth = dict()
  for ann in obs['annotation']:
    if ann['label'].strip() in ['SELLER', 'DATE', 'TTC', 'INVOICE_NUMBERS', 'TVA']:
      ground_truth[ann['label'].strip()] = ann['text'].strip()try:
    ground_truth = {key : ground_truth[key] for key in ['SELLER', 'DATE', 'TTC', 'INVOICE_NUMBERS', 'TVA']}
  except:
    continue

  images_metadata.append({"gt_parse": ground_truth})
  images_path.append(obs['images'][0]['name'].replace(':',''))dataset_len = len(images_metadata)

我们将数据分为训练集、测试集和验证集。为此,只需创建三个文件夹:培训、测试和验证。在每个文件夹中创建一个空的 metadata.jsonl 文件,并运行下面的脚本:

for i, gt_parse in enumerate(images_metadata):
  # train
  if i < round(dataset_len*0.8) :
    with open(processed_dataset_folder+"/train/metadata.jsonl", 'a') as f:
      line = {"file_name": images_path[i], "ground_truth": json.dumps(gt_parse, ensure_ascii=False)}
      f.write(json.dumps(line, ensure_ascii=False) + "\n")
      shutil.copyfile(ubiai_data_folder + '/' + images_path[i], processed_dataset_folder + "/train/" + images_path[i])
      if images_path[i] == "050320sasdoodahfev20_2021-09-24_0722.txt_image_0.jpg":
        print('train')

  # test
  if round(dataset_len*0.8) <= i < round(dataset_len*0.8) + round(dataset_len*0.1):
    with open(processed_dataset_folder+"/test/metadata.jsonl", 'a') as f:
        line = {"file_name": images_path[i], "ground_truth": json.dumps(gt_parse, ensure_ascii=False)}
        f.write(json.dumps(line, ensure_ascii=False) + "\n")
        shutil.copyfile(ubiai_data_folder + '/' + images_path[i], processed_dataset_folder + "/test/" + images_path[i])
        if images_path[i] == "050320sasdoodahfev20_2021-09-24_0722.txt_image_0.jpg":
          print('test')# validation
  if round(dataset_len*0.8) + round(dataset_len*0.1) <= i < dataset_len:
    with open(processed_dataset_folder+"/validation/metadata.jsonl", 'a') as f:
        line = {"file_name": images_path[i], "ground_truth": json.dumps(gt_parse, ensure_ascii=False)}
        f.write(json.dumps(line, ensure_ascii=False) + "\n")
        shutil.copyfile(ubiai_data_folder + '/' + images_path[i], processed_dataset_folder + "/validation/" + images_path[i])

该脚本会将我们的原始注释转换成包含图像路径和基本事实的 JSON 格式:

{"file_name": "156260522812_2021-10-26_195802.2.txt_image_0.jpg", "ground_truth": "{\"gt_parse\": {\"SELLER\": \"TJF\", \"DATE\": \"création-09/05/2019\", \"TTC\": \"73,50 €\", \"INVOICE_NUMBERS\": \"N° 2019/068\", \"TVA\": \"12,25 €\"}}"}{"file_name": "156275474651_2021-10-26_195807.3.txt_image_0.jpg", "ground_truth": "{\"gt_parse\": {\"SELLER\": \"SAS CALIFRAIS\", \"DATE\": \"20/05/2019\", \"TTC\": \"108.62\", \"INVOICE_NUMBERS\": \"7133\", \"TVA\": \"5.66\"}}"}

接下来,转到“/content/donut/config”文件夹,创建一个名为“train.yaml”的新文件,并复制以下配置内容(确保用您自己的路径替换数据集路径):

result_path: "/content/drive/MyDrive/Colab Notebooks/UBIAI_dataset/processed_dataset/result"
pretrained_model_name_or_path: "naver-clova-ix/donut-base" # loading a pre-trained model (from moldehub or path)
dataset_name_or_paths: ["/content/drive/MyDrive/Colab Notebooks/UBIAI_dataset/processed_dataset"] # loading datasets (from moldehub or path)
sort_json_key: False # cord dataset is preprocessed, and publicly available at [https://huggingface.co/datasets/naver-clova-ix/cord-v2](https://huggingface.co/datasets/naver-clova-ix/cord-v2)
train_batch_sizes: [1]
val_batch_sizes: [1]
input_size: [1280, 960] # when the input resolution differs from the pre-training setting, some weights will be newly initialized (but the model training would be okay)
max_length: 768
align_long_axis: False
num_nodes: 1
seed: 2022
lr: 3e-5
warmup_steps: 300 # 800/8*30/10, 10%
num_training_samples_per_epoch: 800
max_epochs: 50
max_steps: -1
num_workers: 8
val_check_interval: 1.0
check_val_every_n_epoch: 3
gradient_clip_val: 1.0
verbose: True

请注意,您可以根据自己的用例更新超参数。

我们终于准备好训练模型了,只需运行下面的命令:

!cd donut && python train.py --config config/train.yaml

图片作者:甜甜圈模型训练

使用启用了 GPU 的 google colab 进行模型培训大约需要 1.5 小时。

为了获得模型性能,我们在测试数据集上测试模型,并将其预测与实际情况进行比较:

import glob
with open('/content/drive/MyDrive/Invoice dataset/UBIAI_dataset/processed_dataset/test/metadata.jsonl') as f:
  result = [json.loads(jline) for jline in f.read().splitlines()]
test_images = glob.glob(processed_dataset_folder+'/test/*.jpg')acc_dict = {'SELLER' : 0, 'DATE' : 0, 'TTC' : 0, 'INVOICE_NUMBERS' : 0, 'TVA' : 0}for path in test_images:
  image = Image.open(path).convert("RGB")

  donut_result = my_model.inference(image=image, prompt="<s_ubiai-donut>")
  returned_labels = donut_result['predictions'][0].keys()for i in result:
    if i['file_name'] == path[path.index('/test/')+6:]:
      truth = json.loads(i['ground_truth'])['gt_parse']
      breakfor l in [x for x in returned_labels if x in ['SELLER', 'DATE', 'TTC', 'INVOICE_NUMBERS', 'TVA']]:
    if donut_result['predictions'][0][l] == truth[l]:
      acc_dict[l] +=1

以下是每个实体的得分:

卖家:0%,日期:47%,TTC: 74%,发票号码:53%,电视广告:63%

尽管有足够多的例子(274),卖方实体的得分为 0。其余实体得分较高,但仍在较低范围内。现在,让我们尝试对不属于训练数据集的新发票运行该模型。

作者图片:测试发票

模型预测是:

日期:“2017 年 1 月 31 日”,

TTC ':' 1455.00 美元',

INVOICE_NUMBERS ':'发票',

TVA ':' 35.00 美元'

该模型在提取卖家名称和发票号码时遇到了问题,但它正确地识别出了总价(TTC)、日期,并错误地标注了税款(TVA)。虽然模型的性能相对较低,但我们可以尝试一些超参数调整来增强它和/或标记更多的数据。

甜甜圈 vs 布局 LM

圆环模型相对于其对应部分布局有几个优点,例如较低的计算成本、较低的处理时间和较少的由 OCR 引起的错误。但是性能对比如何?根据原始论文,Donut 模型在 CORD 数据集上的性能优于 layoutLM。

模型性能得分比较

然而,当使用我们自己的标记数据集时,我们没有注意到性能的提高。如果说有什么不同的话,LayoutLM 已经能够捕获更多的实体,比如卖家姓名和发票号码。这种差异可能是因为我们没有进行任何超参数调整。或者,Donut 可能需要更多的标记数据来实现良好的性能。

结论

在本教程中,我们关注的是数据提取,但是 Donut 模型能够进行文档分类、文档问题回答和综合数据生成,所以我们只是触及了表面。无 OCR 模型具有许多优点,例如更高的处理速度、更低的复杂性、更少的由低质量 OCR 引起的错误传播。

下一步,我们可以通过执行超参数调整和标注更多数据来提高模型性能。

如果您有兴趣标记自己的训练数据集,请不要犹豫,免费尝试 UBIAI OCR 注释功能这里

在推特上关注我们 @UBIAI5订阅这里

浓缩咖啡中的微粒迁移:更有力的证据

原文:https://towardsdatascience.com/fines-migrate-in-espresso-stronger-evidence-15c42a3d41d8

咖啡数据科学

该理论的确凿证据

30 年来,微粒迁移理论已经被公认为浓缩咖啡界的真理。该理论认为,较细的颗粒会在浓缩咖啡的过程中迁移,这种迁移会对萃取产生影响。

之前,我已经向提供了一些实验,在这些实验中我没有发现微粒迁移的证据,但是我仍然觉得有必要检查我的实验,以确保它们没有错。特别是,关于多孔介质中微粒迁移的学术研究表明微粒确实会迁移,我设计了一个与他们的工作相关的更好的实验。

实验结果

之前的,我拿了一个用过的冰球,我把它切成三部分:底部,中间,顶部,在这个镜头里,看起来微粒迁移了,但只是一点点。

除非另有说明,所有图片均由作者提供

我根据文献进行了一次尝试,结果截然不同。

每种粒径的微粒迁移都很明显。我把它分解成另一个条形图来再次检查。

证据是确凿的,我能够做一个基于粒子分析的图像。以下是完整的发行版:

试验设计

我最初的实验使用 3 比 1 的输出和输入浓缩咖啡的比例。文献表明,土壤中的大量细粒迁移发生在水体积(称为 PVI)与土壤体积之比为 400:1 的情况下(研究论文)。

论文标题为含水层和油田中的细颗粒迁移:实验室和数学模型由杨玉龙撰写

作者有几篇期刊论文,根据它们的标题,人们可能会怀疑罚款不会很快转移。

杨玉龙博士论文《多孔介质中微粒运移的分析模型》目录摘录

因此,为了接近这一水平的输出比率,我的实验使用了 300 比 1 的比率。我开始在一个 7 克的 VST 单篮子里放 5 克咖啡。我用了一个弹簧和一个淋浴滤网把咖啡装在过滤器的底部。然后,我用一台像样的浓缩咖啡机在圆盘上运行了一个平坦的压力曲线。我喝了 1500 克水。

我也测量了水的 TDS,但是读数很奇怪。

校准似乎有点偏离,但即使我有 30%的提取率,那将是 1.5 克,1.5 克除以 1500 克水将是 0.1%的 TDS。所以它肯定是在一个难以衡量的范围内。

我看了看冰球的顶部,顶部确实看起来和感觉起来更粗糙。

对于冰球的底部,很难判断颗粒有多细。

我刮掉了顶层和底层,把它们分开。

此外,我让输出液体中的咖啡渣沉淀下来,然后将水倒掉,看看有多少咖啡渣流入杯中。不到之前实验预期的 0.01 克。

该实验证实了咖啡中的细粒迁移与土壤中的细粒迁移行为相似,这意味着细粒迁移直到非常高的产出投入比才开始显著发生。我还没有看到令人信服的数据表明,在 3:1 的常规产出比下,细粒迁移会显著发生。

如果你已经做到了这一步,并对标题是一个点击诱饵感到生气,我道歉。似乎有很多人看到了我文章的标题,却不去读,然后和我争论文章的内容。所以我的目标是颠倒我典型的文章流程,以帮助强化“不要以封面来判断一本书”的概念

如果你愿意,可以在推特YouTubeInstagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以在关注我,在订阅

我的进一步阅读:

我的未来之书

我的链接

浓缩咖啡系列文章

工作和学校故事集

含水层和油田中的细颗粒迁移:实验室和数学模型。地下环境中的流动和迁移。2018 年新加坡施普林格。3–67.

杨,玉龙。多孔介质中微粒迁移的分析模型。Diss。2016.

Osma 咖啡机中的粉末迁移

原文:https://towardsdatascience.com/fines-migration-in-the-osma-coffee-machine-148530bd0657

咖啡数据科学

对我来说是 Osma 数据的开始

我在 Chromatic 和 Hiver 一起品尝一些咖啡,我要求收集一些数据。我想检查一下 Osma 是否与微粒迁移有任何问题,所以我们拿了一些巴西咖啡,我们拉了两个镜头。通过检查颗粒分布,我没有发现 Osma 中微粒迁移的证据。

所有图片由作者提供

我看了一杯奥斯马咖啡和一杯像样的浓缩咖啡。我把视频并排放在一起。两者都有一定的不连续性,但是如果微粒迁移,这仍然会产生横向切割。

Osma 是一款冷(或热)浓缩咖啡酿造器,它使用谐波来摇动咖啡,并提取真正的冷酿造浓缩咖啡。意式浓缩咖啡的效果不同,我怀疑一些可溶物的提取速度与热意式浓缩咖啡不同。我一直有兴趣通过收集一些数据来研究这台机器。

我对其他一些实验,特别是强制微粒迁移的担心是,Osma 的行为可能类似于强制微粒迁移。我拿了一个用过的冰球,把它切成了三块。

然后我用成像和计算机视觉来计数颗粒。我用同样的咖啡,同样的研磨(Versalab 研磨机)在像样的浓缩咖啡机上做了一杯普通的浓缩咖啡。我最初的测量没有显示出差异,我不确定样品是否足够干燥,所以我多干燥了一天。

没有太多的变化,我预计底层的微粒数量会稍微多一些,但事实并非如此。

然后对比奥斯马,我发现了一个相似的模式。

所以为了比较所有的分布,我做了一些测量来观察累积效应。同样,常规或奥斯马注射也没有效果。

我放大到 150 微米以下的颗粒直径,底层和顶层的 Osma 颗粒数量相同。中间层似乎稍微多一点,所以可能有一些从顶部到中间的迁移,但很难确定。

附加测量

在再次干燥之前,我的第一个样品没有显示出差异,对于第一个样品,我\取了额外的样品来验证测量过程。对于每一个,我取了三个样本,大多数情况下,方差并不大。

我检查了 Osma 用过的咖啡球,发现在颗粒迁移中没有观察到大量的微粒迁移。这可以进行更多的研究,以更好地理解 Osma 的功能。

如果你愿意,可以在推特、 YouTubeInstagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在订阅

我的进一步阅读:

我未来的书

我的链接

浓缩咖啡系列文章

工作和学校故事集

首先,我们必须发现。然后,我们可以探索。

原文:https://towardsdatascience.com/first-we-must-discover-only-then-we-can-explore-8334b764535e

结构化数据发现方法的案例

Unsplash 上由 Clarisse Meyer 拍照

B ack 在 20 世纪 70 年代,约翰·图基发表了探索性数据分析,通过这本书,他支持在进行假设检验之前先对我们的数据集进行试验的想法。Tukey 认为,这样做,人们可以发现关于数据和主题的新信息,并开发可能导致一些有趣结果的新假设。他写道,

在你学会衡量你做得有多好之前,衡量你能做什么是很重要的。

从那时起,探索性数据分析(EDA)越来越受欢迎,今天,很难找到一个不以 EDA 开始的 Kaggle 挑战提交笔记本。

如果你有足够的好奇心去阅读探索性数据分析(你最近已经这样做了),你可能会发现它充满了许多过时的技术——比如如何轻松地找到数字的日志,以及如何手工绘制数据点。但是如果你勇敢地阅读这本书,或者翻阅或者思考我们已经从这些史前教程走了多远,你会发现很多有用的例子和想法。首先,你会看到 EDA 不是一个人可以执行的特定指令集——它是思考数据的一种方式,也是练习好奇心的一种方式。

然而,虽然图基出色地描述了 EDA 及其技术,但他的书忽略了数据分析中经常被忽略的第一步:理解主题。虽然这种想法对一些人来说是直观的,但并不是每个人在实践中都这么做。虽然跳入编程或制作美丽的可视化效果可能很有趣,但如果我们不理解数据集代表什么,EDA 可能会误导我们。因此,在开始我们的数据探索之前,我们也应该对我们的数据试图描述的主题或过程感到好奇。

具体来说,我们应该问自己两个问题:

  1. 我们知道什么?
  2. 什么是我们不知道的?

在试图回答这些问题时,我们应该能够建立一个参照系来进行我们的分析。

知识就是力量

当试图解决一个数学问题时,一个好的策略是首先并且最重要的是写下关于这个问题的所有已知信息。类似地,在数据分析中,如果我们已经有了一个计划分析的数据集,很自然地想要知道数据代表什么。如果我们还没有数据集,为了收集数据集的适当需求和理解最终目标,询问关于我们主题的问题是很自然的。在这一节中,我提出了一种结构化的方法来收集有关我们分析的事实。事实上,问题“我们知道什么?”可以分为三个独立的“什么”问题。

是什么题材?

虽然主题专业知识可以留给专家,但一个熟练的数据分析师应该调查主题,并尽可能了解关于该主题的一切。这样做的原因超出了纯粹的好奇心。理解主题有助于确定分析需要什么信息,并有助于收集特定的需求。使用现有数据集时,它在 EDA 过程中会有所帮助。此外,它可以帮助分析师避免做多余的工作。

例如,如果我们知道一家公司向公众公布季度收益,那么它可以帮助解释为什么股票价格在季度基础上经历突然的变化。当分析公司的股票价格波动时,分析师可以将此添加到已知事实的列表中,并在 EDA 过程中节省一些挖掘信息的时间。此外,分析师可以要求季度财务报表作为额外的数据要求。

有哪些定义?

在进行分析之前,重要的是整理一个定义和已知术语的字典。拥有一本可用的字典可以帮助发现分析中的某些细微差别,理解各种计算中涉及的逻辑,并与利益相关者进行交流。编纂一本字典也可以提出一些额外的问题和假设来帮助分析。

如果给你一个葡萄酒质量数据集(就像这个)并要求你预测葡萄酒的质量,你只需要导入数据集,导入 scikit-learn,然后运行一个模型。但是如果你花时间建立一个术语词典,你就会明白,例如,“挥发性酸度”被定义为葡萄酒中低分子量脂肪酸的量度,并且与葡萄酒腐败有关。因此,如果你的模型预测它对预测的葡萄酒质量有积极的贡献,可能是时候重新审视你的模型,或者准备向你的利益相关者证明这个结果。

底层流程是什么?

试图收集知识的最后一步是理解什么潜在的过程控制着你的分析主题。通常,这应该使用系统分析来完成,系统分析可以识别过程,并且应该有助于提出和回答关于数据的各种问题。它也可以作为指导分析的工具。

假设你的任务是确定有助于公司发展的因素。在理解应该收集哪些数据和测试各种假设时,绘制增长路径的系统图是一个很好的起点。例如,一个树形图可以强调你的公司有三种方式可以增加其感知收入:向新客户销售,增加现有客户的收入,或者减少流失客户的数量。从这些因素,你可以开始建立一个详细的图片。首先,你可以确定你的公司获取客户的各种方式,并确定所需的数据,以验证这些因素是否重要。

无知是福

一旦我们对我们所讨论的主题有了更好的理解,是时候问问我们自己了"什么是我们不知道的?“虽然在某些情况下我们可以收集额外的数据,但这个问题的真正目的是了解我们的局限性和我们无法满足的要求。也有必要了解我们的偏见和假设,在此基础上我们将提出建议。

信息完整吗?

为了回答这个问题,让我们看看我们能够获得的数据或我们已经拥有的数据,并评估以下标准:

  1. 数据描绘的是整个画面还是仅仅一部分?例如,如果我们的任务是分析来自电子病历(EPR)的中风数据集,我们只关注有记录的个人,而不是每个可能患有中风的人。这对于我们的分析和我们计划提出的任何建议意味着什么?
  2. 是否有缺失的数据点?数据集通常会有缺失的记录或数据列。通常,分析师必须设计一个策略来处理丢失的数据点。但是,该策略可能会因数据点丢失的原因而有所不同。
  3. 数据质量好吗?在许多情况下,数据转换、数据收集中的错误或手动输入都可能导致较差的数据质量。在使用给定的信息之前,分析师应该首先确认数据是否正确,如果可能的话,与现有来源进行交叉引用,其次,制定一个策略来降低质量差和不确定性。

比方说,你的任务是分析网站上的产品评论(像这些一样的,以便得出趋势和见解,并影响未来的产品库存。这样的数据集依赖于已经输入其产品评论的个人。然而,并不是所有的人都这样做。那么,这个数据集能代表整个人口吗?如果我们对它执行自然语言处理,我们是期望评论的质量是好的还是应该做一些预处理以使一些条目更易读?如果不做预处理,是不是遗漏了某些信息?我们能得到任何额外的数据或结果来改进我们的分析吗?

我们在做什么假设?

在数据不完整或质量差且不可能获得额外信息的情况下,为了继续分析,分析师可能不得不接受它们的命运和设计策略。但是,重要的是要注意必须做出的假设,因为它们将设定分析结果解释的界限和范围。做出假设伴随着风险,决策者必须被告知他们愿意承担的风险,以便利用分析的结果。

例如,如果我们要分析一个全球范围内的幸福得分数据集(就像这个),我们无法获得任何额外的信息,我们必须在几个假设下工作。首先,如果我们想对所代表的国家的总体人口做出推断,我们必须假设各个国家的结果代表了它们各自的人口。这意味着参与调查的个人代表了他们各自国家的其他人(这是一个重大假设)。我们还必须假设进行调查的盖洛普世界民意测验所采用的方法不偏向特定人群或方法(这也是一个重大假设)。我们必须做出的另一个假设是,将调查翻译成不同的语言不会妨碍结果。

我们有偏见吗?

决定分析和解释分析结果的策略的最后一个问题涉及偏见——对某一观点的倾向或偏见。偏见产生于人们感知世界的方式,以及他们的经历和与他人的互动。如果不了解偏见的类型,不通过不带偏见的视角检查我们对信息的理解,我们得出的见解可能不代表现实。相反,他们可能对某个特定的观点有偏见。有偏见的分析不仅不能充分反映现实,还可能不公平和不道德。

在最后这个例子中,让我们来看看美国总统辩论记录的数据集(就像这个)。对这些数据的分析可能会受到新近偏见的影响,在新近偏见中,个人可能会更重视最近的辩论,而放弃考虑文化在历史上的变化和发展。此外,持有强烈政治观点的分析师可能会面临确认偏差,他们可能会忽略不支持其观点的证据。

这篇文章为数据发现的结构化方法提供了一个案例——一个在探索性数据分析发生之前必须发生的过程。这个过程的目标是双重的:理解什么信息是已知的评估什么信息是未知的。回答问题有助于达到发现的目标,这使我们能够发展一种视角和透镜,通过它来解释结果。这也有助于我们理解我们的洞察力是否可以用来做决定,以及我们局限于什么样的决定。

在开始探索未知的土地之前,我们应该发现这些土地,了解它们,调查它们,并为旅程做好准备。只有这样,我们才能安全而自信地探索。

我希望你喜欢这本书!我迫不及待地想在评论区看到你的想法。

参考

[1]图基,J. (1977) 探索性数据分析。爱迪生-韦斯利出版公司。

完全掌握费希尔的 A/B 检验的精确检验

原文:https://towardsdatascience.com/fishers-exact-fb49432e55b5

虽然费希尔精确测试是 A/B 测试的一个方便工具,但测试的思想和结果往往难以理解和解释。不管是纠结于超几何分布还是优势比,本指南为你提供了理解和应用费雪精确检验所需的所有背景知识。

我经常在网上寻找 A/B 测试的好资源。虽然对连续数据的解释很多,但我不认为对离散数据是这样——Fisher 的精确检验就是其中一个例子,我很少能找到对单尾假设检验的好的证明。为了填补这一空白,我将为费希尔的精确测试提供一个可理解和充分的指导,并进行一个右尾测试,以及一个双边测试。

让我大胆地说,这是费希尔精确测试所需的唯一指南。

结构

  1. 基线(对比)模型
  2. 费希尔精确分布和超几何分布简介
  3. 手动计算费希尔 P 值
  4. 假设
  5. 进行测试(代码示例)
  6. 了解优势比
  7. 重述

基线(比较)模型

首先,我们假设在以下结构中收集了捕获大西洋和印度洋中鲨鱼和鲸鱼出现的数据(原始示例来自 Scipy ):

大西洋和印度洋中鲨鱼和鲸鱼出现的 2x2 矩阵

为了获得数据的第一印象,并有一个“基线参考”我将对独立性进行卡方检验。我总是这样做,看看是否已经有强烈的迹象表明这两个变量是(不)独立的。

h0: Independence of the variables (animals and ocean)
hA: Variables are not independent (i.e. there is a "pattern")

如图所示,p 值 0.41 太大,无法拒绝零假设,留给我们的假设是变量是独立的——如零假设下所述。

为了说明卡方检验有意义的原因:如果我们谈论的不是海里的鱼,而是网页上的点击量,我们可以说,两个网页的效率是一样的,因为没有迹象表明一个页面比另一个页面的点击量多/少。独立性实际上可以与没有网页比另一个更好的想法联系在一起——因此你最近的 A/B 测试变化并没有带来用户点击行为的显著变化。

相同的列联表,相同的测试,不同的上下文

费雪精确分布和超几何分布简介

现在让我们来看看为什么我们都在这里,费雪的精确测试。 Fisher 的精确检验假设输入表来自具有参数 a,b,cd (每个像元值)的超几何分布。

与罗纳德·艾尔默·费希尔毫无相似之处——韦小宝拍摄的照片

如果超几何分布现在还没有引起注意,让我来给你举个例子:

抽屉里有 20 只黑袜子,10 只红袜子。你随机挑 8 只袜子,挑 6 只黑袜子和 2 只红袜子的概率是多少?像这样的问题是典型的超几何问题:

超几何分布下的概率

超几何分布下计算概率的公式可以用不同的方式来表达:

超几何分布下的概率

让我们将超几何分布与我们感兴趣的测试联系起来。Fisher 精确测试的核心思想是,我们使用我们的输入表并计算与问题“相关的概率,我们看到随机抽取的表的概率小于或等于我们的输入表的概率(在超几何分布的假设下)的可能性有多大。为了引用我们的数据表,我们使用以下列值:

p 的超几何方程中填充的表的结构

双侧 p 值是在零假设下,随机表的概率等于或小于输入表的概率的概率(链接)。

“手动”计算费雪的 P 值

虽然,你可能对 Fisher 的精确测试更感兴趣,因为编码相关的任务,我将快速详细说明测试的一般工作方式——相信我,它对理解测试结果有很大帮助。

正如我在开始时所说的,文献和例子主要集中在双边场景,因为它更常见(在实践中,你通常需要一个好的理由来进行单边测试),也因为它似乎更容易理解。我将解决所有三种情况,单尾(左和右)和双尾。

基本上,Fisher 的精确测试是通过从左到右或从右到左(1)移动值来尝试数据单元格的不同配置— 注意,当第一行向右移动时,底部一行向左移动,因此边际分布保持不变(理解这一点非常重要)。

对于每一种配置,我们可以计算看到那个表的概率。对于我们的初始表,变化如下所示:

移位—所有可能的表格组合

可以重复上述步骤,直到我们在 Atlantic/shark 单元格中看到 0 值,因为我们不能减少超过该点—您不能观察到负数。如上所述,有 5 个可能的移位,因此对于这个例子有 6 个结果。使用 X(从 Python 索引的角度来看,单元格[1,1]或单元格[0,0])范围从 0 到 5 的那些表给出以下概率质量函数:

PMF 为 1.0(计算 p 的代码如下)

在超几何分布下计算和聚集这些表中的概率允许我们计算 p 值,用于确定是拒绝还是保留零假设。

为了使这一点更加具体,在我们的例子中,我们对 P(X=5)感兴趣。在费希尔的精确检验下,这被翻译为:“对所有 P(X=x)求和,其中 P(X=x) ≤ P(X=5) = 0.2166。

P(X) ≤ 0.217 的 X

“解码”这个:对于 P(X=5 ),我们看到概率约为 0.2166,因此表中低于该值的所有概率都是我们感兴趣的。很明显,对于[0,1,2]中的 X 值,情况就是这样,因此我们将[0,1,2]和[5]的所有概率相加,结果得到 0.30586,这是我们在双边情况下的 p 值。也可以将分数计算为 1-sum(0.277,0.42)对应 X={3,4}(取整值)。

单尾测试将参考假设 X 是<或>而不是值“a”(在我们的例子中,a = 5)。

如果我们选择选项“更大”(右尾),则概率计算为所有 P(X=x)的总和,其中 x>5,而对于“更小”选项,我们则相反(x<5)。

请记住这个值,我们希望在稍后的费希尔精确测试中也能得到这个结果。

要计算上述概率,您可以使用以下代码片段:

如果你觉得很难理解移动 X 值(单元格[1,1])的想法,我可以推荐下面的视频,它从一个更直观的角度解释了移动 X 值的想法以及与超几何分布**的联系 :**

超几何分布下的概率

假设

由于我们已经在 p 值的计算中达到顶峰,让我们后退一步,想想潜在的假设。

该测试基于直接计算获得输入表显示的结果的概率,给出零假设。回想一下,我们可以将独立性解释为:变量之间没有关联(动物对海洋),这在零假设下是正确的(类似于独立性的卡方检验)。

在你通读假设之前,有一点需要注意:“比例”指的是优势比,我将在后面描述。我觉得在这里引入比值比会使事情变得不必要的混乱,所以我们现在跳过它,用更简单的 X (shark)的“比例”来代替:

**Two-sided:**
h0: Sharks are equally likely in both oceans (independence)
OR: There is no difference in the proportion of sharks in both oceans
*****OR: *A random table would have the probability of equal or less than the input table (look below for a detailed explanation of this)*hA: The variables are not independent (one proportion is different to the other)
OR:
The the proportions of sharks are not equivalent in both oceans.**Left sided (less):** h0: The proportion of sharks in the Atlantic is **greater or equal** to the Indian Ocean
hA: The proportion of sharks in the Atlantic is **less** than in the Indian Ocean **Right sided (greater):** h0: The proportion of sharks in the Atlantic is **less or equal** to the Indian Ocean
hA: The proportion of sharks in the Atlantic is **greater** than in the Indian Ocean

***** 零假设的另一种表述特别有趣,因为它确切地指的是前面提到的潜在超几何分布。考虑表格单元格[1,1],随机表格等于或小于我们用作假设检验输入的表格的概率是多少—在我们的示例中 x = 5(大西洋中的鲨鱼)?

在零假设下,对于 x=5 的情况,我们期望概率为 0.22。我们现在只查看小于或等于 P(X=5)的概率,这是 X = {0,1,2,5}导致 P 值为 0.30586 (所有符合条件的概率之和)的情况。

进行测试

让我们从与代码更相关的角度来看这个测试——我使用了 Scipy 的 fisher_exact 测试的实现:

from scipy.stats import fisher_exact as f

让我们从“更容易”的双面案例开始。如前面“手动”所示,测试的 p 值约为 0.31。鉴于这个相对较高的值,我们不能拒绝零假设,因此假设:

  • 独立性
  • 动物与海洋的比例没有差别
  • 观察到概率等于或小于观察到的 X=5 的随机表的概率约为 31%。

让我们继续选择“更大”,分布的右尾。最初,我们假设大西洋和印度洋的鲨鱼比例相等(或更少)。如果我们即将拒绝 h0,我们将假定 X 的值> 5 或在大西洋中更大的一个比率。

p 值为 0.2166

根据 p 值,我们不能拒绝零假设。我们假设 X ≤ 5。回想一下,在 Python 中计算索引时,我们从输入表的单元格[1,1]或单元格[0,0]中获得 5。

另一个“较少”意味着大西洋中鲨鱼的比例比在印度洋中观察到的要少。即使不进行测试,简单地观察表格也表明这似乎不太可能。因此毫不奇怪,我们不会拒绝零假设(替代方案似乎是错误的)。

p 值为 1.0

我们最初的假设不能被拒绝,大西洋中鲨鱼的比例大于或等于印度洋。

当进行这样的测试时,产生一个参考例子总是一个好主意,这个例子看起来如此极端,以至于你会期望测试的结果相对清晰。

考虑以下高度不平衡的表:

显然,大西洋中鲨鱼的比例似乎要高得多,而鲸鱼的比例则低得多

  • 鉴于这种高度不平衡的比例,我会假设双边测试非常重要,因为我构建了一个表,让大西洋中有更多的鲨鱼,印度洋中有更多的鲸鱼。
  • 至于“更大”的选择,我假设我们再次看到了一个重要的结果,因为大西洋中鲨鱼的数量不是少于或等于(h0),而是多得多——再次,是故意的!
  • 至于“更少”的选择,我预计值(接近)1.0,因为我们已经知道鲨鱼在大西洋更频繁。在这种情况下,我们非常自信地坚持 h0,这意味着鲨鱼的数量大于或等于在印度洋观察到的数量。

所有三个不同尾部的测试

了解优势比

细心的读者可能已经意识到,在第一个示例中,p 值输出之前总是有一个值 inf,在后一个示例中是 66.667。

比值比直接从下表中计算得出:

记住观察表,我们计算两个大洋中鲨鱼与鲸鱼的比例。这就缩小了我们之前分析的假设的差距,我有时会使用“比例”。奇数比,简单地比较了我们已经用于原始假设的两个比例。但是,如何解释这些比率呢?

如果奇数比是 1,我们会看到两个海洋中鲨鱼的比例是相同的。给定这个等式,我们也可以解释为什么在某些情况下(第一个例子),当其中一个字母为 0 时,我们会看到 inf

至于第二个例子,我们看到一个 66.67 的高比值比,这意味着这两个比值是非常不平衡的——这是我们通过设计从表格中预期的!

我们现在可以推导出关于比值比的附加/相关假设:

Two-sided Fisher’s Exact Test:
ho: The odds ratio is equal to 1
ha: The odds ratio is not equal to 1“Less” Fisher’s Exact Test:
ho: The odds ratio ≥ 1
ha: The odds ratio is <1“Greater” Fisher’s Exact Test:
ho: The odds ratio is ≤1
ha: The odds ratio is > 1

概述

当涉及列联表上的假设检验时,Fisher 精确检验是最受欢迎的选择之一,这是一个典型的 A/B 检验场景。要完全理解这个测试,基本的超几何分布,比例或优势比的概念是必不可少的。

虽然 Fisher 精确检验的应用很简单,但是通过对各个超几何概率的结果求和,也很容易获得精确的结果。如果比值比感觉很难解释(特别是当在假设中直接考虑它们时),简单比例的想法使比较列联表中的事件变得更实际。

如果你觉得这篇文章有帮助,我会很感激“关注”🫀,直到那时:

{照顾好自己,如果可以的话,也照顾好别人}

—借自史蒂芬·都伯纳

用哈密顿蒙特卡罗方法拟合深度高斯过程

原文:https://towardsdatascience.com/fitting-deep-gaussian-process-via-hamiltonian-monte-carlo-641725b70514

深度高斯过程建模在多大程度上量化了信号中的不确定性

作者图片

如今,先进的机器学习(ML)技术可以回答从你的数据中得出的非常复杂的问题。不幸的是,由于它们的“黑箱”性质,很难评估这些答案有多正确。如果你想在你的家庭相册中找到猫的照片,这可能没问题。但是在我的工作中,处理医疗数据,这是不可接受的,因此经常阻止 ML 模型在实际临床应用中的实际使用。在这篇文章中,我将概述一种结合了现代 ML 的复杂性和经典统计方法的合理置信度评估的生物数据分析方法。

我们在这里谈论什么?

我的目标是提供

  • 对生物数据使用高斯过程建模的理由,特别是深度高斯过程(DGP ),而不是普通的、稳定的过程
  • 汉密尔顿蒙特卡罗(HMC)方法概述及其如何帮助 DGP 建模
  • 一个模拟数据的例子说明了这种方法的应用

分析医疗数据的挑战

想象一下,我们是一群医生,我们正在监测一个已经接受治疗的病人。假设治疗应该影响某种激素的水平。我们正在记录一段时间的测试结果,并逐渐得到这样的图片。

假设的临床数据。这些数据不是真实的,而是作者为了说明而模拟的。图片由作者提供。

这显然是一个非常嘈杂的数据。我们不能马上知道发生了什么,但我们真的想知道激素水平是否在变化。通常,人们做的第一件事,看看是否有任何影响,是好的旧线性回归,将所有噪声聚集成随机正态。在大多数真实世界的临床数据中,这给出的很少。因此,第二个最好的办法是将其建模为高斯过程(GP)。为什么有意义?那么,首先,让我们回顾一下什么是全科医生。

将临床信号视为平稳高斯过程

当执行 GP 建模时,所有数据点都被认为是从多元高斯分布中提取的,如下所示

需要注意两点。1) K(X) 这里是一个你的数据大小的方阵,一般情况下,有非零非对角元素;这与将你的数据视为独立随机抽取的 n 不同。2)数据点由 X 索引,就像我们的例子中的时间,定义了顺序——也就是过程。对于这篇文章来说,这就是我们需要了解的关于 GPs 的全部内容。然而,这个主题已经得到了很好的研究,好奇的读者可以在经典参考文献[1]中找到扩展他们知识的极好来源。

当我们谈论拟合 GP 时,我们指的是找到矩阵 K(X)的参数。为了使其更加明确,让我们将 GP 模型重写为

现在我们有三个参数,我想提出一个论点,它们可以被解释为捕捉我们在这里试图建模的过程的不同方面。论点如下。

当分析时变医学信号时,我们试图通过医学测试来评估某些生物过程的演变。进化是在大量噪音的背景下发生的。仔细想想,我们可以将噪声源大致分为两类。一个是测量噪声,随着现代医学中的测试变得相当复杂,这个元件可能具有相当复杂的结构。另一个来源是生物过程引起的变异,这种变异与我们感兴趣的过程无关。这一部分甚至更复杂,研究也少得多。那么,代表我们试图检测的过程的实际信号可能是高度非线性的。所有这些造成了一大堆数据,我们没有机会精确建模,至少在可预见的未来没有。

但是我们可以尝试在 GP 的框架中半独立地对这三个组件建模。最有可能的是,这两个噪声源具有不同的频率和振幅。首先, g 参数是线性回归分析中随机噪声的后代。从技术上来说,应该是 τ g,但这就是“半分离”的由来,也很容易解释。将该参数与测量噪声联系起来是最自然的。只要我们谈论的是同一类型的测量,这种类型的噪声就不会因患者而异。它甚至可以通过反复测试来训练并保持固定。我们指定由 τ表示的生物噪声。原因是该参数决定了变化的幅度。这很可能取决于患者,但在同一患者的监测期间或多或少保持不变。然后,剩下 θ 负责描述数据点之间的相关程度。如果我们正在观察一个正在接受治疗的病人,并且治疗确实产生了效果,那么它将在连续的测量中反映出来,并且是强相关的。因此, θ 参数是为我们试图检测的信号保留的。

正如我们所看到的,GP 模型可以很好地满足我们将一些数学知识放在医学数据后面的需求。这是线性回归的一个很好的进步。但是有一个问题。模型的所有参数都假定保持不变。对于 gτ 参数来说,这可能是好的,因为正如我们上面所讨论的,它们归因于测量噪声和身体中不受治疗影响的生物变化。但是我们不能指望 θ 也是如此。治疗可以停止,或者改变,或者它可以自行停止工作。这会影响信号的相关性,我们想知道这一点。因此,我们必须考虑到 θ 的变化。

该解决方案带有深度高斯过程模型。就像深度神经网络一样,这意味着增加更多的层。

双层 GP 作为更合适的替代方案

有许多方法可以将层添加到 GP。出于我们的目的,我们简单地在时间索引和测量信号之间插入另一个 GP,如下所示

遵循参考文献[2]的方法。为什么这就够了?如上所述,我们只需要适应变化的 θ 。通过为时间变量引入一个额外的 GP,我们以一种灵活的方式“扭曲”了测量时间点之间的间隔,并产生了预期的效果。

但这也让试衣变得复杂了!不管怎样,我们必须从这个后验分布中为我们的数据找到参数的最优值

这两种可能性定义为

该表达式的推导见参考文献[2]。既然我们已经写出了这个庞然大物,我们需要思考如何把它变成有用的东西。从分析的角度来看,这里做不了什么。最直接的方法是对这个分布进行数字采样。数字采样的最佳方式是哈密顿蒙特卡罗!

哈密顿蒙特卡罗方法的一点体会

出于两个原因,我们需要一个聪明的方法来对我们的分布进行采样,这实际上是同一事物的两种观点。事实是,我们在多维空间中定义了概率分布。空间很大,但概率必须有限。这意味着非零概率区域将被限制在空间的小体积内。但这是我们希望达到的目标。因此,我们不能盲目投掷飞镖的两个原因是:1)浪费时间——我们将在零概率区域花费大量时间,这将是无用的;2)不准确的估计—在某些时候,我们将不得不停止采样,并且我们有可能会错过非零区域。

解决方案来自于类比物理学中一个众所周知的机制所描述的粒子运动。想象一个带电粒子在相反电荷附近的空间中飞行,或者一个实际的物理硬锥体颠倒过来,而你正沿着侧面滑动一个球。在所有这些情况下,运动的物体被吸引并向圆锥体的底部或带相反电荷的粒子所在的地方运动。但是如果它一开始就有足够的速度,如果你把球足够用力地推向一边,它会围绕这个吸引中心反弹。这个图正好显示了——两个可能的轨迹,有不同的初速度,或者动量,我们在物理学中是这么说的。绿色的动量比红色的大,所以它在远离中心的地方花的时间多一点。但是他们两个都在朝这个方向努力。

势场中粒子运动的示意图(侧视图和俯视图)。两个可能的轨迹被吸引到势能的最小值。绿色的动量比红色高,在极小值周围覆盖了更多的空间。图片由作者提供。

这种情况在理论力学中用哈密顿方程来描述

在哪里

是动能和势能的总和。作为一名物理学家,我无法停止欣赏这些方程的简单和美丽。

让 HMC 为我们工作

我们现在要做的就是说,我们的概率分布是 U ,势能。为了把它颠倒过来,我们在它前面放一个负号,因为它已经写在我们的方程中了,并开始用不同的动量值求解哈密顿方程,以探索最大值周围的区域。HMC 的很多细节可以在参考文献[3]中找到。下面是我编写的一个示例代码,用来生成这样的轨迹。

正如我们可能注意到的,有一个小问题——我们需要提供概率函数grad_U的导数。

不可能得出对数似然性相对于任何参数的导数的解析表达式。当然,我们可以尝试估计梯度的数值方法,但我决定测试 tensorflow 库,我想它最终仍然是数值的,但更优雅一些。下面是我使用 tensorflow 函数实现对数似然的代码片段(整个代码太大太无聊了)。对于导数,它使用GradientTape。它实际上创造了奇迹!

它是如何工作的?

我不得不承认,在我的帖子开始的病人数据的假设情节完全是假的。它是这样模拟的。

也就是说,它是从维数为 20 的多变量高斯中提取的,除了中间三个位置的均值为 4 之外,它在任何地方都是零,并且具有第一非对角元素的协方差矩阵都是 0.1。我把整件事乘以 2 只是为了好玩。我生成了 60 个样本。我挑选了一个,并添加了随机正常噪音,方差为 1。如下图所示——蓝线是我选取的样本,蓝点是添加了噪声的样本,灰色虚线是从同一分布中抽取的剩余样本,黑线是它们的平均值。

本文中用于概念验证的模拟数据中的所有样本。蓝点是用于拟合的数据,灰色虚线是相同分布的相似样本,实心黑线是代表信号的那些样本的平均值。图片由作者提供。

背景中的灰线是为了给我们直观地估计来自这种分布的数据的不确定性。它有用的原因是,作为我们方法的一个额外的好处,我们将尝试基于给定的一个样本来估计不确定性。当然,主要目标是估计黑线——信号。

我把一个有噪声的样本,上图中的蓝点,放到我用 HMC 实现的两层 GP 中。这是结果。首先,我们来看转换。我们这里有 24 个参数( τ,g,θ_y,θ_y ,20 个 W s),所以不可能显示全部空间。下图只显示了其中的两个参数,左边的两个 θ 。我故意从远离正确值的地方开始,但是请注意轨迹多快地向一个静止点移动,大概是概率的最大值,并开始在它周围盘旋。右边的图显示了总能量与迭代次数的关系。再次,快速移动到,希望,最低和良好的混合围绕它。

收敛诊断的轨迹图。图片由作者提供。

这是推断出的信号。我使用参数的采样后验值推断了 200 个样本,适当地丢弃了初始老化样本。结果如下图所示,其中蓝线和黑线分别代表原始样本和真实数据的平均值。红线是推断样本的平均值。灰色虚线是所有要与上图模拟数据进行比较的单个推断样本。

灰色线,红色是它们的平均值,代表拟合的数据。为了便于比较,拟合的输入用蓝线表示,真实信号用黑线表示。图片由作者提供。

最后,这就是我们的最终结果。黑色是我们试图恢复的信号,红色是我们的最佳猜测,灰色虚线是我们猜测的 95%置信区间。

双层 GP 通过 HMC 估计真实信号(黑色)的结果(红色),估计值周围有 95%的置信区间(灰色虚线)。图片由作者提供。

是的,这些置信区间可能比真实的东西宽一点,但我们会明确检测到中间信号的变化,我们不会被边上的波动所迷惑。考虑到我们得到的是如此之少(一个样本有不同的变异来源,没有重复),这是一个非常强大的结果,你不觉得吗?

参考

[1] C. E. Rasmussen 和 C. K. I. Williams,机器学习的高斯过程,(2006),麻省理工学院出版社 ISBN 0–262–18253-x。

[2] A. Sauer,R. B. Gramacy,D. Higdon,(2021) 深度高斯过程代理的主动学习,arXiv:2012.08015v2

[3] M .贝当古,哈密顿蒙特卡罗概念介绍,(2018),arXiv:1701.02434

感谢您的阅读。我很乐意回答任何问题,并听取任何意见。我特别感谢 Robert Gramacy 和 Annie Sauer 激发讨论,并感谢《走向数据科学》的评论者。

所有数据科学家都应该知道的五种高级数据可视化

原文:https://towardsdatascience.com/five-advanced-data-visualizations-all-data-scientists-should-know-e042d5e1f532

使用这些可视化工具扩展您的数据技能和工具库

由故事集创建的控制面板矢量—www.freepik.com

虽然每个人都赞美机器学习模型和复杂的神经网络,但许多人忘记了构建强大的数据可视化可以获得的巨大价值。我说的不是像折线图和条形图这样的基本可视化。

在本文中,我将与您分享五个强大的数据可视化工具,您应该知道如何构建它们,因为它们非常强大,深受高管们的欢迎。说了这么多,让我们开始吧!

务必 订阅此处 千万不要错过另一篇关于数据科学的指南、诀窍和技巧、生活经验等文章!

1.群组图表

什么是队列图表?

群组图是一种工具,可以让您衡量不同用户群在一段时间内的表现。我将特别提到基于时间的群组图表,它根据时间框架对用户进行分组。下面是一个群组图表示例:

作者创建的图像

如何阅读队列图表很简单:

  1. 每行代表一个组群。在这种情况下,用户群组基于用户的注册月份。例如,第一行表示 2009 年 12 月注册的用户,第二行表示 2010 年 1 月注册的用户。
  2. 每一列代表一个时期。在这种情况下,每个周期代表一个月的数字。例如,第一列表示每个群组的第一个月(第 0 个月),第二列表示每个群组的第二个月(第 1 个月)。
  3. 每个单元格代表感兴趣的值。在这个特别的群组图表中,我们比较了每个群组第 0 个月的收入在每个期间的百分比。例如,第一组第二个月的收入是第一个月的 58%,第一组第三个月的收入是第一个月的 44%。

为什么群组图表如此有用

群组图表在评估需要时间才能成熟的指标时非常有用。例如,如果你想看看 2021 年与 2022 年相比用户流失的百分比,这不是一个公平的比较,因为 2021 年的用户比 2022 年的用户有更多的时间流失。但是,通过使用群组图表,您可以比较不同群组的同一时期。

作者创建的图像

回到我的例子,如果我们比较第一组(2009 年至 2012 年)和倒数第二组(2010 年至 2011 年),我们可以看到收入百分比从第 0 期到第 1 期显著下降。第一组的百分比是 58%,但在第二组,百分比下降到 8%。这告诉我们,新客户在购买一个月之后,花费会少很多。出现这种情况的可能原因是因为新的群体中的回头客较少,或者产品的质量随着时间的推移有所下降。

群组图表如此强大的另一个原因是它们本质上是整体的。从横向来看,你可以理解用户的整个生命周期,以及在生命周期中用户行为发生剧烈变化的地方。纵向上,您可以比较和评估新用户群与旧用户群的性能。

队列图表的应用

群组图表最常见的应用是:

  1. 评估随时间推移跨群组的流失。
  2. 跨群组评估收入或盈利能力。
  3. 评估跨队列的漏斗转换。

如何构建群组图表

要用 SQL 构建群组图,请查看我下面的教程:

要用 Python 制作群组图表,请查看埃里克·林森的精彩教程:

请务必 订阅此处 千万不要错过另一篇关于数据科学指南、诀窍和技巧、生活经验等的文章!

2.相关矩阵

但是首先,什么是相关性?

相关性是从-1 到 1 的统计度量,它告诉我们两个变量之间的线性关系有多强。

如果相关性是-1,那么在两个变量之间有一个完美的负相关性,意味着一个变量的增加可靠地预测另一个变量的减少

如果相关性是 1,那么在两个变量之间有一个完美的正相关性,这意味着一个变量的增加可靠地预测了另一个变量的增加。

如果相关性为 0,则两个变量之间没有相关性。以下是不同类型的关联模式的一些示例:

图片来自维基媒体(免费提供)

什么是相关矩阵

相关矩阵是一个 n 乘 n 的表格,显示每个行变量和列变量之间的相关系数。

以下面的图表为例,我们可以看到,如果我们观察第一行(固定酸度)和第二列(挥发性酸度),相关系数为-0.26。

作者创建的图像

相关矩阵什么时候有用?

当您想要快速了解所有变量之间的线性关系时,相关矩阵非常有用。这在以下几种情况下构建模型时特别有用:

  • 建立回归模型时,可以使用相关矩阵来确定变量之间的共线性。
  • 在构建机器学习模型时,可以使用相关矩阵来识别可用于模型的潜在强特征。
  • 进行特征重要性评估时,可以使用相关矩阵来移除相关变量。

如何建立相关矩阵

查看下面的链接,构建一个简单的相关性矩阵:

https://datatofish.com/correlation-matrix-pandas/

3.距离图

什么是 distplot,为什么它们有用?

作者创建的图像

distplot 是 distribution plot 的缩写,是数字数据的多种统计表示的组合,用于描述数据分布的变化。distplot 可以是几个图的组合,如直方图、核密度估计(kde)和 rug 图。

distplot 的主要目的是了解和比较数据的分布,以便更好地了解集中趋势、数据的偏斜度和数据的分布。

如何构建 distplot

查看以下链接,了解如何使用 Plotly 构建 distplot:

https://plotly.com/python/distplot/

务必 订阅此处 千万不要错过另一篇关于数据科学指南、诀窍和技巧、生活经验等的文章!

4.瀑布图

什么是瀑布图,为什么它们有用?

瀑布图是一种独特的条形图,它通过一系列正值和负值显示值的净变化。与仅显示指标的起始值和结束值不同,瀑布图显示了指标如何从起始值变为结束值。

瀑布图的主要目的是讲述一个特定指标如何通过一系列子组件增长或下降的故事。

它在分解盈利能力指标时特别有用,例如,您可以查看某个特定公司的所有收入来源,分解所有成本,并以该公司的利润结束(通常称为 P&L 报表)。下面是一个简化的瀑布图示例:

作者创建的图像

如何构建瀑布图

查看下面的链接,了解如何使用 Plotly 构建瀑布图:

https://plotly.com/python/waterfall-charts/

5.漏斗图

什么是漏斗图,为什么有用?

漏斗图是一种可视化形式,用于显示流程中各个阶段的值。例如,您可以使用如下图所示的漏斗图来显示从访问网站到收到发票的每个阶段有多少客户。

作者创建的图像

漏斗图有助于理解高效单位如何从一个阶段流向另一个阶段。我们的目标是让尽可能多的单位从开始到结束,了解哪里有明显的下降非常重要,这样您就可以确定漏斗中需要改进的地方。

在上面的例子中,只有一半的潜在客户询问价格。理解了这一点,你就可以研究为什么下降是不好的。是因为不清楚向哪里要价吗?是因为这些潜在客户仍然缺乏信息,并希望在请求价格之前与某人交谈吗?这是一些如何在商业环境中使用漏斗图的例子。

如何构建漏斗图

查看以下链接,了解如何使用 Plotly 构建漏斗图:

https://plotly.com/python/funnel-charts/

感谢阅读!

务必 订阅此处 千万不要错过另一篇关于数据科学指南、诀窍和技巧、生活经验等的文章!

不确定接下来要读什么?我为你挑选了另一篇文章:

还有一个:

特伦斯·申

数据专业人员的五个基本演示技巧

原文:https://towardsdatascience.com/five-essential-presenting-tips-for-data-professionals-c31708ec52f5

学会如何根据情况调整你的措辞

克里斯蒂娜@ wocintechchat.com 在 Unsplash 上的照片

您的旅程概述

  1. 为什么好的演示很重要
  2. 技巧 1——了解你的受众
  3. 技巧 2——虔诚地使用视觉效果
  4. 提示 3——避免行话,简单明了
  5. 提示 4——将你的工作与大局联系起来
  6. 提示 5——有一个令人难忘的底线
  7. 包装

为什么好的演示很重要

担任数据角色的人员(数据科学家、数据工程师、数据分析人员等。)经常需要向不同的观众展示他们的作品:

  • 你可能在一家咨询公司工作,被要求向客户简要介绍你对一个项目的发现。您的客户拥有丰富的领域知识,但可能缺乏基本的数据素养。
  • 你可能正在为一家传统公司工作,在那里你最近实施了一个机器学习解决方案,以帮助公司的一个中心流程实现自动化。干得好!上级要求你就你的工作召开一次内部研讨会。观众将包括高度技术化的软件开发人员,但也包括来自完全不同部门的人,他们可能不理解你工作的技术细节。
  • 你可能在一个数据和分析团队工作,最近做出了很酷的贡献。你被要求就你的发现向你的其他团队成员做一个非正式的陈述。你可以期待你演讲的所有与会者都有很高的数据素养。

在所有这些情况下,创建一个好的、精心制作的、吸引人的、信息丰富的演示文稿是至关重要的。为什么?这里有三个原因:

外表:这一个很简单。你的陈述变成了你所做的事情的外在表现。如果你的陈述很糟糕,那么这反映了你的不佳。你不希望客户、同事或上级认为你在沟通简单的想法上有困难。

好的演示会建立你的声誉:说实话。你必须非常糟糕才能获得糟糕演讲者的名声。相反,你根本不需要成为一个令人惊叹的天生演讲者,就能获得一个优秀演讲者的名声。你所需要的就是花时间提高你的表达技巧。当人们注意到你的演讲引人入胜、见解深刻时,你就会建立起良好的声誉。这可以带来更多的机会,对于数据专业人员来说,这被视为一种非常积极的特质。

创建数据驱动的文化:通过创建高质量的数据展示,您正在提高数据素养。在听完一次关于物联网设备的精彩演示后,我对时间序列有了更多的了解。通过将演示与观众已经知道的内容联系起来,你可以加深他们对一般主题的了解。这种对演讲质量的关注通常是有感染力的!

在本文中,我将向您提供向他人展示您的数据见解的五个基本技巧。还有很多其他的通用演示技巧,所以我将把重点放在为数据专业人士量身定制的技巧上。这里没有“站直了”或“准备好了”这样的泛泛之谈😅

技巧 1——了解你的受众

第一个提示可能是最令人反感的提示。你可能已经听过这个提示很多次了。问题是小费是正确的😒

如果你的演示不符合听众的知识,那么即使是世界上最好的演示也会失败。

举个例子,当我还是一名博士生的时候,我已经做过几次关于研究级数学的演讲。这非常适合职业数学家的听众。然而,我只能想象如果我向客户展示我的数学研究的细节,事情会如何发展。这对我们俩来说都是噩梦。

在开始起草演示文稿之前,我建议您先找到以下问题的答案:

  • 谁会参加我的介绍会?试着了解一下观众的大致情况。如果你的听众的领域知识很高,那么你可以避免过度解释每个人都已经知道的东西。如果领域知识很少,那么跳过核心术语会毁了你的演示。
  • 受众的数据素养水平如何?如果可能的话,试着接触一些有代表性的听众,询问他们的数据素养。这个超级重要!如果你的听众没有什么数据素养,那么就不要谈论高级统计分布或来自 MLOps 的技术术语。
  • 听众对哪方面感兴趣?努力了解观众感兴趣的是什么。即使你是在向一位数据素养很高的高层领导做演示,她也可能对结果更感兴趣。相反,如果你正在给数据分析团队的成员做演示,那么他们会对技术细节感兴趣。他们可能想自己实现类似的东西。
  • 演示设置的正式程度如何?演示是在团队成员之间的非正式会议上进行吗?那么使用内部笑话,营造一种轻松的氛围可能是合适的。假设你正在向客户汇报一个为期六个月的项目的最终结果。那么你或许应该把你精心制作的关于神经网络的迷因留到下次再说。
  • 观众的心理状态是怎样的?你的演讲是在一整天的会议演讲后进行的吗?那么你的观众会彻底精疲力尽。那么,如果可能的话,让你的演讲稍微少一些技术性,多一些概念性,这可能是个好主意。如果你的演讲真的很长,那么你的听众的精神状态也会受到影响。确保包括休息,并不时总结要点。

照片由 RU 恢复部Unsplash 上拍摄

技巧 2——虔诚地使用视觉效果

为了避免大量的数字和术语,你需要讲述一个有说服力的故事。任何好的演讲的关键是尽可能让你的听众沉浸其中。这说起来容易。但是怎么做呢?一般来说,你应该使用大量的视觉效果。

失去观众注意力的最可靠方法之一是在没有视觉效果的情况下展示技术细节。视觉是表达复杂观点的好方法。它们看起来也很漂亮。这里有三种使用视觉效果来改善你的演讲的情况:

  • 始终用图表来代表趋势。与其说 2019 年第三季度 vs. 2020 年发生了什么,不如说明一下区别?条形图是表现这一点的好方法。对于更连续的数据(如每天的销售额),您可以使用直方图。
  • 使用图表对数量进行分组。如果你有几个组(比如一个公司的几个部门),那么你可以用一个垂直条形图来表示数量(比如每个部门的销售额)。作为一个例子,看看垂直条形图是如何有效的,考虑下面的图表显示在二战中的人员损失:

公共领域:https://commons . wikimedia . org/wiki/File:Human _ losses _ of _ world _ war _ two _ by _ country . png

你能想象我要写多少句子才能捕捉到上面的画面吗?

  • 用流程图说明零件是如何连接在一起的。没有什么比听别人谈论 15 个软件工具如何在没有流程图的情况下组合在一起更好的了。甚至演讲者看起来也很困惑。请帮大家一个忙,用一个流程图。流程图非常适合描述任何类型的过程。对于数据专业人员来说,最明显的例子是描绘管道或云基础设施🤓

技巧 3——避免行话,简单明了

数据专业人士倾向于喜欢技术术语。和同行讨论的时候,有一个技术词汇超级有用。它能让你快速交流复杂的想法。那个 REST API 怎么样了?机器学习模型的 漂移分析 做完了吗?看到了吗?超级有用!

然而,当我们做演示时,这又回来咬我们。使用技术术语是如此诱人,因为它是熟悉的。然而,作为一般建议,您应该尽可能避免这种情况。

其他职业很清楚他们的技术术语令人困惑。你的牙医有没有告诉过你他们需要做根尖切除术?也许上周来你家的水管工说你的饰框有问题?每个职业都有专业术语。职业知道什么时候用,什么时候简化

当你的演示面对的是不太懂技术或数据的观众时,试着减少行话。对于代码质量,有众所周知的格言 保持简单傻 (KISS)。这鼓励软件开发人员尽可能保持他们的代码简单明了。在演示文稿中尝试同样的方法。尽可能保持简单(行话),听众会因此感谢你。

提示 4——将你的工作与大局联系起来

你有没有发现自己在别人的演示中被教导:“这一切都很好,但我为什么要在乎?”。这是完全正常的。当我们看不到所说的和我们所关心的之间有任何联系时,就很容易忽视。

所以,当你在陈述时,试着把你的工作与更大的图景联系起来。这部作品会对观众产生什么影响?这里有两个例子:

  • 如果你是作为咨询公司的代表向客户介绍,那么把你的工作和整个公司联系起来。你所做的事情对组织有什么帮助?你的工作会对组织中的其他部门产生影响吗?你展示的每一份数据都应该讲述一个关于客户组织的故事。因此,你应该做好准备,熟悉客户的组织
  • 如果你正在做一个内部团队演示,那么你的工作和其他团队成员有什么关系呢?他们可能在做类似的事情吗?你应该事先问他们一直在做什么。通过这样做,你可以把你的工作和他们的工作联系起来,使演示更加有趣。

将你的工作与大局联系起来的主要障碍是而不是了解大局。努力去理解观众的需求。通过这种方式,你会自然而然地做出更吸引人的演讲🔥

提示 5——有一个令人难忘的底线

最后一个技巧是数据专业人员做演示时经常缺少的。有一种试图解释一个项目的每一个细节的倾向。这可能会引发一个非常严重的问题:你的演示可能没有一个令人难忘的底线。

大多数伟大的演讲都有一个底线,在演讲中会被强调几次,通常是含蓄地强调。底线比演讲的任何具体细节都更能吸引观众。这方面的例子包括:

  • 向咨询客户演示: 您的会员费率今年下降了 11%。主要原因是缺乏参与。别担心!你可以用最具成本效益的方式来解决这个问题,那就是建立一份简明的月度时事通讯。
  • 向贵公司高层的陈述: 我已经实现了一个跟踪我们物联网温度设备的模型。我估计它可以为我们所有的建筑减少 8%的供暖能源成本。仅今年就节省了 50.000 美元!我们应该进一步调查此事。
  • 向数据素养团队成员演示: 当数据存在季节性变化时,时间序列的异常检测非常困难。尝试解决这个问题的一些工具是……

想象一下,在上面的第二个例子中,我花了整个演示来谈论物联网设备和 MLOps 架构的时序分析。这不是高层关心的。然后,很有可能他们会走出房间,完全不知道这是什么意思。一场精彩的演讲会让人痛苦地明白底线是什么。

你不确定你是否对你正在准备的演示有一个令人难忘的底线?试试电梯间距测试。你能否解释一下,在乘坐电梯的时间跨度内(比如 30 秒),底线是什么?如果没有,那么对你的演讲进行一点精炼可能是个好主意,这样它可以围绕一个令人难忘的底线。

包扎

照片由斯潘塞·伯根Unsplash 拍摄

现在,您应该能够更好地向广泛的受众展示数据见解。如果你有自己的建议,请告诉我!

喜欢我写的?查看我的其他帖子,了解更多 Python 内容:

如果你对数据科学、编程或任何介于两者之间的东西感兴趣,那么请随意在 LinkedIn 上加我,并向✋问好

学习 Tableau 你必须知道的五件事

原文:https://towardsdatascience.com/five-essential-things-you-must-know-to-learn-tableau-2b899403bfcb

免费资源指南,帮助你培养技能

秘鲁的因卡小径。图片由作者提供。

Tableau 是可视化数据的优秀工具。然而,这不是你可以通过发现来学习的直观软件。如果您在没有扎实的数据和可视化原理背景的情况下就开始点击界面,那么您会遇到很多挫折。

本文基于我最近的一篇文章,其中包含了学习 Tableau 的免费资源。

在这里,我通过总结学习者在学习场景之前必须知道的五件重要事情来帮助你最大限度地利用这些和其他资源。我的建议是基于我自己教授新学员数据分析和可视化的经验。

这个列表应该被视为帮助你确定学习活动方向的起点。请小心,认为这是一个详尽的建议列表或一个简短的清单,您可以在使用该软件之前完成。

1.数据类型

中国昆明的街头艺人。图片由作者提供。

确保您对数据类型之间的差异有一个清晰而深刻的理解,尤其是度量(或度量)和维度,以及连续离散之间的差异。Tableau 中的每个决策都取决于对数据的清晰理解。请记住,这些概念广泛适用于数据分析和可视化,而不仅仅是 Tableau。

2.数据结构

一些电的东西。图片由作者提供。

数据可视化中最耗时的工作是准备数据。大多数数据集都是宽的格式,但是 Tableau 更喜欢长长的格式。Tableau 提供了旋转数据的功能,但永远不会告诉你什么时候该是时候了。在学习 Tableau 之前,请确保您对宽而长的数据有坚实的理解。

我强烈建议每个学生花大量的时间来复习 Hadley Wickham 的论文。您可以放心地忽略关于 R 编码的内容,但是关于结构化数据的所有其他内容都是必不可少的。这篇论文对初学者来说是技术性的和复杂的,但对任何严肃的数据工作来说是至关重要的。花点时间通读这篇论文,并在积累数据经验的同时定期重温它。

我还推荐 Jonathan Serrano 在《走向数据科学》中的这篇文章:

https://towards data science . com/long-and-wide-formats-in-data-explained-e48d 7 c 9 a 06 CB

3.电子表格技能

灭火系统。图片作者。

不要低估电子表格技能的价值。出于几个原因,我教授 Tableau 入门课程,并确保我的学生牢牢掌握电子表格技能。电子表格允许您查看您的数据,这对于理解其内容和结构至关重要。处理数据时,您应该不断地查看原始数据和汇总表格。电子表格非常适合查看您的数据。

你有时需要在原始数据中进行修正,这在 Tableau 中是不可能的。此外,用于操作数据的电子表格函数类似于 Tableau 用于创建计算字段的函数。有许多优秀的免费电子表格学习资源。如果你想要一套高质量的免费资源,直接去 Leila Gharani 的 YouTube 频道。她收集了很多精彩的视频,涵盖了你需要了解的关于电子表格的一切。

4.数据透视表

中国重庆交通茶馆。图片由作者提供。

可视化数据类似于使用电子表格数据透视表汇总数据。在建立我的可视化之前,我经常在 Tableau 中创建汇总表,以确保我所有的计算都是正确的。虽然在 Tableau 中通常被称为文本表,但它们本质上与电子表格数据透视表相同。数据透视表将迫使您理解度量和维度之间的关键差异以及不同类型的聚合。由于数据透视表是电子表格技能的一部分,我建议您参考 Leila Gharani 的教程:

5.视觉编码

工具照片。图片由作者提供。

Tableau 是一个可视化数据的工具。不管你对画面的技术熟练程度如何,你的视觉传达知识将是你的限制因素。您需要对定量数据的可视化表示有很好的理解,尤其是度量和维度之间的差异,以及连续值和离散值之间的差异。花时间建立对标志渠道的概念性理解。这里有几个视频可以帮你入门。

还是那句话,记住 Tableau 只是一个工具,学习 Tableau 并不等同于学习数据可视化。我可以教你如何使用锤子,但这并不意味着你可以设计建造房子。出于这个原因,我将冒昧地向您推荐我的一篇早期文章,这篇文章强调了在进行数据可视化时软件和设计角色之间的重要区别。

后续步骤

秘鲁的因卡徒步旅行路线。图片由作者提供。

同样,这篇文章并不是详尽的,而是一个简短的列表,列出了学习 Tableau 的基本要点。请随意在评论区张贴其他资源和对你的学习有帮助的东西。如果您对构建数据技能和学习 Tableau 感兴趣,请务必关注我。

机器学习部门的五项关键技术

原文:https://towardsdatascience.com/five-key-techniques-for-machine-learning-department-df95bba5cc95

2022 年,可能会对机器学习部门的工作方式产生重大影响的技术

瑞典基律纳的 EISCAT 雷达天线。它是斯堪的纳维亚的三个雷达系统之一,用于太空垃圾 追踪以及其他用途。在交付机器学习应用的技术和技术的看似无限的空间中,Tech radar 对于机器学习部门来说扮演着同样重要的角色。图片由 Tpheiska维基共享中提供。

交付机器学习(ML)应用程序的组织的技术雷达是一组主题,每个主题都是一种技术或工艺,在组织中的团队的应用程序开发中起着重要的作用。tech radar 的使用并不新鲜,已经被世界各地从事 IT 和软件开发的组织所采用。

像任何技术组织一样,技术雷达是 ML 组织的一个非常重要的工具。对于组织中的 ML 团队来说,这是一个标准化技术栈和愿景的很好的练习。它增加了组织内的透明度,因此,是增加团队间协作的有利因素。如果面向公众发布,它可以成为招聘和公共关系的一个有影响力的沟通工具。

让我们首先了解一下技术雷达框架的概况。

技术雷达框架

由四个类别和四个采用阶段组成的技术雷达框架。每个类别在采用的每个阶段都有零个或多个主题。这种框架允许个人、团队或组织确定一个感兴趣的主题,以及在相关的技术类别中该做些什么。图片由作者提供。

部分题目的框架和灵感来自于 Thoughtworks 倡导的 Tech Radar。它的知名度相当高,已经被许多知名机构采用,如 ZalandoBackstage 等平台。该框架由 IT 行业公认的委员会管理。它被很好地记录和展示,并带有一个易于采用的开源库

由四个类别和四个采用阶段组成的技术雷达框架。类别定义如下。

  • 技术:影响团队工作方式或结构的方法
  • 平台:团队成员进行技术或团队活动的软件平台
  • 工具:允许团队成员或系统执行技术或团队活动的单一实用软件
  • 语言&框架:团队进行软件开发所使用的编程语言及其包

采用阶段定义如下。

  • 使用:建议在生产使用中尽快采用该主题,因为它解决了一个主要问题,已被广泛采用并在操作使用中表现良好
  • 测试:建议最好进行试验,目的是发现其在生产使用中的适用性,因为它解决了一个主要问题,已被适度采用,但在操作使用中表现良好
  • 考虑:建议开发概念验证,目的是了解更多关于其适用性的信息,因为它解决了一个主要问题,要么以有限的方式采用,要么直接使用在运营中表现良好,要么基于核心技术或广受机构群体欢迎的技术
  • 保持:建议谨慎使用,最好停止使用,因为它会导致不合标准的体验或性能

管理技术雷达

管理技术雷达有三个方面。首先,技术雷达是一个活的工具,这意味着它应该定期更新。其次,技术雷达是一个组织级的工具,这意味着它应该由一个委员会以包容的方式开发,该委员会涵盖广泛的角色,并且有能力和时间进行彻底的研究。第三,技术雷达是一种被广泛使用的手段,即使在组织之外,它也应该以一种易于使用的格式和平台来访问,并且对于定期更新仍然是友好的。

对于第一种情况,一个 ML 组织应该至少每年更新雷达两次。对于第二种情况,管理技术雷达的委员会应该包括高级工程、数据科学、分析师、产品经理和工程师、数据科学、分析师领导。为了保持委员会的灵活性,人数不应太多。但是,组织成员应该从他们的同事和团队成员那里收集反馈,以确保广泛的覆盖面。最后,技术雷达的平台应该是某种仪表板或 web UI 平台,以便于浏览、搜索和可视化信息。我个人会推荐使用 Thoughtworks 开源库开发平台。为了管理信息,我会推荐一个类似于 Thoughtworks 推荐的的 CSV 文件格式。对于部署,我会使用具有易于记忆的 URL 的服务器。此外,我建议以这样一种方式构建平台,即可以在平台中开发多种技术雷达。例如,维护一个技术雷达供内部使用,另一个供外部使用,或者保留所有技术雷达版本。

重要的是,一旦开发出来,就要定期广泛地交流和使用科技雷达,以确保保持更新的动力。

ML 组织的五个关键技术

MLOps

采用阶段:使用

MLOps 包含了与 DevOps 共享血统的元素,但是应用于开发/部署 ML 模型。像 DevOps 一样,它涵盖了系统、实践和人员。在系统方面,它包括持续集成和部署(CI/CD)解决方案,不仅针对代码,还针对 ML 模型。在实践方面,它包括旨在提高生产中模型的自动化和可靠性的过程和工作方式。在人员方面,它包括 ML 团队中的数据科学家和不同的工程师,他们使用那些系统应用那些实践。如果你的组织真的想使用 ML,它应该采用这个咒语。许多组织仍然在做特定的数据科学,并回避使用 ML 增加/创造商业价值的问题。在赶时髦之前,组织应该做认真的尽职调查,以确定这是否是正确的选择。一旦确定了这一点,组织等待采用的时间越长,组织获得价值的代价就越大。

ML 平台产品团队

采用阶段:使用

ML 平台产品和交付这些产品的团队是组织中生产就绪型 ML 应用的重要推动者。在这里,我从团队拓扑中获得了灵感,但是着眼于 ML 应用的上下文而不是典型的 IT 系统。ML 平台产品团队可以被视为仅关注内部平台客户的另一个产品团队,例如组织中的 ML 和分析团队。对于每一个这样的团队来说,拥有一个薄而高效的产品和稳定的赞赏客户流是至关重要的。仅仅因为这些团队只与内部客户合作,it 仍然不应该像任何关注外部的产品团队一样跳过工程规程和工作流程的方式。当一个组织遇到三个或更多的流对齐的 ML 团队时,它应该开始考虑如何孵化一个支持这些团队的平台团队。如果这个数字翻倍,仍然没有平台团队,组织应该确保现实尽快改变。

团队规模的软件

采用阶段:使用

康威定律(Conway's law)指出,一个组织将按照其沟通结构产生一个系统设计。这对于 ML 组织来说没有什么不同。正如在团队拓扑中所提倡的,它颠倒了 IT 团队快速流动的法则,最好保持软件系统的焦点基于团队的认知负荷能力。在这一点上,我建议保持每个 ML 团队是一个双层披萨团队,同时保持 ML 应用程序及其交互的范围在团队中的大小。

远程 Mob 编程

采用阶段:测试

在 COVID 时代,远程结对编程非常盛行。在此之前,除了结对编程, Mob 编程成为软件产品团队快速流程的推荐方法。这项技术已经变得更加重要,因为它允许团队“移动”一个问题或解决方案,而没有任何物理空间/存在的限制。现代视频会议工具、在线编码平台和数字白板使得“聚众”远程团队比以往任何时候都更容易。由于团队中的各种角色,数据科学家、数据分析师、机器学习工程师、数据工程师、软件工程师等,ML 团队通常在竖井中工作。然而,我建议严肃的 ML 团队从成功的跨职能软件团队中吸取教训,打破活动的隔间,并经常使用诸如 mob 编程的方法作为一个整体走到一起,但在“完全”远程设置中。我之所以推荐尝试这种方法,是因为这是一种新的实践,而且事实上,一种接近健康的平台来执行远程 mob 编程的东西还没有出现。

开发/测试环境中的真实数据

采纳阶段:搁置

ML 团队倾向于在开发/测试环境中使用真实产品的样本,主要是因为它的庞大。现代软件团队正试图摆脱这种做法,因为它是一种主要的反模式。在冷酷的人看来,这种做法会导致以下问题,这些问题同样适用于 ML 应用程序。

  • 开发/测试成本增加,这是整个团队基础架构成本中的一个重要因素。
  • 对软件行为的理解减少
  • 由于使用大量数据,反馈回路较慢
  • 由于开发/测试环境中的低安全性设置,增加了安全风险

用于测试 ML 管道的合成数据是一个新领域,一个严肃的、经过实战检验的框架尚未出现。此外,可能存在需要测试中的真实数据的情况,例如,再现源自数据的错误。由于这些原因,我在 ML 团队的练习中非常谨慎。然而,我相信关于合成数据生成的框架将会成熟,特别是由于基于 ML 的信息增强技术的出现,例如生成对抗网络及其在该领域的应用

评论

Thoughtworks 列出了与技术世界相关的 17 种技术,其中一些在主题层面上与候选名单相匹配。然而,由于上下文有些特殊,所以细节有些微妙。除了我在这里列出的,你觉得前五名还应该包括别的吗?请评论分享你的选择。你觉得你能为这些细节做出贡献,并愿意把你的精力放在这些话题上吗?也许我们可以合作。请伸出手。

每个熊猫用户应该知道的五个黑仔优化技术

原文:https://towardsdatascience.com/five-killer-optimization-techniques-every-pandas-user-should-know-266662bd1163

数据分析运行时优化的一步

照片由布拉德在 Unsplash 上整理

设计和构建现实世界适用的机器学习模型的动机一直吸引着数据科学家大规模利用优化、高效和准确的方法。优化在可持续交付真实世界和面向用户的软件解决方案中扮演着基础性角色。

虽然我知道不是每个人都在大规模构建解决方案,但是了解各种优化和省时技术仍然是有帮助的,并且高度适用于甚至一般的数据科学/机器学习用例。

因此,在本文中,我将向您介绍一些令人难以置信的技术,来减少使用 Pandas 进行常规表格数据分析、管理和处理任务的运行时间。为了得到一个简要的概述,我将在这篇文章中讨论以下主题:

# 1 CSV 输入/输出# 2 基于分类数据的过滤# 3 合并数据帧# 4 Value _ counts()vs group by()

此外,你可以在这里获得这篇文章的笔记本。

我们开始吧🚀!

CSV 上的#1 输入/输出

CSV 文件是目前最流行的读取和存储数据帧的来源,不是吗?这是因为 CSV 在使用pd.read_csv()df.to_csv()方法的输入和输出操作环境中提供了极大的灵活性,例如:

  1. CSV 可以在 Excel 中打开,并以 Excel 允许的任何方式进行操作。
  2. 通过将列指定为列表并将其作为pd.read_csv()方法的usecols参数传递,CSV 使您能够在需要时只读取列的子集。
  3. 如果需要的话,可以使用pd.read_csv()方法的nrows参数来只读前n行,等等。

虽然我承认使用 CSV 文件有许多优点,但同时,如果您正在寻找运行时优化,它们远不是首选方法。让我解释一下。

Pandas 对 CSV 文件的输入输出操作是序列化的,这不可避免地使它们非常低效和耗时。虽然有足够的并行化空间,但不幸的是,Pandas 还没有这种功能。

在此之前,如果您无法阅读 CSV 文件,有两种非常快速的方法可供选择,我在下面的流程图中描述了这两种方法:

确定读取 CSV 的替代方法的流程图(图片由作者提供)。

路径 1

如果您的 CSV 文件是静态的,并且您认为您会多次读取它,可能是在同一个管道中或在重新加载内核之后,请立即将其保存为一个泡菜羽毛拼花文件。但是为什么呢?这一点我已经在下面的帖子中讨论过了:

下面的代码块演示了从 CSV 格式到所需格式的转换:

现在,当您想要读回数据帧时,不要从 CSV 文件中读取它,而是使用您创建的新文件读取它。Pandas 中重新加载数据集的相应方法如下所示:

此外,这些单独的文件中的每一个都将把记录解释为熊猫数据帧。这可以使用 Python 中的type()方法进行验证,如下所示:

下面的条形图描述了所有四种文件格式在运行时的预期速度提升:

以各自的格式加载和保存数据帧所花费的时间。(图片由作者提供)

为了获得这四种格式的运行时间,我用 Python 生成了一个包含一百万行三十列的随机数据集——包括字符串、浮点和整数数据类型。我测量了负载和保存运行时间十次,以减少随机性,并从观察结果中得出公正的结论。以上结果显示了十次实验的平均值。

路径 2

如果您的 CSV 文件不是静态的,或者您只打算使用一次 CSV 文件,那么转换为新的格式是没有意义的。取而代之的是路径 2,即使用数据表库进行输入和输出操作。您可以在 Jupyter 笔记本中使用以下命令安装 DataTable:

当使用数据表时,CSV 文件将被解释为数据表数据帧,而不是熊猫数据帧。因此,在加载 CSV 文件后,您需要将其转换为 Pandas DataFrame。我在下面实现了这一点:

类似地,如果您想将 Pandas 数据帧存储到 CSV 中,最好选择 DataTable 路线,而不是 Pandas。这里,要使用 datatable 生成 CSV 文件,首先需要将 Pandas 数据帧转换为 DataTable 数据帧,然后将其存储在 CSV 文件中。这在下面实现:

左图:描绘使用 Pandas 和 DataTable 将数据帧存储到 CSV 所需时间的折线图。右侧:描绘使用 Pandas 和 DataTable 从 CSV 读取数据帧所用时间的折线图。(图片由作者提供)

如上面的折线图所示,DataTable 通过 Pandas 为 CSV 文件提供高速输入和输出操作。

关键要点/最终想法

  1. 如果由于某些限制,您必须使用 CSV 文件,千万不要使用 Pandas read_csv()to_csv()方法。相反,更喜欢 datatable 的输入输出方法,如上所示。
  2. 如果您将重复读取同一个 CSV 文件,请将其转换为 Pickle、Feather 和 Parquet 之一,然后使用新文件进行输入操作。

#2 基于分类数据的过滤

数据过滤是熊猫中另一种常见且广泛使用的操作。核心思想是选择符合特定条件的数据帧片段。

为了演示,考虑一个我自己创建的超过 400 万条记录的虚拟数据框架。下图显示了前五行:

虚拟数据集的前五行(作者图片)

下面的代码块演示了我的实现:

假设您想要过滤所有属于“Amazon”的记录。这可以通过以下方式完成:

进行相同过滤的另一种方法是使用[groupby()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.groupby.html)并使用如下所示的[get_group()](https://pandas.pydata.org/docs/reference/api/pandas.core.groupby.GroupBy.get_group.html#pandas.core.groupby.GroupBy.get_group)方法获得单个组:

与通常的过滤方法相比,后一种方法提供了高达 14 倍的加速,这在运行时间上是一个巨大的改进。

此外,get_group()方法将单个组作为熊猫数据帧返回。因此,您可以继续进行通常的分析。我们可以通过检查方法 1 和方法 2 中获得的数据帧类型来验证这一点,如下所示:

关键要点/最终想法

  1. 如果您要对分类数据执行数据框架的重复过滤,最好首先使用groupby()方法对数据进行分组。之后,使用get_group()方法获取所需的组。
  2. 警告:这种方法仅适用于基于分类数据的过滤。

#3 合并数据帧

Pandas 中的合并是指根据一个连接条件合并两个数据帧。这类似于结构化查询语言(SQL)中的连接。您可以在 Pandas 中使用[pd.merge()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.merge.html#pandas.DataFrame.merge)方法执行合并,如下所示:

虽然上述链接数据帧的方法没有任何问题,但是有一个更快的方法可以使用[join()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.join.html)方法来连接两个数据帧。

在下面的代码块中,我使用merge()方法和join()方法实现了合并操作。这里,我们使用两种方法来测量合并操作所花费的时间。

使用join()方法,我们注意到相对于熊猫的标准merge()方法,改进超过 4 倍

这里,join()方法首先希望您更改索引列,并将其设置为您希望在其上执行表间连接的特定列。这是在熊猫身上用set_index()方法完成的,如上图所示。

如果您想在多个列上执行一个连接条件,您也可以使用join()方法来完成。首先,将希望执行连接条件的列作为列表传递给set_index()方法。然后,像以前一样调用join()方法。下面演示了这一点:

关键要点/最终想法

  1. 在执行连接时,始终更改两个数据帧的索引,并将其设置为要对其执行连接条件的列。

#4 Value_counts()与 GroupBy()

我们在 Pandas 中使用value_counts()来查找一个序列中各个元素的频率。例如,考虑我们在第 2 节中使用的虚拟雇员数据框架。

虚拟数据集的前五行(作者图片)

我们可以使用value_counts()方法找到该数据集中属于每个公司的员工人数,如下所示:

使用groupby()也可以进行类似的频率计算。下面的代码演示了:

value_counts()的输出按频率降序排列。另一方面,groupby()size()的输出在索引列上排序,在本例中是Company Name

假设我们不关心输出如何排列或排序,我们可以测量两种方法的运行时间差异,以获得所需的频率,如下所示:

尽管这两种方法本质上做的是相同的事情(如果我们忽略一次输出的顺序),但两者之间有一个显著的运行时间差异— groupby()value_counts()1.5 倍

当您想要获得归一化频率时,事情会变得更糟,归一化频率表示序列中各个元素的百分比/分数。在这种情况下,运行时间比较如下:

同样,尽管这两种方法做的是同样的事情,但两者之间有一个显著的运行时间差异——与value_counts()相比,groupby()要慢2倍。

关键要点/最终想法

  1. 对于基于频率的测量,最好使用value_counts()而不是groupby()
  2. value_counts()可一次用于多列。因此,如果您想要计算来自多个列的值的组合的频率,请使用value_counts()而不是groupby()来完成。

#5 迭代数据帧

循环或迭代数据帧是单独访问每一行并对记录执行一些预定义操作的过程。虽然在这种情况下,最好的办法是首先避免循环,而选择向量化的方法,但是在某些情况下,循环是必要的。

Pandas 中有三种方法可以实现迭代。下面,我们将讨论它们,并比较它们在下面几节中使用的雇员虚拟数据集上的运行时间。要重新查看,下图显示了数据框的前五行。

虚拟数据集的前五行(作者图片)

循环遍历数据帧的三种方法是:

  1. 使用range(len(df))进行迭代。
  2. 使用[iterrows()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.iterrows.html)进行迭代。
  3. 使用[itertuples()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.itertuples.html)进行迭代。

我在下面的代码块中实现了三个利用这三种方法的函数。该函数的目标是计算数据框架中所有雇员的平均工资。我们还在下面的同一个数据框架中找到了这些方法的运行时间。

方法 1:使用range(len(df))迭代

迭代超过 400 万条记录的平均运行时间是46.1 ms

方法 2:使用 iterrows()迭代

iterrows()方法在迭代过程中提供了实质性的改进,将运行时间从46.1 ms减少到18.2 ms的 2.5 倍

方法 3:使用 itertuples()进行迭代

结果证明itertuples()方法甚至比iterrows()更好,从18.2 ms773 µs进一步减少运行时间超过 23 倍

关键要点/最终想法

  1. 首先,您应该避免在代码中引入 for 循环来迭代数据帧。如果可能的话,考虑一个矢量化的解决方案。
  2. 如果矢量化是不可能的,那么利用 Pandas 中预先实现的方法进行迭代,比如itertuples()iterrows()

结论

在这篇文章中,我讨论了 Pandas 中五个令人难以置信的优化技术,你可以在你的下一个数据科学项目中直接利用它们。在我看来,我在这篇文章中讨论的领域是改善运行时间的微妙方法,在寻求优化时经常被忽略。尽管如此,我希望这篇文章能让你深刻理解这些熊猫的日常功能。

如果你喜欢读这篇文章,我希望你也会喜欢下面的文章:

</20-of-pandas-functions-that-data-scientists-use-80-of-the-time-a4ff1b694707>

感谢阅读。

作者在imgflip.com创造的 Doge meme。

增强您的 ML 工作流程的五个 MLOps 技巧

原文:https://towardsdatascience.com/five-mlops-tips-to-enhance-your-ml-workflows-d9689ba7c60f

图片来自 Pixabay张秀坤·卡什

组织中的机器学习成熟度和流畅性已经达到令人印象深刻的水平,并且在不断上升。一段时间以前,维护一个 ML 决策支持系统可能被认为是一个充满曲折的危险旅程,你每隔几个月左右就要开始一次。如今,它几乎渗透到了大多数组织的敏捷软件开发周期中。

这意味着,作为一个 MLE,你不能再忽视组织的其他部分而使用 d-word;“哦!但是数据科学与软件开发不同。ML 不再孤立于组织的其他部分,而是需要与您的软件一起发展。这意味着,你必须运营并积极维护你的机器学习管道。要做到这一点,你需要应用行业最佳实践,编写好的代码,等等。在您的组织中建立稳固的 MLOps 文化有助于您为客户提供最佳价值,并做出基于数据的准确决策。

这篇文章将讨论几个你可以关注的技巧,以提升 ML 管道的可操作性。这将是对这些技巧的一次高级演练。只要有可能,我会提供关于工具/库的建议。但是这取决于读者去评估它们的适用性和可行性。

1.可观察性:在训练和服务期间记录

图片来自 PixabaySimon

你最不希望看到的事情就是在离开你的模型一夜训练,却不知道哪里出了问题之后,丢失了训练过的工件。为了避免这种不愉快的情况,在模型训练和服务过程中引入持久日志记录非常重要。一些需要小心的事情,

  • 如果您经常训练许多模型,那么在开发和生产过程中考虑不同级别的日志记录(例如DEBUGINFO),以避免日志在文件大小上膨胀。
  • 考虑使用循环、压缩来管理写入日志的存储要求
  • 在服务期间记录时要小心,在服务期间记录大量数据会导致更高的延迟

在 Python 中,你可以合并像 Python loggingloguru这样的库(标准logging库的简化版本)。如果您在大规模运行,每天记录数 GB 的数据,那么您可以使用弹性搜索和一个日志检查仪表板,如 Kibana 来有效地聚合和搜索大量日志。您还可以使用基于云的一体化日志工具,如 logglypapertrail 等。

2.避免生产中的流氓模型

图片由来自 PixabayFrankGeorg 拍摄

渗透到生产环境中的流氓模型会给公司带来严重的后果。这是一个潜伏很长时间的问题,会慢慢侵蚀性能。它们可能相当难以捉摸,因为模型仍将起作用,但会产生质量差/不准确的预测。因此,尽早采取安全措施来避免/检测问题非常重要。这里有一些你可以做的事情。

数据新鲜度检查

数据新鲜度检查确保您的模型是基于最近的数据而不是几个月或几年前的数据进行训练的。它们可以捕获数据仓库或数据接收管道中的问题,并通知您陈旧的数据。

这些检查可以像检查加载的数据集的最大时间戳一样简单。如果您正在使用像dbt这样的工具来转换数据和构建表,它提供了开箱即用的新鲜度检查

数据验证检查

数据验证和新鲜度检查一样重要。在数据验证中,

  • 空值
  • 分类特征的分布
  • 数字特征的范围

这些检查中的一些(例如检查空值)可以在dbt中实现。您还可以使用 Python 库,如ydata — quality(【https://github.com/ydataai/ydata-quality】)来简化 ML 工作流中的数据质量检查。

受祝福的模特

最后,在盲目地将一个新版本的模型投入生产之前,进行一些模型验证检查。支票可以是这样的,

  • 新模型的验证精度必须大于或等于以前的模型
  • 新模型的验证精度需要在[x,y]范围内

注意不要对你的祝福政策过于严格,因为这会导致生产中的陈旧模型。

3.自动化培训工作

图片来自 PixabayGerd Altmann

与其手动训练模型,不如为模型设置自动训练计划。不仅如此,它将为您省去手动触发培训任务的麻烦,而且还会迫使您编写生产就绪的可重用代码。但真正的好处是,你的模型总是最新的,最少的人工干预,不会随着时间的推移而变得陈旧。

您可以设置各种调度程序,从使用 bash 脚本的简单 cron 作业到像 Apache airflowArgo 这样的工具。使用成熟的 too 而不是 bash 脚本的好处如下。

  • 更容易定义复杂的工作流程(例如,引入分支、条件、循环等。)
  • 更容易监控/跟踪工作流程的进度/错误
  • 代码更具表现力,可读性更强,并且更少需要从头开始编写

4.跟踪实验

图片来自 Pixabay翻拍工作室

如果你不能衡量它,你就不能改进它——彼得·德鲁克

随着您组织的 ML 用例及工作流的发展,很自然地会更频繁地产生更多的模型和相同模型的版本。随着这一点变得越来越明显,最好投资一种工具,将您的所有实验结果以及其他相关元数据聚合并集中起来,以实现您的 ML 实验的可见性/透明度。最重要的是,它们将帮助您基于模型性能做出特定于 ML 的决策(例如,选择最佳超参数,将模型发布到生产环境中,等等)。).

MLFlow (自托管)和Weights&bias(基于云的 w 私有托管选项)这样的工具就是围绕这种需要而设计的。它们提供了方便的 API,可以帮助你组织实验,并以一种容易找到结果的方式呈现出来。该工具可以帮助您的一些示例场景如下:

  • 搜索在特定或相对日期或时间发生的实验的日志
  • 从超参数搜索中识别最佳超参数
  • 日志记录/版本跟踪训练(例如,训练模型)/评估(例如,图、图表)工件

5.检测并避免培训服务偏差

图片来自 PixabayKawita Chitprathak

众所周知,很难做到的一件事是验证你的模型在服务期间的表现是否符合预期。如果没有,这通常被称为训练-发球偏斜。对于实时(即在线)提供机器学习模型的公司/用例来说,这通常是一个问题。训练和服役期间的主要区别,

  • 在培训期间,我们进行批处理,而在服务期间,我们进行在线处理
  • 在服务期间,需要从持久存储中加载存储的已训练工件

由于这种差异,训练模块和服务模块之间的代码不可避免地会重复。培训和服务代码分离的需求加剧了这种情况。这种分离有助于我们维护职责分离,其中服务代码只负责服务。这种重复意味着什么?这意味着,作为一个人,你可能会在实现功能时无意中引入错误,

  • 输入验证检查
  • 处理缺失数据
  • 特征工程

谷歌机器学习规则的第 32 条规则建议在训练和服务管道之间重用代码。但是要正确地做到这一点,是一项费时费钱的工程工作。如果操作不当,会导致代码不可读和不可维护。这里有几个更实用的解决方案,直到您弄清楚如何在训练和服务管道之间重用代码。

  • 为评估保留一个纵向(第二天)验证集。我的意思是,按时间分割数据。例如,前 3 个工作日生成训练数据,后 2 个工作日生成验证数据— 规则#33 。进行这种分割时,要注意数据的季节性(例如工作日与周末)
  • 记录培训/服务输入/预测。一些云服务提供了开箱即用(例如 Google cloud 请求-响应日志)。您甚至可以编写一个自定义管道,1)在训练时,简单地将原始输入、特征和预测记录到 SQL 表中,2)在服务时,记录到缓存中,然后缓存将数据卸载到 SQL 表中以保持低延迟
  • 保持用同一种语言训练/服务代码,这对代码重用非常重要。

好处:标准化你的模型的 IO

图片来自 PixabayAlicja

我们刚刚谈到在模型训练和服务中使用相同的语言。但实际可行吗?试着说服后端工程师用 Python 写后端。现在是 2022 年,很有可能,你还是会输掉这场辩论。Python 正在增加其在后端开发中的主导地位。但是在大多数地方,由于性能(例如处理并发请求)和成熟度,Java 仍然比 Python 更受青睐。

这与在训练和服务逻辑之间重用代码的能力严重矛盾。幸运的是,不完全是!与语言无关的数据序列化框架,如 Google 的 [protobuf](https://developers.google.com/protocol-buffers)Apache [arvo](https://avro.apache.org/docs/current/)促进了语言之间的代码重用

protobuf指定各种对象的消息格式(如输入数据——批量或单个记录、预测等。).该消息使用提供的语法写成一个.proto文件。然后,您可以使用为您选择的语言提供的编译器来生成您指定的实体的类。

这意味着您不用手动编写模型的输入/输出模式,而是自动生成代码。这降低了将错误引入代码的风险。

结论

让我们总结一下我们的讨论。

  • 随着组织拥抱机器学习,它不再是孤立的,而是与周围的软件融为一体
  • 良好的 MLOps 实践,为您节省大量时间和金钱
  • 在培训和服务期间记录数据,同时注意非功能性需求(例如存储、服务延迟)
  • 尽早检测并防止流氓模型。数据可能是罪魁祸首。有保障措施来确保数据质量
  • 自动化培训工作以避免生产中的陈旧模型
  • 跟踪实验以做出基于证据的决策,并维护 ML 实验的详细历史记录
  • 当心训练-发球倾斜。如果不采取适当的措施,可能很难检测到
  • 使用数据序列化技术,如protobufarvo,标准化模型的 IO

参考

谷歌的机器学习规则

在开始任何数据科学项目之前,问自己五个问题

原文:https://towardsdatascience.com/five-questions-to-ask-yourself-before-starting-any-data-science-project-f2025c9ea37

在开始任何数据科学项目之前,问自己五个问题

开始下一项伟大的数据科学工作时的考虑事项

虽然技术技能在处理任何数据科学工作时都是不可否认的重要,但数据科学和机器学习有一种艺术,似乎不像纯技术技能那样经常被讨论。这些更软的技能可以帮助经验丰富的数据科学家尽可能无缝、高效地把握众多机会。事实是,几乎每一项数据科学工作都有自己的天赋,带来了独特的挑战,这些挑战可能(或者坦率地说,可能不)值得追求。

因为应用数据科学总是以寻找现实世界问题的解决方案为基础,所以拥有关于现实世界问题的专业知识是绝对必要的。当然,期望数据科学家自己拥有该领域的专业知识是不合理的。例如,如果您要创建一个预测天气的机器学习模型,那么期望数据科学家了解像时间序列建模这样的一般知识是合理的,但是期望数据科学家拥有气象学方面的专业知识是不合理的。在这种情况下,数据科学家需要与气象学家等专家合作,以更好地理解问题,制定解决问题的潜在解决方案。

也就是说,下面要考虑的这些问题旨在帮助数据科学家知道如何最好地“浏览”任何新的或潜在的项目。虽然我绝对不想低估拥有这些技术硬技能的价值,但我想说,这些更注重“软技能”的问题将有助于你最佳地优化你和你的公司持续生产有价值的预测模型的时间。

让我们开始提问吧!

对于您希望解决的问题,该解决方案的潜在成本效益分析是什么?

这一点看起来似乎很容易,但却经常被忽视。让我把话说清楚:即使你可以创建一个预测模型来解决一个问题,考虑到机会成本,也不值得这么做。例如,如果你为一家收入数万亿美元的公司工作,该公司批量购买廉价的书写笔,那么数据科学家可能不值得花费时间来创建一个预测模型,该模型可能会在从不同供应商购买相同的笔时每年节省几分钱。

“节省一分钱的笔模型”的例子显然是一个极端的例子,但有更多的“灰色区域”的情况下,潜在的数据科学努力在值得和不值得之间徘徊。显然,我不能在这里给出一个“一刀切”的答案,但在推导自己的成本效益分析时,您可以考虑以下几点:

  • 数据科学家和合作主题专家构建预测模型所需的时间和成本(如工资)
  • 运行预测模型可能需要的硬件(例如,GPU 可能非常昂贵)
  • 将模型实施到生产环境中所需要的严格性(例如,创建一个新的模型来取代传统的基于纸张的系统可能非常具有挑战性,因为您还必须实施一系列新的硬件来支持系统,这也可能会受到反对变革的人的反对)
  • 预测模型在推进公司目标方面可能具有的潜在优势(例如,更低的成本、更快的上市时间、更快的处理等。)

我能合理地确定在我试图解决的问题中有一个预测模式/信号吗?

当数据科学家第一次开始一项新的、潜在的工作时,我实际上要说的是,查看数据不应该是数据科学家做的第一件事。请记住,数据科学家通常不具备解决现实世界问题的专业知识,因此立即查看数据可能不会有任何好处。此时,数据科学家将无法确定从数据中提取的任何“信号”。

此外,非数据科学从业者对数据科学家的期望可能不可靠,不可靠是指许多人倾向于将数据科学/人工智能/机器学习视为“灵丹妙药”的解决方案。尽管意图良好,但这些人可能会抛出“左领域”的想法,这些想法由于完全缺乏模式/信号而永远不会在数据科学中体现出来。或者更微妙的是,可能看起来有某种模式,但对问题更仔细的分析可能会证明事实并非如此。

鉴于这些信息,我建议在开始新的数据科学工作时,首先要做的是与主题专家进行某种正式或非正式的面谈。这不仅有助于你更好地理解你要解决的问题,也有助于剔除那些“左领域”的想法。此外,它还提供了一个很好的机会,既可以教育对数据科学一无所知的主题专家,又可以与这些人建立融洽的关系。

(好消息是,随着时间的推移,我发现即使数据科学领域的非从业者最终也能够通过对该领域的工作方式有所了解来过滤出自己的想法,但你可能会在开始与其他方互动时遇到这种情况,特别是如果你是其他公司的数据科学顾问。)

我需要什么样的数据来支持我的项目,我有能力获得这些数据吗?

好的,在与你的主题专家同事会面后,你已经确定他们想要追求的解决方案似乎是可行的。作为前一个问题的一部分,我们讨论了这个想法,即在探索数据之前,举行一次访谈,看看这个项目是否有一个总体的机会。在同一次或随后的访谈中,与主题专家一起确定在考虑建模解决方案时什么样的因素(如数据特征)可能会有影响是有益的

当然,知道自己需要什么数据是一回事;实际获得这些数据是另一回事。你工作的公司并不总是拥有这些数据,即使他们有,也可能如此“混乱”,以至于——回到成本效益分析——可能不值得清理。虽然可能存在公共 API 来获取您需要的数据,但您可能需要检查该数据的“合理使用”。使用未经授权的数据可能会导致法律后果。

围绕项目本身或基础数据是否有任何伦理考虑?

坦率地说,这可能是最难接近的主题之一。一家公司会为了自己的利益而滥用数据吗?绝对的。网飞纪录片《T2:社会困境》非常清楚地说明了这一点。这部纪录片的范围更广,几乎包括了数据的每个方面,但鉴于数据科学是数据相关活动的子集,数据科学工作中存在大量的伦理困境。

我肯定会认为自己是一个有道德的人,亲爱的读者,我会假设你也是。也就是说,我非常鼓励你在钻研项目时注意道德挑战。而某些数据特征可能更容易避免(如种族、性取向、年龄等)。),更微妙的要谨慎的是余弦相似度。当我说余弦相似性时,我指的是这种想法,你可以使用其他间接特征,基本上意味着与直接特征相同的想法。

例如,假设您没有一个人的性别数据,但是有一个人服用的药物种类的信息。如果一个人服用避孕药,这个人很有可能会被识别为女性,所以从技术上讲,你可以使用“处方”特征来代替直接的“性别”特征。在我看来,从伦理的角度来看,这是没有区别的,所以这些“余弦相似”的特征应该像你对待更直接的特征一样,得到同等水平的伦理判断。(我知道这有点疯狂,因为使用处方数据也是非常不道德的,但我想不出更好的例子了。😅)

如果当前项目失败,我什么时候应该放弃并开始新的努力?

显然,我们希望所有的数据科学工作进展顺利,但现实是,并非所有东西都能变成金子。即使对于开始时看起来非常有希望的努力,也有许多原因可能会破坏数据科学项目。我亲眼目睹的最微妙的情况是,虽然数据中似乎确实存在信号,但数据本身并不能产生那个信号。在那些看似有信号的情况下,实际上可能是一些未知的数据特征影响了信号,但如果你没有办法合理地捕捉到这些特征,那么很不幸项目无法向前推进

这几乎是不可避免的情况。并不是每一个数据科学项目都会发展成对你的公司有用的东西,所以我认为在一开始就知道何时“放弃”并转向下一个机会是很重要的。同样,这里没有一个“放之四海而皆准”的答案,所以当你建立这些“不要去”的指导方针时,你可以考虑以下一些事情:

  • 基于时间的阶段限制执行特定活动需要多长时间(例如,如果一个月内无法获得数据,我们将继续进行)
  • 成功度量的阈值(例如,如果我们在第一次建模尝试中不能达到 80%的准确性,我们就继续前进)
  • 确定其他数据科学项目的优先级(例如,如果我们需要为优先级更高的工作腾出时间,我们会搁置或永久搁置当前优先级较低的工作)

这篇文章到此结束!我希望这些问题对您考虑下一个数据科学项目有所帮助。你会给这个列表添加什么?我总是渴望听到其他人如何优化他们对这类项目的考虑,所以请在评论区分享你的想法。感谢阅读,下一篇帖子再见!

功能工程是最大化数据科学影响的关键的五个原因

原文:https://towardsdatascience.com/five-reasons-feature-engineering-is-key-to-maximizing-data-science-impact-97f1d88f951

评估数据产品和建立高影响力团队的框架

光子由斯蒂芬·道森送上的 Unsplash

所有组织都有数据,需要将这些数据整理成可操作的功能。在本文中,我们将回顾特性的含义,以及为什么它们是好的业务和有效的数据产品不可或缺的一部分。然后,我们将讨论为什么数据科学家最适合开发这些功能,功能工程的镜头如何量化您的数据科学项目的价值,以及如何使用这些知识来建立高影响力的数据科学团队。

所有组织都需要特性工程。

从数据管理和 EDA 到模型测试,“数据科学”这个术语的含义太多了。问题困扰着处于不同分析成熟度水平的组织。我的组织需要在哪里利用数据科学?数据科学家比分析师/预测师/数据工程师更适合在哪里工作?我如何量化数据科学项目的影响?

(图片由作者提供)

数据科学家是特性工程师。

有时数据科学家确实会对数据进行预处理,以剔除垃圾和边缘案例。在绘制问题空间时,他们可能会参与探索性数据分析。他们可以部署统计或机器学习技术来预测趋势和未来结果。有时他们会进行实验来寻找因果影响。他们甚至可能进入操作化的杂草中,部署管道和“生产”代码来服务于他们的劳动成果(但希望不会,稍后会有更多的介绍……)。但最终,这一切都是为了一个核心目标:以特征的形式为他们的客户提供可操作的、精选的数据。

特性是所有数据产品的基础。

我在这里将一个特性定义为简单的数据转换。这是一个黑匣子:数据进入,特征出现。介于两者之间的魔法是数据科学领域——管理数据并将其转化为可操作的信号,这些信号可以为业务决策提供信息、实现流程自动化、提供客户洞察并推动整个数据产品世界的发展。

(图片由作者提供)

根据这个定义,几乎所有的预处理,人口统计,人工智能/ML 和数据操作都是特征工程。因此,我们希望选择对给定用例影响最大的最直接的策略。

特征是粒状的

特性是基于数据的决策的基本组成部分。组装成一个更大的叙述的数据可以有更重要的影响(以后会有更多的讨论),用特性的语言来组织工作产品迫使可解释性成为一个小块,容易被技术领域的涉众消化。随着特性变得更加细致入微、更具预测性和规范性,分析的成熟度也在不断提高。

数据科学家最适合功能开发。

将数据转化为可操作的特性需要产品意识和技术知识的巧妙结合。有影响力的数据科学团队通过深入的领域知识、了解组织的数据资产(包括边缘案例和警告)以及掌握统计和 ML/AI 技术来实现这一点。

他们最好把时间花在测试假设和改进数据产品上。拥有自助服务平台的团队可以最大限度地减少花在流程编排和扩展上的时间,从而有更多时间改进产品。他们还通过拥有数据科学家的作品来提升他们的地位。用 Jeff Magnuson1 的话说,“工程师不应该写 ETL。”

你可以绑定特性的值。

在使用特性语言设计产品问题时,我们可以通过直接询问利益相关者、客户或潜在用户来预测项目的价值——了解这些信息的价值是什么?通过考虑信息如何影响收入、留存、终身价值或服务客户的成本,数据科学家可以帮助利益相关者用美元来表达该价值。

例如,考虑一个假设的场景,其中一个企业想要运行一个赢回计划,当客户做出积极反应时,该计划获得净回报,但当客户做出消极反应时,该计划没有额外收入(实际上执行该计划需要成本)。在某些情况下,如果期望值为负,企业可能不会选择联系任何流失的客户,这取决于成功率。然而,如果他们能够在选择有资格的客户之前预测哪些客户会积极响应,投资回报是有保证的!在这种情况下,客户做出积极响应的可能性的值将是没有该信息的策略(根本不部署该程序,或零)与他们拥有完美信息(仅将该信息部署给做出积极响应的客户)之间的差异。

这就是所谓的千里眼 (VOC)的值。它是一个有价值的工具,用于在规划甚至构思阶段阐明和界定数据产品的影响,而无需运行一个单独的实验。虽然您可能不总是能够以完美的准确度或精确度交付这些信息,但是估计 VOC 意味着团队可以计算描述性特征的值,或者概率特征的期望值(EV)(例如,流失的可能性)。

结论

功能是分析团队的支柱——所有组织都使用精选数据来推动业务决策或交付用户价值。从功能的角度思考数据科学和数据产品开发可以帮助您:

  • 根据功能描述数据产品的影响。这个框架可以:
    ——通过离散的特征描述增加数据产品的可解释性和可解释性。
    –利用洞察力的价值来量化项目的影响。
  • 为数据科学家提供他们开发的功能的端到端所有权。拥有深厚领域知识的数据科学家可以拥有其数据产品的最终影响,并负责通过持续改进(例如,通过实验)来维护性能。
  • 通过水平平台支持端到端的功能所有权,使数据科学家能够专注于功能工程科学。

我发现这个框架有助于建立有影响力的数据科学团队和规划新的数据产品,我希望你也一样。

[1] J .马格努松,工程师不该写 ETL (2016),https://threaded . stitchfix . com/blog/2016/03/16/Engineers-should nt-Write-ETL/

你应该(尝试)对 Tableau 的超市数据集样本感兴趣的五个原因

原文:https://towardsdatascience.com/five-reasons-why-you-should-try-to-be-interested-in-tableaus-sample-superstore-dataset-4b529ed6c7ee

建立有效且高效的学习计划

智利圣地亚哥的酒吧。图片由作者提供。

如果您花时间学习 Tableau,您会看到对样本超市数据集的多个引用。这个数据集与 Tableau 一起打包,包含一个虚构企业的产品、销售和利润信息。我是一名应用社会科学家,所以我对这个内容领域不感兴趣。我记得每次听到“销售总额”这个词时,我都会退缩。

也许晚于我应该意识到的,我对这个数据集的厌恶不利于我的技能发展。花时间去理解超级商店的数据集样本会带来即时和持续的学习收获。在我教授的数据可视化课程中,我也提升了数据集的重要性。由于我的学生也是应用社会科学专业的学生,他们中的许多人都表达了和我在学习初期一样的厌恶。

本文旨在帮助新手思考这个数据集的价值。我解释了为什么您应该学习和使用这个数据集,即使内容区域与您的兴趣不一致。

1.您可以更好地理解 Tableau 文档

为了有效地使用复杂的软件,你需要理解文档。我认为 Tableau 的文档非常好。大多数例子和解释都是建立在样本超市数据集上的。因此,如果你对数据集没有理解,你将很难使用这些文档。

2.你可以从 DataFam 中找到更多的学习例子

Tableau 社区通常被称为 DataFam ,提供基于样本超市数据集的学习活动和示例。基本原理很简单:每个人都可以访问这个数据集,允许学习者复制演示的任何内容。数据集包含所有类型的数据,因此几乎可以创建任何图表。

3.你可以远离杂草

对特定内容领域的深入理解使您能够仔细和批判性地思考数据的各种细微差别。与此同时,进入细微之处——你可以很快很容易地进入杂草中。有时,知识的深度会干扰对特定技术或程序的学习,因为你关注的是错误的东西。

我试图向我的学生解释,使用样本超市数据集就像在学习一项运动时进行核心训练一样。如果你正在学习像网球或羽毛球这样的球拍类运动,你必须发展基本的击球。特定的训练和练习可以帮助你比通过实际的游戏学习更快地建立基本的笔画。我认为这同样适用于构建数据技能。

4.您可以在不共享敏感数据的情况下获得技术支持

我的研究涉及处理我不能分享的敏感数据,这是人类研究的典型特征。因此,寻求帮助的通常方法是创建一个小的虚拟数据集,该数据集以我试图解决的问题为模型。因为示例超市数据集包含几乎所有类型的数据,所以我可以经常使用这个数据集来建模我的问题,从而节省大量时间。我不必花费不必要的时间来创建新的数据集。更广泛的 DataFam 也熟悉这个数据集,使其他人能够将时间集中在解决方案上,而不是理解一个新的数据集。

5.你可以在外面建立你的实质性思维

再说一次,我是一名应用社会科学家,我并不觉得分析桌子、椅子、复印机和信封等物品的销售很有趣。而且我当然也不指望别人对我的学习领域感兴趣。然而,请记住,有趣的事情和有用的事情之间存在着显著的差异。在我与非营利组织的数据咨询工作中,学习使用我领域之外的数据被证明是有益的,在那里我经常接触到许多新问题。新学员可以使用样本超市数据集来扩展他们的实质性思维。

局限性和实际的后续步骤

虽然我提倡新的学习者花时间使用样本超市数据集,但我认为强调一些限制对于建立整体学习策略是必不可少的。超级商店的样本数据集非常干净,这对其预期目的很重要。同时,这也是一个限制,因为它不能反映你在现实世界中可能遇到的情况。几乎专门使用样本超市数据集的新学习者将很快面临一个不可避免的现实。当处理真实世界的数据时,开发数据可视化需要花费大量的时间来准备数据。事实上,在我的大部分项目中,这是我花费大部分资源的地方。

虽然我是数据可视化的粉丝,但我强烈建议新学习者积极地、持续地培养数据准备技能,作为他们整体学习计划的一部分。在数据密集型产品中,大部分时间和资源通常用于数据准备。参与数据准备有助于加深对数据的理解,这对于创建引人注目的可视化效果至关重要。Tableau 的新学习者应该花时间熟悉一个相对较新的工具 Tableau Prep,它已经成为我的数据科学工具箱中的一个重要工具。新学员可以使用 Tableau Prep 准备其他数据源,使他们能够应用通过使用示例超市数据获得的技能。

破解数据科学面试的五个技巧

原文:https://towardsdatascience.com/five-skills-to-crack-data-science-interviews-b1ec2d73dedb

什么是强有力的候选人?

JESHOOTS.COMUnsplash 上拍照

W 我们应该在数据科学面试中寻找什么?无论是招聘实习生还是高级职员,我都希望建立一个由优秀的批判性思考者组成的团队,他们能够独立开发数据产品,并与同事建立良好的关系。在这里,我将讨论我在各个级别的数据科学家面试中寻找的品质,以及我在第一次会见候选人时如何衡量这些品质。

在我们开始之前,先声明一下——这些品质对于建立一个全才团队至关重要。这部分反映了我的偏好。我相信,大量的环境转换和跨学科思考的机会会让工作更令人满意,并产生更多新颖的解决方案。

这也是我们的团队和业务所需要的。随着我们的组织成长为一家数据产品公司,数据科学发展的机会空间也在增长。许多新问题——关于产品、架构、技术方法和流程——需要解决,我们需要能够轻松适应和应用于新问题空间的通才。你的成就可能会因你的组织和团队的需求而异,但我发现这五个品质对候选人在我们的环境中取得成功至关重要。

对角色的兴趣

虽然这听起来很老套,但对这个角色的强烈兴趣可能是长期满意度的主要指标之一。我们赋予我们的数据科学家更大的自由,让他们能够加速和追求探索性项目,这需要对产品和用户体验有强烈的好奇心和想象力。对问题空间表现出真正兴趣的候选人更有可能对工作感到满意,并带来新的创造性想法。

我们的团队还参与数据产品的上游研究和开发,这可能需要很长时间才能见天日。在一个开发新颖的、受监管的数据产品的组织中,耐心有助于完成项目。在此类项目中,当通往成功的道路漫长时,对任务的真正兴趣可能是一种能量源泉。

数据科学熟练程度

不出所料,我们确实关心候选人的数据科学资格。然而,作为一个多面手团队,我们很少为特定的项目或专业进行招聘,并且对使用特定工具、技术或技巧的经验不太感兴趣。我从成功扮演该角色所需的基础知识中寻找安慰——表格操作、假设检验等。—但我最感兴趣的是候选人如何处理的新问题。

在互动和协作面试中,我更喜欢通过案例研究来探究候选人解决问题的方法。在探索性数据分析过程中,他们的第一直觉是什么?他们如何免除边缘案例?他们选择一个模型架构而不是另一个的理由是什么?在他们的回答中,我寻找清晰的、结构化的思维。候选人应该能够使他们的设计决策合理化,展示他们对方法中的任何权衡的自我意识,并假设他们将如何减轻任何弱点(通过额外的数据、资源、实验等)。).

产品感

我们的团队是一个中央数据科学职能部门,支持没有分析资源的业务部门,并将数据科学家嵌入产品团队,以研究和开发新功能。通常作为跨职能团队的数据专家,成功的角色需要的不仅仅是技术掌握和领域知识。我们的科学家需要解释如何利用组织的数据来满足客户需求,无论这些客户是我们的产品用户还是内部业务利益相关者。

与数据科学熟练程度一样,我更喜欢案例研究,通过假设的用例来评估候选人是如何思考的。这些练习的主要目标是清楚地阐明一种结构化的方法,以确定利益相关者的需求,提出技术解决方案,并评估成功或影响。他们是如何定义指标的?对最终用户有什么影响?他们是否将我们的数据产品定位为整体用户体验的一个组成部分?最强的候选人突出了用户体验和数据产品对其技术性能的整体影响。

照片由大卫·特拉维斯Unsplash 上拍摄

沟通能力

因为我们的数据科学家通常是跨职能团队中所有数据相关查询的主要联络人,所以我们通常扮演数据解释者的角色。我们的团队负责将用户需求转化为数据产品需求,与技术利益相关方和同行一起开发和评审这些产品,并将结果转化为报告或演示给业务利益相关方。

在我们的面试过程中,与技术和非技术利益相关者清晰有效地沟通的能力是一项关键技能和关键标准。虽然我们在技术面试中不采用任何带回家的部分,但我要求候选人向一个跨职能小组做一个简短的陈述。听候选人介绍他们的工作,可以让其他数据科学家和商业人士评估候选人如何向不同的受众演讲,如何使用数据可视化,以及如何用数据讲述故事。

协作方法

我们的数据科学家不在筒仓中工作;见证他们的沟通方式与理解某人如何与其他人一起工作一样重要。我们是一个多元化公司中的全球分布团队,我们重视沟通和协作中的包容性。我们在招聘过程中反映了这种文化,我们寻找渴望在注重公平的文化中合作的人。

协作体验包括与其他数据科学家和技术贡献者以及业务利益相关者或产品团队的合作。我想了解候选人在共享代码库、结对编程和执行技术评审方面的经验,以及他们召集利益相关者、明确角色和责任以及管理冲突解决的方法。职位越高,我赋予这些技能的权重就越大。我们的资深科学家花更多时间担任技术项目负责人,负责管理跨职能团队,在这些团队中,良好的协作和影响能力至关重要。

最后的想法

斯科特·格雷厄姆Unsplash 上拍照

我们流程中我没有花太多时间讨论的一个方面是技术筛选。在我们的面试漏斗早期,我们让候选人参与表格操作练习,但我不喜欢花费太多精力搜索特定的工具包或技术背景。作为一个多面手团队,我认为应该让数据科学家有足够的时间进行文献综述、学习新技能和测试新技术。无论你的背景如何,如果你具备上述素质,一旦你加入了团队,我们会抽出时间来适应任何相关的工具或技术。为团队提供成功所需的工具和时间是我管理哲学的核心原则,也是我们面试过程的一部分。

这些指导原则指导我完善我们的面试流程,并建立一个高影响力的数据科学功能。我希望这份指南能帮助你为下一次面试做好准备。

其中表达的观点是我的个人观点,不代表任何组织、其附属机构或员工的观点。

每个数据科学项目的五个阶段

原文:https://towardsdatascience.com/five-stages-of-every-data-science-project-8a62885e46de

为什么它们对成功发布如此重要

SpaceX 在 Unsplash 上拍摄的

数据科学工作流往往发生在广泛的领域和专业领域,如生物学、地理学、金融或商业等。这意味着数据科学项目可能面临非常不同的挑战和关注点,导致使用非常不同的方法和数据集。然而,这并不意味着这些项目没有一个共同的工作流程。在大多数情况下,数据科学项目必须经历五个关键阶段:定义问题、数据处理、建模、评估和部署。在这里,我将介绍每个阶段通常包含的内容,以及为什么它们对于成功的数据科学项目非常重要。

定义问题

任何数据科学项目的第一阶段都是识别和定义要解决的问题。没有一个明确定义的要解决的问题,就很难知道如何解决这个问题。对于数据科学项目,这可以包括使用什么方法,例如分类、回归或聚类。此外,如果没有一个明确定义的问题,就很难确定你衡量成功的标准是什么。没有一个成功的定义,你永远不知道你的项目什么时候完成或者好到可以用于生产。这可能导致资源和时间的浪费,例如当简单的逻辑回归而不是复杂的分类神经网络达到目标时。

这方面的一个挑战是能够定义一个足够小的问题,使其能够单独解决/处理。在许多情况下,一个问题或任务可以被分解成许多更小的问题,这些问题一旦被定义,就更容易解决和度量。这方面的一个例子是一家不盈利的制造企业。试图将此作为一个数据科学项目整体来解决可能非常困难。实际上,这可以分解为一些更小的问题,比如传送带太慢,意味着产品组装不够快,关键部件的延迟会减缓某些建设阶段的速度,或者工人调度效率低下,导致工厂工人太少。这些问题中的每一个都可以在他们自己的数据科学项目中得到解决,因为他们可以被清楚地定义、衡量,并对成功有一个清晰的定义。如果你没有正确地定义一个小问题,那么你就不能正确地解决它。

一旦你有一个需要解决的业务问题,你就可以确定你将如何着手解决它,以及如何定义成功。这一点很重要,如果你没有规划出一个潜在的路线图或衡量成功的标准就直接投入到一个问题中,你经常会得到一个解决方案,这个解决方案不能解决你原来的问题,甚至会产生一个更大的问题。这可能是因为在过度设计的解决方案上浪费了时间和资源,或者对成功的不正确衡量导致了更多的问题。在上述制造工厂的情况下,这只会导致成本增加,甚至利润下降!一个不成功的数据科学项目。

数据处理

一旦你有了自己的问题,你将如何衡量成功,以及你将使用的方法的想法,你就可以着手执行所有重要的数据处理任务。这通常是任何数据科学项目中耗时最长的阶段,通常也是最重要的阶段。这是因为,不管你的最终解决方案有多复杂或多花哨,如果你把垃圾放进去了,你就会把垃圾弄出来。这意味着你必须确保你有所有你需要的数据,数据是准确的,并且你有正确的格式,无论你建议使用什么方法。

根据你要解决的问题,在这个阶段需要完成各种各样的任务。首先是经常寻找创建或捕获尚不存在的数据的方法。这可以包括找到测量传送带通过制造工厂的速度的方法,以及它在哪个阶段停止和多长时间来识别堵塞,或者基于腕部的健康设备的速度和/或方向来识别何时有人跌倒。许多数据科学项目将不得不应对这一挑战,因为尽管有许多、许多、许多数据源,有时您需要找到一种创新的解决方案来创建和收集更多的数据。

一旦创建了这些数据,您就需要在某个地方以对您的模型有用的格式收集这些数据。这将取决于你将在建模阶段使用什么方法,但这将涉及到你如何将数据输入到你的模型中。这将决定您是使用流式处理还是批处理,以及将采用何种格式。在图像分类的情况下,通常需要将图像以某种形式的数组输入到模型中,而在回归中,您将需要 Y 列和 X 列。

最后一部分是执行任何预处理步骤,以确保数据足够干净,建模方法可以工作。这可能涉及删除异常值或选择保留异常值、处理空值、空值是否是一个度量值或是否应该归入平均值,或者标准化度量值。在这个阶段,可视化数据通常是关键,这样你就能很好地理解你正在处理的数据,并能初步暗示你的模型的结果可能是什么。这样你就可以检查你的最终结果,看看是否有什么地方出错了。然后,您可以创建一个从数据到模型的管道!

照片由 Helio DilolwaUnsplash 拍摄

系统模型化

下一个部分,通常也是最有趣和最激动人心的部分,是数据科学项目的建模阶段。这将采取的形式将主要取决于问题是什么以及你在第一步中如何定义成功,其次取决于你如何处理数据。不幸的是,这通常是任何数据科学项目中花费时间最少的部分,尤其是当有许多框架或库存在时,如 sklearn、statsmodels、tensorflow,并且可以随时使用。

在“定义问题”阶段,您应该已经选择了将用于数据建模的方法,这可能包括简单的图形探索、回归、分类或聚类。在任何情况下,这里的大部分工作只是挑选最相关的方法,确保数据是该方法的正确格式,然后训练模型。在聚类的情况下,这可能涉及 K-均值、d b-扫描或分层聚类的使用,而在回归的情况下,这可能涉及线性回归、随机森林和神经网络。

在这个阶段使用多个模型通常更好,以便在进行评估时能够对它们进行比较和对比。这是因为虽然你可能认为你知道最佳解决方案,但当涉及到数据科学项目时,最佳方法可能会让你感到惊讶,你需要理解为什么。在这一点上,您需要具备选择应该应用的正确模型的技能和知识,尤其是在资源有限的情况下,但是除非您正在使用最前沿的模型或创建自己的模型,否则您通常不会冒险远离现有的框架。

估价

一旦你创建并实现了你的模型,你就需要知道如何评估它。同样,这又回到了问题形成阶段,在这个阶段你已经定义了成功的衡量标准,但这通常是最重要的阶段之一。如果您没有正确地评估您的模型,那么您可能最终得到一个实际上无用的模型,您不知道它是否如预期的那样执行。

根据您处理数据和设置模型的方式,您可能拥有可用于评估模型的维持数据集或测试数据集。在该数据集上,您的目标是查看模型在准确性和可靠性方面的表现。第一个是你的模型在你设定的目标方面表现如何,而第二个是你的模型能实现或不能实现这个目标的一致性。在这种情况下,过度训练的模型可能在训练中表现良好,但在测试和评估中表现不佳,而训练不足的模型可能在这两方面都表现不佳。你的目标是拥有一个既准确又可靠的模型。

您用来评估模型的度量标准将取决于它的用途。在某些情况下,您可能希望纯粹关注模型的准确性,这可以通过 R、准确性、AUC 或 F1 得分等指标来衡量。在其他情况下,您可能希望用准确性来换取其他一些功能,例如降低 1 类或 2 类错误的概率。例如,在假阳性的成本很高的情况下,比如在医学中,你会愿意有高水平的 2 型错误,而不是 1 型错误。在其他部门,假阴性的成本很高,那么你会愿意接受一些 1 型错误,如果它能减少你的 2 型错误。在某些情况下,评估指标甚至不一定是数字,而是您是否能够提取您想要的结果或看到您想要的模式。如果这个模型不符合你的成功标准,你可能需要回到第一阶段,重新开始。

部署

最后,一旦您对您的模型进行了可靠的评估,并且对结果感到满意,那么您就可以将它部署到生产中了。这可能意味着各种事情,例如您是否使用来自模型的洞察力来对您的业务进行更改,您是否使用您的模型来检查所做的更改是否成功,或者模型是否部署在某个地方以持续接收和评估实时数据。

然而,部署通常不是项目旅程的终点,因为您需要持续关注您的模型是如何执行的。这是因为您输入到模型中的实时数据可能与您使用的训练或评估数据不同,或者如果模型在不断学习,那么它的行为可能会发生变化。这可能是由于随着时间的推移行为的微妙变化,比如对你的模型的反应,或者突然发生的行为的戏剧性转变。前者可能发生在当人们看到你项目的成果,从而如你所愿改变他们的行为时,或者是因为你正在看的主题变老了,发生了变化。后者可能发生在重大事件发生时,如疫情和戴面具。在这些情况下,您需要持续监控结果是否有任何偏差或变化,并且您可能需要根据新数据重新训练您的模型,或者甚至完全更改模型,以确保它仍然能够解决最初的业务问题。

结论

虽然每个数据科学项目在其关注点或主题方面可能有所不同,但有一些阶段与大多数数据科学项目是一致的。这包括:定义问题、数据处理、建模、评估和部署到生产中。如果一个项目缺少这些步骤中的一个,这通常意味着你可能会得出错误的结论,或者它可能设计得不好。但情况并非总是如此,因为这一规则也有例外。这也不意味着这些阶段必然以线性方式进行。事实上,每个阶段都可以相互补充,你可以在各个阶段之间来回移动,尤其是当项目并不总是按预期进行的时候。下图通常可以表示这些不同阶段有时是如何相互作用的

作者图片

虽然这不是显示所有交互的详尽图表,但它通常是一些最常见的交互。

记住这一点,在处理数据科学问题时保持灵活性并了解不同阶段如何相互作用是非常重要的。一个数据科学项目永远不会与另一个完全相同,但所有项目都应该有某种形式的可识别元素。祝你好运!

如果你喜欢你所读的,并且还不是 medium 会员,请使用下面我的推荐链接注册 Medium,来支持我和这个平台上其他了不起的作家!提前感谢。

https://philip-wilkinson.medium.com/membership

或者随意查看我在 Medium 上的其他文章:

将数据科学引入贵公司的五大策略

原文:https://towardsdatascience.com/five-strategies-for-introducing-data-science-to-your-company-faae8b6404af

将数据科学引入贵公司的五大策略

帮助您让领导兴奋地将数据科学引入您的公司的提示

毫无疑问,数据科学行业在过去十年里取得了长足的进步,但你可能会惊讶地发现,如今的现有公司仍有很大的增长潜力。也许其中一个很大的原因是我们一直面临着合格人才的短缺,但我认为另一个原因是非从业者并不真正理解数据科学和人工智能可以带来的价值。他们听到“人工智能”或“机器学习”这些词,就会联想到好莱坞的刻板印象,比如《2001 年 T2:太空漫游》中的哈尔或《T4》中的天网。

当然,数据科学从业者认识到,那些好莱坞人工智能代表了人工通用智能(AGI)的虚构潜力,但这个空间远不止一台会说话的计算机。从适用于结构化数据的随机森林分类器,到适用于文本或图像等非结构化数据的深度学习,数据科学家可以通过许多不同的方式带来价值。

因此,最大的问题是,我们如何才能激起人们对数据科学的热情,将其引入我们自己的公司?

这就是这篇文章中的策略将帮助你做到的。现在,在我们进入这些策略之前,请允许我给你一个你会在这里看到的主题:我们在这里基本上谈论的是营销。就像电视节目 Shark Tank 中的企业家一样,我们在这里讨论的是创建一个战略,来推销你在数据科学价值方面的领导力。反过来,我们需要牢记任何企业领导者的优先事项,包括以下内容:

  • 领导者通常被提出许多增长的想法,但能力(如预算)有限,无法全部执行,因此他们必须选择几个,放弃其他的。
  • 对于前一点,领导者在短时间内有很多事情要做,所以他们不会记住他们听到的所有事情是很自然的。
  • 有时领导也向其他高级领导汇报,所以他们也必须向他们的高级领导推销同样的想法。

好了,记住这些事情,让我们进入我们的策略!

1.让你的交流尽可能简单。

我将这一策略放在列表的首位,因为数据科学社区非常容易以技术术语的形式掉进这个非常复杂的语言的兔子洞。记住,你需要在与领导见面。这并不是说一个领导者无知或无能。相反,这是一种认识,即我们都说不同的“语言”,我相信这些领导者可以围绕数据科学从业者谈论非数据科学主题。也就是说,除非被直接问到,否则我会尽量避免所有的数学或统计学话题。还要记住,许多人对数学和统计学没有美好的记忆。谈论微积分的导数如何在梯度下降中发挥作用,可能会让你的领导想起那些可怕的高中时光,当时 Cluver 先生分发了可怕的统计测试。

(有趣的事实:我的高中数学老师的名字实际上是 Cluver 先生,我可以在我的小笑话中使用他,因为他实际上是一个了不起的数学老师。我一直没有机会感谢他对我的耐心,所以,如果你读到这封信,谢谢你!😃)

除了技术术语,保持你的沟通简单也意味着保持简洁。我们将在后面的文章中对此进行更多的讨论,但是请记住,与你的领导的最初交流应该尽可能简短和甜蜜。再回想一下的鲨鱼池,回想一下,提出想法的企业家只有很短的时间。同样,您应该以类似的方式设计您的数据科学策略。

2.尽可能用数字开头。

在这个列表的所有策略中,这个是最“言归正传”的策略。让领导者对数据科学感到兴奋是一回事,但如果你不能以某种方式切实展示数据科学的潜在价值,你的努力可能会徒劳无功。此外,数字本身也能向你的领导传达很多信息。例如,如果你可以切实证明数据科学可以帮助你的公司每年节省 10 亿美元,你的领导绝对会有兴趣听到更多,即使他们可能不太关心这个话题。

当然,您可以分享许多不同类型的数字,因此让我们列出一些潜在的数字,供您在制定数据科学战略时考虑:

  • 如果你能证实一个积极的投资回报率(ROI ),投资回报率(ROI)总是一个领导者喜欢听到的话。显而易见,ROI 可能是一个难以证实的数字,但一种可能性是下一个项目符号…
  • 在大公司中,一个部门已经在数据科学领域取得成功的情况并不少见,因此,如果您向其他部门的领导推销将数据科学引入他们领域的想法,展示这些其他数据科学领域的成果肯定会有所帮助。成本节约、流程改进和更快的上市时间是人们喜欢听到的事情,特别是如果已经在同行中得到证明的话。
  • 虽然没有人喜欢谈论预算,但你应该准备好谈论将数据科学引入该领域的潜在成本。具体来说,这意味着谈论数据科学家的数量以及与这些数据科学家相关的薪酬。我个人不会以此信息开头,但我们将在最终战略中详细讨论,无论如何,您都需要准备好讨论这些数字。

3.展示一些不同的、令人兴奋的项目,激发人们对数据科学的普遍兴趣。

正如我在简介中提到的,我认为数据科学今天没有被更广泛采用的最大原因之一是因为领导者在试图寻找将数据科学应用于其领域的具体机会时没有一个参考框架。他们的参照系仅限于前面提到的好莱坞人工智能,老实说,我个人很难想象像 HAL 这样的 AGI 对大多数公司有什么价值。

也就是说,你需要向他们展示数据科学在多个领域取得成功的例子。(如果您能找到与贵公司利益直接相关的例子,则加分!)以下是一些你可以收集并展示的项目初步想法:

  • 一个基本的监督学习模型,展示如何从结构化数据中提取价值
  • 一个基本的无监督学习模型,向您展示如何从结构化数据中提取价值,即使您头脑中没有特定的目标
  • 时间序列模型的一个例子,特别是如果您的公司处理大量时间序列数据,如财务分析
  • 自然语言处理(NLP)的一个或多个不同应用,如语义分析、主题建模或信息提取
  • 计算机视觉的一种或多种不同应用,如从图像中提取文本、图像分类,甚至无人驾驶汽车

4.可视化,可视化,可视化。

我最近在 YouTube 上看到的最好的视频之一是 TensorFlow 制作的 2:25 剪辑,讲述了他们的技术给非洲人民带来的价值。(链接)视频在多个层面上发挥作用。很明显,他们在宣传他们的技术的价值,但真正的基础是他们如何帮助这些非洲人种植作物的超级酷的项目。在对非洲当地人的采访、农作物的拍摄和温暖人心的音乐之间,他们讲述了这个非常精彩的故事,让任何人都觉得 TensorFlow 正在帮助这些人。

现在,我并不是说你应该加入专业的视频团队(虽然如果你可以的话肯定会很棒),但是你仍然有办法以视觉形式展示数据科学的价值。在做类似 PowerPoint 演示的东西时,试着更多地使用视觉效果而不是项目符号列表。我并不是说完全没有任何文本,但是当一个领导者忙于考虑他们一天中还有什么要做的时候,大量的文本会让他立刻失去兴趣。

(我很幸运在大学里上了这门新的展示设计实验课。我们在那堂课中提到的一些优秀书籍包括加尔·雷诺兹的《T4》Presentation Zen和南希·杜阿尔特的《共鸣》 。)

如果你成功地让你的领导对数据科学的前景感到兴奋,他们可能会想知道更多,这将很好地引导我们的最终战略…

5.准备好向你的领导提供他们想要的信息。

我们在列出策略时首先注意到,至少在开始时,你希望你的沟通尽可能简单。一旦你成功地诱使你的领导想知道更多,他们很可能会想知道更多信息。我们最后的鲨鱼池回忆,注意到“鲨鱼”立即开始要求企业家提供关于任何给定主题的越来越多的细节并不罕见。此外,众所周知,“鲨鱼”会狠狠打击那些不知道这些小细节的企业家。

诚然,并不总是会有一个领导者立即开始询问那些基本细节,但正如童子军教我的那样,无论如何你都需要“做好准备”。“准备好”可以指几件事。很明显,你会想知道所有这些细节,就像你的手背一样,但就有形资产而言,我会建议你把放在一个由分割部分组成的投资组合中,这样你就可以给你的领导他们想要的多或少。例如,如果他们只想要美元储蓄,就给他们这些美元储蓄。或者,如果他们想要全部的东西,你应该准备好给他们全部的投资组合。

同样,你不希望一开始就先发制人地放弃一切。回到我们的第一个策略,我们从简单和容易开始,随着我们继续进行对话,我们准备向我们的领导层提供他们可能想要的尽可能多的信息!

这篇文章到此结束!希望这些策略对你们有用!你还有什么要补充的吗?我一直希望自己继续成长,所以如果你发现了什么特别有价值的东西,请在评论区与我们分享。我知道我喜欢看那些东西!感谢大家的阅读,下期帖子再见!

撰写优秀数据科学论文的五个技巧

原文:https://towardsdatascience.com/five-tips-for-writing-a-great-data-science-thesis-37e0f38f7880

为你的读者写作,而不是为你自己

一篇好的论文总是聚焦于读者。了解哪些原则是必要的。绿色变色龙Unsplash 上的照片

在这篇文章中,我将分享一些关于如何提高你的数据科学论文的技巧。多年来,我指导过我参与的数据科学论文项目,从四大公司到本地中小企业,从跨国银行到软件咨询公司。我正在进行的学术项目通常涉及实习,在实习中,数据被用来解决公司问题,比如设计决策支持仪表板,用机器学习算法检测金融异常,或者改善实时包裹路线。尽管教育计划、惯例和论文要求千差万别,但我希望为目前正在撰写数据科学论文的学生提供一些通用指南。

这篇文章提供了五个指导点,但可以有效地总结为一行:

“为你的读者写作,而不是为你自己。”

数据科学是一个复杂的领域,无数的算法、性能指标和数据结构即使对于最有经验的老手来说也很难完全掌握。因此,作为一名作家,你的工作就是尽可能地帮助读者消化你的研究,尽可能地引导和澄清。每个人都可以把事情变得更复杂,但是简化它们才是对你技能的真正考验。

一.导言、内容、结论

介绍

总是以概述读者所期待的内容为开头。关键是要让这个介绍具体。不要只是提到你将进行文献综述、收集数据、模拟问题——预示你将研究什么、收集什么数据、模拟什么结构或决策。学术写作与延长情节的激动人心的小说相反——学术读者不喜欢惊喜。恰当的介绍提供了一个框架,帮助读者组织接下来的内容。

内容

内容将占据页面的大部分,因此必须清晰地组织它。事先想好你想要传达的信息。首先写出文本的“框架”通常是有帮助的(例如,章节主题、章节目的、每段的要点)。验证信息是否有逻辑顺序,并形成一个连贯的故事。试着限制自己每段一个关键信息。没有预定义的结构和深思熟虑的信息,内容很快就会陷入公式、数据结构和实验结果的混乱中。

结论

总是用一个清晰的结论或总结来结束你的文章(无论是论文、一章,甚至是一个段落)。你的听众可能不会阅读、记住或理解你记下的每一个细节。当然,结论的长度应该与得出的结论相称。对于论文本身,你看到的是一整章,对于一个段落,一行结束语就足够了。以强闭包结尾对你的论文至关重要。

二。重述、解释、说明

真正阅读和理解一篇论文是一项艰巨的工作;读者需要他们能得到的所有帮助。作为一名作家,你可能会在几周内完全沉浸在这个主题中,但对于你的读者来说,这篇论文可能是许多要浏览的文件之一。事实上,很少有人会从头到尾阅读你的作品。因此,作为一名作家,有责任尽可能多地帮助读者。回顾一下你 10 页前提到的ω_t。解释 0.7 AUC 的实际含义。解释你为什么进行 t 检验。不要假设读者会知道如何把这些碎片组合在一起——通过你的研究积极引导你的读者。

论文的主要目的是解释和诠释你的研究。仅仅展示结果是不够的。[图片由马克·谢弗Unsplash 上拍摄]

三。选择适合问题的解决方案

在选择要使用的技术时,问题应该始终是首要的。探索本周的机器学习宣传很有吸引力,但很可能它根本不是这项工作的最佳工具。首先(I)研究你的问题设置,(ii)定义合适的研究问题,(iii)设定要求和限制,(iv)分析你的数据集,以及(v)确定成功的标准。只有当所有这些都完成后,你才能真正对适当的解决方法做出明智的决定。平心而论,论文项目通常会比通常遇到的项目提供更多的探索空间——毕竟,有时公司只是想探索一些新事物是否可行。尽管如此,总是让问题驱动解决方法,而不是相反。

四。开始广泛,结束广泛

数据科学论文倾向于深入到微小的方面,例如,微调超参数或运行大量实验。就其本身而言,这很好。然而,如果你试图解决的问题从一开始就不清楚(问题陈述,上下文分析),或者你的实验结果从来没有联系到最初的研究动机(结论,建议),你就错过了对你的研究产生有意义影响的机会。以下结构可能有助于翻译您的作品:

  • 沙漏模型:从企业/社会问题层面开始,逐渐放大到技术层面,然后将结果转化为管理见解。
  • 双菱形 型号:在研究阶段和设计阶段交替发散和收敛思维。刻意安排探索时间和专注时间。

你的大部分工作可能是在内容层面(数据收集-和清理,建模,参数调整,实验)。然而,不要忘记首先勾画场景,并以引人注目的最后一幕结束。

动词 (verb 的缩写)定义您的关键指标

试着找出决定你研究成功的关键指标。对于平衡的观点,通常需要报告多个指标(精度、召回、AUC、F1 分数等)。).然而,您应该避免仅仅提供一系列缺乏全面解释的指标。你的欺诈检测模型有 98.3%的准确率,这听起来很棒,但它的实际可用性却很少。您的结果表包含数百个指标,令人印象深刻,但是您能用一句话抓住它的关键信息吗?与基本模型相比,更高的精度和更低的召回率是一种改进吗?从各个角度积极探索你的结果,但确保为你的 Twitter 总结提炼出关键结果

虽然数据分析通常是多方面的,但突出关键指标对于理解您的主要观点非常重要。[图片由卢克·切瑟Unsplash 上拍摄]

包扎

本文讨论了帮助撰写数据科学论文的五个技巧。最重要的原则是始终把你的读者放在心上,并为目标受众积极地组织、解释和诠释你的研究。这五条建议可以总结如下:

  • 介绍、内容、结论 —在你的文章的各个层次(论文、章节、段落)使用一致的结构,以介绍性的大纲或信号开头,以结论或总结结尾。
  • 重述、解释、说明 —一篇成功的论文会引导读者完成你的研究,提供有用的解释来支持你的技术和结果。
  • 选择适合问题的解决方案 —在选择适合任务性质的解决方法之前,确保彻底研究问题、背景和期望。
  • 广泛地开始,广泛地结束 —深入是非常好的,但是不要忘记(I)清楚地概述问题的背景,以及(ii)将你的主要发现转化为切实的见解。
  • 定义你的关键指标——一个单一的指标很少足以捕捉一项分析的全部深度,但最终有必要将你的研究归结为一些易于理解的数字。

提升 Python 代码可读性的五个技巧

原文:https://towardsdatascience.com/five-tips-to-elevate-the-readability-of-your-python-code-7b049bbf72e6

现在就将这些应用到您的 Python 代码中,以提高可读性——您的同事和未来的自己都会感谢您的!

图片:不飞溅

TL;博士

1.使用自动格式化程序,如blackisort

2.使用代码检查器,如flake8pylint

3.添加类型提示以消除函数参数中的模糊性

4.用pre-commit自动化代码质量检查

5.使用 Python 文档字符串编写好的文档

我以前从不太担心代码的可读性。

很明显,我在乎我能在开发过程中阅读它。但是我从来没有过多考虑过 6 个月后我是否还能读它。

“当然,我知道那个变量包含什么类型的数据,以及那个函数负责什么——这是显而易见的,对吧!?"

给过去的自己的提示:你会忘记🤦‍♂️.

与软件工程师不同,作为数据科学家,我们倾向于独立地编写代码。

是的,我们在团队中工作,但是你和其他人在同一个代码文件中工作是不寻常的。通常情况下,您将对项目的特定部分(例如,您的个人笔记本)承担个人责任和所有权,而不是由多个团队成员共同承担责任。

这往往会导致项目代码库的代码格式不一致,其他人不太关注可读性和可维护性。

自职业生涯开始以来,我主要是在“绿地”项目中担任咨询角色。这些项目通常包括编写只有我负责的新代码。仅仅过了几年,我才着手我的第一个项目,这个项目涉及到继承的遗留代码库。

这时,我终于意识到编写可读代码的重要性。不仅仅是为了你自己,也是为了将来加入这个项目的开发人员。这也让我反思我自己在以前的项目中糟糕的编码习惯,以及我希望以前的开发人员在他们的代码中添加了什么,以节省我几个小时的时间。

幸运的是,在 Python 中,你可以应用一些简单的修饰性的改变,使你的代码变得更加可读和易于管理

在这篇文章中,我给出了五个可行的技巧,你可以在编码时应用它们来提高你的 Python 代码的可读性,而不需要重构。

让我们开始吧!

1.使用自动格式化程序来标准化代码🧹

提高代码可读性最简单的方法是使用自动代码格式化工具来标准化你的代码。

Python 中有很多工具可以帮助你格式化代码。一些最受欢迎的包括:黑色伊索特自动驾驶 8YAPF

我个人使用black进行代码格式化,使用isort对我的库导入语句进行排序。

在整个项目中定义并坚持一致的编码风格/格式会大大增加你的代码对其他开发人员的可读性。

阅读格式不好的代码就像阅读一篇语法不好的文章。你仍然可以理解这篇文章,但意思可能是模糊的或令人困惑的。

遵循 PEP8 标准的一致格式有助于开发人员在阅读代码时确定方向。他们知道从代码布局中可以期待什么,并且可以专注于重要的部分——理解代码逻辑。

标准化和自动化的代码格式也有助于版本控制。

想象一下您创建一个不遵循 PEP8 约定的新文件的场景。然后,您的同事在版本控制上签出该文件,并对的一行代码进行更改。在保存文件时,他们的 IDE 会自动格式化原始文件以符合 PEP8(例如,最大行长度 79 个字符,删除函数之间不必要的空白行,函数参数之间无关的空白等)。然后,他们将文件签回到存储库中。现在有一个问题。旧代码和更新后的代码之间会有很大的差异(“diff”),即使对底层代码逻辑只做了很小的更改。

标准化、自动化的代码格式化降低了版本控制中与被更改代码的功能无关的较大差异的风险。

如何使用代码格式化程序

您可以从命令行对您的代码运行自动代码格式化程序。或者,大多数 ide(比如 VSCode)都有设置和插件,可以在保存代码时自动格式化代码。

例如,在命令行上,您可以将黑色代码样式应用到您的代码中,并使用组织您的导入,如下所示:

*# install the black and isort libraries*
pip  install black isort*# use black command line tool to format code*
black SOURCE_FILE_OR_DIRECTORY*# organise import statements with isort*
isort SOURCE_FILE_OR_DIRECTORY

下面是我的 VSCode settings.json文件的摘录,它在 VSCode 中保存文件时自动将black格式应用于我的 Python 文件。它还按照字母顺序组织我的导入(类似于 isort 的功能),当有许多导入的库或函数时,使导入语句更易读。

# vscode settings.json
{
    **"editor.formatOnSaveMode"**:"file",
    **"editor.formatOnSave"**:**true**,
    **"python.formatting.provider"**: "black",
    **"editor.codeActionsOnSave"**: { **"source.organizeImports"**: **true**},
}

虽然建议遵循pep 8编码约定,但是并没有普遍接受的 Python 代码样式规则。您会注意到不同项目之间的差异,这是开发团队自身偏好的结果。

参与项目的所有团队成员都遵守相同的代码样式规则是至关重要的。因此,最好明确你所遵循的风格指南。

您可以在 *README.md* 中描述您的项目使用的编码风格,但是更好的是,您可以使用您的项目目录中的 配置文件 来显式定义该风格。上面提到的每个工具都可以自动读取配置文件,以确保项目中的所有开发人员都遵循相同的规则。

2.使用代码检查工具(linters)来捕捉错误并遵循最佳实践🔎

代码检查工具,通常被称为 linters ,有助于突出 Python 代码的语法和风格问题。

Linters 帮助您保持代码 PEP8 的兼容性,在运行代码之前捕捉一些错误,并警告不良行为。例如:

  • 发现未使用的变量或赋值前使用的变量
  • 识别未使用的导入库
  • 确保所有函数/类都有文档字符串
  • 警告不要使用裸异常

在某些情况下,linter 突出显示的错误并不是真正的问题。例如,pylint建议每个 Python 模块应该有一个 docstring 来描述它的用途。对于您的项目,您可能认为这是不必要的。您可以使用配置文件或行内注释来控制 linters 的行为,它们可以被设置为忽略某些错误。

同样,Python 有许多林挺工具。最常见的两种是: flake8pylint

我倾向于使用flake8作为我的预提交钩子(见下面的提示)。我还在开发过程中使用 Pylint 来帮助捕捉问题并确定可能的改进,然而,与flake8相比,它对看似良性的问题要严格得多,这使得它无法用作向您的 repo 提交新代码的看门人。

Pylint 还给你的代码打了 10 分,这很酷。尽管当你的完整工作代码最初被评为满分 10 分中的 3 分时,这会伤害你的自尊😞。

如何使用 linters 检查你的代码

如上所述,linters 可以从命令行运行,也可以内置到您的 IDE 中。

*# install flake8*
pip install flake8*# run flake8 checks*
flake8 SOURCE_FILE_OR_DIRECTORY

您可以使用settings.json文件在 VSCode 中启用林挺。例如,使用 pylint:

# vscode settings.json
{
    **"python.linting.pylintEnabled"**:**true**,
    **"python.linting.enabled"**: **true**,
}

顶端提示💡

使用nbQA库将代码检查工具扩展到您的 Jupyter 笔记本上。

如果你使用 Jupyter 笔记本,使用代码检查工具变得比简单的 *.py* 文件更重要。

本质上,在 Jupyter 笔记本上的工作是实验性的,当你测试或调试代码时,可能涉及到以不同的顺序运行单元格。因此,很容易意外地留下未赋值的变量,或者在变量被定义之前就使用它们。在将笔记本提交到 git repo 之前,也很难手动捕捉这些错误。

nbQA 针对您的笔记本运行 Python 林挺工具,以帮助防止损坏的笔记本提交到您的存储库中。

3.静态类型提示💡

在我的代码中使用类型提示对我来说是一个游戏改变者。

当你开始使用类型提示时,你的代码的可读性提高了多少,我怎么强调都不为过。如果你从这篇文章中学到了什么,那就是开始使用类型提示。

什么是类型提示?

Python 是一种动态类型语言。这意味着您不需要在代码中显式声明变量数据类型。运行代码时,数据类型会根据传递给变量的特定数据结构自动分配给变量。

动态类型语言较低的语法开销对初学者来说可能更宽容和更有吸引力,但是,它可能会导致坏习惯和可读性较差的代码,尤其是在较大的项目中。

Python(3.6 以上版本)中的类型提示是可选的注释,它向代码的读者表明特定变量或函数所期望的数据结构/类型。例如strlistdictint等。

Python 解释器忽略类型提示。如果没有类型提示,或者即使错误地指定了类型提示,您的代码仍然会运行。类型提示的目的纯粹是为了开发者的利益

示例

以下面的函数为例,类型提示可以用来改善开发人员的体验。

**def** get_largest(items):
    **return** max(items)

上面的函数旨在返回数字列表中的最大值。简单吧?

现在,假设您是该项目的新开发人员,看到了这段代码。你应该如何使用这个功能?

当最初编写这个函数时,我确信作者确切地知道这个函数做什么以及它应该用于什么——它是非常简单的 Python 代码。然而,作为一个新的开发人员,这可能会导致不正确地使用函数或得到意想不到的结果。

这个例子中最大的问题是:我应该把什么数据结构传入名为items的函数参数中?

Python 关键字可以处理许多不同的数据结构,包括:字典、列表、字符串、字符串列表、集合、元组。如果将这些数据结构传递给上面的函数,它不会抛出错误。然而,只有整数列表(或者可能是浮点数)才能给出最初开发者想要的结果。

类型提示(和改进的变量命名)可以极大地提高可读性,并减少以后出错的机会:

*# improved with type hints (python 3.9+ syntax)*
**def** get_largest(input_list: list[int]) -> int:
    """Return largest number in a list"""
    **return** max(input_list)

上面的代码片段使用了:语法来指定函数所期望的数据结构是一个整数列表,而->语法表示函数将返回一个整数。

你可以在 文档 中阅读更多关于 Python 类型提示语法的内容

虽然仍然没有什么可以防止未来的开发人员意外地将一个字符串或字符串列表传递给这个函数,但是类型提示极大地帮助了开发人员,并指明了该函数的预期用途。

类型提示工具

您可以使用类似于 mypypylance (在 VSCode 中)的工具来检查可疑的键入错误。

如果指定了类型提示,这些工具将检查代码中该变量/函数的实例,并确保使用了正确的数据结构。

例如,在下面的 VSCode 编辑器中,我已经指定了函数需要一个整数列表并返回一个整数。VSCode 发现了两个问题。首先,我从函数中返回一个字符串,而不是一个整数。其次,当我试图在其他地方使用这个函数时,我得到一条红线警告我,我正在向这个函数传递一个字符串列表,而不是一个整数列表。

作者图片

这些警告在开发过程中非常有用,有助于快速捕获潜在的 bug。

我的 vscode settings.json文件中的相关设置是:

{
    **"python.linting.pylintEnabled"**:**true**,
    **"python.linting.enabled"**: **true**,
    **"python.analysis.typeCheckingMode"**: "strict",
}

或者,您可以使用mypy作为命令行工具,对您的代码运行静态类型分析:

*#install mypy*
pip install mypy*# run mypy*
mypy SOURCE_FILE_OR_DIRECTORY

4.用预提交钩子自动处理一切🪝

作为程序员,我们喜欢自动化。代码格式化和检查也不例外。

您可以使用预提交钩子来自动化代码样式和检查。pre-commit框架允许您在提交对 git 库的更改之前,指定要对代码运行哪些检查。这些检查在提交代码时自动运行。如果任何检查失败,代码将被拒绝,直到它们被修复。

这有助于防止不良代码被添加到您的项目历史中,并确保 repo 中的所有代码都符合您的代码样式指南。

预提交挂钩入门

  1. 安装预提交
pip install pre-commit

2.将.pre-commit-config.yaml配置文件添加到您的项目目录中

*# .pre-commit-config.yaml*
**repos**:
-   **repo**: https://github.com/pre-commit/pre-commit-hooks
    **rev**: v2.3.0
    **hooks**:
    -   **id**: trailing-whitespace
-   **repo**: https://github.com/psf/black
    **rev**: 19.3b0
    **hooks**:
    -   **id**: black

3.安装 git 挂钩

pre-commit install

遵循这些指令后,下一次您尝试向 repo 提交文件时,预提交钩子将运行,并确保您的代码与black格式化程序兼容。在上面的例子中,预提交还将检查并删除文件中的任何尾随空格

查看我的关于预提交钩子的文章,获得更详细的教程和我用于数据科学项目的完整模板.pre-commit-config.yaml文件。

5.写有用的文档✍️

我们都希望我们的代码能够超越我们对项目的投入而继续存在。但是不管你的软件有多好,如果文档不好,人们就不会使用它。

在开发过程中很容易跳过文档,这通常是在项目结束时最后引起我们注意的事情。

虽然为仍在开发中的功能编写详细的文档是没有意义的——糟糕的或不正确的文档比没有文档更糟糕——但是有一些简单的策略可以帮助您在没有太多开销的情况下掌握全局。有了更好的文档,你就增加了其他人使用你的代码的机会,也将使你未来的生活更加轻松。

在 Python 中,文档通常有三种形式:行内注释、文档字符串和外部文档(例如 README.md)。

内嵌注释

一般来说,你应该试着遵循 PEP8 设定的行内评论准则。

在代码中编写内联注释是一个令人惊讶的有争议的话题。但是我喜欢采纳马丁·福勒在他的书《重构》中的建议:

“注释应该解释‘为什么’代码在做某事,而不是‘它在做什么’——马丁福勒 重构

代码正在做什么应该是不言自明的。然而,为什么开发者选择在代码中使用那个‘神奇’的数字、不寻常的方法或变通方法可能并不明显。这就是行内注释很有意义的地方。

我喜欢在代码意图不明显的地方添加行内注释。一个很好的例子就是当你使用十六进制代码来表示颜色时——你永远不会仅仅通过看十六进制代码来记住颜色。

下面是我在上发表的可视化资产价格相关性的文章的一个片段,我为此使用了行内评论。

**def** assign_colour(correlation: float) -> str:
    """Assign hex code colour string based on correlation value"""
    **if** correlation <= 0.0:
        **return** "#ffa09b"  *# red*
    **else**:
        **return** "#9eccb7"  *# green*

文档字符串

文档字符串用于描述函数或类的操作。它们是使用函数声明下面的三个引号语法"""定义的。

**def** add(a,b):
    """Add two numbers together"""
    **return** a + b

将简单的单行文档字符串添加到代码函数中,即使您认为函数很简单,也会对提高可读性和减少未来开发人员的歧义有很大帮助。

需要注意的是文档字符串和行内/块注释是不可互换的

与行内/块注释不同,文档字符串可以在代码运行时使用__doc__ dunder 属性或内置的help()函数来访问。这意味着文档字符串可以被 ide 解释,并在输入新代码时显示为提示。

我注意到来自其他语言的程序员没有正确使用 Python 的文档字符串特性。例如,使用 Python 函数上面的块注释而不是使用 doc 字符串语法:

*# function to add two numbers together*
**def** add(a,b):
    **return** a + b

这不仅不是“Pythonic 化的”(参见技巧 1 —代码格式化),而且通过编写这样的注释,您在运行时放弃了对文档字符串的访问。因此,在 IDE 中编写时,您将无法查看函数描述,也无法使用自动文档工具,如 sphinx (见下文)。

根据函数的复杂程度,一个简单的单行文档字符串可能是合适的。如果函数名和参数本身提供了足够的描述性细节,就不要添加不必要的细节。

顶端提示 💡

您可以使用 VSCode 中的 Python 文档字符串生成器 等工具自动生成文档字符串模板

外部文件

最详细的文档应该留给项目README.md

自述文件应该向项目的维护者和用户提供关于如何设置环境、项目的目的、项目目录结构等的一般信息。

你可以利用 GitHub 上的自述文件模板

此外,如果您已经用文档字符串记录了您的代码,那么您可以使用像 Sphinx 这样的工具在一个漂亮的用户界面中自动生成文档。

额外收获:阅读他人的代码👨‍🎓

所以这条建议与改进你自己的 Python 代码没有直接关系。然而,学习如何编写可读和可维护的代码的最好方法之一是阅读其他人的代码。

你可以通过查看 GitHub 上流行的开源库来获得其他人的代码,或者如果你在一个团队中工作,通过审查其他人的代码。

通过阅读他人的代码,你将能够发现在试图理解他们的代码库时让你感到沮丧的事情。这可能是命名不当的变量、函数所需的数据结构不清楚、文档不充分(例如,没有函数的文档字符串、自述文件)、不一致或不符合 PEP8 的代码使其难以阅读。

从外部的角度来看别人的代码,你可以从另一个程序员的角度将这些知识应用到你自己的代码中,而这个程序员以前并不了解代码库。

例如,只有当我参与一个继承了遗留代码库的项目时,我才意识到类型提示对于帮助其他开发人员(以及您自己)是多么有用!)来理解在代码中传递的数据结构。

结论

您可以显著提高项目的可读性和可维护性,而无需重构任何代码逻辑,只需利用 Python 开源社区开发的工具。

根据我的经验,你应该假设你会在未来的某个时刻忘记每个函数或变量的用途。因此,在第一次编写代码时,最好尽可能地明确(例如,编写描述性的文档字符串,添加明确的类型提示等)。).将这些策略应用到您自己的 Python 项目中,将会极大地改善合作者的开发体验,如果不能,它至少会让您未来的生活更加轻松。

我在这篇文章中提到的五大技巧包括:

  • 使用自动格式化程序,如blackisort
  • 使用代码检查器,如flake8pylint
  • 使用类型提示来消除函数参数中的歧义
  • 使用pre-commit自动检查
  • 使用 Python 文档字符串编写好的文档

将这些原则应用到您的开发中,将极大地提高代码的可维护性,并使您的未来生活变得更加轻松!

资源

本文最初发表在我的博客上,engineeringfordatascience.com

数据科学行业值得关注的五大趋势

原文:https://towardsdatascience.com/five-trends-to-watch-for-in-the-data-science-industry-97bf1589f9ca

意见

分析行业的发展方向,以及你应该如何准备

作者创作的图形

有一句著名曲棍球运动员经常引用的话,“我滑向冰球要去的地方,而不是它已经去过的地方。”这句话几乎适用于所有行业,数据科学也不例外。尽管概念数据科学并不是一门新的学科,但应用数据科学相对来说仍处于起步阶段。也许只是在过去的 7 年左右,我们才看到数据科学在主流公司中获得了牵引力,因此它仍然非常适合持续发展。

自然,任何人都应该为未来做好准备。我已经在这个领域从业多年,因为我看到我自己的职业生涯在这里继续发展,我想确保我现在做的事情是正确的,为未来做准备。当然,我没有水晶球,所以我不能肯定地说,我下面分享的趋势肯定会发生。

但是根据过去推断未来是我们的工作!我们使用数据科学来寻找数据的趋势,因此同样,我正在使用我自己在多个渠道的专业经验来对行业可能的发展方向做出有根据的猜测。具体来说,我是一名《财富》50 强公司的机器学习工程师,经常发表和阅读关于数据科学的文章。我指导各种各样的学生、实习生和寻求职业转型的人,我定期梳理 LinkedIn 和 Reddit 等网站,看看数据科学领域的“热门话题”是什么。当然,肯定有比我更了解数据科学的人,但我想我已经掌握了这个令人惊叹的行业的发展方向。

下面的列表按重要性从低到高的降序排列。同样,这只是我对数据科学发展方向的个人看法,我完全承认我可能是错的。我希望你能看透这些趋势,并了解如果这些趋势成为现实,你该如何调整自己的技能,做好最好的准备。

事不宜迟,让我们跳进名单吧!

5.更多地方的更多人工智能

正如我上面提到的,概念数据科学并不新鲜;只是在过去几年里,由于通用硬件设备计算能力的提高,它变得更加流行。如果考虑企业级软件,计算能力方面的许多“重担”都来自于一般用户无法访问的一些数据中心的大型服务器。随着硬件继续发展,以更小的尺寸提供更快的计算速度,我想我们也会看到越来越多的设备使用人工智能。

仅在过去的五年里,我们就已经看到我们称之为“物联网”(IoT)市场的设备数量有了相当大的增长。迄今为止,这些物联网设备的功能相对简单,从打开灯泡到打开车库门。我个人还没有见过很多物联网设备在数据科学方面做如此复杂的事情,这可能是因为大多数物联网设备仍然没有达到能够以毫秒级延迟处理深度神经网络等事情的程度。在小型硬件环境中实现这些复杂的人工智能技术仍然是一个挑战,但如果技术继续保持过去几十年的趋势,这将很快成为物联网设备的一个问题。一旦我们达到这一点,我打赌我们会看到应用人工智能在各种用例中的增长。

4.各种渠道的欺诈数量不断增加

我不知道你怎么想,但我个人的手机号码和 WhatsApp 账户上的垃圾短信数量明显增加。好消息是,大多数人能够相当容易地检测出什么是欺诈,什么是真实的,但挑战在于,不幸的是,检测出什么是真实的可能会越来越难。以“深度假货”为例。到目前为止,我们真的没有看到一个像“深度伪造”视频这样的东西真正引起麻烦的重大事件。这在很大程度上是因为人们相对善于检测视频何时是“深度伪造”的视频。(例如,不自然的动作、声音和嘴部动作之间的不匹配等。)但和任何技术一样,随着时间的推移,情况会变得更好。我收到的那些假短信会变得越来越真实,我敢打赌“深度假”视频最终会变得与真实内容难以区分。

有许多方法可以抵消欺诈活动,当然,我希望人工智能/机器学习是我们能够用来更好地检测这种欺诈的工具之一。我甚至可以看到某种网络安全/数据科学组合角色在未来成为主流角色,利用数据科学实践与网络安全实践来打击所有这些不同类型的欺诈。如果你是这两个领域的从业者,在相反的领域提高技能会让你受益匪浅,这样你就能很好地为这些潜在的新工作角色定位。

3.伦理人工智能

在过去的五年中,我们已经开始看到这种对消费者数据的负责任使用的巨大推动。例如,我们仅在 2018 年就看到了主要的立法:加利福尼亚州的加州消费者隐私法(CCPA) 和欧盟的一般数据保护条例(GDPR) 。这是两项开创性的立法,我个人认为这是朝着正确方向迈出的步伐。通过数据滥用消费者隐私太容易了,所以我非常期待这种趋势在人工智能和机器学习等数据科学领域继续下去。

好消息是,你可以——我认为应该——在你今天的工作中减少不道德的人工智能实践。虽然常识会让你在制定道德的人工智能实践方面走很长的路,但有很多很好的资源可以帮助你更好地理解那些更多的“灰色区域”在美国,你可以查看像美国平等就业机会委员会(EEOC) 这样的政府网站,以获得关于“受保护阶层”等问题的明确答案,你可以合理地假设你在创建自己的预测模型时应该尊重这些问题。许多组织(尤其是大型组织)也有自己的法律团队和外部验证团队,他们通常精通道德最佳实践。当有疑问时,不要害怕向这些团体中的任何一个咨询。他们应该能够给你明确的建议来解决这些“灰色区域”,这样你就可以创建一个有效和道德的人工智能模型。

2.MLOps

因为应用数据科学仍处于起步阶段,我们还没有太多关于模型退化的担忧。新训练的预测模型通常在几个月或几年内保持良好的性能,因为对于许多用例来说,正在推断的基础数据不会发生很大变化。但这并不意味着数据永远不会改变,在行业部署这些模型大约七年后,我不得不相信,它们中的许多不再像最初接受培训时那样高性能。

这正成为许多公司的一个巨大问题,尤其是那些维护数百或数千个模型的公司。鉴于以下因素,重新培训和重新部署模型的能力是一个巨大的挑战…

  • 员工离开公司,因此关于一个模型是如何被训练的知识也消失了
  • 用于最初训练模型的原始数据和/或代码已经丢失
  • 没有足够的人既服务现有的模型又创造新的模型。

MLOps 试图通过创建这个无缝的流程来回答这些问题,该流程训练、部署并定期验证模型在生产中保持高性能。如果模型经历衰退或漂移,那么 MLOps 管道应该能够自动地重新训练/重新部署模型。显然,这对于这些拥有许多模型的公司来说是一个巨大的福音,因此尽管感觉 MLOps 已经成为当今的一个大话题,但我真诚地相信,我们才刚刚开始在企业级水平上真正成熟这些实践。实现 MLOps 的最大挑战是它很大程度上需要一套工程技术,这就引出了我们的最终趋势…

1.对全栈数据科学家的需求日益增长

我在介绍中提到,我经常指导各种希望进入数据科学领域的人,这也包括定期查看招聘信息,了解对员工的期望。毫无疑问,对于这些数据科学角色的需求,传统高等教育和专业公司之间存在巨大的脱节。具体来说,数据科学课程似乎没有教授如何部署预测模型。我指导过许多你能想到的最知名大学的学生,每个学生都告诉我同样的事情:他们专注于如何建立模型或创建算法,但几乎没有时间致力于如何在现实世界中实际实现它们。从每一个和我交谈过的新朋友那里一遍又一遍地听到同样的情绪,真是令人兴奋不已!

一些公司试图通过雇佣软件工程师来代表数据科学家执行这一实现来回答这个问题,但不幸的现实是,为了胜任地部署预测性人工智能模型(特别是使用 MLOps 原则),软件工程师真的需要理解数据科学家会理解的原则。没有这种背景的软件工程师最多能做好工作,但我个人从未见过任何软件工程师成功部署完全启用的 MLOps 管道。需要说明的是,这并不是对普通软件工程师的打击。只是这实际上是一个双重技能集,所以要求一个数据科学家也拥有足够强大的软件工程技能集来部署他们自己的模型是很难的。

尽管这是一个很高的要求,但我已经在今天的招聘启事中看到了。此外,如果我们真的像许多人预测的那样即将进入衰退,许多公司的自然倾向是“少花钱多办事”,这意味着要求他们的数据科学家保持“全套”技能,以便他们也可以部署自己的预测模型。是的,对一个人来说,这绝对是一个具有挑战性的要求,但我可以看到这种要求变得越来越普遍。

这篇文章到此结束!你同意这个列表吗,或者这是你可能添加的另一个趋势?在评论中分享你的想法吧!我很想听听你的想法。感谢您的阅读,我们将在下一篇文章中再见。

用 Python 记住过去(模型状态)的五种方法

原文:https://towardsdatascience.com/five-ways-to-remember-the-past-model-state-in-python-2c8430d29679

从闭包函数和迭代器到状态机 Python 库

e55evu/AdobeStock

据说…

“那些不记得过去的人注定要重复过去”。桑塔亚那,1905 年

就像在现实生活中一样,在软件中,知道如何保存“状态”是很重要的,这可能是一种可取的行为,也可能不是。S tate 是动力系统在某一时刻的行为。在软件中,系统的数学状态通过保留一组感兴趣的变量值来建模。更详细地说,软件中的状态通过状态机来建模,状态机的特征是状态、转移、以及通常从一个状态转移到另一个状态的概率。机器学习中有限状态机(简称状态机)的例子有马尔可夫链马尔可夫模型【1】。状态机在各种应用中非常重要:

  • 安全关键系统。例子包括医疗设备[2]、配电系统[3]和运输系统,如铁路[4]、飞机[5]和自动驾驶车辆[6]。
  • 区块链数据传输、存储和检索系统。区块链也被称为无限状态机[7]。
  • 视频游戏[8],对话式人工智能[9],图形用户界面实现[10]。
  • 用 BLAST(基本局部比对搜索工具)寻找蛋白质序列的相似性[11]。
  • 时间序列预测的深度状态空间模型[12]。

每个软件开发人员都知道,在不同的函数调用之间保持变量状态的最简单(也是最难看)的方法是将这个变量声明为一个全局变量。但是,让我们忘记丑陋,描述用 Python 建模状态的五种不同方式,在一个虚构的、风景如画的地中海酒店管理客人的用例中。我们的酒店叫做阿耳忒弥斯柏树(其中阿耳忒弥斯是古希腊女神,c ypress 是她的神树)。

在创建管理酒店客人的功能时,我们将研究以下在 Python 中管理状态的方法:

  • 作为默认函数参数的空列表。
  • Python 闭包
  • 迭代器
  • 类别变量
  • 状态机 Python 库。

1.作为默认函数参数的空列表

在我们进入第一个用例之前,让我们注意以下几点:一般来说,将空列表作为默认函数参数是一个糟糕的主意。我们将在下面的用例中解释原因。

在我们的第一个用例中,酒店的预订专员希望跟踪带宠物的客人预订的房间,以便分配额外的清洁人员。所以每次当一个房间被预订,客人有宠物时,她的软件调用下面的函数, roomsWithPet()。该函数接受一个位置参数( roomNumber )和一个命名参数, lr,,默认值为一个空列表。该函数被调用三次,只有 roomNumber 参数。 lr 会怎么样?第一次调用该函数时, lr 被创建并用 roomNumber 的值填充,随后每次,新的 r oomNumber 值被追加到第一次调用时创建的 lr 列表中。

因此,输出是:

在我们的例子中,这是理想的行为,但是在许多情况下,当您希望在每次函数调用时都创建一个新的列表时,却不是这样。因此,知道将空列表作为默认参数传递给函数是很重要的,在函数调用之间保留列表的状态。

2.Python 闭包

在我们的第二个用例中,四个在酒店参加会议的客人到达接待处办理退房手续。客人是同一家公司的员工,在酒店共用一个大套房。接待员需要计算每个人应得的余额。她可以调用下面的函数,输入参数是套房号(501)、住宿天数(3)、套房价格/晚(400 美元)以及每间套房的额外费用(酒店餐厅的餐费)。但是调用一个函数四次,每次只有最后一个参数不同,这有点麻烦。

Python 闭包为我们提供了一种更优雅的方式来做这件事。它们是内部函数,可以访问外部函数中的值,即使在外部函数已经完成执行之后【13】。换句话说,闭包函数有能力记住一个封闭函数的值,即使这些值已经不在内存中。一般来说,当我们不想写类时,Python 闭包提供了一种简洁的方式来实现数据隐藏,这是实现数据隐藏最常见的方式。

在下面的代码片段中,内部函数 increaseByMeals() 是一个闭包函数,因为它会记住外部函数 balanceOwned() 的值,即使在后者执行之后。因此,这允许我们编写下面的代码,其中 balanceOwned() 只用它的三个参数调用一次,然后在它执行后,我们用每个客人的餐费调用它四次。

优雅多了。现在的输出是:

3.迭代器

在我们的第三个用例中,酒店的客人可以为个人会议预订瑜伽教练或体能教练。在下面的代码中,D 是一个字典,其中键是房间号,值是“Y”或“PT”(“Y”表示请求的瑜伽教练,“PT”表示请求的体能教练)。

每天早上,都会创建一个新的字典,根据提出请求的时间输入房间号。然后,为了获得已经请求 Yoga 的房间,执行随后的 filter() 命令。现在中央服务器已经为瑜伽教练准备好了。每次瑜伽教练要求去下一个房间时,服务器只需调用 next(ff),就会返回下一个房间来找瑜伽教练。以这种方式,瑜伽教练不需要彼此交流接下来去哪个房间。一个简单的迭代器保留房间服务的整体状态,而 next() 命令将我们带到下一个需要服务的房间。

输出是:

4.类变量和状态机 Python 库

在本节中,我们将讨论前面提到的在 Python 中保留状态的最后两种方法:

  • 类变量。这些变量是在类构造中定义的,在构造类时声明,由类的所有实例共享。通过类实例。如果一个类的实例改变了一个类变量,那么该类的所有实例都会受到影响,这样,类变量在类实例之间保持它们的状态。
  • Python 状态机库。这是一个开源库[15],允许定义状态和状态间转换的动作。

我们将在最终用例中演示类变量和 Python 状态机库的使用,该用例模拟了我们酒店中桑拿室的使用。酒店在两个不同的位置有两个桑拿房,每个房间可容纳两人。

我们的类Sauna下面的继承自状态机并具有类变量* inst_counter ,它通过每次调用该类的构造函数来计算该类的实例数。我们为每个桑拿房定义了三种状态: (0 个客人进入) space_avail (一个客人进入)(两个客人进入)。然后我们给可能的转变命名。例如, nospace 意味着我们已经从状态 s pace_avail 过渡到状态 full 。*

根据转换的定义,我们定义转换上的动作。例如,函数 on_enter_space_avail() 实现了当一个客人在先前空着的桑拿房内时的动作。除了打印有一个桑拿座椅可用,该功能还:

  • 递增实例变量 guestcount 。员工想知道每个房间有多少客人使用过,以评估特定桑拿场所的受欢迎程度。
  • 获取当前时间。然后将其添加到列表 entryTimes 中,这是一个实例变量。知道进入的时间是有用的,这样在繁忙的时候,工作人员可以安排额外的毛巾等。
  • 记录正在使用房间的客人的房间号,将其附加到列表房间号中。这是出于计费目的(每周两次会议以上是免费的,额外会议将向客人收费)。在我们的例子中,房间号是随机生成的。另外,有趣的是, roomnumbers 列表是该类的私有成员,为了访问它,我们定义了属性 getroomnumbers

最后,我们定义循环,是状态的管道(序列):被占用无空间两个空间

下面我们创建一个桑拿房的实例,通过调用 cycle() 来完成状态转换。

输出如下所示。在第一个周期()呼叫后,处于占用状态,因此打印“一个桑拿座位可用”。第二次呼叫后,它处于状态 nospace,并显示“对不起,桑拿房已满”。第三次呼叫后,它处于状态间隔 42 个,并打印“欢迎来到桑拿房!”。

现在,让我们获得一些附加信息:客人的进入时间、当前状态、到目前为止的客人数量和房间号。

下面是上面代码的输出。正如所料,我们目前处于状态,到目前为止有两位客人使用了 ArtemisSauna1 ,我们目前有一个桑拿类的实例。

Python 提供了许多保持状态的方法,换句话说,就是跟踪过程。这是很有价值的,特别是对于安全关键系统,软件同时跟踪许多物理过程的状态,并将状态变化映射到适当的动作。另一方面,正如许多 GUI(图形用户界面)开发人员所证实的,最令人讨厌的缺陷之一是 GUI 不响应用户动作,而是被锁定在特定状态。总之,无论我们的目标是否是保留状态,我们都需要了解 Python 的状态保留特性。

本文的代码可以在 Github 目录中找到:链接到代码

感谢您的阅读!

参考文献

  1. 便当,c .,现实生活中讲解的马尔可夫模型和马尔可夫链:概率性的锻炼套路,中:走向数据科学 2020 链接文章
  2. Bombarda,A .、S. Bonfanti 和 A. Gargantini,开发从抽象状态机到嵌入式系统的医疗设备:智能药盒案例研究,JM 布鲁尔市马扎拉。软件技术:方法和工具。工具 2019。计算机科学讲义(),第 11771 卷。Springer,链接到文章
  3. 赵,j .等,用带变量的有限状态机对离散事件系统建模与控制及其在电网中的应用,系统&控制信,第 61 卷,第 1 期,212–222 页,链接文章
  4. Thapaliya,a .、D. Jeong,d .和 G. Kwon,使用故障状态机对安全关键系统进行故障分析。载于:Park,j .,Loia,v .,Yi,g .,Sung,y .(编辑)《计算机科学与普适计算的进展》。可爱的 CSA 2017 2017。电气工程讲义,第 474 卷。2018 年新加坡施普林格。
  5. Spagnolo,c .,S. Sumsurooah 和 S. Bozkho,面向更多电动飞机应用的先进智能电网配电系统,国际电工综合与系统会议,2019 年第 1–6 页,链接至文章
  6. Bae,S. H .等人,基于有限状态机的城市环境下自动驾驶车辆系统,第 20 届控制、自动化与系统国际会议 (ICCAS) ,第 1181–1186 页,2020 年。
  7. Brooks,s .,区块链:无限状态机,中:走向数据科学,2017 年 4 月,链接到文章
  8. Seemann 和 D. M. Bourg,游戏开发者的人工智能,奥赖利媒体,2004 年 7 月。
  9. Zamanirad,s .等人,基于状态机的人-机器人对话模型和服务,高级信息系统工程国际会议,2020 年 6 月,链接到文章
  10. Couriol,b .,稳健工程:你可以信任的状态机用户界面,【2019 年 4 月,https://www . infoq . com/articles/Robust-User-Interfaces-with-State-Machines/
  11. Arpith,j .等, Mercury BLASTP:加速蛋白质序列比对, ACM 反式可重构技术。系统。2008 年 6 月;1(2): 9、链接到文章
  12. Rangapuram,S. S .等,时间序列预测的深态空间模型, NeurIPS 会议,2018。
  13. Python 内部函数:它们有什么用处?、链接到文章
  14. Pathak,o ., Python 闭包,GeeksforGeeks,链接到文章
  15. StateMachine Python 库,链接到库。

如何修复 ModuleNotFoundError:没有名为“sklearn”的模块

原文:https://towardsdatascience.com/fix-modulenotfounderror-sklearn-99db60ed5d

了解如何正确安装和导入 scikit-学习 Python

Milad FakurianUnsplash 上拍摄的照片

介绍

刚接触 Python 的人通常会在安装scikit-learn包时遇到麻烦,这个包是事实上的机器学习库。在源代码中导入包时,一个非常常见的错误是ModuleNotFoundError

ModuleNotFoundError: No module named 'sklearn'

这个错误表明scikit-learn(又名sklearn)包没有安装,或者即使安装了也无法解决。

在今天的简短教程中,我将介绍一些关于在 Python 上安装包的基本概念,这些概念最终可以帮助您摆脱这个错误,并开始处理您的 ML 项目。更具体地说,我们将讨论

  • 通过pip安装包的正确方法
  • 如何将scikit-learn升级到最新版本
  • 如何正确使用虚拟环境和管理包版本
  • 如果你正面临这个关于 anaconda 的问题,该怎么办
  • 如果您在 Jupyter 笔记本中遇到此错误,该怎么办

我们开始吧!

使用 pip 以正确的方式安装软件包

事实上,您可能在本地机器上安装了多个 Python 版本。每次你安装一个软件包,这个安装只与一个版本相关联。因此,有可能您已经为一个 Python 版本安装了scikit-learn,但是您正在使用一个不同的版本执行您的源代码,这可能就是为什么找不到scikit-learn的原因。

因此,确保使用正确的命令安装sklearnpip。通常,许多用户试图使用命令安装软件包

$ pip install package_name

或者

$ pip3 install package_name

以上两个命令都将安装与 Python 相关联的指定包。例如,您可以通过运行

$ pip --version**pip 19.0.3 from /usr/lib/python3.7/site-packages/pip (python 3.7)**

相反,确保在通过pip安装 Python 包时使用以下符号

$ python3 -m pip install scikit-learn

这将确保将要安装的包对于您将用来运行源代码的 Python 版本是可用的。您可以通过执行以下命令找到特定 Python 可执行文件在本地机器上的位置

$ which python3

将软件包升级到最新版本

此外,确保您使用的是最新版本的scikit-learn而不是非常旧的版本可能会有所帮助。要更新到可用的最新版本,您可以运行以下命令:

$ python3 -m pip install -U scikit-learn

使用虚拟环境

Python 的[venv](https://docs.python.org/3/library/venv.html#module-venv)模块允许创建所谓的虚拟环境。每个虚拟环境都是完全隔离的,都有自己的 Python 二进制。此外,它还可能在自己的站点目录中有自己的一组安装包。

这意味着,如果某个软件包安装在特定的虚拟环境中,它对于系统范围内安装的软件包或任何其他虚拟环境都是不可见的。

如果您目前没有使用虚拟环境,我建议您开始这样做,因为它将极大地帮助您更轻松、更有效地管理包依赖关系。

现在来看我们的具体用例,如果你希望从事一个需要scikit-learn的项目,那么你必须遵循三个步骤。

首先,为您的项目创建一个虚拟环境,并将其放置在您想要的位置。让我们使用名称my_project_venv创建一个

$ python -m venv /path/to/your/venv/called/my_project_venv

既然已经创建了虚拟环境,您现在应该激活它。您可以使用以下命令来完成此操作:

$ source /path/to/your/venv/called/my_project_venv/bin/activate

如果虚拟环境已经成功激活,您应该能够在命令行中看到 venv 名称作为前缀(例如(my_project_venv))。

现在,您终于可以使用我们之前讨论的命令安装sklearn(以及构建 Python 应用程序所需的任何其他依赖项)。

(my_project_venv) $ python3 -m pip install scikit-learn

并最终执行您的脚本

(my_project_venv) $ python3 my_script_using_sklearn.py

如果您正在使用 anaconda,该怎么办

如果您目前正在使用 conda,您可能必须小心您实际使用的环境。

如果您想在根目录下安装scikit-learn(可能不推荐,因为我提到了为每个项目使用隔离虚拟环境的重要性),那么您可以使用

$ conda install scikit-learn

或者,如果您想要将scikit-learn包安装到特定的 anaconda 环境中,那么您可以使用-n标志来指定环境名称。例如,下面的命令将把scikit-learn安装到名为my_environment的 conda 环境中:

conda install -n my_environment scikit-learn

如果以上方法都不起作用,那么你仍然可以安装scikit-learnpip,即使是在 conda 环境下工作。在 anaconda 提示符下,只需运行

$ pip install scikit-learn

如果你和 Jupyter 一起工作,该怎么做

最后,如果你在 Jupyter 笔记本上安装了这个ModuleNotFoundError,那么你需要确保 Jupyter 和scikit-learn安装在同一个环境中。

第一步是检查 jupyter 笔记本在本地机器上的安装路径。举个例子,

$ which jupyter
$ which jupyter-notebook

如前所述,如果你在孤立的环境中工作会好得多(而且肯定会节省你的时间和精力)。

$ conda install -c anaconda ipython

或者

conda install -c anaconda jupyter

如果您在特定的 conda 环境中工作,请确保在同一环境中安装 Jupyter 笔记本电脑和scikit-learn软件包:

$ conda install -n my_environment jupyter
$ conda install -n my_environment scikit-learn

如果你在 Python 虚拟环境(又名venv)中工作,那么:

$ python3 -m pip install jupyter
$ python3 -m pip install scikit-learn

最后从激活的环境中打开你的 Jupyter 笔记本,导入scikit-learn。你现在应该可以走了!

还在烦恼吗?

如果您在导入scikit-learn时仍然有问题,那么可能是其他地方出错了。你可能会在我的一篇文章中找到答案

最后的想法

在今天的文章中,我们讨论了在 Python 源代码中导入sklearn的主要原因。此外,我们探讨了一些可能与此问题相关的不同主题,最终可以帮助您解决导入错误。更具体地说,我们讨论了如何使用正确的pip安装命令解决问题,如何在隔离的虚拟环境中管理您的项目,以及如何在使用 Anaconda 和/或 Jypyter 笔记本时克服ModuleNotFoundError

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章你可能也喜欢

</14-must-know-pip-commands-for-data-scientists-and-engineers-a59ebbe0a439>

修正基础是使用高级人工智能的关键

原文:https://towardsdatascience.com/fixing-fundamentals-is-essential-to-use-fancy-ai-b867ffc650b8

三种技术可以帮助公司构建基础,为基于数据的竞争优势奠定基础

迈克·科诺诺夫在 Unsplash 上拍摄的照片

从商业智能到大数据到机器学习再到人工智能,数据世界在过去二十年里取得了令人眼花缭乱的进步。然而,研究人员总是不断指出关于公司未能利用数据的的令人清醒的数字。通常,缺乏管理层的支持、数据团队没有关注真正的业务问题、公司没有合适的技能组合等都被视为罪魁祸首。

在我看来,通常是缺乏专注和耐心来建立基础。坦率地说,构建基础是枯燥、乏味且耗时的。谁真正喜欢治理和文档之类的东西?我的猜测是,既不是想尝试新技术的数据科学家,也不是在季度报告的世界里寻求快速洞察力和结果的高管。然而,事实是,除非基本面是固定的,否则获得洞察力和利用最新的技术是非常困难的。但是这些基本面是什么呢?有三个关键方面:

  • 干净可靠的数据源。至少对最常用或最关键的数据集,如收入、产品使用和销售漏斗,有单一的真实来源(黄金数据表)。
  • 治理。至少黄金数据集应该被维护、治理、附加 SLA、跟踪血统、提供数据字典,并为用户提供数据契约。
  • 稳健流程。在生产数据、处理数据和消费数据的团队中,明确、定义和记录角色和职责,以及操作手册。

公司可以做以下 3 件事来建立这些基础:

  1. 限制分心

我们生活在一个技术快速创新和需要快速见效的时代。每一次新的进步发生时,都有一个不可避免的问题,即如何利用它来创造竞争优势。以最近关于 ChatGPT 的兴奋为例。无疑是一项举足轻重的技术。也许它可以通过轻松理解现有代码来帮助一名新雇佣的工程师快速提升,或者通过修复 bug 或编写测试用例来提高效率。但这无助于理解为什么产品和财务团队创建的成本基础不同,哪一个是用于定价决策的正确依据,这反过来会影响对华尔街的收入指导。如果公司的数据成熟度较低,这些技术有时会分散注意力。

一个人可以通过专注于以最简单的方式实现结果来限制分心。一个完美的例子是通过分析一部电影的情感轨迹来预测它的成功。而先进的 NLP 技术擅长于表面处理那些通常表现良好的弧。电影的主要演员和导演的过去 IMDb 评级的简单平均值是特定电影成功的更好预测。

2。做一个围栏和钢螺纹的基础构建

修正基本面通常是一项跨职能部门、耗时多年的大规模工作。这可能令人望而生畏,但有两种方法可以帮助解决这个问题:

圈地能力。这里的目标是保证容量和优先级。通常会有多个团队参与端到端的修复工作:平台/基础设施、数据工程、商业智能、数据科学/分析、职能团队、数据治理等。修复基础是所有团队的首要任务,这一点至关重要,他们要么为项目投入特定人员,要么保证容量,例如,如果使用 sprints,则保证一定数量的故事点。

钢线。这是成功的关键。简单地说,在开始时,除了一个用例之外,构建所有端到端的东西。它是“钢”线的原因是它不应该断裂,不会让任何元素滑脱。“一个”用例很重要,因为如果一个人试图解决所有的基本问题,就会适得其反,看起来更像一个大规模的 it 项目。专注于一个用例有助于快速向业务交付价值,获得知识并创造成功。虽然每个用例都是独一无二的,但是一条主线将会创建模板并建立可以构建的功能。例如,一个用例的模型文档创建了一个如何做文档的框架。类似地,可扩展的能力得到构建,例如数据沿袭工具。下面是一个使用客户流失预测模型作为用例的起点示例,以及要“穿钢丝”的元素、要问的问题和要创建的功能。

“钢丝”集合了交付用例的所有方面,提出了更高层次的问题,并帮助创建可复制的能力和过程(图片由作者提供)

3。关注实现

有没有想过为什么你公司的人不使用数据或者对你开发的酷工具感兴趣?也许他们只是不知道如何使用它。或者他们想做,但是觉得太费时间了。或者这可能是一个简单的犹豫寻求帮助的问题。

为了充分利用数据,整个组织都需要支持。每个人都需要在学习的旅途中一起来。数据团队需要学习讲故事,职能团队需要学习听数据故事。这也是我所说的“基础建设”的问题。选择一个对业务团队真正重要的用例,并将其付诸实践。例如,销售团队可能关心客户最有可能购买什么产品。通过创建可靠的转换数据集、在其上构建模型、创建帮助销售团队轻松获得洞察力的工具/仪表板、开展培训课程以及创建治理工件来实现。

构建基本面是一项艰巨的工作,但影响也很大。一旦构建了一个钢丝,我已经看到了用例构建的指数级进展。这为许多可能性打开了大门,组织最终开始相信它可以使用数据来创造竞争优势。这感觉比人工智能更奇妙。

LinkedInMedium 上关注我,获得更多关于人工智能&数据驱动决策和人机合作的见解

用图论将一个四维立方体展平到你的桌子上

原文:https://towardsdatascience.com/flattening-a-4-d-cube-onto-your-desk-a4730312d91c

用计算机科学算法探索更高维的物体

图论在 web2.0 环境中的适用性是显而易见的,像脸书(友谊的无向图)和 Twitter(关注者的有向图)这样的网站就是围绕着它们建立的。另一个明显的应用是在计算机网络中。运筹学领域也充满了图论问题,它始于将数学应用于战场后勤领域。例如,见这里的

但是图论是如此的多才多艺,它在数据科学中也有应用。除了神经网络是特殊类型的图这一事实之外,它还是组合测试领域的前沿和中心,在组合测试领域,我们以涵盖重要属性或属性组合的方式设计实验。比如,看这里的。这是我第一次开始了解它的背景。

然而,在本文中,我们将讨论一个不同的应用。我从小就对四维立方体着迷。我想把它握在手中,像操纵一个三维立方体一样操纵它。正如我们将看到的,我一点也不知道缺失的部分是图论(由于它在数据科学中的应用,我最近才把它加入我的武器库)。

我)那么立方体有什么新鲜事吗?

I-A)展平一个立方体

立方体是一种生活在三维空间中的物体,大多数人都很熟悉它(思想盒子)。它有六个相同的正方形面,其中一些面之间有 12 条边。你用立方体盒子做什么?你把它们切开(看看里面是什么)。这通常通过沿边缘切割来完成。有一些方法可以做到这一点,使六个面仍然与主体保持连接,并平放在地板上。事实证明,当我们以不同的方式进行切割时,会出现 11 个拓扑不同的网格(这意味着你不能平移和旋转一个网格,使其与任何其他网格完全重叠)。这些如下所示。

图 1:切开一个立方体盒子,把它平放在桌子上,得到 11 个不同的网格。图片作者。

请注意,在每个网格中,正好有五条幸存的边。这不是巧合。如果我们把立方体的面看作一个图的顶点,把幸存的边看作这个图的边,那么上图中的每个网格就是一棵生成树。这意味着只有一条路径连接任意两个顶点。由于原始立方体中有十二条边,我们正好切掉其中的七条来得到十一个网格中的任何一个。选择要切割的七条边的方式总数为{ 12 \选择 7} = 792。但是很多都不会导致生成树,因为一个或两个面与身体的其他部分完全隔离。结果是,略少于一半的切割导致生成树,384。并且每个生成树都通向一个有效的网状网络。在这 384 个网格中,如果我们旋转和平移它们,大多数都是以前看到的网格的重复(这意味着它们在拓扑上是等价的)。换句话说,如果我们可以以任何方式将一个网格放置在另一个网格上,使得后者完全覆盖前者,则这两个网格被认为是同一个网格。所以只有上图中的 11 个拓扑独特的出现。

不清楚是什么时候有人第一次数出了这 11 个网眼。这是一个不用计算机也能在合理的时间内解决的问题。因此,在计算机出现之前是否考虑并解决了这个问题是一个有趣的问题。今天大多数作者都引用了 1984 年的文献。在[2]中,加德纳提出了存在多少个网格的问题,他似乎知道答案(在 1966 年),尽管他没有明确说明。

如果你想知道这种东西的应用,顺便说一下,一个例子是太阳帆,它必须在发射时紧凑,并展开成具有大表面积的东西。

I-B)展平一个宇宙魔方

立方体的概念可以扩展到任何维度的空间。在二维空间中,我们有正方形。在四维空间中,我们得到一个宇宙魔方,一个由三维立方体组成的四维物体。就像一个三维立方体可以展平到二维空间以形成一个正方形网格一样,一个立方体也可以展平到三维空间以形成一个三维立方体网格。

加德纳在 1966 年提出了存在多少这样的网格的问题(见[2]第 x 页)。1985 年(261 目)在[1]中通过一个叫“成对树”的新概念回答。网格最终在 2015 年的 MathOverflow 帖子中可视化了[3]。

到目前为止,似乎还没有人考虑过把一个四维立方体一直展平到二维空间,变成一个正方形网格。这就是我们将在第三节探讨的内容。但是首先,让我们描述一些符号。

II)立方体符号

在这一节中,我们建立一些符号来标识 n 维立方体的各种元素。让我们从二维立方体或正方形开始。我们放置它的方式是,正方形的中心在原点,(0,0),如下图 2 所示。

图 2:正方形中不同的兴趣点。图片作者。

为简便起见,我们称整个正方形为“00”,因为它的质心恰好位于(0,0)。正方形顶边的中心是(0,+1)。同样,为了简洁起见,我们将其命名为“0+”。类似地,底边被命名为 0-。左边缘为“-0”,右边缘为“+0”。左上角的顶点是(-1,+1),所以我们称它为“-+”,依此类推。

对于 d 维空间中立方体的任何给定实体,其名称由 d 个字符表示,每个字符可以是-,0 或+中的一个。一串全零是立方体的中心(并且只有一个)。其中一个零被 a +或 a-替换,成为一组(d-1)维立方体。有 2d 个这样的立方体(d 种选择非零字符位置的方法,然后给它分配 a +或 a -)。类似地,有 d(d-1)/24 = 2d(d-1)维(d-2)的立方体(选择非零字符的两个位置,然后用“+”或“-”以 4 种方式填充这两个位置)等等。

III)二维网格

我们在第(1)节中看到了如何将一个三维立方体展平成一个二维的桌子,使得所有的面保持连接并且没有一个面重叠。有没有可能用四维立方体或宇宙魔方做同样的事情?有点令人惊讶的是,答案是肯定的。下面显示的就是一个这样的网格。我通过小心翼翼地将“顶部”的三维立方体展平到一张桌子上,其中一些面仍然贴在上面,然后通过反复试验尝试展平剩余的面,使它们不会重叠,从而获得了它。

图 3:将一个四维立方体展平到桌面上的一种方法,这种方法不会使任何一个面相互重叠。图片作者。

显而易见的问题是,有多少这样的网格存在?

III-A)有效网格的属性

对于图 1 中立方体的有效网格,我们注意到两个关键属性,它们也适用于图 2 中的立方体网格:

  1. 很明显,所有的面仍然彼此相连(根据定义)。因此,可以从网格中的任何面开始,到达任何其他面。
  2. 还要注意,任意两个面之间,最多有一条边。原始立方体或宇宙魔方是这样,因此网格也一定是这样。

结合以上两个事实,并将固体的面视为图的顶点,很明显,网格形成了一个生成树(一个图,所有顶点都可以从任意顶点到达,并且任意两个顶点之间至多有一条边)。这种见解可能是 Turney 在[1]中用来计算一个宇宙魔方的三维网格数的第一步。因此,立方体或宇宙魔方的每一个有效的二维网格(或者就此而言,任何(d

Fig-4: The graph of the 2-d faces of the Tesseract. Image by author.

For the case of the cube, each physical edge maps 1:1 with a graph edge (this might seem obvious, but you’ll see later why I called it out). So, we start with 12 edges and have to cut exactly 7 edges (so 6–1=5 remain) before “opening it up” into a mesh (which is why all the meshes in figure 1 had 5 edges connected). Once we make the 7 cuts (in a way that all the vertices remain reachable from each other), there is only one way to flatten to a mesh.

For a Tesseract, each physical edge actually corresponds to three edges of the graph. This is because three faces meet at every physical edge (as opposed to 2 for a cube). So while the number of physical edges is just 32, the number of graph edges (in the graph where each face is a vertex) is actually 32*3 = 96 (3 ways to choose 2 of the faces meeting at each physical edge). Of these 96 graph edges, only 23 can remain in the flattened mesh. For a given physical edge, if all three graph edges are intact, it will be impossible to flatten the Tesseract to a table without overlap.

IV)生成树到网格

IV-A)基本操作

假设我们有一个可以展平成网格的立方体面图的生成树,用邻接表表示。我们如何从这个图形到实际的展平的立方体网格(如图 2 所示)?我们可以想象通过旋转面部来做到这一点,就像在现实世界中一样。d 维立方体的任何两个相连的面都是垂直的。例如,三维立方体的两个面(根据第 II 节中的术语)00 和-00。请注意,在本节的图中,我已经将原点从立方体的中心移动到其中一个顶点,以使展平过程更加清晰。

图 5:两个面的简单展平操作。图片作者。

我们总是可以固定其中一个面,并沿着连接它们的边将另一个面旋转到它的平面,如上图所示。这是我们将在其上构建的基本操作。注意,无论展开发生在多大的空间维度,初始和最终的构型都是唯一的。总是有一个围绕连接两个面的公共边(这里是 y 轴)的简单旋转,可以用矩阵形式表示。在上面的例子中,这个旋转矩阵(x-z 平面上的旋转)是:

图 6:描述 x-z 平面旋转的旋转矩阵。

在计算机上执行此操作的一个微妙之处是,旋转(取决于角度是正还是负)可能会将-00 面带到 00 面的顶部或附近。我们希望避免旋转导致面重叠。如果在旋转矩阵中插入\pi/2 导致面重叠,我们只需插入-\pi/2。

IV-B)两个简单网格

现在,让我们只取一个三维立方体的三个面。比如 00-,0–0 和-00。这三个面如下图所示。

图片作者。

在原始立方体中,它们都是相互连接的。这三张脸的脸图看起来像图 7-a。

图 7:仅对应于立方体的三个面的图形。作者图片

但这不是一棵树。在树中,任意两个顶点之间只有一条路径。在上图中,在-00 和 0–0 之间有两条路径(直接路径和通过第三个顶点 00-)。为了使它成为一棵树,我们必须切掉一边。在图 3-b 中,我们选择剪切-00 和 0–0 之间的顶边。现在我们有了一棵树,我们需要得到相应的网格。

首先,我们选择一个面,在展平的网格中,所有其他面都将旋转到该面的平面中。如果我们打开一个立方体的盒子,这将是放在地板上的那张脸。这一张是“基面”,选择是任意的。在上面的例子中,我们选择 00-(图中的底面)作为基础面。

这里,剩余的两个面直接连接到基础面。我们可以简单地将它们分别旋转到基面的平面,就像前面描述的“基本操作”一样,得到下面的网格。

作者图片

这是一个简单的例子,两个面都必须直接连接到基础面进行旋转。让我们回到图 7-a,这次在 0-0 和 00-之间切边。我们得到了另一棵有效的生成树。让我们保持基面不变。

作者图片

现在,我们必须分两步进行旋转。0–0 面连接到-00 面。如果我们首先旋转-00 面,将 0–0 面带到其平面所需的旋转(旋转矩阵以及围绕其旋转的轴)将会改变(因为现在一切都已旋转)。为了避免这种情况,我们首先旋转 0–0 面并将其带入-00 平面。只有这样,我们才能将这两个面带到 00 面所在的 x-y 平面。这种操作顺序如下所示。

作者图片

请注意,这里 0–0 面需要两次旋转,因为它距离树中的基面(00-)有两次跳跃,而-00 面需要一次旋转,因为它距离基面有一次跳跃。有了这两种情况的直觉,给定我们选择保留的边的生成树,我们可以开发将立方体展平为二维网格的通用算法。

IV-C)用于执行旋转的算法

给定从 d 维立方体到网格的生成树,可以设计一种算法,以正确的顺序将旋转应用于所有的面。首先,我们知道立方体的所有面都需要被访问(并且必须对那些访问中的面执行某些操作)。由于面被表示为图形,所以首先想到的是图形探索算法(探索图形的所有顶点的算法)。

最简单和最广为人知的图探索算法被称为深度优先搜索(DFS 参见 Cormen 等人的《算法导论》[4]的第 22.3 节)。我们不是将它应用于立方体的原始图,而是应用于生成树(它也是一个图,只是边的数量少得多),从基面开始。这是因为它是映射到我们所追求的网格的生成树。

我们需要在所有面上执行的基本操作是旋转,将它们带到与基面相同的平面上。当我们从一个面到下一个面(执行 DFS)时,我们知道根据第 IV-A 节将后者带到与前者相同的平面所需的旋转。因为我们从基面(b)开始,这是唯一没有旋转的面。生成树中直接连接到基础面的面(让我们称它们为“第一层面”)旋转一次。连接到第一层面(“第二层”)的那些得到两次旋转(首先将它们带到与相应的第一层面相同的平面,然后到基础面的平面)。

对于任何给定的面(比如 f),在树中有一条从基面(b)到它的路径。从基面 b 开始,沿着这条路径遇到的每个新面都有一个相应的旋转,可以应用到它,使它与路径中的前一个面在同一个平面。沿着路径累积的这些旋转中的每一个都必须应用于给定的面 f。我们可以通过在树上的深度优先搜索来保存所有这些旋转的记录。DFS 中有一个顶点颜色的概念(再次参考 Cormen 等人[4]的 22.3 节),可以是白色、灰色或黑色。白色意味着图(或树)的顶点还没有被碰到(所有顶点在算法开始时都是白色的)。灰色表示它已被访问过,但探索中其下游的所有顶点尚未被处理,黑色表示算法已处理完该顶点及其所有下游顶点(一旦算法完成,所有顶点均为黑色)。只要我们还在遍历一条路径(还没有完成),沿着这条路径遇到的所有顶点都将被标记为灰色。因此,我们可以维护对应于每个灰色顶点(或面)的要执行的旋转的列表。当一个顶点被标记为黑色时,我们只需从列表中删除相应的旋转。

然而,将这些旋转应用到给定面(f)的顺序也很重要,正如我们在截面 IV-B 的三面网格的第二个示例中看到的。给定面应首先旋转到它直接连接的面的平面,然后旋转到路径中它之前的平面,依此类推,顺序相反,直到它最终与基面在同一平面。这确保了我们不必在其他旋转发生时更新旋转。“stack”数据结构非常适合这种情况,因为它具有“后进先出”的属性(想象一堆脏盘子)。

因此,我们不是在遇到面时应用对应于灰色顶点的所有旋转,而是将旋转和要应用旋转的面存储在一个堆栈中。一旦深度优先搜索完成了对所有顶点的探索,我们实际上通过从堆栈中一次弹出一个面和相应的要应用的旋转来应用旋转。下面是这种算法通过执行所需的旋转序列从立方体到网格的直观演示。

图片由作者创作,使用:https://github.com/ryu577/pyray

而这里的是上述算法的 Python 实现。

这是一个将这种算法应用于常见的三维立方体的视频:

V)计算界限

当我们在第三部分看到我们的第一个二维网格时(图 3),我们问有多少个。在没有确切答案的情况下,我们希望上限和下限尽可能紧密。

我们将在第六节中描述从其中一个开始生成随机网格的方法。如果我们只保留拓扑上不同的网格,我们不仅会建立一个网格库,还会建立一个计算下限,这是我们设法收集的不同数字。任何理论下限最终都会被计算下限所取代。到目前为止,该算法(我偶尔运行它 10 分钟,并添加到网格集合中)已经成功收集了大约 600 个不同的网格(这个计数一直在增加)。因此,一个立方体的二维网格数的当前计算下限是 600。

对于上界,回想一下 IV-C 节中的算法,该算法在可能的情况下从面图的生成树生成有效的网格(这通常是不可能的)。即使在我们用这种方法得到的网格中,也会有与之前看到的其他网格拓扑相似的版本。因此,生成树的数量应该是不同网格数量的一个合理上限。

此外,由于每个网格对应于一个唯一的生成树(但是一个生成树可能不会产生一个网格),因此计算网格数量的一种方法(同时建立一个网格库)是遍历所有可能的生成树。对于每一个,我们检查一个网格是否是可能的,如果它碰巧是一个“拓扑新颖”的网格(意思是我们以前没有见过任何与之拓扑相似的网格),我们把它保存在某个地方。

问题是生成树的数量是 10 个⁰(可以通过将基尔霍夫矩阵树定理应用于图 4 中的图来获得)。因此,即使我们花一毫秒来处理一棵生成树,也要花比目前宇宙年龄更长的时间来穷尽所有的生成树。因此,我们从这种攻击中得到的唯一东西是网格数的一个非常大的上限。有很多方法可以改进这一点,但我们现在不会深入讨论它们,因为它们仍然保持天文数字的上限。

VI)工作网格生成算法

在 IV-C 节中的算法将总是给我们一个对应于生成树的网格,如果这样的网格是可能的话(通常不是这样)。如果我们有办法迭代生成树,我们就可以在可能的地方生成网格

如前一节所述,获得不同网格数量下限的一种方法是简单地开始生成它们,并消除那些与之前看到的网格在拓扑上相同的网格。这种算法将继续“收集”网格,有点像优惠券收集者问题,并维护迄今为止看到的不同网格的“库”。这种方法的一个问题是,我们永远无法确定我们是否以及何时看到了所有的网格。当我们非常接近生成所有网格时,它将变得越来越难“收集”了。到最后,我们随机绘制的每个网格几乎都是以前见过的。因此,如果我们已经生成了许多网格,而没有遇到新的网格,这可能是因为我们已经简单地完成了我们的收集,或者剩下了一些,当随机绘制时,很难看到新的网格。

现在,我们选择忍受这个缺点,简单地收集尽可能多的网格。我们的算法有两部分。

第一种方法从一个给定的网格开始,并从它生成另一个有效的随机网格,这个随机网格保证与前一个不同。这是通过切割第一个网格的两个连接面之间的边来完成的。这创建了两个子网格和两个子树(原始生成树被一分为二)。然后,我们找到另外两个面(一个来自第一个子网格,一个来自第二个)将两个子树“粘贴”在一起,并创建一个新的生成树。因为两个“子树”再次连接,所以能够从任何面到任何其他面的属性被恢复。然后,我们可以使用 IV-C 节中的算法,看看这个新树是否能生成有效的网格。如果没有,我们简单地生成一个新的树,并继续下去,直到我们找到另一个有效的网格。网格以文本文件的形式保存在磁盘上,文本文件是相应生成树的邻接表。

第二部分将新的网格与所有现有的网格进行比较,并删除以前见过的网格。

这些想法在下面的 Python 代码中实现:https://github . com/Ryu 577/pyray/blob/master/pyray/shapes/twod/tst _ sq _ mesh . py。第二部分是真正的计算瓶颈。我们收集的网格越多,将一个新网格与所有以前的网格进行比较的代价就越大。如上所述,这个两部分算法可以永远循环运行,直到它变得非常非常难以找到新的网格。

结论

计算一个四维立方体的二维网格的问题有两个最流行的数学问题的特征。这很容易用一种几乎任何人都能理解的方式来解释和构建。与此同时,似乎即使是我们最先进的工具也无法解决这个问题。至少,我们可以在这个问题上释放我们的计算能力,发现几乎所有的网格。


如果你喜欢这个故事,成为推荐会员:)

https://medium.com/@rohitpandey576/membership

参考文献

[1]论文 1984,其中三维网格被映射到成对的树(【https://unfolding.apperceptual.com】T4)。

[2]马丁·加德纳 1966 年的书(《数学巨著》):https://www . logic-books . info/sites/default/files/The _ 庞 _ 书 _ of _ 数学. pdf

[3] MathOverflow 帖子,其中绘制了所有不同的三维网格(https://math overflow . net/questions/198722/3d-models-of-the-unfoldings-of-the-hyper cube/)

[4]科尔曼等人的《算法导论》

使用线性规划的车队和劳动力计划

原文:https://towardsdatascience.com/fleet-and-workforce-planning-with-linear-programming-16db08c7f91d

线性编程解决一些商业问题的能力预览

Pop &斑马Unsplash 上拍照

B 企业(应该)花费大量的时间和精力来预测他们的产品/服务的需求,或者完成不同任务所需的精力/资源,以使他们的回报最大化,同时避免两种不希望的结果:

  • 缺乏资源或工人来应对需求,这可能导致流程失败或销售和客户损失;
  • 有太多未使用的资源(库存过多)或失业工人,这意味着储存货物的额外成本和员工的机会成本(也许他们可以被分配到其他任务)。

假设一家公司已经成功开发了产品/服务需求的准确预测模型,那么下一步可能是组织产品补给背后的物流,并根据未来需求分配任务。例如,这可能意味着找到以下问题的答案:

i. 从哪个仓库向我的每个商店发送预计在下周/下个月内售出的产品“P”件比较便宜?

二。考虑到每个仓库的可用单位数量,我如何才能做到这一点?

三。可利用的交通资源如何?我如何利用现有的货车/卡车将库存箱子从仓库运到商店?考虑到每辆货车运输的箱子数量有限,哪种选择成本更低?

四。考虑到我的一些员工在上周的国际象棋锦标赛中打架,如果他们被分配到同一辆车上,可能会再次打架,我如何将可用劳动力分配到每辆货车?

接下来的问题是,如何回答这些看起来非常具体且性质不同的问题?嗯,我们需要一个框架,允许我们从头开始构建一个完全可定制的解决方案,在这个框架中,我们可以指定我们的目标和现实世界的约束,这些约束定义了这个目标的可行结果。把现实生活中的问题写成方程组怎么样?

在本文中,我们将模拟这些场景,并使用一种称为线性规划的数学建模方法,对提出的 4 个问题给出一个简单的解决方案。虽然不在数据科学的聚光灯下,但这是一种著名的优化技术/框架,用于求解线性方程组,该方程组由一个目标函数(我们希望最大化/最小化什么,例如最大化利润同时最小化成本)和一组线性约束组成,这些约束指定了我们需要满足的不同条件,以便获得可行的解决方案。

基本上,我们用线性术语写下我们想要解决的问题,指定目标和约束,然后使用优化算法(求解器)以智能和有效的方式浏览数千或数百万个可行和不可行的结果,使我们至少接近最优解。但是,我们所说的最优是什么意思呢?在这个框架中,最优意味着构成最终解决方案的参数的最佳可行配置。请注意,由于这个原因,“最佳”可能指的是完全不同的东西,这取决于企业的偏好/规则。例如,对一个公司来说,满足客户的需求可能比节省运输成本更重要,而对其他公司来说可能不是这样。最后,这一切都归结于业务目标及其约束的正确规范。

一个问题,一个框架,一个解决方案

为了回答前面的问题,我们先介绍一个虚构的场景。上下文将保持简单,因为本文的主要目的只是介绍框架以及我们如何使用它通过开发定制解决方案来解决现实生活中的问题。在开始之前,我想澄清一下,从这里开始出现的图像/等式/代码块都是来自我个人的阐述。好了,我们直奔主题吧。

1.上下文和示例结果

假设你拥有一家公司,拥有一个目录,其中有 5 种产品( P=5 )、 3 个仓库( W=3 )、3 个商店( S=3 )、4 辆货车( V=4 )用于运输产品,以及 8 名员工( E=8 ),这些员工可以被分配到每辆货车上(成对)正如开始提到的,你已经对预期需求有了一个估计。此外,我们将假设由于你已经估计了需求,你也储存了足够的库存来满足它,因此知道你在每个仓库有多少库存。

为了简化问题,我们不考虑产品的单位,而是讨论产品的箱数,即产品的需求和库存是以产品的箱数来衡量的(在现实生活中,在预测以箱/包形式交付的产品需求后,我们需要将原始数字转换为所需的箱数或分组单位数,因为这是运输它们的方式)。

接下来,我们假设您也知道使用任何货车将每种产品的一箱从您的任何仓库运送到您的任何商店的成本。除了可变成本之外,每辆车的使用成本也是固定的(你可以把它看作折旧/维护成本)。

此外,我们假设每辆货车可以行驶的次数是有限的。在这种情况下,限制是 5 次旅行。此外,每辆货车都有一个默认的箱子数量。最重要的是,每辆车(如果使用的话)都需要两人一组(一名司机和一名助手)来操作。此外,我们只能给每个员工分配一辆货车,如果我们这样做,他将获得 1500 美元的固定工资。

最后,在上周举行的国际象棋锦标赛期间,一些员工发生了争吵,因此希望避免将两个有冲突的工人分配到同一辆货车上;事实上,我们有 23 对相互冲突的员工( J=23 )。如果我们最终把他们分配到同一辆货车上,我们将不得不处理后果,即罚款 500 美元。

总之,我们的上下文变量如下:

  • “P”产品= 5
  • “W”仓库= 3
  • “S”商店= 3
  • “V”型货车= 4 辆
  • “E”员工= 8 人
  • “J”对相互冲突的雇员= 23

这些假设是:

  • 我们知道每个商店对每种产品的需求;
  • 我们知道每个仓库里有多少箱我们的产品;
  • 所有的盒子都有相同的大小(为了简化问题);
  • 我们知道每辆车能装多少箱子;
  • 根据产品-仓库-商店-货车的组合,发送一个箱子的成本不同;
  • 每辆货车的使用都有固定成本;
  • 没有运输至少 1 个箱子,货车不能从仓库到商店;
  • 货车最多只能行驶 5 次。我们可以假设它们都发生在同一天;
  • 每辆货车能运送多少箱子是有限制的;
  • 没有一辆货车可以重复同样的行程;
  • 我们需要为每辆货车分配 2 名员工,以便能够成行;
  • 如果我们指派一名员工,我们必须支付他这项任务🙂;
  • 每个员工只能被分配到一辆货车上;
  • 希望避免将冲突的员工分配到同一辆车上。

然后,我们要解决的问题是找到最优(成本更低)的方式将产品从仓库运输到商店,使用不同的货车和工人,同时满足需求、库存、货车和工人分配的约束。

结果看起来像这样:

此表说明了在某些参数(冲突罚金、货车固定成本和员工人数)存在部分差异的不同情况下获得的结果(最终成本)。注意,试验不同参数的可能性是这个框架的主要优点之一。当然,如果我们同时尝试几项改变,比如允许货车进行更多的旅行,同时减少处罚成本和增加冲突数量,分析会更丰富。事实上,我鼓励你以后尝试这些改变。

好的,从现在开始,事情会变得更技术性一些…

2.变量、目标和约束

我们需要建立的第一件事是,我们将在定义问题的方程组中使用的变量。以下是它们的列表,以及它们的描述和值的范围:

既然我们已经指定了变量,我们就来写问题。用数学术语来说,要解决的问题如下:

i) 客观

(1)这是用四项之和表示的目标函数: a) 单位成本之和乘以从仓库【w】发送到仓库【s】使用货车vb)F所用面包车的固定费用总和; c) 员工工资之和 e 分配到面包车vd) 不遵守不将冲突的一对雇员 j 分配到同一辆货车 v 的可选约束的惩罚成本。

ii) 约束

(2)第一个限制规定必须满足每个商店的每个产品 p 的需求,即使用任何车辆v【从每个仓库 w 发送到每个商店 s 的所有产品箱组合的总和**

(3)规定不能寄没有的箱子。换句话说,从仓库发出的产品箱数总和必须低于或等于其可用箱数;

(4)规定我们不能超过货车所能容纳的箱数限制,因此每辆车运输的箱数总和必须低于或等于货车每次行程的容量(【capacity _ v】)。有了这个约束,我们用辅助变量 Z_wsv 来计算货车的行程数,因为 capacity_v 是车辆 v 在每次行程中可以运输的最大箱子数,我们可以将几个产品的运输算作一次行程。此外,该约束隐含地阻止了重复相同行程的可能性;

作者图片

(5)发出信号,表明没有一辆货车可以行驶超过 5 次,同时检查每辆货车是否被使用过。请注意,这个约束与前一个约束是链接在一起的。怎么会?好吧,一旦我们通过使用约束(4)知道是否进行了一次旅行,我们简单地将 Z_wsv 的数目相加,并要求总数小于或等于 5(旅行限制)。这里,由于等式的规定,除非货车闲置,否则 T_v 将等于 1;

作者图片

(6)指定每辆货车将被分配 2 个或零个雇员;

作者图片

(7)要求每个员工只能分配到一辆货车上;

(8)该约束说明了对冲突雇员对的期望约束 j 是可选的。当一对相互冲突的员工j =(E1e2 )被分配到同一辆面包车(A _ ve1+A _ ve2= 2)时,惩罚被激活( G_vj =1)如果只有一个成员被分配到货车,那么 H_vj =1。如果对 j 的冲突雇员中没有一个被分配到货车 v 中,则所有元素都为零。

(9)最终约束规定了每个变量的上限和下限。这里我们声明哪些变量是二进制的,哪些是整数。

完整的问题被表达为:

既然我们已经设法写出了问题,我们可以继续使用谷歌的 或 Python 的工具的来编码这些例子的解决方案。但是,在继续之前,重要的是要强调花时间完成前面的步骤是非常重要的,因为这将使您更好地掌握手头的问题,同时潜在地避免代码中的一些未来错误,并帮助您向数据科学的同事解释您的推理。

3.解决办法

你可以在这个笔记本里查看整个解决方案。

首先,我们导入将在本例中使用的包。

*import numpy as np
import pandas as pd
from ortools.linear_solver import pywraplp*

该解决方案由以下步骤组成:

  1. 设置一个能够复制模拟的种子;
  2. 申报仓库、产品、商店、货车、员工和行程限制的数量;
  3. 为模拟数据设置一些阈值,并为固定工资和将一对有冲突的雇员分配到同一辆货车的处罚设置值;
  4. 生成成本矩阵(每种产品 1 个)、库存向量(每种产品 1 个,显示每个仓库的可用库存)、需求向量(每种产品 1 个,显示每个商店的需求)、每辆货车的容量列表(每辆货车每次行程可以运输多少箱子)以及最终的冲突雇员对列表;
  5. 调用一个求解器的实例,可以用来寻找手头问题类型的解(整数规划或混合整数规划);
  6. 创建变量;
  7. 定义约束条件;
  8. 定义目标函数和问题(最大化/最小化);
  9. 求解并验证结果是否符合约束。

我们从步骤 1-4 开始。当我们模拟这个问题时,成本和数量是用一个随机变量创建的,但是在实际场景中,我们需要使用企业提供的输入。该过程如下所示:

现在,要完成第 5 步,我们需要实例化一个求解器。本例中,我们使用的是来自 Google 的 pywraplp 或者-Tools 。注意在 pywraplp 中有几个可用的解算器,比如 GLOPSCIPGUROBIIBM CPLEX 。由于 GUROBICPLEX 需要许可证,而 GLOP 是为简单线性编程设计的,但是我们手头的问题需要求解器来处理整数或混合整数线性问题,我们将使用SCIP(可以处理整数和混合整数线性问题的最快的非商业求解器之一)。

*solver = pywraplp.Solver.CreateSolver('SCIP')*

之后,我们继续第 6 步,即定义构成线性方程组的变量。

首先,对于产品、仓库、商店和货车的每个组合,我们需要创建一个索引为 x 的变量 p (产品) w (仓库) s (商店) v (货车),它告诉我们产品 p 的整数箱数这些整数变量(求解器。IntVar)被限制为正数(下限 = 0 和上限=solver . infinity())。此外,为了跟踪每个特定的变量和未来的约束,我们仔细地给它们命名。其中一个变量的例子是 x_1_1_1_1 。****

*x = {}
for p in range(P_products):
  for w in range(W_warehouses):
    for s in range(S_stores):
      for v in range(V_vans):
        x[p,w,s,v] = solver.IntVar(lb=0,
                                  ub=solver.infinity(),
                                  name=f"x_{p+1}_{w+1}_{s+1}_{v+1}")*

其次,我们生成布尔变量(解算器。BoolVar)T _ v它告诉我们是否使用了 van v 它需要能够为其使用分配成本。

*T = {}
for v in range(V_vans):
  T[v] = solver.BoolVar(name=f"T_{v+1}")*

第三,我们创建变量 A_ve ,如果 employee e 被赋值给 van v ,则发出信号。我们需要这样做,以便能够考虑员工工作的成本。

*A = {}
for v in range(V_vans):
  for e in range(E_employees):
    A[v,e] = solver.BoolVar(name=f”A_{v+1}_{e+1}”)*

第四,我们生成变量 Z_wsv ,这是一个辅助布尔变量,用于计算货车的行程次数。他们每个人都会告诉我们从仓库 w 到商店 s 的行程是否被分配给货车

**Z = {}
for v in range(V_vans):
  for w in range(W_warehouses):
    for s in range(S_stores):
      Z[w,s,v] = solver.BoolVar(name=f”Z_{w+1}_{s+1}_{v+1}”)**

最后,我们生成变量 H_vjG_vjH_vj 表示冲突对 j 中只有一个冲突员工被分配到 van v 。变量 G_vj 表示一对冲突雇员 j 中的两个成员都被分配到 van v 中。

**H = {}
G = {}
for v in range(V_vans):
  for j in range(len(J_employees_conflicts)):
    H[v,j] = solver.BoolVar(name=f"H_{v+1}_{j+1}")
    G[v,j] = solver.BoolVar(name=f"G_{v+1}_{j+1}")**

创建变量后,我们继续生成线性约束(我们使用解算器方法。添加来完成此操作)。关于需求约束,我们声明 sum ( 求解器。每个仓库发出的库存总和必须等于商店的需求。在库存约束的情况下,我们指定发送到每个商店的库存总和必须低于或等于每个仓库的可用库存。在这两种情况下,我们都使用来自库 itertools 的函数 product 来获得可能组合(仓库、货车)和(商店、货车)的笛卡尔乘积。

***# Demand constraint** for p in range(P_products):
  for s in range(S_stores):
    solver.Add(
    solver.Sum(
    [x[p, j[0], s, j[1]] for j in itertools.product(
                                  range(W_warehouses),
                                  range(V_vans))]) == demands[p][s],
                                  name='(2) Demand')**# Stock constraint** for p in range(P_products):
  for w in range(W_warehouses):
    solver.Add(
    solver.Sum(
    [x[p, w, j[0],j[1]] for j in itertools.product(
                                   range(S_stores),
                                   range(V_vans))]) <= stocks[p][w],
                                   name='(3) Stock')*

注意,我们使用参数“name”为每个限制添加了一个名称。这将允许我们使用函数解算器在标准问题表示(LP 格式)中识别它们。ExportModelAsLpFormat* ,我强烈建议你用的 :*

*print(solver.ExportModelAsLpFormat(obfuscated=False))*

下面是约束条件的快照:

这是需求和库存约束的一个例子(记住 x 采用的形式是 x_pwsv )。第一个,按照要求,显示了使用任何货车从任何仓库 w 发送到商店s = 3v 的产品的箱数 p=5 必须等于商店3 需求=3 库存约束表明,从仓库 w=1 发送到任何商店、使用任何货车的产品 的箱数 p = 1p = 1w = 1**必须低于或等于库存=11 仓库 1 随机生成的产品 1 的库存。这些特定约束的其余部分遵循相同的逻辑。

接下来,我们添加行程和货车的使用约束。对于第一个问题,我们要求每辆运货车每次运输的箱子不能超过其容量,同时还要检查从仓库【w】到仓库【s】的行程是否分配给了运货车。第二个约束帮助我们验证是否使用了 van v**

****# Trip verification constraint**
for v in range(V_vans):
  for s in range(S_stores):
    for w in range(W_warehouses):
      solver.Add(
      solver.Sum(
      [x[p, w, s, v] for p in range(P_products)])
      <= capacities[v]*Z[w, s, v],
      name='4) TripVerification')**# Van use and trip limit constraint** for v in range(V_vans):
  solver.Add(
  solver.Sum(
  [Z[j[0], j[1],v] for j in itertools.product(
                            range(W_warehouses),
                            range(S_stores))]) <= trip_limit*T[v],
                            name='5) TripLimit')**

让我们来看看这些约束的示例:

一方面,约束 TripVerification_63 检查 van v=4 是否被分配从仓库 w=3 到商店 s=3 。注意,术语 Z_wsv 乘以 4,就是面包车 v 的容量。这意味着,为了符合约束条件,运输数量的最高总和不能大于 4。同样,在任何情况下,对于所有大于 0 的量, Z_wsv 必须等于 1。这就是我们如何核实一次旅行是否被分配完成。

另一方面,三极限约束的例子意味着货车 v=1 的所有可能行程路径的总和( w_s )必须低于或等于 5(我们在开始时设置的行程极限),因为我们有术语 -5T_1 。请注意,这最后一项也将告诉我们是否会使用 van v=1 。相同的逻辑适用于该类剩余约束中的其余货车。

在这之后,我们跟进最终的约束:*(6)employee requirement(7) JobLimit(8)conflict verification。关于员工要求*我们要求使用货车时,必须指派两名员工。接下来, JobLimit 意味着我们不能将一名员工分配到一辆以上的货车上。最后,构建 ConflictVerification 来验证每对 J 员工冲突是否被分配到同一辆货车。**

****# Number of employees per van**
for v in range(V_vans):
  solver.Add(
  solver.Sum(
  [A[v,e] for e in range(E_employees)]) == 2*T[v],
  name='6) EmployeeRequirement')**# Number of vans an employee can be assigned to** for e in range(E_employees):
  solver.Add(
  solver.Sum([A[v,e] for v in range(V_vans)]) <=1,
  name='7) JobLimit')**# Verification of the constraint compliance** for v in range(V_vans):
  for idx,j in enumerate(J_employees_conflicts):
    solver.Add(
    solver.Sum([A[v,j[0]-1]])==-A[v,j[1]-1]+H[v,idx]+2*G[v,idx],
    name='8) ConflictVerification')**

前两者的一个例子是:

这两个约束都很简单。 EmployeeRequirement_69 告诉我们,要使用货车 v=4 ,我们至少需要为其分配 2 名员工。 JobLimit 指定雇员 e=1 只能被分配到一辆货车。

关于最后一个约束,可能是最难理解的一个,我们有两个例子:

conflict verification _ 152检查如果冲突的一对员工 j=8 ,由员工 e=2e=3 组成的 v=4 。如果这些雇员中只有 1 个被分配给它,那么 H_4_8 必须等于 1,等式才能等于 0。如果两个雇员都被分配到这辆货车,那么 G_4_8 必须等于 1,等式才能等于 0。请注意,只有第二种情况会对总成本产生影响,因为 500 美元的罚金将被激活。在 ConflictVerification_152 的情况下,我们可以直接看到,它检查的是完全相同的东西,但对于由雇员 e=2e=4 组成的冲突对 j=9

既然我们已经写完了一系列约束条件,我们准备继续第 8 步,即目标函数的定义和问题的类型(最大化/最小化)。为此,我们首先创建一个列表来保存前面描述的每一项,因为目标函数就是所有项的总和。然后,在添加了所有的项之后,我们指定我们想要解决一个最小化问题( solver)。最小化

****# Objective Function** objective_function = []**# First term -> Transportation variable costs** for p in range(P_products):
  for w in range(W_warehouses):
    for s in range(S_stores):
      for v in range(V_vans):
        objective_function.append(costs[p][w][s][v] * x[p, w, s, v])**# Second term -> Transportation fixed costs** for v in range(V_vans):
  objective_function.append(costs_v[v]*T[v])**# Third term -> Salary payments** for v in range(V_vans):
  for e in range(E_employees):
    objective_function.append(fixed_salary*A[v,e])**# Fourth term -> Penalties for not avoiding conflicts** for v in range(V_vans):
  for j in range(len(J_employees_conflicts)):
    objective_function.append(conflict_penalty*G[v,j])**# Type of problem** solver.Minimize(solver.Sum(objective_function))**

这是整个目标函数,其中所有变量都是二进制的,除了 x ,这是一个整数:

最后,我们使用 Solve 方法运行优化算法。

**# Call the solver method to find the optimal solution
status = solver.Solve()**

剩下的就是检查解决方案了。为了做到这一点,我们称之为解决方案状态,如果它是最优的( pywraplp)。我们打印目标函数的值,如果不是这样,我们应该检查我们以前的工作,寻找问题定义中的不一致。

**if status == pywraplp.Solver.OPTIMAL:
  print(
   f'\n Solution: \n Total cost = ${solver.Objective().Value()}'
   )
else:
    print(
    'A solution could not be found, check the problem specification'
    )**

对于此模拟,解决方案是:

这意味着使用货车将产品从仓库运送到商店的最佳运输成本最终为 22,499 美元。现在我们必须决定这是否足够好。如果我们认为可以通过修改问题的定义找到更好的解决方案,我们应该考虑如何添加/删除或修改一些约束,或者如果可能的话,如何改变上下文变量。如果我们认为这是可以的,那么下一个自然的步骤将是检查模型变量的最优值,因为从计划的角度来看,能够辨别哪些是将要使用的货车,哪些是将要在这些任务中工作的雇员是非常相关的;他们中的哪些人被分配到哪辆货车;哪些是需要计划的行程以及在每个行程中运输的产品的数量和类型。

为了完成这篇文章,我们假设没有什么可做的了,这样我们就可以通过提取解决方案的细节来检查相关变量的最优值。

4.车队和劳动力计划详细信息

为了开始提取有用的细节,我们遵循一个简单的程序:I)提取每个变量的最佳值;ii)预处理并将数据重新排列到表格中;iii)使用不同的过滤器查询表格。这里是我)和 ii):

**result_list = []**# Extract the solution details and save them in a list of tables** for var in [x,Z,T,A,H,G]:
  variable_optimal = []
    for i in var.values():
      variable_optimal.append(i.solution_value())

      var_result=list(zip(var.values(),variable_optimal))

      df=pd.DataFrame(var_result,columns=['Name','Value'])

      result_list.append(df)**# Concatenate the tables and extract the variable names** results=pd.concat(result_list)
results['Name']=results['Name'].astype(str)
results.reset_index(drop=True,inplace=True)
results['Variable']=results['Name'].str.extract("(^(.)\d?)")[0]
results['Variable']=results['Variable'].str.upper()
results['Value']=results['Value'].map(int)**# Create a mapping of variables and indices to simplify the analysis** variable_indices={'X':'X_product_warehouse_store_van',
                  'A':'A_van_employee',
                  'T':'T_van',
                  'H':'H_van_pair',
                  'G':'G_van_pair',
                  'Z':'Z_warehouse_store_van'}results['Indices']=results['Variable'].map(variable_indices)**# Order the columns** results=results[['Variable','Indices','Name','Value']].copy()**

这是主表的一个示例:

接下来,在创建我们的主数据框架之后,我们通过用“T”过滤列变量(表示车辆使用情况的二进制变量)和用 1 过滤列(这意味着最优解意味着我们需要使用这些货车)来寻找我们要使用的货车:

**list(results[(results[‘Variable’]==’T’)&(results[‘Value’]==0)].Name)**

结果显示将使用 vans 1、2 和 3。

对于下一部分,我们将只搜索与 van 1 相关的变量,从回答使用该车辆进行哪些旅行开始。这里重要的是要记住,van v=1 对应于索引 v=0 :

**trips_van_1=[]
for w in range(W_warehouses):
  for s in range(S_stores):
    for v in range(V_vans):
      if v==0:
        trips_van_1.append(str(Z[w,s,v]))trips_df=results[(results['Variable']=='Z')&(results['Value']>0)]display(trips_df[trips_df['Name'].isin(trips_van_1)])**

我们可以看到,货车 1 被分配了以下 5 次行程:

  • 仓库 1 存放 1 和 3;
  • 仓库 2 到商店 2;
  • 仓库 3 存放 2 和 3;

接下来,我们需要找到将负责货车 1 的交付操作的员工:

**employees_van_1=[]
for v in range(V_vans):
  for e in range(E_employees):
    if v==0:
     employees_van_1.append(str(A[v,e]))

employees_df=results[(results['Variable']=='A')&(results['Value']>0)]display(employees_df[employees_df['Name'].isin(employees_van_1)])**

该表告诉我们,雇员 5 和 6 被分配到货车。现在,关于货车 1 的最后一个问题是,在每次行程中,它必须运输多少箱子和哪些产品。让我们在从仓库 2 到商店 2 的行程中这样做:

**transport_df = results[(results['Variable']=='X')&(results['Value']>0)]transport_trip_2_2 = []for p in range(P_products):
  for w in range(W_warehouses):
    for s in range(S_stores):
      for v in range(V_vans):
        if w==1 and s==1 and v==0:
          transport_trip_2_2.append(str(x[p,w,s,v]))display(transport_df[transport_df['Name'].isin(transport_trip_2_2)])**

因此,在从仓库 2 到商店 2 的行程中,货车 1 将运输 5 个箱子(其最大容量),4 个产品 2 和 1 个产品 3。请注意,在这些示例中,我们验证了模型的预期效果,即考虑了约束条件以获得最佳解决方案。最后,我们只需要检查冲突的两个雇员的约束条件发生了什么:

**results[(results['Variable']=='G')&(results['Value']!=0)]**

嗯,看起来这一对相互冲突的雇员 j=14 (雇员 3 和 4)被分配到最优解中的货车 2。如果是这样,那么 A_2_3 和 A_2_4 应该等于 1,我们来查一下:

**results[(results['Variable']=='A')&(results['Value']!=0)]**

就这样,我们终于完成了例子。

结束语

总而言之,我们已经看到,将现实生活中的问题写成线性方程组确实是可能的。更重要的是,我们也能够解决它们。当然,找到一个最佳解决方案严格地依赖于环境变量、目标和约束集。然而,真正复杂的设置可能会使找到最佳解决方案变得更加困难,因为需要分析更多的参数组合,这反过来需要更多的计算能力。

最后,尽管本文中提供的示例与车队和劳动力规划问题有关,但该框架的应用范围要广得多。严格地说,我已经用这种方法解决了与物流供应链管理定价和收入管理相关的问题,但是可能的应用更进一步,因为它也经常用于解决与医疗保健优化城市设计管理科学体育分析相关的问题。

对于那些到达这一步的人,我希望你能够获得一些见解来构建你自己的解决方案。别担心,尽管可以说最难的部分是定义问题,但是一旦你做了,接下来的步骤就很简单了。感谢阅读!

别忘了喜欢和订阅更多与解决真实商业问题相关的内容🙂。

参考

[1]线性规划:基础与扩展(运筹学国际丛书&管理科学第 285 册)

飞行影响:将碳排放加入旅程

原文:https://towardsdatascience.com/flight-impact-adding-carbon-emissions-to-the-itinerary-9ebdf7ad5b1c

构建一个交互式应用程序,为关注气候的旅行者提供支持

克里斯·莱佩尔特在 Unsplash 上的照片

对许多美国人来说,航空旅行是生活中根深蒂固的一部分。无论是去看望家人度假,参加会议,还是快速休假,我们中的许多人在准备起飞时都没有考虑到我们的飞行可能会有非货币成本。我们的旅行计划中经常缺少的是对旅行碳排放的调查。虽然大多数旅行者没有考虑到环境影响,但欧洲运输和环境联合会警告说,随着航空排放到 2050 年增加一倍或两倍,该行业可能会消耗全球碳预算的 1/4。

关于为什么关注个人的“碳足迹”不是遏制气候危机的有效方法,有许多非常有见地的论点。然而,航空业确实提供了一个有趣的机会。2018 年,只有 11%的全球人口乘坐过飞机,而仅仅 1%的人却承担了 50% 的航空排放。这意味着这个小群体中的个体有一种独特的能力,可以通过相对较小的行为变化产生影响。

带着这个想法,我着手我的最后一个项目 METIS 数据科学训练营,希望建立一些东西,让消费者能够自我教育,并在旅行中做出明智的决定。就这样,飞行冲击诞生了!(点击本文底部链接查看!)

这项任务

在这个项目中,我和我的同学们面临着建立一个完整的数据管道的挑战,从 API 到交互式 web 应用程序。Flight Impact 背后的管道如下:调用 Climatiq API (CC BY-SA 4.0),清理 Google Colab 中的数据,存储在 MongoDB 中,最后在一个. py 文件中操纵它,该文件用于从 Github 启动我的 Streamlit 应用程序。

Climatiq API 提供了从客运到航运等各种活动的排放量计算。当给定两个机场的组合时,API 返回目的地之间单程的单个经济舱乘客的二氧化碳排放量(以千克为单位)。我能够指定我想要使用的计算标准,并且我选择了美国环保局对短程、中程和远程飞行的计算方法,以确保可重复性。使用世界大型机场(每年服务数百万乘客)的列表和 Python 请求库,我自动化了 180,000 对全球目的地的 API 请求。

接下来,我在 Google Colab 中清理了我的数据,添加了一些对前端用户体验有用的功能,如机场的完整国家名称(而不仅仅是一个代码)和航班排放量的计算(以吨为单位)。在 Google Colab 上,我使用 pymongo 将清理后的数据插入 MongoDB 数据库,该数据库托管在 CleverCloud 上。

在 Streamlit 中构建应用

管道的最后一步是在一个. py 文件中构建飞行影响的用户体验。 Streamlit ,一个将 Python 数据脚本转化为 web 应用的平台,允许你直接从 Github repo 中的文件启动应用。你只需要在代码中添加一些 Streamlit 命令来指定特性,比如下拉菜单和地图(这里有一个链接指向我的 Streamlit。py 文件,如果你正在寻找一个例子)。在这个过程中,我学会了不要低估 Streamlit 的缓存特性的重要性。每当用户改变页面上的某些内容时,Streamlit 都会重新运行脚本,但是在函数之前添加一个简单的@st.cache 命令会告诉应用程序仅当输入或函数本身发生变化时才重新运行该函数。这有助于应用程序运行更快,使用更少的内存。

简介:飞行冲击

当用户访问 Flight Impact 时,我们的目标是让他们能够探索自己的选择,并以更明智的消费者身份离开。用户可以:

  • 查看所有从其所在城市出发的全球航班,以及每个航班的相关碳排放量
  • 按出发国家或城市、目的地国家或城市或排放限制进行过滤,以比较各种路线
  • 根据他们的路线是否已经确定,查看影响较小的替代方案或使他们选择的航班购买更省油的方式,例如经济舱、避免中途停留、只乘坐满员航班

当个人在寻找参与解决气候危机的方法时,我们的旅行习惯可以发挥作用。展望未来,让碳排放成为旅行计划过程中与获得靠窗座位同样重要的一部分。

自己试试这个应用这里!注意:此链接使用 Streamlit 的免费版本,一段时间后可能会超过使用限制。如果是这样的话,下面的视频也展示了飞行撞击体验。

要深入了解我对这个项目的代码或演示,请参见 项目回购

floWeaver 将流数据转换成 Python 中的 Sankey 图

原文:https://towardsdatascience.com/floweaver-turn-flow-data-into-a-sankey-diagram-in-python-d166e87dbba

实践教程

floWeaver 将流数据转换成 Python 中的 Sankey 图

用几行代码创建和定制一个 Sankey 图

动机

假设您想要显示两个域(例如,公司和工作)之间的映射,或者从一个位置到另一个位置的路径。

如果可以用图表把这种关系可视化不是很好吗?

作者图片

这时桑基图就派上用场了。桑基图是描述流量的图表,其中线条的宽度与流量成比例。

流程可以是:

  • 从一个地区到另一个地区的运动
  • 能量从源头到目的地的运动
  • 政党之间选民的流动

还有更多。

在本文中,我们将学习如何使用 floWeaver 在 Python 中创建一个 Sankey 图。

floWeaver 是什么?

floWeaver 是一个 Python 库,允许您轻松创建和定制 Sankey 图。

要安装 floWeaver,请键入:

pip install floweaver

要在你的 Jupyter 笔记本上显示桑基图,安装 ipysankeywidget

pip install ipysankeywidget
jupyter nbextension enable --py --sys-prefix ipysankeywidget

确保 ipywidgets 已启用:

jupyter nbextension enable --py --sys-prefix widgetsnbextension

酷!现在我们已经准备好试用 floWeaver 了。

准备数据

让我们从从 Kaggle 加载 Cruise_Travels 数据集开始。

接下来,按照EMBARK_PORTDISEMBARK_PORTCRUISE_REGION对数据帧进行分组。

floWeaver 根据它们的名称将不同的列用于不同的目的。具体来说,

  • source指定流程开始的位置
  • target指定流程结束的位置
  • type指定流的类型
  • value指定流量

因此,我们将重命名一些列,以便它们符合 floWeaver 的格式。我们将使用:

  • EMBARK_PORTsource
  • DISEMBARK_PORTtarget
  • CRUISE_REGIONtype
  • PRICE_PAIDvalue

创建桑基图

开始

首先创建两个组:embark_portdisembark_port

接下来,指定图表中每个组的顺序。列表中的第一组将放在最左侧。列表中的最后一组将放在最右侧。

为了指定哪些连接应该出现在图中,我们将使用Bundle:

将所有内容放在一起,创建一个桑基图:

作者图片

这看起来不是很有用,因为所有端口都被归入各自的类别。让我们使用Partition来分隔这些端口:

作者图片

不错!这样更好看!在上图中:

  • 左边的节点代表不同的上船港口。
  • 右边的节点代表不同的卸载端口。
  • 这条线显示了从一个港口到另一个港口的航行路线。
  • 线越粗,旅行就越贵。

组合不同的类别

上面的图表看起来有点乱。我们还可以将不同的类别组合成一组,使图形看起来更清晰。让我们试着根据港口所属的大洲对不同的港口进行分类。

作者图片

不错!图表现在看起来更清晰了。

彩色亮度

除了线条的粗细,我们还可以用颜色强度来表示一个流的值有多大。

作者图片

我们也可以使用palette属性来改变调色板。让我们用Blues_9调色板来表现海洋的颜色。

作者图片

找到所有调色板的列表这里

现在更容易看出哪些旅行最贵。似乎最昂贵的旅行是:

  • 从大洋洲到欧洲
  • 从澳大利亚到欧洲
  • 从欧洲到欧洲
  • 从欧洲到亚洲

区分不同的流动类型

type代表巡航区域。为了根据线的类型给线着色,我们将使用SankeyDefinition中的flow_partition属性。

作者图片

对相同类型的线进行分组

为了对相同类型的线进行分组,我们将在图的中间添加节点。这些节点被称为路点。

作者图片

有意思!从图中可以看出,大多数穿越西部和东南亚的旅行似乎比穿越其他地区的旅行更贵。

结论

恭喜你!您刚刚学习了如何使用 Python 从流数据集创建桑基图。我希望这篇文章能给你提供使用 Sankey 图分析你自己的流量数据所需的知识。

随意发挥,并在这里叉这篇文章的源代码:

https://github.com/khuyentran1401/Data-science/blob/master/visualization/floweaver_example/travel.ipynb

我喜欢写一些基本的数据科学概念,并尝试不同的数据科学工具。你可以在 LinkedIn 和 T2 Twitter 上与我联系。

这个回购如果你想检查我写的所有文章的代码。在 Medium 上关注我,了解我的最新数据科学文章,例如:

https://python.plainenglish.io/find-the-top-bootcamps-for-data-professionals-from-over-5k-profiles-92c38b10ddb4

参考

伊万·佩雷斯。2020–02–16.游轮旅行。
CC0:公共领域。从https://www.kaggle.com/ivanpv/cruise-travels 取回 2021–01–12

荧光神经元细胞数据集—第一部分

原文:https://towardsdatascience.com/fluorescent-neuronal-cells-dataset-part-i-ac123196b963

一种新的目标分割、检测和计数基准

国家癌症研究所Unsplash 上拍摄的照片

在过去十年中,计算机视觉成功的关键因素之一是大量标记数据的可用性。

事实上,收集智能手机、网络摄像头和其他现代设备图像的可能性产生了前所未有的大量数据,描述了广泛的日常生活环境。随着可用性的提高,对所描绘场景的熟悉程度使得求助于未经培训的操作员来注释图像成为可能,因此需要相对较低的成本。反过来,这允许研究人员和从业人员通过利用监督学习技术来解决几个学习任务,如人脸识别关键点检测自动驾驶等。

不幸的是,这并不适用于更多的利基应用,在这些应用中,数据更少,标记需要一些专业知识。例如,考虑像生物学或生命科学这样的领域,由于缺乏适当规模的精选数据,这些领域的技术水平仍然落后于更主流的应用。

在本系列的第一篇文章中,我们将呈现 荧光神经元细胞数据集 :收集了小鼠脑切片283 张高分辨率图片 (1600x1200 像素)和相应的地面真相蒙版

图一。数据集-图片由作者提供

数据

荧光神经元细胞数据集(莫雷利等人,2021) 是 283 张高分辨率(1600x1200 像素)小鼠脑切片图片的集合,可在此免费下载

这些图像是通过成像技术荧光显微镜获得的,以研究啮齿动物麻木的机制( Hitrec 等人,2019 )。在实践中,在小鼠接受受控的实验条件后,一种标记被注射到它们的大脑中,以“突出”一些感兴趣的神经元细胞。因此,这些结构在合成的、通常较暗的背景上表现为亮度和饱和度不同的黄色斑点(图 1,顶行)。

虽然图片的数量有限,特别是与计算机视觉应用中通常涉及的大规模数据集相比,但它们的高分辨率允许将它们分成更小的块。通过这种方式,再加上典型的增强管道,数据总量可以增加数百倍,从而保证有足够的信息可供学习。

真相面具

除了图像,数据还包含用于语义分割的相应地面实况注释,即二进制掩码,其中如果每个像素属于一个细胞,则标记为 255(白色),否则标记为 0(黑色)(见图 1,底部一行)。

为了减轻标记的工作量,开发了半自动程序,包括自适应阈值和领域专家的手动分割。

特别是,大多数图像(252 张图片)首先通过自适应阈值自动注释。这意味着根据每个图像的像素强度分布来选择亮度截止值,所有高于该阈值的像素都被视为细胞。

图二。自适应阈值处理—图片作者

然后,这些草稿被手动修改以排除假阳性和/或增加假阴性。

相反,剩下的 31 幅图像是由领域专家手工分割的。后一组中包括了特别相关的例子,目的是为最具挑战性的观察收集高度准确的注释。

学习任务

荧光神经元细胞数据集可用于研究不同的学习任务。

有了二进制基础事实掩码,更自然的方法是在语义分段设置中按原样利用标签。或者,通过在分割对象周围绘制边界框,可以很容易地将其扩展用于对象检测。此外,可以通过仅考虑每个图像中的细胞总数来关注对象计数,从而将数据作为回归问题进行分析。

在第一篇文章中,我们介绍了荧光神经元细胞数据集:一组精选的荧光显微镜图像和相对真实的遮罩。

我们介绍了它的起源、数据格式,并简要提到了一些可以用这些数据来研究的学习问题。

在下一篇文章中,我们将更详细地研究这些数据,强调与这些数据相关的一些特殊特征和挑战。

如果你喜欢这个话题,你可以在[ 12 中阅读更详细的讨论。另外,你可以继续下载数据集,用原始论文的代码做实验,用数据玩自己。
让我知道你在评论里发现了什么!

参考

[1] L. Clissa,通过机器和深度学习支持科学研究:荧光显微镜和操作智能用例 (2022), AlmaDL [2] R .莫雷利等人,通过 c-ResUnet 的深度学习实现荧光显微镜中的细胞计数自动化 (2021),科学报告

荧光神经元细胞数据集—第二部分

原文:https://towardsdatascience.com/fluorescent-neuronal-cells-dataset-part-ii-e1ac27e26d7

独特的特征和挑战

JESHOOTS.COMUnsplash 上拍照

在本系列的第二篇文章中,我们将更详细地浏览 荧光神经元细胞(【FNC】)的数据,强调它们的一些特有特性和挑战。

如果您错过了第一部分,请查看更多有关数据收集方式及其含义的详细信息:

独特的特征

荧光神经元细胞数据集带有一些特殊的特征,可能值得一提,以帮助分析这些数据。

RGB 通道

由于有意选择了特定波长的光线,图片主要由两种流行的色彩构成:

  • 荧光标记发出的黄色调
  • 背景的暗色调。

因此,要填充的颜色通道只有红色和绿色,它们的组合产生黄色,而蓝色通常是空的(图 1 )。

图一。像素强度分布— 作者图片

因此,通过 3D 色彩空间表示 FNC 数据可能是多余的,并且较低维度的表示可能是足够的(例如灰度)。

另一方面,分散在额外维度中的剩余信息可能有助于从背景中辨别细胞的像素——为什么要丢弃它呢?然而,RGB 并不是唯一可用的色彩空间,所以人们可能会想,对于 FNC 数据的分析来说,其他表示法是否可能更方便

图 2 将一幅图像的像素显示为三维色彩空间中的点。颜色反映了图像中像素的外观,而位置是使用 RGB(左)或 HSV (右)编码确定的。

图二。 RGB 与 HSV 色彩空间— 作者图片

在 RGB 表示中,点是杂乱的,并且几乎是对齐的。相反,云在 HSV 空间中分散得多,这可以促进细胞像素和背景之间更容易的分离。

这一观察表明不同的色彩空间对于特定的学习任务来说或多或少都是方便的,因此选择一个色彩空间可能会影响结果。出于这个原因,FNC 数据集的作者在他们为这些数据提议的 c-ResUnet 架构中插入了一个学习过的色彩空间转换。

细胞特征

除了技术规格,考虑要分割/检测/计数的对象的特征也很重要。

例如,我们可以检查像面积最大值 费雷特直径 这样的量的分布,以了解不同神经元细胞之间大小的可变性。

图 3。细胞面积和费雷特直径— 图片由作者提供

这两个指标表明,大多数细胞尺寸较小,第 75 百分位分别约为 150μm 和 21μm。尽管如此,这两种分布呈现长尾效应,延伸到更高的值,达到上述度量值的三倍以上。

另一个重要的方面是每张图片中对象的数量。

图 4。计数分布— 图片作者

大多数图像呈现低数量的神经元,中值为 21,第一个峰值为 56 个图像,没有细胞 ( # cells=0 )。剩下的那些反而形成了一条非常长的尾巴——事实上是分布的一半——在一幅图像中,较高的值挤在几个局部峰值周围,最多可达 68 个单元。

总而言之,这些细胞在大小、形状和数量方面表现出相当的异质性。因此,这需要一个足够灵活的模型来处理这种可变性。

挑战

FNC 数据集提出了一些在训练期间必须解决的特定挑战。

阶级不平衡

其中之一肯定是背景和细胞像素之间的极度不平衡。

图 5。阶层失衡— 作者图片

几乎 90%的图像包含不到 1%的细胞像素。更重要的是,这个百分比在最好的情况下不超过 5%,最大值为 4.86%。

就绝对值而言,在 50%的图像中,背景像素大约是细胞像素的 20 到 300 倍,分布的重要部分延伸到超过 1000 倍。

当然,学习任务通常会受到班级比例失调的影响,因此对 FNC 数据的分析应该考虑必要的补救措施来处理它。

确凿的例子

除了班级不平衡之外,一般来说,FNC 和荧光显微镜成像也面临一些特殊的挑战。

图 6。挑战— 作者图片

例如,有时多个单元靠得很近,甚至相互重叠。在这种情况下,精确的分割对于正确处理细胞团块至关重要。因此,可能需要一些技巧来帮助模型分离不同的细胞实例(例如分水岭后处理)。

嘈杂的标签

此外,复杂性的一个重要来源是由于细胞识别中固有的任意性而导致标签中存在噪声。

事实上,有时甚至人类专家也不同意是否应该将标记染色视为细胞。因此,可能会发生类似的例子在图像上被稍微不同地标记的情况。

不幸的是,这个问题很难解决。然而,在评估结果时,人们可能希望将它考虑在内,或者至少在查看纯度量时意识到这个问题。

史前古器物

最后,学习任务受到偶然存在的荧光团积累的阻碍,这些荧光团产生与细胞非常相似的发射。

当这种情况发生时,图片可能包含虚构的物体或在形状、大小或颜色方面类似神经元细胞的无趣结构。

图 7。生物文物— 作者图片

这些伪像可能从细丝和点状伪像(图 7 和图 8 )的小区域变化到更大的结构,如图 7 中的条纹或图 8 中的“马卡龙”形物体。

图 8。技术神器— 作者图片

因此,该模型确实需要理解细胞的形态,并将其与颜色信息一起考虑,以便识别它们。

干得好,走到了这一步!

在本文中,我们介绍了在荧光神经元细胞数据集上执行的一些 EDA,强调了在分析这些数据时要解决的一些特殊特征和挑战。

在本系列的下一篇也是最后一篇文章中,我们将讨论一些建议的度量标准,以评估特定于这些数据的检测和计数性能。

如果你喜欢这个话题,你可以在[ 12 中阅读更详细的讨论。另外,你可以继续下载数据集,用原始论文的代码做实验,用数据玩自己。
让我知道你在评论里发现了什么!

参考

[1] L. Clissa,通过机器和深度学习支持科学研究:荧光显微镜和操作智能用例 (2022), AlmaDL [2] R .莫雷利等人,通过 c-ResUnet 的深度学习实现荧光显微镜中的细胞计数自动化 (2021),科学报告

荧光神经元细胞数据集—第三部分

原文:https://towardsdatascience.com/fluorescent-neuronal-cells-dataset-part-iii-287c2a4f1a22

评估的基准指标

斯科特·格雷厄姆Unsplash 上拍照

在本系列的第三篇也是最后一篇文章中,我们检查了一个特定于任务的指标列表,它非常适合对 荧光神经元细胞 ( FNC ) 数据的分析。

如果您错过了第一部分,请查看更多关于 i) 数据是如何收集的以及它们代表了什么的详细信息:

ii)FNC 数据相关的具体挑战:

模型评估和性能评估是数据分析管道中的关键步骤。当然,有各种方法可用于此目的。

鉴于这种多样性,需要记住的关键方面是每种策略都强调模型的不同功能。因此,性能可能会因参考指标的不同而有很大差异。

由于这个原因,我们必须明智地选择评估计划,以反映我们模型的最终用途。

在下文中,我们将讨论一些适用于 FNC 数据的指标。具体来说,我们根据学习任务考虑 3 种场景:语义分割、对象检测对象计数。

细分指标

扎克里·纳尔逊Unsplash 上的原始照片。作者添加的注释。

对于语义分割,我们可以采用标准的度量,例如 骰子系数平均交集超过并集

然而,它们可能被对边界单元的主观识别和注释中潜在的不准确性所破坏。因此,我们在解释这些指标时需要考虑到这一点。

噪声的主要来源来自注释过程。事实上,地面实况标签是用半自动方法产生的,包括自适应阈值和手动注释。前者生成具有锯齿状单元轮廓的遮罩,而后者呈现具有更平滑边界的对象。

因此,即使大部分细胞被正确识别,我们也可能在边界分割中观察到微小的重复错误。

因此,单一指标值不足以进行真实的评估。相反,一个彻底的评估需要一个更大的画面,并且必须适合分析的最终目标。

在实践中,当目标是精确细分时,建议追求更高的性能。相反,当最终的兴趣更多地在于识别对象时,我们可以放宽要求。

检测指标

阿诺·塞纳尔在 Unsplash 拍摄的照片

关于对象检测度量,可以采用常用的指标,如 F1 得分、精度和召回率 。确定的关键要素是真阳性、真阴性和假阳性的定义。事实上,这必须根据我们数据的具体特征来定制

在 FNC 的例子中,设计了一个专用算法。这使得预测对象和目标对象之间的关联具有合理的灵活性。

具体来说,将每个预测对象与相应基本事实标签中的所有像元进行比较,并与最接近的像元进行唯一链接。如果它们的质心距离小于平均像元直径(50 像素),则预测的元素被视为匹配。因此,它增加了真正的正计数(TP)。

在这个过程的最后,所有没有匹配的真实对象都被认为是假阴性(FN)。同样,剩余的检测到的不与任何目标相关联的项目被认为是假阳性(FP)。

作者图片

对于检测指标,我们不会遇到之前描述的分割指标的相同缺陷。

尽管如此,边界细胞的存在使得我们的评估容易受到一些注释的主观性的影响。在这种情况下,目标和预测对象之间的不一致通常在操作员主观解释的范围内。

然而,这种一致性并没有被度量所捕捉。因此,我们观察到较低的性能,尽管结果与人类的判断完全一致。

总之,我们可以综合考虑所有指标,以全面了解我们模型的优势和劣势。

计数指标

克里斯·贾维斯在 Unsplash 上的照片

有几种评估模型计数能力的方法,各有利弊。建议的策略是综合利用不同的指标从多个互补的角度评价结果

一种方法是简单地考虑基本事实掩膜中的像元数与预测像元数之间的差异。例如,我们可以考虑绝对误差来了解目标计数和预测计数之间的实际距离。

然而,根据目标单元的总数,给定的余量表示或多或少的严重误差。为此,我们可以添加百分比误差作为额外的评估要素。此外,这提供了我们是否高估/低估计数的信息。

虽然上述数量是直观的,但当计数分布的可变性较低时,它们可能会隐藏较差的性能。因此,我们可以通过查看 R 决定系数来补充评估。这可以理解为模型解释的方差部分。因此,它给出了我们的模型如何很好地捕捉现象的可变性的感觉。

总之,建议将这三个指标结合起来看,以更全面地了解我们模型的优势和劣势。

在本文中,我们检查了几个基准,用于评估使用荧光神经元细胞数据集训练的模型。

当然,最终的选择还是要看你分析的具体要求。此外,请记住,由于数据的自然干扰,纯指标值会受到限制。

现在我真的很想知道你的拍摄!

你认为这个列表是详尽的吗?你能想到更好的或互补的度量标准吗?

在评论里告诉我吧!

如果你喜欢这个题目,你可以在[ 1 2 ]中阅读更详细的讨论。此外,您还可以下载 数据集 ,用原论文的 代码 数据 进行实验。

参考

[1] L. Clissa,通过机器和深度学习支持科学研究:荧光显微镜和操作智能用例 (2022), AlmaDL [2] R .莫雷利等人,通过 c-ResUnet (2021),科学报告

MNIST 上的 Flux.jl 性能分析

原文:https://towardsdatascience.com/flux-jl-on-mnist-a-performance-analysis-c660c2ffd330

布拉登·科拉姆在 Unsplash 拍摄的照片

在上一篇文章中定义了各种模型和训练算法后,我们现在来看看它们在应用于 MNIST 数据集时的表现。

介绍

在之前的一篇文章(flux . JL on MNIST——主题变奏曲 )中,我们定义了三种不同的神经网络(NN),目的是使用 MNIST 数据集识别手写数字。此外,我们实现了梯度下降算法(GD)的三个变体来训练这些模型,它们可以与不同的成本函数和不同的优化器结合使用。

因此,我们有相当多的积木,它们可以以各种方式组合,以实现我们识别手写数字的目标。具体来说,我们有以下组件(要了解更多信息,请查看上面引用的前一篇文章):

  • 型号: 4LS,3LS,2LR
  • GD 变体:批量 GD、随机 GD小批量 GD
  • 成本函数:均方误差( mse )和交叉熵( ce )
  • 优化者:下降亚当

在本文中,我们将分析这些组件的不同配置对于图像识别的表现如何,以及需要多少努力(以 GD 的迭代次数和用于该目的的时间表示)来训练它们。

学习曲线

我们不仅对模型在用 GD 训练后的性能感兴趣,而且我们还想通过在每次迭代后查看成本函数(或损失函数的同义词)的值来了解 GD 在执行过程中的表现。

绘制这些值与迭代次数的关系,我们得到所谓的学习曲线。这条曲线很好地描述了 GD 收敛的速度,以及在训练过程中是否存在问题(例如,如果 GD 陷入平稳状态)。

为了在培训期间收集这些值,我们必须在 GD 实现中添加几行代码:

  • 由于计算损失在计算上可能是昂贵的,我们不希望总是在每次迭代时都这样做,而是只在每次到第 n 次迭代时才这样做。这由新的(可选的)关键字参数logstep(第 1 行)控制。1 的logstep意味着我们在每次迭代后计算并存储损失,例如 100 的值意味着我们仅在每次第 100 次迭代后才这样做。
    仅举例说明差异有多大:使用随机 GD 对 4LS 模型进行 4000 次迭代,不记录损失需要 3.8 秒,每次迭代计算损失需要 262.9 秒。
  • 在第 2 行中,用于存储损失历史的数组hist被初始化为零。
  • 在第 9 行中,我们检查是否已经到达应该记录损失的迭代,并将相应的损失值存储在hist数组中(第 10 行)。
  • 在函数的末尾(第 13 行),我们返回完整的损失历史。

注意 : Flux 提供了一种回调机制,可以根据时间间隔来实现这种日志记录。即,使用该机制,损失值不会在每 n 次迭代之后被记录,而是在每 n 秒之后被记录。

准确(性)

训练完模型的参数后,我们想知道这组参数在用于识别手写数字时的表现如何。因此,我们将测试数据集上的模型预测与测试数据集的实际标签(test_y_raw)进行比较,并计算正确识别数字的比例。这个分数叫做模型的精度

例如,我们通过调用model4LS(test_x)获得 4LS 模型的预测。结果是一个由 10,000 个热点向量组成的数组。为了将它们与test_y_raw中的标签进行比较,我们必须将独热向量转换成数字。这可以使用函数Flux.onecold()来完成,它简单地返回一个热向量中 1 位的索引。也就是说,如果第一位是 1,函数返回 1,如果第二位是 1,我们得到 2,依此类推。由于我们的模型产生一个第一位设置为 1 的独热向量,如果“数字 0”被识别,我们必须将onecold()的结果减 1 以获得正确的标签。

因此,精度计算如下:

function accuracy(y_hat, y_raw)
    y_hat_raw = Flux.onecold(y_hat) .- 1
    count(y_hat_raw .== y_raw) / length(y_raw)
end

例如,调用accuracy(model4LS(test_x), test_y_raw)将获得测试数据集上 4LS 模型的准确性。

accuracy功能使用 btw。.-算子广播朱莉娅的机制。第一个公式中的运算.-从表达式左侧数组的每个元素的中减去 1。第二个公式中的.==y_hat_raw每个元素与y_raw中的每个对应元素进行比较(产生一个布尔数组)。

培训和分析

以下所有的训练都是在一台 8 GB 内存的苹果 M1 上使用 Julia 1.7.1 和 Flux 0.13.3 完成的。

列出的所有运行时间都是使用等于批量的logstep完成的。即损失函数值仅在运行结束时取一次,因此对运行时间没有显著影响。

给那些想要自己重复分析的人一个提示:如果你想要用相同的配置分析不同数量的迭代,例如,比较 1000 次迭代和 2000 次迭代的结果,你不必运行 1000 次迭代和 2000 次迭代。相反,这可以以相加的方式完成(即,首先运行 1000 次迭代,然后再运行 1000 次),因为每次迭代都应用于同一组模型参数。

分析

批量梯度下降

我们从一个“经典”组合开始分析:批处理 GD 变体和Desc-优化器。对于 4LS-和 3LS-模型,我们将使用 mse 损失函数和 2LR 交叉熵。正如前一篇文章中所讨论的,这些模型和成本函数的配对应该工作得最好。

一些实验表明,0.2 的学习率在这种情况下效果很好。

4LS 和 3LS 的结果 使用 500 次迭代,我们可以看到 4LS 模型在大约前 200 步快速收敛,然后明显变慢。在 3LS 模型上,这种减速在不到 100 次迭代时就已经开始了:

迭代 1–500[图片由作者提供]

迭代 1–500[图片由作者提供]

批次 GD — 4LS、3LS[图片由作者提供]

训练 3LS 模型需要更长的时间,因为它有更多的参数。但是我们可以看到:并不是随着参数数量的线性增加。更高的努力也导致了更好的结果,在大约 2000 次迭代后显示:4LS 保持在大约 11%的精度,而 3LS 增加到几乎 20%。

但是在这两种情况下,结果都不是压倒性的(至少在评估的迭代范围内)。

2LR 的结果
2LR-型号的结果完全不同。通过 500 次迭代,我们在大约 200 秒内获得了几乎 93%的准确率。

迭代 1–500[图片由作者提供]

因此,在将迭代次数增加到 4000 次后,这些数字进一步提高,达到 96%以上的准确度。

放大迭代 8000 次到 16000 次之间的学习曲线,我们可以看到在损失方面仍有良好的进展。

迭代 1–16,000[图片由作者提供]

迭代 8000–16000 次[图片由作者提供]

批量 GD—2LR[图片由作者提供]

进行更多的迭代仍然会减少损失:从 16,000 次迭代到 32,000 次迭代会将其值从 0.033 减少到 0.013。但是准确性没有明显的变化(它保持在 96%和 97%之间,甚至随着迭代次数的增加而有所下降)。所以这可能是我们从这个配置中能得到的最好的了(真的不差!).

在这一点上请注意:损失是根据训练数据计算的,而准确性是基于测试数据的。因此,这里描述的情况可能是对训练数据的过度拟合(因此损失进一步减少),这不会导致对测试数据的更好预测(即准确性没有提高)。

随机梯度下降

现在我们来看看 GD 的下一个变种。使用随机 GD,我们应该期望大大降低运行时间,但可能也会降低质量。

4LS 和 3LS
的结果,实际上,4LS-model 在不到一秒的时间内完成 500 次迭代,达到 0.090 的损耗和 10.28%的精度。对于 3LS 模型来说,同样数量的迭代需要一秒多一点的时间,而我们得到的准确率接近 14%。

迭代 1–500[图片由作者提供]

迭代 1–500[图片由作者提供]

仔细观察学习曲线,我们还可以看到它们不像批次 GD 中的曲线那样平滑。曲线上的小突起是由于随机 GD 并不总是以最佳方式达到最小值。

这里出现了与上面相同的问题:我们能通过更多的迭代获得(显著)更好的结果吗?用 4LS 做 1000 次迭代,精度(至少有一点)提高到 11.35%。但是在 2,000 次迭代时,我们再次变差(10.28%),在 8,000 次迭代时,我们回到 11.35%。发生了什么事?

放大学习曲线(迭代 2,000 到 4,000 次之间)很有启发性。在这里,我们可以在一个具体的例子上看到我们之前在理论上描述的:随机 GD 不以最直接的方式移动到最小值,而是以之字形路径移动。而在这里,它似乎根本没有朝着正确的方向前进。

迭代 2000–4000 次[图片由作者提供]

对于 3LS 模型,同样的情况似乎会发生,因为我们在 500 次迭代时达到 13.94%的精度,在 1000 次迭代时回落到 13.12%。但是学习曲线表明这里的情况不同:

迭代 500–2000 次[图片由作者提供]

曲线也是振荡的,但是有一个明显的下降趋势。因此,经过 16,000 次迭代后,我们的准确率接近 80%,经过 512,000 次迭代后,我们的准确率达到 96.64%。

除此之外,学习曲线以一种有趣的方式发展:

迭代 1–16,000[图片由作者提供]

在最初几次迭代中迅速降低之后,损耗在大约 4,000 次迭代之前没有太大变化。有一个平稳期。但是我们又一次看到了显著的进步。

所以也许 4LS 显示了同样的现象,我们不应该这么早放弃?事实上,在 15,000 次和 20,000 次迭代之间的范围内,损耗开始下降得更快,在大约 60,000 次迭代以上的范围内,学习曲线再次变得更陡:

迭代 4001–54,000 和 4001–154,000[图片由作者提供]

通过超过 1 M 的迭代,我们在 4LS 的情况下实现了 94.78%的准确度。

随机 GD — 4LS,3LS[图片由作者提供]

2LR 的结果 与 4LS 和 3LS 相比,2LR 在 500 次迭代时的性能明显更差:我们仅获得 9.75%的精度(但与使用批处理 GD 的大约 200 秒相比,只需要 2.7 秒)。不幸的是,如果我们做更多的迭代,结果不会变得更好。即使迭代超过 200 万次,我们也只能得到 10.1%的准确率。学习曲线显示了与这一发现一致的清晰画面:

迭代 8,001–2,048,000[图片由作者提供]

为什么我们用这种配置会得到这么差的结果?答案大概是:太无知了。随机 GD 忽略 60,000 个实例中的 59,999 个(这是一个很大的数目!).然后我们将剩下的少量信息输入损失函数。 mse 以同样的方式使用该信息的每一位来计算损失,但是 ce 本身有一些内置的“无知”:它只考虑产生的唯一热向量的一个值(最大值)。也就是说,它非常重视那个值,而忽略了其他值。从许多实例来看,这可能是一个好策略,但是在我们的情况下(尤其是从一个未经训练的模型开始),这可能只会导致随机的无意义。

小批量梯度下降

由于小批量 GD 是其他两种变体之间的折衷,它应该在我们到目前为止考虑的所有配置上都工作良好,但是比随机 GD 运行需要更长的训练时间。让我们看看,如果训练运行符合我们的期望。

4LS 和 3LS 的结果 实际上,我们对 4LS 和 3LS 模型都获得了极好的结果:在大约 50 万次迭代时,可分别实现 94.56%的准确度,在 200 万次迭代时,可分别实现 97.68%的准确度。

小批量 GD — 4LS,3LS[图片由作者提供]

2LR 的结果
同样适用于 2LR 模型:这里我们在 64,000 次迭代中有 96.98%的准确度。例如,学习曲线比带有 mse 的 4LS 模型的学习曲线更加振荡,但它明显收敛:

迭代 4,001–512,000[图片由作者提供]

小批量 GD—2LR[图片由作者提供]

结论

当我们查看每种测试配置所能达到的最佳精度时,我们会看到下图:

训练时间的最高准确度[图片由作者提供]

每个模型都有一个 GD 变体,这并没有带来好的结果(至少在测试的迭代范围内)。剩下的两个 GD 变量导致所有模型的精度值远远超过 90%,这是非常好的。但是在训练时间上的差异是惊人的:3LS 和 mini-batch 用了将近一个小时,而 2LR 和 mini-batch 用了不到 50 秒。

在下表中,我们计算了一个名为“努力”的绩效指标,它是训练时间和准确性之间的比率。所以你可以看到这方面的佼佼者:

按“努力”排名[图片由作者提供]

另一个有趣的观察是,更多的模型参数不一定导致更好的准确性。

因此,我们可以看到我们的“构建模块”的不同配置如何导致行为和 KPI 的有趣变化。…但是我们是不是忘了什么?亚当怎么办?嗯,那是另一篇文章的素材:-)。

MNIST——主题变奏曲

原文:https://towardsdatascience.com/flux-jl-on-mnist-variations-of-a-theme-c3cd7a949f8c

詹姆斯·奥尔在 Unsplash 上拍照

Flux.jl 是一个 ML-stack,提供轻量级组件来创建模型并训练它们。使用 MNIST 数据集,我们将看到通过将这些组件组合在一起,构建不同的数据集分类方法是多么容易。

概观

Flux.jl

Flux.jl 是一个 100%用 Julia 写的包。它旨在建立模型,这些模型通常使用基于自动微分的迭代方法进行训练。这类模型中最常见的可能是神经网络(NN ),它使用梯度下降算法(GD)的一种变体来训练。

与其他一些包含现成的“机器”来构建和训练模型的 ML 库相比,Flux 提供了一套轻量级的乐高积木,您可以根据自己的特定需求将它们组装在一起。

在本文中,我将展示这种类似 lego 的方法如何应用于构建几个 NN 模型(准确地说是 多层感知器 )来对图像进行分类,并实现 GD 算法的不同变体来训练它们。

MNIST 数据集

为此,我们将使用众所周知的 MNIST 数据集,它由 70,000 张手写(和标记)数字图像组成。这些图像中的 60.000 个用作训练数据,剩余的 10.000 个实例将用于测试模型。每个图像由 28×28 灰度像素组成。

使用MLDatasets包可以轻松加载整个数据集:

第一个表达式(第 3 行)自动获取带有相应标签的 60.000 个训练图像,第 4 行的变体使用split = :test获取其余的用于测试。所以train_x_raw是一个 28x28x60000 元素的数组,test_x_raw经过这次运算后是一个 28x28x10000 元素的数组。标签存储在train_y_rawtest_y_raw中,它们分别是大小为 60.000 和 10.000 的数组,包含 0 到 9 之间的整数。

函数convert2image允许我们显示这样的图像。我们通过调用convert2image(MNIST, train_x_raw[:,:,10])得到例如第 10 个训练图像:

手写数字 4 的灰度图像[图片由作者提供]

相应的标签(4)在train_y_raw[10]

这张图片(在train_x_raw[:,:,10]中)的数据只是一个 28x28 元素的矩阵,包含 0 到 1 之间的数字,每个数字代表一个灰色阴影。

模型

如上所述,我们的目标是创建能够对这些图像进行分类的不同模型。也就是说,我们的模型的输入将是一个手写数字的图像(以 28x28 矩阵的形式),输出是一个 0 到 9 之间的数字,告诉我们图像包含哪个数字。

基本处理流程[图片由作者提供]

数据

不幸的是,神经网络既不直接处理矩阵,也不以我们想要的方式输出数字。所以我们必须对我们的数据做一些调整。

神经网络的输入必须是包含所有值的(列)向量。因此,我们必须转换我们的 28x28 矩阵,并将每张图片的 28 列堆叠在一起。结果是一个 784 元素的向量(28 x 28 = 784)。通量函数flatten()就是这样做的:

train_x = Flux.flatten(train_x_raw)
test_x  = Flux.flatten(test_x_raw)

这分别产生一个 784x60000 和一个 784x10000 元素的数组。

用于这种分类问题的 NN 的输出通常是所谓的独热向量。这是一个位向量(在我们的例子中有 10 个元素,因为我们有 10 个不同的类),其中正好有一位是 1,所有剩余的位都是 0。值为 1 的元素表示相应的类别,即如果第一位为 1,则表示“数字 0”,如果第四位为 1,则表示“数字 3”,依此类推。

通量函数onehotbatch()对整个标签阵列进行这种转换:

train_y = Flux.onehotbatch(train_y_raw, 0:9)
test_y  = Flux.onehotbatch(test_y_raw, 0:9)

自变量0:9告知必须由结果独热向量表示的数字的(可能)范围。这些语句的结果分别是 10x60000 和 10x10000 位数组。

因此,我们为图像预测调整的处理管道看起来像这样:

改编的处理流程[图片由作者提供]

注意:在现实世界的应用中,神经网络很少会产生“真正的”单热点向量。相反,将产生包含概率的向量,并且如果 NN 工作良好,这些值中的一个将接近 1,而所有其他值将接近 0。

多层感知器网络

我们想要使用的模型是所谓的多层感知器网络 (MLP)。这些是“经典”神经网络,由几层神经元组成,其中一层的每个神经元连接到下一层的所有神经元(也称为全连接密集网络):

一个三层感知器[图片由作者提供]

不同的 MLP 在

  • 层数,
  • 每层中神经元的数量
  • 所谓的激活函数,其在传递到下一层之前应用于神经元的结果;这种激活功能可能因层而异。

MNIST 分类模型

在我们的例子中,第一层中输入值的数量已经由输入数据固定(到 784),最后一层必须有 10 个神经元(产生 10 个输出值),因为我们有 10 个类。但是我们可以自由选择模型的其他特征。

为了简单起见,我选择了三个可以在互联网上其他地方找到的模型:一个来自格兰特·桑德森在他的 YouTube 频道“3Blue1Brown”上关于神经网络的精彩视频(“T10”在这里你可以找到第一个及其精彩的可视化),第二个(改编自)来自我们主题的这个深度处理,第三个来自 Flux 文档

具体来说,我们有以下型号(我根据它们的主要特征命名)

  • 4LS :一个 4 层模型,使用 16 个内层节点和s 形激活函数
  • 3LS :一个三层模型,使用 60 个内层节点和 sigmoid 激活函数
  • 2LR :使用内层 32 个节点和 relu 激活功能的 2 层模型

旁注:为了更接近 Flux 中的以下模型定义,我没有将输入和输出作为一个单独的层(在别处是这样做的)。

使用通量的方法,我们可以将这三个模型定义如下(在注释中,您可以看到每个模型的参数数量):

第三种模式(model2LR)仅在第一层使用relu-激活功能。在第二层上,没有指定这样的功能。这意味着使用默认流量(身份)。因此,该层可能会产生[-∞,∞]范围内的值,这不是我们想要的。因此,softmax-函数应用于结果,将它们标准化到从 0 到 1 的范围,并确保它们的总和为 1。

使用sigmoid-功能激活的前两个型号是相当“传统”的 NNs,而现在sigmoid已经大部分被relu取代。

梯度下降训练

训练这样的模型意味着找到最佳的参数值,以便该模型能够以高精度进行预测。

价值函数

为了测量一组特定的参数相对于这个目标的表现有多好,我们需要一个所谓的成本函数。该成本函数 C 将利用特定参数集做出的预测 ŷ 与训练数据中的实际类别 y 进行比较,并在此基础上计算该“距离”(= C(ŷ,y) 的指标。即成本函数的结果越小,选择的参数越好。所以我们的目标是找到一组使成本函数最小的参数。

在这种情况下使用的典型成本函数是:

mse 通常与具有sigmoid激活功能的型号一起使用,而 ce 通常与relu激活功能一起使用。这些配对背后的基本原理是,在这些星座中,可以保证成本函数是凸的,即它恰好有个最小值。因此,用于寻找(全局)最小值的算法将不会被可能的其他(局部)最小值分散注意力。

话虽如此,我不会隐瞒“损失函数中局部最小值的存在在实践中似乎不是一个主要问题”(这是“某种程度上的理论谜团”),正如 LeCun 等人在他们关于“ 基于梯度的学习应用于文档识别 ”的论文中所写的那样。IEEE 的,1998 年 11 月)。因此,也使用其他配对。

梯度下降

用于训练模型的梯度下降算法的基本思想(即,为最小化成本函数 C 的模型找到一组参数)如下:

  1. 从任意选择的一组参数开始 W₀
  2. 在位置w₀(∇c(w₀):
    w₁=w₀–α∇c(*w₀)向 C梯度的反方向移动一小步(α),计算出一组新的“改良”参数 W₁*

由于位置 W₀ 处的 C (矢量)的梯度指向 C 最陡的方向,我们必须向相反方向移动以达到最小值(因此公式中为负)。

重复第 2 步,直到 C 向最小值收敛(或者直到我们有一组参数 Wi 为我们想要的应用提供足够的精度):

梯度下降算法的迭代步骤

步长α称为学习率。启发式选择。如果太大,算法可能会“超调”。如果太小,算法收敛非常慢。在实践中,用 0.1、0.01 和 0.001 这样的值进行实验并根据结果进行调整是一个好主意。

Flux.jl 中的实现

那么,如何使用 Flux.jl 在 Julia 中实现这些概念呢?模型(model4LSmodel3LSmodel2LR)已经定义,训练数据已经可以使用(在train_xtrain_y)。

剩下的是成本函数的定义(Flux 称之为损失函数)和梯度下降算法的实现。Flux 中每个 GD 实现的核心是Flux.train!()函数。它在 GD 的每次迭代中被调用,并计算模型参数的(稍微)改进版本(如上所述)。必须将以下参数传递给train!():

  • 成本函数 loss
  • 模型参数 params ( Wi 来自最后一次迭代步骤或第一次运行时的初始参数)
  • 训练数据 data(在train_xtrain_y中)
  • 一个所谓的优化器opt;这是用于产生模型参数的新(改进)版本的公式,如上所示(梯度下降算法的迭代步骤)。上面的公式是最基本的变体。近年来,已经开发出了更复杂的变体。与基本变体的主要区别在于,它们在每个迭代步骤上动态地调整学习速率α。
    除了我们在Descent(α)中得到的基本变体,我们将应用一个最先进的版本: ADAM (=自适应矩估计),它在ADAM(α)中可用。

我们可以使用Flux.params()功能访问每个模型的参数:

params4LS = Flux.params(model4LS)
params3LS = Flux.params(model3LS)
params2LR = Flux.params(model2LR)

成本函数 msece 也在 Flux 中预定义(如Flux.Losses.mse()Flux.Losses.crossentropy())。由于train!()期望一个可以直接传递训练数据(train_xtrain_y)的成本函数,而两个预定义的损失函数期望基于train_x的模型预测作为它们的第一个参数,我们必须定义一些“转换”:

loss4LSmse(x,y) = Flux.Losses.mse(model4LS(x),y)
loss4LSce(x,y)  = Flux.Losses.crossentropy(model4LS(x),y)
loss3LSmse(x,y) = Flux.Losses.mse(model3LS(x),y)
loss3LSce(x,y)  = Flux.Losses.crossentropy(model3LS(x),y)
loss2LRce(x,y)  = Flux.Losses.crossentropy(model2LR(x),y)

由于 mse 成本函数不能很好地与使用relu的神经网络一起工作,我们只为 2LR 模型定义了基于 ce 的损失函数。

现在,我们可以通过调用这些函数来查看模型的初始(随机)参数损失有多大。例如,loss4LSmse(x_train, y_train)在我的环境中提供 0.2304102 的值。但是由于初始参数是随机选择的,如果你在电脑上尝试,你会得到另一个值。因此,这个值只是用于(相对)比较,以便查看在 GC 的一些迭代之后它会发生多大的变化。

实现梯度下降的变体

批量梯度下降

然后我们准备使用 Julia 和 Flux.jl 实现 GD 的第一个版本:

我们梯度下降的第一个版本train_batch()将训练数据Xy、损失函数loss、优化器opt、模型参数params和我们想要训练的迭代次数epochs作为参数。GD 的这个实现本质上是在每次迭代中调用train!()的 for 循环。

由于train!()期望训练数据被包装在数组的元组中,我们在第 2 行准备了这个结构。就是这样!

例如,为了使用 mse 成本函数和标准 GD 公式训练我们的 4LS 模型,在 100 次迭代中学习率为 0.1,我们必须写出:

train_batch(train_x, train_y, loss4LSmse, 
            Descent(0.1), params4LS, 100)

执行此语句后,我们应该看到损失值有所改善。在我的环境中,我通过调用loss4LSmse(x_train, y_train)得到一个值 0.12874180。所以我们可以在这 100 次迭代中减少一半的损失。

GD 的这种变体被称为批次梯度下降,因为在每次迭代中,损失函数的梯度在完整的训练数据集上进行评估。即所有 60.000 幅图像都用于计算该值(在每次迭代中!).

显然,这在计算上相当昂贵!因此,开发了 GD 的其他变体。

随机梯度下降

随机梯度下降算法在每次迭代中仅使用训练数据的单个(随机选择的)实例(即一个单个图像)来评估损失函数的梯度。这显然成本更低。

考虑到仅仅一个实例(而不是 60.000)当然不会产生损失函数的实际梯度。即合成向量不会指向该函数最陡的方向。但实际上,产生的矢量并没有那么差。

使用这种方法,我们不会得到到达最小值的最直接的路径,而是到达(或接近)那个点的曲折路径。即使我们没有直接达到最小值,结果通常也足够接近,并且我们以相当低的成本达到最小值。所以这通常是首选的变体。

实现如下所示:

小批量梯度下降

GD 的第三个变体是我们看到的前两个变体之间的一个很好的妥协。它既不作用于整个数据集,也不作用于每次迭代的单个实例,而是选择例如 100 个实例的随机样本,并在此基础上计算梯度。这个样品被称为小批量

这个小批量的大小可以自由选择。因此,为此我们需要一个额外的参数batchsize:

我们在这里实现了一个简单的策略来提取小批量:在第 4 行中选择整个数据集内的一个随机位置,然后我们从该位置(第 5+6 行)开始提取一个大小为batchsize的小批量。

更高级的策略是随机选择小批量的所有实例。这可以通过使用焊剂[DataLoader](https://fluxml.ai/Flux.jl/stable/data/mlutils/#DataLoader)来实现。

GD 的第三个变体当然是最通用的实现,我们可以用train_minibatch()来表示前两个变体,如下所示(因此我们实际上只需要小批量 GD 的实现):

train_batch(X, y, loss, opt, params, epochs) = 
        train_minibatch(X, y, loss, opt, params, epochs, size(X)[2])train_stochastic(X, y, loss, opt, params, epochs) = 
        train_minibatch(X, y, loss, opt, params, epochs, 1)

结论

我们已经看到 Flux.jl 如何用于预处理数据和定义模型。此外,使用构建模块,如

  • 损失函数
  • 优化者
  • train!()-功能

我们可以很容易地实现 GD 算法的不同变体。

在后续文章中,我将结合不同的损失函数、优化器和其他参数来评估这里提出的模型,以了解需要花费多少努力来训练它们,以及从 MNIST 数据集中识别手写数字可以达到什么精度。所以,敬请期待!

MNIST——亚当呢?

原文:https://towardsdatascience.com/flux-jl-on-mnist-what-about-adam-fb78e7f02acd

西蒙·李Unsplash 上的照片

到目前为止,我们已经看到了使用标准梯度下降优化器的性能分析。但是,如果我们使用像亚当这样更复杂的方法,我们会得到什么结果呢?

在关于 MNIST 的flux . JL——主题的变体中,我展示了三种用于识别手写数字的神经网络,以及三种用于训练这些网络的梯度下降算法(GD)。

关于 MNIST 的后续文章flux . JL——性能分析显示了使用不同参数的每个模型的性能以及训练它们需要付出的努力,这取决于所应用的 GD 变体。GD 算法使用标准的 GD 学习公式,在该分析中利用恒定的学习速率。

现在,我们将使用高级学习公式(现在称为“优化器”)执行此分析,并将结果与使用标准公式实现的性能进行比较。我们将使用的优化器名为 ADAM (= 自适应矩估计),由 Diederik P. KingmaJimmy Ba2015 年发表。它适应每次迭代的学习速率,是目前该领域最复杂的方法之一。

与最后的分析类似,我们将应用所有三种 GD 变量进行训练:批量 GD、随机 GD 和小批量 GD。

批量梯度下降

4LS-型号

使用 batch GD 将 ADAM 应用于 4LS 模型,导致了显著的改进:仅用 500 次迭代,我们就获得了 74.16%的准确率,提高到 91%以上。相比之下,标准的Descent优化器停留在 11.35%。

批量 GD—4LS[图片由作者提供]

学习曲线说明了这种行为:左边是标准优化器,迭代 500 次,右边是 ADAM,迭代 1000 次。ADAM 非常快地将损失降低到低于 0.1 的值,然后稍微停滞,直到大约迭代 300,在那里它再次开始降低。Descent也相当快,直到低于 0.1(请注意 y 轴上的不同缩放比例),然后停滞不前。除此之外,使用Descent减少损失并不能转化为准确性的提高(在测试数据集上)。

迭代 1-500(下降)和 1-1000(亚当)[图片由作者提供]

3LS 型号

对于 3LS 模型,我们得到了类似的结果。ADAM 在这里也显著地改进了结果:在 500 次迭代时,我们获得了 95.66%的准确度,在 1000 次迭代时,准确度可以略微提高到 96%以上。另一方面,标准优化器只能产生低于 20%的精度。

批量 GD—3LS[图片由作者提供]

学习曲线也显示了这一点:两个优化器都非常快地将损耗降低到低于 0.1 的值(再次注意 y 轴上的不同缩放),ADAM 甚至比Descent更快,然后降低得更多。

与 4LS-model 相比,ADAM 在这里停滞得更快,但在这一点上已经取得了比 4LS 更好的准确性。

迭代 1-500(下降)和 1-1000(亚当)[图片由作者提供]

2LR 型号

在 2LR-model 上,我们已经用标准优化器得到了很好的结果,并且我们用 ADAM 达到了相同的准确度水平(超过 96%)。但是 ADAM 已经迭代了 500 次(然后略有下降),而标准优化器需要 4,000 次迭代才能达到这个水平。

批量 GD—2LR[图片由作者提供]

在这种情况下,除了 ADAM 在开始时下降较快之外,学习曲线非常相似:

迭代 1-1,000 次(下降和亚当)[作者图片]

随机梯度下降

对于随机 GD,标准优化器在 4LS-和 3LS-模型上提供了相当好的结果。让我们看看,亚当是否能做得更好。

4LS-型号

对于两个优化器,4LS 模型的学习曲线具有相同的基本形状,但是 ADAM 导致更强的振荡。

迭代 1–1,024,000 次(下降和亚当)[图片由作者提供]

ADAM 用更少的迭代给了我们更高的精度,但最终我们没有得到从Descent得到的质量(在 200 万次迭代中,94.16%对 92.12%)。

随机 GD—4LS[图片由作者提供]

3LS 型号

对于 3LS 模型,我们得到了几乎相同的情况:基本上相似的曲线,其中 ADAM 在开始时更快地收敛到较低的损失值(并且以这种方式振荡得更多):

迭代 1–1,024,000 次(下降和亚当)[图片由作者提供]

这反映在我们能够达到的精度上。同样,ADAM 也没有达到我们用标准优化器达到的水平(在 200 万次迭代中,93.73%对 97.64%):

随机 GD—3LS[图片由作者提供]

2LR 型号

用标准优化器训练 2LR 模型是失败的。我们从来没有达到 20%以上的准确率。在这种情况下,ADAM 是一个真正的改进,在大约 50 万次迭代后达到了 87%以上的精度(然后又变得更差)。但是我们不能像其他配置那样达到 95%以上。

随机 GD—2LR[图片由作者提供]

使用 ADAM 的学习曲线显示出剧烈的波动,它并不真正符合上表中的数字,因为我们做的迭代次数越多,损失似乎越大。

迭代 1–1,024,000 次(下降和亚当)[图片由作者提供]

前 128,000 次迭代的 100 移动平均线更好地显示了该部分发生的情况。至少在最初的 40,000 次迭代中,损耗显著降低。但是这仍然不能解释为什么精度在高达大约 50 万次迭代的范围内提高。

移动 100-平均值-迭代 1–128,000(ADAM)[图片由作者提供]

小批量梯度下降

小批量 GD 在所有三种模型上都取得了非常好的结果。所以留给亚当的空间并不多。让我们来看看不同之处。

4LS-型号

正如上面在其他配置中看到的,ADAM 在开始时收敛得更快(500 次迭代时 92.46%的准确性),但没有达到标准优化器的水平(200 万次迭代时 94.49%对 93.28%):

小批量 GD—4LS[图片由作者提供]

学习曲线反映了这些发现(同样,亚当导致了更多的振荡):

迭代 4,001–512,000(下降)和 1–512,000(亚当)[图片由作者提供]

3LS 型号

3LS 型号也是如此。ADAM 在 500 次迭代时已经达到了 94.47%的准确度,但是在 200 万次迭代时最终稍差(97.68%对 96.17%)。

小批量 GD-3LS[图片由作者提供]

以类似的方式,这反映在学习曲线中:

迭代 4,001–512,000(下降)和 1–512,000(亚当)[图片由作者提供]

2LR 型号

对于 2LR 型号,情况有点不同。亚当在开始时并没有收敛得更快。同样,它也达不到与标准优化器相同的准确性水平(97.0%对 91.33%)。

小批量 GD—2LR[图片由作者提供]

学习曲线在这里显示了(与随机 GD 一样)显著的振荡:

迭代 4,001–512,000(下降)和 1–512,000(亚当)[图片由作者提供]

结论

血统 vs .亚当

下图概述了使用标准优化器(左)和 ADAM(右)获得的最佳结果。它显示了与训练时间相关的准确性。在这里,我们可以看到,亚当做得很好:几乎所有的结果都在左上角。即以相对较小的努力实现了高精度。标准优化器提供了更广泛的结果。但最终它产生了一些最好的结果。

准确度与训练时间(下降和亚当)[图片由作者提供]

顶级表现者

下图显示了表现最好的人(准确率> 90%)。最好的结果是 3LS/下降(亮蓝色),然后是 2LR/下降(亮黄色)。4LS 变异体(绿色)通常不太好。

准确性与训练时间——最佳表现者[图片由作者提供]

努力

最后,在最近的分析中,我计算了一个名为“努力”的性能指标,这是训练时间和准确性之间的比率(描述了达到一定水平的准确性需要多少训练时间)。所以你也可以看到这方面的佼佼者:

按“努力”排名[图片由作者提供]

摘要

有趣的是,在标准优化器失败的三种情况下,我们都可以通过 ADAM 获得明显更好的结果。但在所有其他情况下,亚当都没有达到我们用Descent获得的水平。在这些情况下,迭代次数越少,收敛速度越快,但最终还是于事无补。因此,我们可以得出结论,这两种优化器可以很好地互补,但没有一种优化器可以适用于所有情况。

焦点损失:交叉熵的更好选择

原文:https://towardsdatascience.com/focal-loss-a-better-alternative-for-cross-entropy-1d073d92d075

据说焦点损失在许多情况下比交叉熵损失表现得更好。但是为什么交叉熵损失会失败,焦点损失如何解决这些问题,让我们在这篇文章中找到答案

梯度下降,由 Unsplash 上的 Rostyslav Savchyn 拍摄

损失函数是计算预测值与实际值偏离程度的数学方程。较高的损失值表明模型存在重大误差,而较低的损失值表明预测相当准确。目标是尽可能减少损失函数。模型使用损失函数来学习可训练的参数,例如权重和偏差。因为参数的权重更新方程具有损失函数相对于权重或偏差的一阶导数,所以该函数的行为将对梯度下降过程产生显著影响。

权重更新公式,图片来源:作者

现在有很多损失函数可用。他们每个人都有不同的数学公式和不同的惩罚模型错误的方法。每种方法都有优点和缺点,在决定使用哪种功能之前,我们必须权衡利弊。

现在我们已经定义了损失函数,让我们来看一下分类交叉熵损失引起的问题以及焦点损失如何解决它们。

分类交叉熵损失

分类交叉熵损失传统上用于分类任务。顾名思义,这个的基础就是熵。在统计学中,熵是指系统的无序性。它量化了变量的模型预测值的不确定程度。所有概率估计的熵之和就是交叉熵。

交叉熵的表达式,图片来源:作者

其中 Y 是真实标签,p 是预测概率。

注意:上面显示的公式是针对离散变量的。在连续变量的情况下,求和应该用积分代替。

对数图清楚地表明总和将是负的,因为概率范围从 0 到 1。因此,我们添加一个减号来反转求和项的符号。log(x)和-log(x)的图形如下图(x)所示。

红色为 log(x ),蓝色为-log(x ),图片来源:作者。使用 Desmos 绘制的图表。

交叉熵损失表现不佳的情况

  • 阶级不平衡继承过程中的偏见。多数类示例将主导损失函数和梯度下降,导致权重在模型的方向上更新,在预测多数类时变得更有信心,同时不太强调少数类。平衡交叉熵损耗处理这个问题。
  • 分不清的例子。困难的例子是那些模型反复出现巨大错误的例子,而简单的例子是那些容易分类的例子。因此,交叉熵损失未能更多地关注硬例子。

平衡交叉熵损失

平衡交叉熵损失为每个类别添加一个加权因子,用希腊字母 alpha,[0,1]表示。α可以是逆类频率或由交叉验证确定的超参数。alpha 参数取代了交叉熵方程中的实际标签项。

平衡交叉熵的表达式,图片来源:作者

尽管这个损失函数解决了类不平衡的问题,但是它不能区分难的和容易的例子。通过焦点丢失解决了问题。

焦点损失

焦点损失侧重于模型出错的例子,而不是它可以自信地预测的例子,确保对困难例子的预测随着时间的推移而提高,而不是对简单例子过于自信。

这到底是怎么做到的?焦损失通过所谓的向下加权来实现。向下加权是一种减少简单示例对损失函数的影响的技术,导致对困难示例的更多关注。这种技术可以通过向交叉熵损失添加调制因子来实现。

表情为焦损,图片来源:作者

其中,γ (Gamma)是使用交叉验证调整的聚焦参数。下图显示了不同γ值下焦损失的表现。

向下加权随着γ的增加而增加,图片来源:焦损失研究论文

伽玛参数是如何工作的?

  • 在错误分类样本的情况下,pi 很小,使得调制因子近似或非常接近 1。这使得损失函数不受影响。因此,它表现为交叉熵损失。
  • 随着模型的置信度增加,即 pi → 1,调制因子将趋向于 0,从而降低了分类良好的示例的损失值。聚焦参数γ ≥ 1 将重新调整调制因子,使得简单的例子比困难的例子权重更低,减少它们对损失函数的影响。例如,假设预测概率为 0.9 和 0.6。考虑到γ = 2,为 0.9 计算的损耗值为 4.5e-4,向下加权系数为 100,为 0.6 计算的损耗值为 3.5e-2,向下加权系数为 6.25。从实验来看,γ = 2 对于焦损失论文的作者来说效果最好。
  • 当γ = 0 时,焦损相当于交叉熵。

在实践中,我们使用聚焦损失的α平衡变体,其继承了加权因子** α和聚焦参数 γ的特征,产生比非平衡形式稍好的精度。**

α平衡焦损失的表达式,图片来源:作者

震源损失自然地解决了类别不平衡的问题,因为来自多数类别的样本通常容易预测,而来自少数类别的样本则很难预测,这是因为缺少来自支配损失和梯度过程的多数类别的数据或样本。由于这种相似性,焦点损失可能能够解决这两个问题。

感谢您阅读这篇文章😃。祝你有愉快的一天。

关注这五个领域,进入数据科学领域

原文:https://towardsdatascience.com/focus-on-these-five-areas-to-break-into-data-science-4a7ad39818f8

有抱负的数据科学家们,为了获得第一份数据科学工作,以下是你们必须知道的事情

杰基·霍普Unsplash 上的照片

这是我在网上找到的一个问题的答案:

作为一名有志于进入 AI/ML 和数据科学领域的学生,我应该关注什么?

数据科学是一个复杂且快速发展的领域。这是一项艰巨的任务,尤其是像我这样自学成才的人。我没有结构化的课程,所以确定重点领域对我来说有点像猜谜游戏。

因此,为了让任何人都容易,下面是你应该关注的五个领域,以获得你的第一份数据科学工作。

想进入数据科学领域吗?参加我的免费数据科学基础课程,开始回归和分类项目!

数据操作

首先,你需要知道如何获取和操作你的数据。

您可能必须访问数据库来提取数据集。了解 SQL 基础知识是最基本的。至少,你应该知道如何:

  • 查询两个日期之间的数据
  • 将表格连接在一起
  • 过滤您的表格

当然,掌握窗口函数和公共表表达式(cte)会更好。

然后,一旦你有了数据,你必须知道如何操作它。现实生活中的数据集令人讨厌。它们有缺失值、异常值,并且列有模糊的标签。因此,你必须知道如何处理这些不同的问题,并提取有意义的信息。

对于任何数据操作,熊猫和 NumPy 都是必不可少的。

了解你的算法

第二,你必须知道你的算法。

回归、分类和聚类任务对于数据科学家来说很常见。你必须知道如何实现每个任务的算法。您还应该对哪种算法通常工作得最好有一些直觉。

这里列出了你必须知道的基本算法:

回归

  • 线性回归
  • 套索和岭回归
  • 决策树(随机森林、装袋、助推)

分类

  • 逻辑回归
  • 决策树
  • SVM

聚类

  • k 均值聚类
  • 分层聚类

我没有在这个列表中包括任何深度学习,因为入门级的工作很少需要它。我的建议是,如果需要的话,在进入深度学习之前,先关注基础知识。

解释您的评估指标

第三,你必须对你的评价指标有深刻的理解。

知道如何解释你的评估标准是获得第一份工作的关键。我们必须认识到,作为数据科学家,我们与非技术人员交流。对他们来说,回忆、精确、F1 分数或平均寿命没有任何意义。把我们的科学成果翻译成通俗易懂的语言是我们工作的一部分。

此外,您必须知道如何为您的问题选择合适的指标。例如,我曾经采访过一些候选人,他们使用精确度来评估不平衡数据集上的分类模型。在这里,使用绝对指标是一个巨大的错误。

因此,您必须训练自己的技能,为正确的情况选择正确的指标。

磨练科学方法

职位名称是这样写的:我们是科学家。重要的是表明我们遵循科学的方法,我们进行实验。

这个领域有很多方面。首先,让我们考虑创建一个健壮的测试集。首先,你的测试集应该在整个实验中保持不变。它还应该代表您的解决方案的实际应用。

例如,假设您正在开发一个聊天机器人。然后,你的测试集不应该只包含完美的句子,没有语法或标点符号错误。在现实中,人们会犯错误,使用缩写,有时根本不用标点符号!您的测试集必须反映您的算法必须处理的数据类型。

另一种表明你遵循科学方法的方式是一次修改一个变量。例如,不要在设计新功能的同时改变算法。如果您的评估指标提高或降低,您将无法判断这是由于新特性还是新算法。

永远保持一切不变,一次改变一个变量。当然,跟踪你的实验,并评估每一个实验。

准备好解释你的工作

最后,你必须能够解释你工作的每一步。

同样,我面试的候选人不能证明他们在项目中采取的步骤是正确的!做出正确的决定是不够的,你必须能够证明它是正确的。

在项目的每一步,你都应该问自己:我为什么要这样做?

这将有助于你深入理解你的工作,并证明你的想法。

你必须解释为什么放弃那个专栏。

你必须解释为什么用平均值而不是中值来填充缺失值。

你必须知道你为什么设计这个新变量。

你必须解释你为什么选择这个评估标准。

你必须解释你是如何选择冠军模特的。

如果你发现自己在没有数据支持的情况下,出于直觉采取了一个步骤,那么这可能是个坏主意。

实验失败并知道原因,好过实验成功却不知道原因。

另外,你可以肯定这种类型的问题会在面试中出现!我知道我喜欢问他们,因为这将优秀的数据科学家与平庸的数据科学家区分开来。

你有它!专注于这五个领域,保证你能打入数据科学领域。

我希望这篇文章对你有帮助!

确保订阅更多文章、免费赠品、课程通知和 VIP 活动邀请!

另外,查看我的免费课程,开始学习数据科学!

叶和 Choropleth 地图:从零到专业

原文:https://towardsdatascience.com/folium-and-choropleth-map-from-zero-to-pro-6127f9e68564

使用带有图层控制和自定义工具提示的 follow 创建交互式 Choropleth 地图的分步教程

图片来源: Pixabay

介绍

Choropleth 地图可能是最流行的地理空间数据可视化之一。它是一种专题地图,在地图上的预定义区域使用不同的阴影图案或颜色来表示与变量或指标相关的地理特征。

它可以绘制在世界地图、美国地图上,或者按照不同的地理区域(如州、县、邮政编码等)进行划分。下图展示了一些来自维基百科的 choropleth 地图的例子。

图片来源:维基百科

在 Python 中,有几个图形库可以用来绘制 choropleth 地图,如 follow、Plotly、Matplotlib 等。其中,follow 是一个高度专业化的地理空间可视化库。它是健壮的和可定制的,可以在任何地理区域或国家以各种风格和设计绘制 choropleth 地图。对于想要开始使用 Python 进行地理空间可视化的初学者来说,这是一个理想的选择。

在本教程中,我们将学习 learn 的核心概念,并使用它创建一个 choropleth 地图,该地图通过自定义工具提示和图层控件将美国县级的新冠肺炎案例可视化。我们将要创建的 choropleth 地图如下所示:

作者图片

先决条件

#1:安装叶片:

您可以使用下面的命令,根据 folio 的文档页安装 folio:

$ pip install folium

或者

$ conda install folium -c conda-forge

#2:安装 GeoPandas:

根据 GeoPandas 的文档页面,建议使用 Anaconda / conda 安装 Geopandas。首先按照指令下载并安装 Anaconda,然后使用以下命令安装最新版本的 GeoPandas:

conda install geopandas

下载数据

制作 Choropleth 地图需要两种主要的输入:

  1. 包含地理区域标识符(例如,国家名称、县 fips 代码、邮政编码等)的表格数据集。)和我们希望在地图中可视化的指标(例如,人口、失业率等。)
  2. GeoJSON 文件,包含几何信息并定义预定义地理区域的边界(例如,国家、美国县、邮政编码等)。)

作者图片

现在,我们已经了解了数据输入要求和 GeoJSON 文件的作用,我们可以继续下载教程所需的两个数据集。这两个文件都是开放的数据集,可以免费下载和使用。

数据集 1 : 美国新冠肺炎县一级的社区传播

数据来源:疾病预防控制中心

数据集 2 : 美国各县的 GeoJSON 文件

你可以去public.opendatasoft.com,向下滚动到“地理文件格式”部分,下载美国各县的 GeoJSON 文件。

作者图片

导入库并读取数据

让我们导入所有必要的库并将数据读入 Python。请注意,为了读取 GeoJSON 文件,我们需要使用 GeoPandas,这是一个开源库,专门用于处理地理空间数据类型,使使用 python 处理地理空间数据变得更加容易。

作者图片

接下来,让我们仔细看看 covid_df 数据帧。

作者图片

该数据集跟踪每个 report_date 在美国县一级的新冠肺炎指标。由于我们正在创建一个静态 choropleth 地图,因此我们将数据框限制为最近的 report_date(即我们示例中的 12/29/2021)。

我们还需要做一些数据争论来清理一些数据字段。我们会将“fips_code”的数据类型更改为 string,如果缺少前导零,则填充前导零。我们还将创建两个具有正确数据类型和格式的新列“new_cases_7days”和“pct_positive_7days ”,并删除原始列。

清理后的 covid_df(图片由作者提供)

最后,让我们将 geojson 与 covid_df 结合起来,以创建最终数据集,为可视化做好准备。通过连接这两个数据集,我们确保 covid_df 数据框中的 fips_code 与 GeoJSON 文件中的 coty_code 完全匹配,这在稍后使用 follow 绘制 choropleth 地图时至关重要。

可视化的最终数据框(图片由作者提供)

使用 leav 创建 Choropleth 地图

第一步:启动一个基础叶子地图

要使用 leav 创建 choropleth 地图,我们需要首先使用 leav 启动一个基础地图。映射(),然后向其中添加图层。我们可以通过使用 location 参数将起始坐标传递给地图。我们在这里选择的起始坐标(40,-96)大致代表美国地图的中心。

我们可以从内置图块集列表中选择所需的地图图块(例如 tiles="Stamen Terrain "),默认为 OpenStreetMap,或者只需将图块选项保留为“无”。我们还可以使用 zoom_start 参数设置地图的初始缩放级别。

步骤 2:将 Choropleth 地图图层添加到底图中

基础地图最初是空的。我们可以使用 leav 创建 choropleth 地图图层。Choropleth()并使用 add_to()方法将其添加到基本地图中。在叶子里。Choropleth()函数,有几个导入参数我们需要指定:

作者图片

请注意,在上面的代码中,我们可以使用 quantile()和 tolist()创建一个自定义色标,并通过 threshold_scale 参数轻松传递我们的自定义色标,而不是使用固定值范围。仅用几行代码,我们就用 Folium 创建了一个带有自定义色阶的基本 choropleth 地图!

作者图片

步骤 3:向地图添加自定义工具提示

我们上面创建的基本地图看起来相当不错!然而,目前,当用户悬停在地图上时,没有工具提示显示每个县的信息,如县名、Covid 病例数、阳性率等。这将是一个很好的添加到地图的功能,以使可视化更具交互性和信息量。

要添加自定义工具提示,我们需要使用 follow . features . geo JSON()方法和 GeoJsonTooltip 函数来设置“字段”和“别名”参数。这些字段将是我们想要在悬停工具提示中显示的 df_final 中的任何列,而别名将是我们给予这些字段的标签/名称。

请注意,我们还可以将 HTML 元素传递给“aliases”参数来格式化工具提示。例如,我们可以使用
将长格式文本分成两行,如下面的代码所示。

作者图片

添加层控制以在指标之间切换

到目前为止,我们的 choropleth 图仅可视化了来自 df_final 数据帧的一个指标——“新病例 _ 7 天”。如果我们也有兴趣了解不同县之间“pct_positive_7days”指标的变化情况会怎样?

我们可以按照上一节中显示的相同步骤,为“pct_positive_7days”创建另一个映射。但是有没有什么方法可以在同一个地图上用一个切换按钮显示多个指标,这样我们就不需要分别显示多个地图了?

答案是肯定的,解决问题的秘密武器是使用叶子。FeatureGroup()和 leav。LayerControl()。

叶子。FeatureGroup()方法创建要素组图层。我们可以把东西放在里面,把它们作为一个单独的层来处理。我们可以创建多个要素组图层,每个图层代表一个我们希望在地图上显示的度量。然后,我们可以添加 LayerControl 来勾选/取消勾选 FeatureGroup 图层,并在不同的指标之间切换。

让我们看看如何通过下面的代码实现这一功能:

步骤 1: 我们启动一个底图,创建两个名为 fg1 和 fg2 的特征组图层,并将它们添加到底图中。我们为将在 LayerControl 中显示的每个 featureGroup 图层命名。“覆盖”参数允许您将层设置为覆盖层(勾选图层控制中的复选框)或基础层(勾选单选按钮)。

步骤 2: 我们将第一个 choropleth 地图图层(用于“新病例 7 天”指标)添加到 fg1:

第三步:我们将第二个 choropleth 地图图层(用于“pct_positive_7days”指标)添加到 fg2。除了与“pct_postive_7days”指标相关的一些更改之外,代码结构与步骤 2 中的相同。

步骤 4: 我们添加图层控制到地图中。请注意,在代码中,我还添加了一个平铺层来选择暗模式和亮模式。我们还可以将交互式地图保存到 HTML 文件中。

Choropleth 地图(图片由作者提供)

收尾思路

Folium 是我学习并用于地理空间可视化的第一个地理空间绘图工具,我非常喜欢它!它易于使用,但非常强大和灵活。这是一个很棒的 python 库,可以让您开始了解地理空间数据可视化领域中使用的概念和技术,也是添加到您的数据科学家工具箱中的一个很好的工具。感谢阅读,我希望你喜欢这篇文章!

参考和数据来源:

  1. Folium 的 Github 文档页面:https://python-visualization.github.io/folium/
  2. 数据来源:美国新冠肺炎县一级社区。这是一个开放的数据集,无需许可即可公开使用。
  3. 数据来源:美国县界。这是一个由public.opendatasoft.com提供的开放数据集,你可以免费下载。

你可以通过这个推荐链接注册 Medium 会员(每月 5 美元)来获得我的作品和 Medium 的其他内容。通过这个链接注册,我将收到你的一部分会员费,不需要你额外付费。谢谢大家!

树叶映射:在地图上显示标记

原文:https://towardsdatascience.com/folium-mapping-displaying-markers-on-a-map-6bd56f3e3420

Python 中向叶子地图添加标记的简短指南

照片由 GeoJango 地图Unsplash 上拍摄

是一个强大的 Python 库,可以很容易地可视化地理空间数据。它利用了 Leaflet.js 的强大功能,这是一个流行且领先的 JavaScript 库,可用于创建可跨桌面和移动平台使用的交互式地图。

该库的设计使其易于使用,同时保持良好的性能。

有多种方法可以将数据显示在一张树叶地图上,包括:

  • 标记(点和圆)
  • 热图
  • 氯派思

在这个简短的教程中,我们将看到如何在一个叶子地图上显示单个和多个标记。

在 follow Python 教程中显示标记

安装叶片

如果您尚未安装 leav,您可以通过 pip 安装它:

pip install folium

或者蟒蛇:

conda install folium

进口叶子

第一步是导入我们将要使用的库。

在本教程中,我们将使用 pandas 加载 CSV 文件中的数据,并使用 follow 在地图上显示我们的数据。

从 CSV 加载井位数据

我们在这个例子中使用的数据来自挪威石油管理局的网站,包含了在挪威大陆架上已经钻探的所有油井的位置。

数据可在此下载:

https://fact pages . NPD . no/en/井筒/表视图/勘探/全部

这些数据得到了挪威政府的 NOLD 2.0 许可,详细信息可以在这里找到:挪威开放政府数据许可(NLOD) 2.0

下载完数据后,我们现在将使用 pandas 函数 [read_csv()](https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html)加载数据。

当我们调用数据帧(df)时,我们看到以下内容:

我们可以看到有 5 列:

  • 井名—井的名称
  • 目的——钻井的目的是什么
  • 完井年份——油井完井的年份
  • 纬度—以十进制单位表示的井的纬度位置
  • 经度-以十进制单位表示的井的经度位置

显示空白树叶地图

要用 leav 显示基本地图,我们需要调用folium.map()。在这个类方法中,我们可以传入许多参数。

  • location —地图将居中的位置
  • zoom_start —地图的初始缩放级别
  • control_scale —比例尺控件是否显示在地图上

我们还可以使用许多其他参数。如果您想了解更多信息,请查看关于leave . map类的帮助文档。

这将返回以下以上面指定的位置为中心的地图。

以挪威为中心的底图。图片由作者提供。

向树叶地图添加单个标记

在地图上显示数据的最简单方法是使用标记。

我们可以通过调用folium.Marker并传递该点的位置来给地图添加一个标记。在这个例子中,我们将显示数据的平均纬度和经度。

为了让它出现在地图上,我们需要应用.add_to()函数并传入我们的地图对象。

当我们调用map对象时,我们得到下面的地图,在所有数据点的平均位置(地图的中心)有一个标记。

显示我们所有油井平均位置的叶图。图片由作者提供。

更改树叶地图上标记的颜色

我们还可以通过调用 Marker 方法中的[**icon**](https://python-visualization.github.io/folium/modules.html?highlight=icon#folium.map.Icon)参数来改变标记的颜色,并将其设置为:

folium.Icon(color='red', icon='')

标记颜色可以是以下任何一种:

*‘red’, ‘blue’, ‘green’, ‘purple’, ‘orange’, ‘darkred’, ’lightred’, ‘beige’, ‘darkblue’, ‘darkgreen’, ‘cadetblue’, ‘darkpurple’, ‘white’, ‘pink’, ‘lightblue’, ‘lightgreen’, ‘gray’, ‘black’, ‘lightgray’*

要删除叶子标记中的任何符号,我们可以将icon设置为一个空字符串。

一旦我们选择了颜色,我们就需要使用.add_to(map)将它添加到我们的地图中。

完整的代码如下所示:

这将生成下面的地图,我们的标记颜色改为红色。

改变颜色和移除符号后的叶子标记。图片由作者提供。

更改树叶地图上标记上的图标

在上面的例子中,我们移除了图标,但是如果我们想要使用我们自己选择的图标,我们可以从下面的 Bootstrap 网站传入一个命名的符号。

https://getbootstrap.com/docs/3.3/components/

例如,我们可以将图标设置为图钉

然后,当我们运行代码并查看地图时,我们可以看到图标已经更新为图钉。

更改符号后的叶子标记。图片由作者提供。

当我们放大时,我们可以更清楚地看到图标的样子。

放大我们的标记和所选符号的视图。图片由作者提供。

向树叶地图添加多个标记

我们可以继续一个接一个地添加标记,这对于小数据集来说可能是好的。但是,当您有一个像这样的超过 2000 行的数据集时,我们需要考虑一种替代方法。

在下面的代码中:

  • 我们调用我们的地图并将zoom_start改为 3,这将比之前的地图更大程度地缩小地图
  • 然后我们迭代数据帧中的每一行
  • 然后我们创建一个 iframe 对象,它允许我们对弹出窗口的外观和内容有更多的控制。在这种情况下,我们只显示井的名称
  • 接下来,我们创建弹出窗口,并传递 iframe 对象和弹出窗口的尺寸
  • 最后,我们获取每一行的纬度和经度,并将它们添加到地图中

当我们运行这个并调用我们的 map 对象时,我们得到了下面的地图,其中显示了挪威近海油井的所有位置。

添加多个标记后的叶子图。图片由作者提供。

我们可以单击任何一个孔来查看弹出窗口和孔的名称。

在包含井名的 iframe 中弹出。图片由作者提供。

按类别控制树叶标记颜色

我们可以根据数据中的属性来改变颜色,而不是用单一颜色来表示叶子标记。

在本例中,我们将根据井的用途更改标记的颜色。

为此,我们需要用目的和相关颜色建立一个字典。在这种情况下,我们有三个类别:野猫,评估和野猫-CCS,分别分配给红色,绿色和蓝色。

由于该列包含一些缺失值(nan ),我们需要在第 15 到 19 行设置一个简短的 try-except 块。

为了调用这些颜色,我们在对folium.Icon()的调用中改变颜色参数的参数。

当我们呼唤地图时,我们现在有了根据目的着色的标记。

带有按类别着色的标记的树叶地图。图片由作者提供。

我们可以放大并检查一个标记,看看弹出窗口显示什么。

带有按类别和自定义弹出窗口着色的标记的树叶地图。图片由作者提供。

摘要

Folium 为显示地理空间数据提供了一个很好的平台。一旦你理解了它的基本原理,使用起来就很简单了。如果你有时间,值得探索一下叶库中可用的其他地图类型。

感谢阅读。在你走之前,你一定要订阅我的内容,把我的文章放到你的收件箱里。 你可以在这里做!*或者,您可以* 注册我的简讯 免费获取更多内容直接发送到您的收件箱。

其次,通过注册会员,你可以获得完整的媒介体验,并支持我和其他成千上万的作家。每月只需花费你 5 美元,你就可以接触到所有精彩的媒体文章,也有机会通过写作赚钱。如果你用 我的链接报名,你直接用你的一部分费用支持我,不会多花你多少钱。如果你这样做了,非常感谢你的支持!

遵循以下高质量数据接收的最佳实践

原文:https://towardsdatascience.com/follow-these-best-practices-for-high-quality-data-ingestion-deb9e2a7f6cb

如何选择正确的工具并将其集成到您的数据管道中

乔恩·泰森在 Unsplash 上的照片

数据摄取可能是 ETL/ELT 过程中最重要的一步。毕竟没有数据是做不了任何转化的!数据接收管道将您的工具和数据库连接到您的数据仓库,即整个数据堆栈的中枢。

您为将数据接收到仓库中而设置的流程为您的数据团队中的所有其他流程设定了标准。您随后的转换和分析取决于您接收的数据的质量。这就是为什么您必须从一开始就遵循最佳实践,并在整个过程中记录下来。

当您从一开始记录最佳实践时,您被迫遵循一个固定的结构。没有这种结构,事情就会变得一团糟。您创建了一个新的数据连接器,它没有遵循您想要的命名约定,但是您已经为要接收的数据付费了,现在要更改它已经太晚了。您与一家摄取公司签订了合同,该公司不提供对您的业务最重要的连接器集成。这些都是你应该避免的现代数据堆栈的噩梦。

那么,创建数据接收管道的最佳实践是什么呢?在本文中,我将分享我从零开始构建数据堆栈时学到的最佳实践。这些实践包括选择摄取工具、记录来源、编排、测试和监控。通过分享我过去所做的事情,你可以在开始自己的旅程之前,从我的成功和失败中学习。

‍Compare 数据接收工具

当我第一次着手决定我们的数据堆栈将使用的工具时,我当时的经理已经准备好了一些非常棒的系统。我是一名新的分析工程师,从未在初创公司做过全职工作。我只是在学习诀窍!幸运的是,他在决定合适的工具和组装框架方面有丰富的经验。如果不是他制作了一个精美的 Excel 电子表格来帮助比较我们正在选择的数据摄取工具,谁知道我们今天的摄取会是什么样。

那么,这个决定摄取工具的框架到底是什么呢?它基本上是一个记分卡,上面有我在标题中提到的三个不同的类别——必须拥有的、最好拥有的和交易破坏者。然后,在每个类别下都有某些品质/特性,我们根据工具的功能给它们打了 1-3 分。

在设计数据接收管道时,有许多重要的事情需要考虑,下面是我们包括的几个:

  • 可用的数据连接器(Shopify、Azure Blob、脸书、NetSuite 等。)
  • 您团队的能力(建立时间、维护时间、技能组合等。)
  • 预算(接收预期数据量的每月成本)
  • 支持(松散的社区、专门的支持代理等。)

这些都是为您的团队决定合适的摄取工具的关键。你可能找不到能满足你所有需求的工具,但是你确实想找到最能满足你需求的工具。如果你在一个工具中找不到你想要的东西,那就策划一下如何使用几个不同的工具,并且仍然能获得相同的结果。例如,也许你有一个分析工程师,他可以用 Airbyte 为你的大多数来源设置和维护连接器。然而,有一些没有集成,所以你需要让你的工程团队建立一些东西。

记录您的数据摄取管道来源

我怎么强调文档的重要性都不为过,尤其是当你在一个小型的数据团队中工作的时候。当出现问题,文档的所有者生病、休假或离开公司时,文档就是您的生命线。您的现代数据堆栈中的所有内容都应该记录在案,以便团队中的任何其他人都可以在需要时接管。数据摄取也是如此。

确保您保留了您正在使用的各种摄取工具以及您在该工具中设置的连接器的文档。如果不这样做,就很容易忘记原始数据的来源。此外,请注意为使该连接器工作,您必须做出的任何特殊要求或更改。例如,对于我们的一个连接器,我必须在数据库上启用变更数据捕获,以便正确地捕获数据。把这些事情写下来,这样对你的团队来说就不是猜谜游戏了。

随时在你的仓库里保存一份所有原始数据的副本

我在雪花数据仓库架构:如何组织数据库、模式和表中详细地描述了这一点,所以如果你还没有,一定要去看看。数据摄取的一个最基本的最佳实践是保护您的原始数据。原始数据应该始终保存在数据仓库中的一个单独的数据库中。任何转换工具或人员都不应该对此数据库拥有写访问权限。它应该是严格只读的。

这是一个从不同来源接收所有原始数据的数据库。这个数据库将作为您所有数据的“备份”,以防您的数据建模出现问题。假设您意外运行了一个数据模型,该模型删除了表中的随机行并混合了列值。你不能再相信那些数据了。你不想删除它,因为那样它就永远消失了,但是你不能用它。您可以删除数据模型中保存的所有数据,并通过引用原始数据源重新运行模型。

原始数据的救援!

‍Run 同步建模

根据您运行数据模型的频率,您的原始数据应该总是在您的模型运行之前同步到您的仓库。无论你是像我一样使用 dbt 进行转换,还是其他,一个转换应该紧接着另一个发生。这确保了相互依赖的 dbt 模型不会并行运行,从而在不同的分析中给出不准确的结果。通过同步运行 syncs 和 dbt 模型,您还可以对数据进行更准确的验证。

幸运的是,像 Airbyte 这样的数据摄取工具一直在与 Airflow、Prefect 和 Dagster 这样的强大部署工具合作。就我个人而言,我使用 Prefect 来部署我的数据模型。他们通过提供“Airbyte 连接任务”使同步变得容易,这允许你直接在你的数据管道内同步你的数据连接器。通过在管道中直接同步数据,您可以在模型和这些同步之间创建下游依赖关系。这意味着您的数据模型中总是有最准确和最新的数据。

作者图片

您只需指定 Airbyte 服务器的托管位置、post、api 版本和连接 id。就这么简单!然后,在完美流中使用这些任务,并将它们设置为 dbt 模型运行的上游依赖项。您可以阅读本教程,用 Airbyte、Prefect 和 dbt 创建一个数据接收和传输管道。

数据源上的‍Create 警报

您必须在源头创建数据警报和测试。许多人认为测试和警报应该在最终的数据模型上进行,但实际上恰恰相反。当你专注于下游而不是上游时,它就变成了一个你永远无法解决的一万块拼图。您必须做大量的挖掘工作,才能发现问题实际上从来都不是数据模型。

相信我,我知道因为我也经历过。当我们在数据可视化平台中编写所有的数据模型时,就不可能调试中间模型。我不得不花数周时间调查一个问题,结果发现是数据源本身造成的。您构建的第一个警报应该在数据源级别。如果您的数据源有问题,您需要在它影响任何下游数据模型之前立即知道。

更不用说,修复数据源的问题要比检查数据模型、测试每一行代码以查看其性能是否符合预期容易得多。数据源中的错误可以简单到修复电子表格中的人为错误、更新凭证或重新触发 cron 作业。与涉及大量代码的情况相比,它们要简单得多。

我最喜欢在源代码中设置测试和警报的方式是使用 dbt tests 和 re_data。使用 dbt 测试,您可以检查空值、主键和预期值。您可以使用这些简单的测试来确保您的数据符合预期。 re_data 是一个 dbt 包,允许您监控数据的新鲜度、行计数和描述性统计等指标。您甚至可以设置松弛警报,在指标超出其典型 z 值时提醒您。

松弛警报本身就很强大。我们每天都在 Slack 上跟公司和团队沟通。将数据提醒直接发送到 #data-alerts 频道,这样你就不需要每天早上解析电子邮件或手动访问仪表盘。如果有问题,你的工具会通过 Slack 告诉你。确保您总是设置它们!

结论

从数据堆栈的初始阶段开始遵循数据接收最佳实践,将为您的团队未来的成功做好准备。这些最佳实践的文档将从一开始就使需求变得清晰,几乎不给出错留下空间。当为拼图的一部分设定了高标准时,它必然会渗透到其他部分。

再次重申,在决定一个接收工具并创建一个数据接收管道到您的堆栈时,请记住以下几点:

  1. 写下所有对你的业务最重要的连接器,你的团队必须提供的技能和时间,以及在决定合适的摄取工具时你的预算。
  2. 记录所有数据源以及它们是如何被接收到数据仓库中的,包括任何特殊的设置。
  3. 始终保持一个包含原始数据的数据库。
  4. 同步运行数据同步和模型,以便数据中没有间隙。
  5. 在数据源而不是下游数据模型上创建警报。

虽然这可能会增加您的工作量,但现在遵循这些最佳实践只会为您省去麻烦。您正在主动解决数据接收管道中的某些问题,在对业务产生负面影响之前找到解决方案。从一开始就考虑这些事情总是比对出错的事情做出反应要好。

订阅我的分析工程简讯以了解最佳实践、新工具和所有现代数据堆栈的最新信息。

用 SQL 提取字符串的简单公式

原文:https://towardsdatascience.com/fool-proof-formula-to-extracting-strings-with-sql-9b35c57de224

解决讨厌的字符串问题的分步示例

Unsplash 上由Bozhin karivanov拍摄的照片

你有没有遇到过一个字符串提取问题,几个小时后却发现自己快疯了?人们很容易迷失在所有潜在的字符串函数中。

字符串是我最不喜欢使用 SQL 的数据类型。对于模式匹配,您需要了解许多不同的函数和字符。但是,当你正确使用它们时,我在本文中向你展示的方法是相当简单的。

我写这篇文章的目的是让字符串不那么烦人。从我的错误和许多小时的挫折中吸取教训!我将向您介绍我正在研究的问题、我的解决方案中的关键功能以及解决方案本身。到本文结束时,您将成为使用 SQL 提取字符串的专家。

问题是

我最近受命为营销团队重新创建一个数据模型。很大一部分逻辑是从活动链接中提取 utm 信息。当您在 Google 上做广告时,会使用 utm 信息创建特殊链接,如媒体、出版商、活动和来源。这些都组合在一起形成一个链接,看起来像这样:

[https://www.etsy.com/market/lamp_shades?utm_source=google&utm_medium=cpc&utm_term=etsy%20lamp%20shades_p&utm_campaign=Search_US](https://www.etsy.com/market/lamp_shades?utm_source=google&utm_medium=cpc&utm_term=etsy%20lamp%20shades_p&utm_campaign=Search_US_Brand_GGL_ENG_Home_General_All&utm_ag=Lamp+Shades&utm_custom1=_k_Cj0KCQiAt8WOBhDbARIsANQLp94WN__lDzNNnwS6yptN8pqbeU09mUzcKN9-5hHMFTWbS4msnQJqh4YaAtaOEALw_wcB_k_&utm_content=go_6518959416_125883546672_536666915699_aud-459688891835:kwd-308153672827_c_&utm_custom2=6518959416&gclid=Cj0KCQiAt8WOBhDbARIsANQLp94WN__lDzNNnwS6yptN8pqbeU09mUzcKN9-5hHMFTWbS4msnQJqh4YaAtaOEALw_wcB)

关键是识别模式。不只是一种模式,而是所有可能的模式。你可能认为你已经找到了一个有效的模式。但是,随后会出现一个特殊的用例,您也需要将它构建到您的逻辑中。找出您正在处理的字符串列中的所有模式。相信我,越早找到他们越好。将这些模式写在列表中,如下所示:

  • utm_source 位于“utm_source”之后和“&”之前
  • utm_medium 位于“utm_medium”之后和“&”之前
  • utm_campaign 位于“utm_campaign=”之后

一旦你找到了字符串中的模式,你就可以把它们翻译成代码。开始用文字而不是代码编写逻辑要容易得多。有了字符串提取,你的代码会变得非常混乱,非常快。几乎到了无法阅读的程度…

提示:边走边注释你的代码。这不仅有助于你在编写代码时更好地理解它,也有助于阅读你的代码的人。

SQL 字符串函数

CHARINDEX()

在处理字符串时,我喜欢使用两种不同的函数。第一个是 CHARINDEX() 函数。这会返回您提供给它的字符串的索引。它有两个输入,您正在搜索的字符串和您在中搜索的列。因此,如果我在campaign_link列中寻找‘UTM _ medium ’,我会将其写成:

charindex('utm_medium', campaign_link)

这段代码将返回“utm_medium”在该列值中所处位置的索引。如果它不在列值中,函数将返回 0。

子字符串()

我要使用的下一个函数是 SUBSTRING() 函数。这需要您想要定位字符串的列和两个索引——一个用于字符串的开头,一个用于结尾。它返回所提供的列中这两个索引之间的字符串。

如果我知道字符串“utm_medium”在索引 5 和 10 之间,我会编写如下所示的代码:

substring(campaign_link, 5, 10)

这将返回campaign_link列中索引 5 和 10 之间的字符串。

其他有用的 SQL 命令

选择语句

CASE 语句总是很方便,但在处理字符串时更是如此。它帮助我们处理我们在数据中发现的所有奇怪的一次性场景。 CASE 语句遵循一个简单的“当这种情况发生时,则执行这个模式。

CASE 
    WHEN utm_medium='Facebook' THEN 'Social'
    ELSE 
END AS marketing_category

您还可以在 CASE 语句中添加一个ELSE来处理其他可能破坏代码的场景。并且,用END结束你的案例陈述!

LIKE 运算符

LIKE 操作符对于在字符串中搜索模式特别有用。虽然它不能像CHARINDEX()SUBSTRING()那样帮助提取那些模式,但它在 CASE 语句中特别有用。它们通常由我写的案例陈述中的“ when 部分组成。

当像使用一样使用时,你指定一个你正在搜索的字符串。根据您希望它在要搜索的列中的位置,您需要使用特殊的运算符。

  • _ 代表单个字符
  • %代表一个、一个或多个字符

我通常在要查找的字符串的开头和结尾使用%。只要字符串出现在列值中的任何位置,这将被证明是正确的。

CASE 
    WHEN campaign_link LIKE '%facebook%' THEN 'Social'
    ELSE 
END AS marketing_category

请注意,我用单引号括起了要搜索的字符串,并用%号括起来。

解决方案

现在,让我们进入解决问题的解决方案,并利用我刚才提到的这些功能。请记住,我是在反复试验之后才得出这个结论的。重要的是,如果你发现有什么不对劲的地方,就要不断地检查你的代码,并反复强调。

当我写字符串提取代码的时候,我是分几部分来做的。首先,我使用了CHARINDEX()函数来查找我正在寻找的字符串部分的索引。这将返回 utm_medium 中“u”的索引,所以我们要将这个字符串的长度加到 index 函数中。这将返回=之后的字符串的索引。

charindex('utm_medium=', campaign_link) + 11

然后,我将它与SUBSTRING()函数结合使用,以获取字符串索引右侧的所有内容(我通过在函数 100 中创建第二个索引来实现这一点)。

substring(campaign_link, charindex('utm_medium=', campaign_link) + 11, 100)

之后,我添加了另一个CHARINDEX()函数来查找下一个字符左边的字符串。为此,从索引中减去 1,因为它是字符串的结尾,只有 1 个字符长。

substring(campaign_link, charindex('utm_medium', campaign_link) + 11, charindex('&', campaign_link) - 1)

最后,我在这个逻辑中添加了一个 CASE 函数。但是,这只是在我意识到并非每个活动链接都有 utm_source、utm_medium、utm_publisher 等时才添加的。我需要一个案例来处理这些字符串后面没有“&”的情况,就像我逻辑中的最后一个要点。

substring(campaign_link,           # starting with the index of utm_medium in the link
          charindex('utm_medium', campaign_link) + 11,           # if this string contains an & then return that index
          # if not, return 100 as the ending index
          CASE 
              WHEN substring(campaign_link, charindex('utm_medium', campaign_link) LIKE '%&%' THEN charindex('&', campaign_link) - 1
              ELSE 100 
          END
)

看到事情变得多混乱了吗?在逻辑中添加 CASE 语句会使代码变得难以阅读,这就是为什么注释代码很重要的原因。阅读它的人需要很长时间才能意识到它在做什么。

让事情变得更加复杂的是,我们需要将它添加到另一个 case 语句中,如果 utm_medium 出现在campaign_link中,这个语句将只提取它。这看起来像这样:

CASE
# check if the link contains a utm_medium
    WHEN campaign_link LIKE '%utm_medium%' THEN 
# return the string after utm_medium=
substring(campaign_link, charindex('utm_medium=', campaign_link) + 11,
# if this extract link contains an &, find the index of that
CASE 
    WHEN substring(campaign_link, charindex('utm_medium=', campaign_link) LIKE '%&%' THEN charindex('&', campaign_link) - 1
# if it doesn't, return 100 as the ending index
    ELSE 100 
END
)
# if it doesn't contain utm_medium, return null 
    ELSE NULL
END AS utm_medium

结论

我发现字符串提取的关键是要有耐心。不要试图一口气写完整段代码。编写各个部分,验证它们是否如您所期望的那样工作,然后将它们集成在一起。虽然这看起来像是更多的工作,但从长远来看,它最终会花费更少的时间。如果你一头扎进去,肯定会出错,你会花更多的时间来调试你的代码。

如果您的工具箱中有这些函数和操作符,您将很快用 SQL 提取字符串。记住,注释你的代码!下次再来看看“ 3 个 SQL 交换以写出更好的代码”和“如何使用这 6 个不寻常的 SQL 函数”。

被统计意义所迷惑

原文:https://towardsdatascience.com/fooled-by-statistical-significance-7fed1bc2caf9

不要让诗人欺骗你

看看世界上最短的关于#统计学的讲座,以及人们对待它的方式的所有错误:

或者说: p =0.042

来自 thesaurus.com 的截图。我的另一个词库很糟糕,很糟糕,也很糟糕。

统计上的不重要

与普遍的看法相反,术语“”并不意味着某件重要的重大的令人信服的发生了。如果你认为我们在这里使用的单词 significant 会让你的词典感到自豪,那么你就成了一个狡猾花招的受害者。不要让诗人欺骗你。

“你不应该让诗人骗你。”比约克

对于那些喜欢尽量少接触统计数据的人来说,以下是你需要知道的关于“统计显著性”这个术语的所有信息:

  • 这并不意味着发生了什么重大的事情。
  • 这并不意味着结果是“大的”或值得注意的。
  • 这并不意味着你会对数据感兴趣。
  • 意思是某人声称对某事感到惊讶。
  • 如果你不太了解问题中的 某人某事 ,它不会告诉你任何有用的东西。

对于除了正在讨论的决策者之外的所有人来说,统计上有意义的结果很少是重要意义上的重要——它们偶尔很适合提出有趣的问题,但通常它们是不相关的。

安德鲁·乔治在 Unsplash 上拍摄的照片

当非专家使用这个术语时,要格外警惕,尤其是当它伴随着令人窒息的旺盛时。有时,特别厚颜无耻的江湖骗子更进一步,去掉“统计”一词,挖掘诗歌的全部力量。“嘿瞧”,他们告诉你,“我们所谈论的在宇宙的眼中是有意义的。”**

不,不是的。

最糟糕的可能罪犯是那些把“统计显著性”发音为“确定的”或“确定的”或“完美无瑕的知识”的同义词的人——这里有一些讽刺被忽略了。该术语来自一个处理不确定性的领域,因此(根据定义!)只属于我们的知识是 而不是 完美无瑕的设定。

行话预警!

对于那些喜欢用行话来对付行话的人,我将在下一节中帮助自己使用更正式的语言。你可以不去想那一点,但是如果你同时对这里的新事物感到好奇,那就走一条小弯路在短短 8 分钟内浏览所有统计学中最重要的观点:

*

我的文章中的大部分链接会将你带到博客文章,在那里我会给你重点主题的更深入的概述,所以你也可以将这篇文章作为选择你自己的冒险关于数据科学的迷你课程的发射台。

统计显著性

“统计显著性”仅仅意味着一个 p 值 低到足以改变决策者的想法。换句话说,我们用这个术语来表示一个无效假设拒绝。**什么是无效假设呢?测试有多严格?_(ツ)_/*

欢迎来到统计学,这里的答案是 p = 0.042 但是你不知道问题是什么。

从技术上来说,设定假设检验条件的决策者唯一的 人,对他来说,检验的结果可能具有统计学意义。

统计是个人的

统计学为你提供了一套决策工具,但如何使用它们取决于你自己——它将像任何其他决策一样是个性化的。

照片由 Towfiqu barbhuiyaUnsplash 上拍摄

这个过程包括非常仔细地措辞你的决策问题,挑选你愿意接受的假设,对你的答案可能错误的不同方式做出一些风险权衡*(因为随机性是一个混蛋),然后使用数学来获得你特定问题的风险可控的答案。

作为一种修辞欺凌的支撑,它的流行有些反常和滑稽。

这就是为什么真正的专家永远不会用统计数据像锤子一样敲打敌人。两个决策者可以在相同的数据上使用相同的工具,然后得出两个不同的——完全有效的——结论……这意味着它作为修辞欺凌的一个支柱,其受欢迎程度既反常又滑稽。

统计意义是个人的。仅仅因为我对这些数据感到惊讶,足以改变我的想法,并不意味着你也应该如此。

当我理解了统计是如何工作的,我不禁惊叹于在不了解统计决策局限性的人面前宣布某件事具有统计学意义是多么的傲慢——几乎是粗鲁。这个术语听起来太普遍了,对任何人都没有好处;这就像是“闭嘴,相信我,因为我的方法很奇特”的修辞手法。我希望你能和我一起给这种修辞方式以它应得的“ pffft ”。

借用别人的统计意义

等等,难道我们就不能从别人的统计结果中学到什么吗?

这就有点哲学了,所以我需要一篇单独的文章来阐述我对这个问题的看法:

*https://kozyrkov.medium.com/why-do-we-trust-scientists-98c24e3b9f0e

简而言之,我的建议是,把你的一些决策权委托给其他人是没问题的,只要你相信他们是有能力的,并且把你的最大利益放在心上。当他们被说服时,你会借用他们的意见,这样你就不用自己重做他们所有的工作了。

通过使用别人的统计结论,你的决定不是基于数据,而是基于你对个人的信任。

请注意,使用别人的结果,你的决定不是基于数据,而是基于你对个人的信任。选择信任他人没有问题,所以你不需要从零开始凭经验建立你的整个世界观——知识共享是人类如此成功的一部分——但值得注意的是,你可能是你认为你正在收听的任何“知识”的几个回合的断电话下游。

如果你让某人站出来代表你做决定——这意味着消耗别人的 p 值和决策结论——那么确保这个人是你认为足够胜任和值得信任的。

你不应该让诗人欺骗你

如果那个对你大放厥词的人是你不信任的人怎么办?往山上跑!

无论何时,只要有一丝关于统计意义的声明的说服力,就要格外小心的发言人兜售的任何商品。如果你信任和你谈话的人,你不需要他们的呼吁有统计学意义。你只需要知道他们相信。如果你不信任他们,你就不会信任他们的统计术语,就像你不会信任他们的爵士乐手一样。

如果你连问题是什么都不明白,那么回答有什么用呢?

如果有一件事我想让你从这篇博文中学到的话,那就是:如果你不太了解决策者,不太了解他们如何着手弄清楚他们是否应该改变他们的想法(确切地说是关于什么),那么他们关于统计显著性的主张 对你来说完全没有意义 。如果你连问题是什么都不明白,那么回答有什么用呢?

感谢阅读!人工智能课程怎么样?

如果你在这里玩得开心,并且你正在寻找一个为初学者和专家设计的有趣的应用人工智能课程,这里有一个我为你制作的娱乐课程:

在这里欣赏课程播放列表,它被分成 120 个单独的一口大小的课程视频:bit.ly/machinefriend

https://kozyrkov.medium.com/membership

喜欢作者?与凯西·科兹尔科夫联系

让我们做朋友吧!你可以在 TwitterYouTubeSubstackLinkedIn 上找到我。有兴趣让我在你的活动上发言吗?使用表格取得联系。

寻找动手 ML/AI 教程?

以下是我最喜欢的 10 分钟演练:

脚注

*如果你很想知道什么是 p 值,我制作了一个视频来帮助你:

这是我 YouTube 播放列表上的第一个视频,你可以在 http://bit.ly/quaesita_p1 找到

  • 要了解假设检验的解释,请查看我关于该主题的博客文章,或者看看这段视频:*

被泰勒级数愚弄

原文:https://towardsdatascience.com/fooled-by-taylor-series-b6656bce2b46

利用 Python 进行足球教练分析

原文:https://towardsdatascience.com/football-coaches-analysis-using-python-db38aaab28e9

以下是我如何利用数据科学从最佳国际锦标赛中提取关于教练的深刻信息。

照片由 Nguyen Thu HoaiUnsplash

声明:我不是足球分析师!我不以做足球分析为生。这篇文章中的假设是由一个热情的足球迷和爱好者做出的。不要打仗,要做爱。❤

在意大利,足球几乎是一种宗教。

我从小就追随我的球队(❤罗马),从那以后我就爱上了足球。

另一个有趣的事实是,我和数据打交道。特别是我在航天工程与工程力学系工作,每天的主要任务就是分析数据,写机器学习算法。

我最喜欢统计学的一点是,事实上,它可以应用于所有具有可重复性的研究。足球由季节、比赛和比赛中反复出现的事件组成。出于这个原因,这是一个“统计研究”的完美例子,足球分析是一个新兴领域。

在这篇文章中,我将对欧洲最重要的 4 个联赛(德甲、英超、意甲、西甲)的教练进行统计研究。我将使用 Python 和一些基本的库,用最简单易懂的方式来做这件事。尽管如此,即使对于像我这样的足球迷来说,一些发现也是非常有趣和有见地的。我希望你也会觉得它们很有趣。:)开始吧!

1.图书馆和数据

这些是您需要导入的库:

可选:如果你想让情节看起来和文章中的一模一样,导入这个:

好了,现在让我们“谈数据”。

1.1 来源和可用性

数据集来自https://www.kaggle.com/,可以从这个下载。
数据使用不需要版权许可( CC0 1.0 通用(CC0 1.0) ),可以免费下载使用。该数据集相当新(6 个月前更新),并且可以在几秒钟内下载,因为它非常小(大约 100 行和 13 列)。

1.2 概述

让我们来看看:

举个例子,我们把英超的所有教练都打印出来:

appointment_date 和 contract_until_date 列实际上没有什么帮助。让我们摆脱他们。另外,让我们去掉重复行和 NaN 行:

很好,现在我们准备好了,让我们开始一些探索。

2.教练的出生地

首先,我们来看看每个联盟有多少教练:

好吧,公平。我们每个国家有近 20 名教练,这是可以预见的,但他们来自哪里?

为了清楚起见,让我单独报告图像:

作者图片,使用上面的代码生成

我们可以看到,大部分来自欧洲,这是可以预见的。我们在美国和澳大利亚有几个异常值,在南美有 6 个。

我们在大洋洲看到的两位教练之一是安托万·孔波雷

南美 6 帅之一是皇家贝蒂斯主帅曼努尔·佩莱格里尼:

杰西·马希是一名来自美国的教练。

3.教练条款

我们已经看到杰西·马希并没有真正的团队……有多少教练没有俱乐部?

因此,我们有一个由被解雇的教练组成的数据集的一致部分。多少年后,教练最终会被解雇?

为了进行此分析,让我们使用“avg _ term _ as _ a _ coach”列。让我们使用下面的代码将它转换成 float,看看有多少行在转换中有“问题”:**

只有一个。因此,用平均值替换有问题的行值是合理的,这不会给研究带来什么麻烦。让我们找出其中的意思。

让我们替换它:

让我们把它画出来:

作者图片,使用上面的代码生成

所以欧洲的平均期限是 1-3 年。我们有一些大约 5-6 年的异常值和过去 8 年的异常值。需要考虑的一件有趣的事情是每个联盟的蔻驰平均任期。换句话说,问题如下:

“在意大利/德国/英国/西班牙执教的蔻驰的平均任期是多少”

除了几个国际教练,一个教练倾向于在他的联赛内流动(例如因扎吉从拉齐奥到国际米兰,或者尤里克从维罗纳到都灵,或者意大利人从拉斯佩齐亚到佛罗伦萨)。这意味着一个团队一个任期的平均长度基本上就是项目的平均长度。当然,一个经常改变项目的团队很可能没有像他们期望的那样赢得很多比赛,所以我们可以把这看作是一个“不好的信号”。另一方面,如果一名教练的平均任期更长,这意味着他所在的俱乐部信任他,这也是一个好迹象。

三言两语:

一个联盟的长期平均“教练平均任期”是增加联盟“健康”并使其更具挑战性的一个因素

让我们用箱线图来绘制它:

作者图片,使用上面的代码生成

“德国”联赛(德甲)上的离群值使得剧情非常难以解读。我们知道在德甲有一个局外人在俱乐部呆了超过 8 年,但是为了更清楚,让我们把它切掉:

所以在,项目通常是。在英国,即使分布变化很大,项目平均更长,法国也是如此

绘制统计数据的另一种有效方法如下:

作者使用上述代码制作的图像

除了局外,英格兰足球(众所周知更具挑战性的足球)在几乎所有四分之一的领域都有更长的项目。这在某种程度上证实了我们的假设。

4.教练年龄

注意:下面的分析使用了与上面非常相似的代码。由于这个原因,它没有被充分报道。

另一个非常有趣的因素是教练的年龄。我不是“革命思想”的粉丝,我认为足球归根结底是一项非常简单的游戏,不需要任何革命:它本身就是美丽的。因此,我相信成为足球专家会有很大的不同。要有经验,你需要足够老。现代史上最好的教练(安切洛蒂、穆里尼奥或者瓜迪奥拉)都年过五十,这不可能是巧合。

当然也有过类似 纳格尔斯曼阿特塔 (34 岁和 40 岁)这样的反例但是,如下面的情节所示,他们是分布的左尾:)

属于作者的图像

但有趣的是,我们注意到有些联赛确实更喜欢年轻教练,比如德国的:

属于作者的图像

法兰西是拥有最老教练的国家,即使最高值低于意、英、西。西班牙的平均值还是挺高的。****

英格兰处于趋势中间。总的来说,它更喜欢年轻球员(中位数),但第一四分位数比意大利和德国大。****

需要强调的是,唯一与其他联赛明显不同的是德国,它拥有所有联赛中最年轻的教练。

5.教练的想法!

好吧,但是他们踢球的实际方式是什么?他们如何安排队员在球场上的位置?让我们展示一下:

作者使用上面显示的代码制作的图像

所以:

  1. 他们中的很多人更喜欢 4 名后卫
  2. 一小部分使用 3 个防守者
  3. 他们中很少有人(一两个)使用 5 后卫

但是一个教练阵型不亚于他的足球理念。那么他们是如何“看”不同联赛的足球的呢?

作者使用上述代码制作的图像

所以英超的踢球方式是最异类的。那里有许多不同的队形。法甲是最同质的一个(只有 5 种不同的方式)。在意大利6 教练用 3 后卫而不是 4(那是最高的!).以进攻方式著称的西甲,最常见的阵型是 4–3–3 进攻。数据科学是令人敬畏的家伙:)

6.摘要

让我们总结一下我们接触到的一些要点。

  1. 大多数欧洲教练实际上来自欧洲,有些来自南美,只有少数来自美国。
  2. 15.3%的欧洲教练在球队被解雇时无法(立即)找到另一支球队。意大利的项目比其他地方都要短,而英国的项目最长。
  3. 除了德国之外,整个欧洲的足球教练年龄分布都非常相似(见第四章统计)。在德国,教练相当年轻
  4. 英超的踢球方式最异质,而法甲的踢球方式最同质。西甲教练更喜欢进攻型的阵型。****

7.结论

如果你喜欢这篇文章,你想知道更多关于机器学习的知识,或者你只是想问我一些你可以问的问题:

A.在 Linkedin 上关注我,在那里我发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大数量的故事”,你可以阅读我(以及成千上万其他机器学习和数据科学顶级作家)写的任何关于最新可用技术的文章。

对于数据科学家来说,总是到了做决定的时候

原文:https://towardsdatascience.com/for-data-scientists-its-always-decision-time-f1e9043d5cfe

数据科学家帮助他们的同事做出最佳、最明智的决策。他们的工作变得更有价值,因为它让利益相关者能够做出违反直觉或不太传统的选择,否则他们不会考虑。

然而,我们难道不应该关注数据科学家自己做出的决定吗?毕竟,每一个基于数据的洞见都是从业者无数次选择的结果,他们将原始的、笨拙的价值塑造成其他人可以消化和分析的东西。本周,我们将近距离观察决策的内部运作——由数据科学家进行并为他们服务。让我们开始吧。

  • 决策智能在组织中属于什么位置? 将数据和分析功能整合到一家公司有很多种方法。Erik Balodis 在 TDS 上的第一篇文章中,概述了一个超越传统数据驱动思维的新框架,旨在保持工作流的灵活性和优化性。
  • 用户故事可能是表现更好的细分项目的关键 。将客户划分为不同的类别是行业数据科学家的一项关键任务。Ana Isabel 展示了如果流程不仅包括量化元素,还包括叙述性的、基于用户角色的元素,那么更好的结果是如何触手可及的。
  • 如何选择上下文相关的统计值 。Tyler Buffington 博士警告我们不要默认传统智慧。通过具体的例子,他展示了我们应该如何警惕自动突出中值而不是平均值,并强调应该决定我们选择的不是我们正在处理的分布的形状,而是我们希望告知的那种决定。

泰勒·希里在 Unsplash 上的照片

  • 对于缺失数据,正确的插补方法 至关重要。一旦你走出整洁的、预处理过的实践数据集的泡泡,不完整的和杂乱的数据无处不在。有许多技术可以用来估算您需要的值,但是正如 Adrienne Kline 在她的详细讲解中所阐明的,它们都有各自的优点和局限性。
  • 神经网络:是还是不是?看情况 。在机器学习中,很少有决策比选择正确的模型更重要。对于 Ygor Serpa 来说,重要的是要记住花哨复杂的选项不一定是最好的。

对于其他主题的启发性阅读,这里有几个最近的突出之处——与我们本周的主题一致,你需要做出一些艰难的决定!(当然,除非你读完了所有的书,这是你绝对应该做的。)

感谢您加入我们又一周伟大的数据科学写作。如果你想支持我们作者的工作,并获得访问我们全部档案的权限,考虑成为一名媒体会员

直到下一个变量,

TDS 编辑

Python 中的预测协调

原文:https://towardsdatascience.com/forecast-reconciliation-in-python-8203c16254b2

使用 Python 将分层预测协调成一致的预测。

Unsplash 上由 Fabrice Villard 拍摄的照片

介绍

根据我的经验,处理没有潜在“级别”的实时时间序列数据有点不寻常。例如,考虑可能通过杂货店的交易产生的数据。可以在单个产品级别、购物者级别或商店级别描述交易。所购买的产品也可能被归类到特定类型的产品中,而这些类别又可能落入更广泛的类别中。对于企业主来说,这种复杂的层级结构可能会使对其业务做出准确和公正的预测变得复杂。一个数据驱动的人最有可能对每个不同的水平做出预测,但它们并不总是相加。这就是和解变得必要的地方。

我在这篇文章中的大部分解释都是基于 Rob J. Hyndman 和 George Athanasopoulos 的《预测:原理和实践》一书。这是一个很好的预测资源,而且完全免费。我强烈建议你花些时间阅读 Hyndman 的更详细的解释在某些时候协调层次和分组预测。我并不打算在这里取代这本书,虽然我仍然打算给出一个预测协调的解释。相反,我写这篇博文的目的是为阅读这篇文章的人提供开发他们自己的预测协调代码所需的背景知识。我发现现有的包没有得到一致的维护,或者(在我看来)它们没有达到在生产环境中被认为是可靠的开发阶段。但是,即使你真的使用了现有的和解软件包,盯着书本上的数学公式或应用别人创建的一些预定义函数,也不是获得对实际发生的事情的确切理解的最佳方式。希望通过分享关于如何编写自己的 Python 代码来协调层次预测的简要说明,我还可以提供一些其他方式可能无法获得的额外见解。

分层时间序列

首先,我将简单介绍一下层次时间序列和分组时间序列的区别(不,它们不是完全一样的东西)。当层次结构中的较低级别仅属于一个域时,时间序列被视为层次结构。例如,考虑以下层次结构树:

Sub-Category 1归属于Category 1Sub-Category 4归属于Sub-Category 2等。这种排他性定义了一个层次时间序列。同样需要注意的是,从数学上来说,每一个子类别应该加起来就是它们上面的类别,Total是每一个底层类别的总和。数学表达式如下所示:

T=C1+C2=SC1+SC2+SC3+SC4+SC5
C1=SC1+SC2+SC3

另一方面,一个分组的时间序列是子域之间的排他性不存在的时候。例如,考虑相同的层次结构树,但是只有三个子类别分布在两个主要类别中的每一个上:

同样的层次结构也可以用下面的树来描述:

层次结构组的多种排列的复杂性意味着所使用的原始公式不再有效。Hyndman 这种结构概念描述为“不会以独特的层级方式自然分解。”

理解分层时间序列和分组时间序列之间的区别很重要,因为求和矩阵(在下一节中解释)取决于时间序列的结构。

连贯预测的基石

调和的——或者说一致的——预测由几个关键部分构成:

基本预测:
每个层次级别的预测,表示为一个 m x n 矩阵( m 行和 n 列),其中矩阵的列表示数据的层次级别,行表示预测范围的每个时间段。

求和矩阵:
求和矩阵描述了数据的层次/分组结构。对于分层求和矩阵,列数与底层的唯一类别数相匹配。行数由所有级别中唯一类别的总数决定。求和矩阵的值是二进制值,表示哪个底层(列)类别映射到所有级别的每个层次结构。考虑先前共享的具有两个类别和五个子类别的分层数据的例子。求和矩阵如下所示:

因为分组的时间序列数据不会以“唯一的方式”分解,所以不存在唯一的底层。这意味着求和矩阵需要额外的列和行。帖子前面的分组数据如下所示:

映射矩阵:
预测对账的关键组件是映射矩阵。该矩阵根据所使用的对账方法而变化,但原则是相同的。当您将映射矩阵与求和及基本预测矩阵相乘时(对于分层或分组时间序列),结果是一组一致的预测。接下来的挑战是找到一个“最佳”映射矩阵,它可以用来使预测与最小的方差相一致。

对账方法

存在许多不同的调和方法来寻找最佳映射矩阵,并且一些方法比其它方法更好。我不会详细讨论其中的大部分,但我仍然会列举几个。对账方法分为两类:单级最小痕迹方法。

一些单级方法包括:

  • 自下而上
  • 自上而下
  • 中间输出

一些最小跟踪方法包括:

  • 普通最小二乘法(OLS)
  • 方差缩放的加权最小二乘(WLS)
  • 具有结构缩放的 WLS

对于不同的最小痕迹方法的更严格的解释——以及一些额外的方法——请随意查看本文。

可用的协调工具

如前所述,有几个现有的工具/包可以应用大多数协调方法。前面提到的 Rob Hyndman 帮助开发了一个名为[fabletools](https://fabletools.tidyverts.org/reference/reconcile.html)的 R 包,在我看来这是最好的预测包之一,也是预测协调的最好工具。开发名为[scikit-hts](https://scikit-hts.readthedocs.io/en/latest/readme.html)的 Python 包的工作也在进行中。然而,它仍然有很多问题,并且不具备fabletools中存在的所有功能。

Python 中的预测协调

我不会分享对所有可用的不同方法进行编码的解释,也不会严格展示机器学习的所有典型部分(即,我不会将数据分成训练/测试集,生成预测,查看准确性指标等。).我的目的是演示如何编写自己的协调算法,所以我将只使用一个带有预先存在的预测的样本数据集。

以下是样本数据集中每个等级级别的图:

调节该数据集中的预测的第一步是定义基本预测和求和矩阵。为了做到这一点,我们需要定义层次结构的底层/中层/顶层。我发现使用以下格式的数据可以使这个过程变得非常简单:

注意,我已经将dateparentchildforecast作为列包含在数据集中。当然,你选择怎样组织你的数据完全取决于你自己。但是我用来收集协调预测所需的所有信息的方法要求数据以这种方式格式化。

接下来,我们可以在该数据集中定义不同的层次结构,并将数据重组为正确的基本预测矩阵结构:

从重构的数据集中,定义求和矩阵可以使用以下代码轻松完成:

最后,我们可以定义我们的映射矩阵。在这篇文章中,我将演示如何使用普通的最小二乘法来协调预测。既然您已经定义了基本预测和求和矩阵,那么其他方法无需对代码进行太多重大调整就可以轻松实现。如果你想使用任何其他方法,你可以参考 Hyndman 书中的这一部分,了解不同的最佳协调方法。

在使用 OLS 调节法时,我们将使用以下公式:

设求和矩阵sm = S ,基本预测矩阵bf = F ,预测范围= h. 那么,对于映射矩阵 M:

以及协调的预测输出 R :

你还应该使用本文前面给出的公式,仔细检查所有的东西加起来是否一致:

T=C1+C2=SC1+SC2+SC3+SC4+SC5
C1=SC1+SC2
C2=

就是这样!您已经使用普通最小二乘法成功地调节了预测。

参考

*除特别注明外,所有图片均为本人所有。

[1] Rob J. Hyndman 和 George Athanasopoulos,预测:原则和实践,第 3 版。* (2022)、https://otexts.com/fpp3/
【2】沙妮卡·l·维克拉马苏里亚、乔治·阿萨纳索普洛斯、罗布·海德曼;通过轨迹最小化对分层分组时间序列进行最优预测协调(2019);
【3】米切尔·奥哈拉-怀尔德、罗布·海德曼、埃罗·王;寓言工具(2022);https://fabletools.tidyverts.org/reference/reconcile.html
【4】卡洛·马扎费罗sci kit-HTS(2019)https://scikit-hts.readthedocs.io/en/latest/readme.html*

* https://medium.com/@bentontripp/membership *

具有缺失值的预测时间序列:超越线性插值

原文:https://towardsdatascience.com/forecast-time-series-with-missing-values-beyond-linear-interpolation-2f2adf0a0cba

比较备选方案以处理时间序列中的缺失值

照片由 Kiryl SharkouskiUnsplash 上拍摄

拥有干净易懂的数据是每个数据科学家的梦想。不幸的是,现实并不那么美好。我们必须花费大量的时间来进行数据探索和清理。然而,一个好的探索性分析是提取最有用的见解和产生更好结果的关键。

在预测应用的背景下,数据中呈现的动态的详细概述是做出最佳决策的良好起点。从预测架构的选择到预处理技术,有很多可供选择的方案。最重要的,同时也被低估的,是用来处理缺失值的方法

遗漏的观察值没有全部相同的含义。由于缺乏信息或摄入过程中的问题,可能会缺少值。在大多数情况下,没有对所有情况都有效的黄金法则来填充缺失值。我们能做的就是了解分析领域。对于时间序列,我们必须考虑系统中的相关性动态以及数据中存在的时间相关性

在这篇文章中,我们试图解决一个存在缺失值的时间序列预测任务。我们研究不同的策略来处理时间序列的缺失观测值。从标准的线性插值到更复杂的技术,我们试图比较不同的可用方法。令人兴奋的部分在于我们只管理 scikit-learn 的实验。用简单的 scikit 预测时间序列——用 tspiral 学习是可能的。我发布了 tspiral ,目的是利用 scikit-learn 生态系统的完整性和可访问性来解决时间序列预测任务。

实验设置

我们的范围是测试不同的插补策略如何影响时间序列预测的性能。为此,我们首先生成一些具有每日和每周季节性的每小时合成时间序列。

模拟时间序列的季节性模式(图片由作者提供)

其次,我们人为地生成一些缺失的区间,插入到我们的时间序列中。

缺失值的时间序列示例(图片由作者提供)

至此,我们已经准备好开始建模了。我们想要测试预测准确性如何根据用于填充缺失值的方法而变化。除了众所周知的线性插值之外,我们还想测试总是应用于表格数据集的技术如何也适用于时间序列。具体来说,我们测试了 k 近邻(knn)和迭代插补。

对于 knn 方法,我们使用 k-最近邻方法来填充缺失值。每个缺失要素都使用最近邻要素的值进行估算。缺少多个要素的样本的相邻要素可能会有所不同,具体取决于要估算的要素。另一个有趣的方法是使用迭代插补。每个具有缺失值的要素都被建模为其他要素的函数。通过这种方式,我们拟合一个模型来预测每个特征,将其他特征作为输入。由此产生的预测用于估计缺失的观察值并提供插补。估计过程可以重复更多次以保证鲁棒性,直到重建误差足够低。

在时间序列上下文中,我们将相同的技术直接应用于滞后的目标特征,保持基础算法和预测策略不变。

不同插补策略的重构比较(图片由作者提供)

着眼于重建能力,迭代和 knn 插补看起来很有前途。用简单的内插法,我们只限于连接更近的观测值,而不考虑系统的性质。使用 knn 或迭代插补,我们可以复制数据中的季节性模式和潜在动态。通过在我们的预测管道中采用这些预处理技术,我们可以提高插补能力,从而实现更好的预测。

不同插补策略的预测比较(图片由作者提供)

结果

将缺失值填充合并到机器学习预测管道中非常简单。我们只需选择所需的插补算法,并将其叠加在所选的预测算法之上。插补在滞后目标上进行,在没有缺失值的完整特征集上安装预测算法很有用。下面是一个使用递归预测方法的迭代插补(使用线性模型)示例。

from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.impute import IterativeImputer 
from tsprial.forecasting import **ForecastingCascade**model = **ForecastingCascade**(
    make_pipeline(
        **IterativeImputer**(Ridge(), max_iter=10), 
        Ridge()
    ),
    lags=range(1,169),
    use_exog=False,
    accept_nan=True
)
model.fit(None, y)
pred = model.predict(np.arange(168))

我们在我们的合成时间序列上比较了三种提到的填充技术(线性插值、knn 和迭代插补)。丢失的时间间隔有一个随机的长度,并被随机插入到我们的时间序列的最后部分。该选择希望同时测试重构能力和插补对预测未来值的影响。

不同插补策略的性能比较(图片由作者提供)

正如所料,结果显示了 knn 和迭代估算器对根据测试数据计算的性能的积极影响。他们可以捕捉数据中的季节性行为,并提供更好的重建,从而实现更好的预测。

摘要

在这篇文章中,我们介绍了线性插值的有效替代方法来处理时间序列场景中的缺失值。我们发现如何简单地使用 scikit-learn 和 tspiral 将我们的自定义插补策略整合到我们的预测管道中。最后,我们发现所提出的技术可以产生性能提升,如果适当地采用并根据分析案例进行验证的话。

查看我的 GITHUB 回购

保持联系: Linkedin

使用 Scalecast 更轻松地预测 Python 中的 ARIMA

原文:https://towardsdatascience.com/forecast-with-arima-in-python-more-easily-with-scalecast-35125fc7dc2e

使用 ARIMA、SARIMA 和 SARIMAX 进行指定、测试和预测比以往任何时候都更容易

米盖尔·阿尔坎塔拉在 Unsplash 上拍摄的照片

自回归综合移动平均(ARIMA)模型仍然是预测时间序列数据最流行和最有效的方法之一。这是一个线性模型,它将一个序列的过去的滞后、误差和平稳性相关联,以形成关于数据的基本统计属性的理论。它利用这些信息来预测未来的价值。

用 Python 实现 ARIMA 模型的一种常见方式是使用 statsmodels 。有许多关于这种实现的教程,大多数拥有数据科学相关学位的人都经历过这种练习。

如果您了解 ARIMA 模型的基础并对其感到满意,那么您可能会喜欢一个减少数据准备和实现该模型所需的代码行的库。事实上,我可以非常快速地编写脚本,用 ARIMA 进行验证和预测,结果证明是准确的。这都涉及到使用 scalecast 包。易于安装:

pip install scalecast

我们将使用一个开放数据库许可的 Kaggle 上提供的每月乘客计数数据集。参见本文中使用的代码这里。参见 GitHub 上的 scalecast 项目。

Scalecast

这个软件包是为那些知道他们的模型需要被验证并用于产生未来预测的预测者准备的。也许你不想花太多时间去分解模型的细节。如果是这样的话,从导入结果到测试和预测,代码的基本流程是这样的:

作者图片

这个模型很容易指定,代码的每一行都有很强的可解释性。当然,这个例子会给你一个类似于“天真”预测的东西,其中统计属性没有真正被检验,模型没有真正预测任何有用的东西。因此,可以使用稍微复杂一点的脚本,如下所示:

作者图片

作者图片

在这个过程中,你会看到一些图表的结果,包括 ACF、PACF 和季节分解图。你也可以解释统计测试的结果,如增强的 Dickey-Fuller 和 Ljung-Box,以查看系列的期望水平和模型的有效性。使用发现的洞察力,您可以尝试指定和重新指定模型,直到找到您喜欢的模型。还好这里选的那个好像还不错,没多久我就找到了。

总的来说,这个过程仍然比 statsmodels 需要更少的代码行,但它变成了一个迭代过程,需要人工干预和对图形的解释,以找到使模型有用的最佳 ARIMA 阶数。也许你没有时间做这些,想把人的部分自动化掉。

全自动化

自动化可能会给建模过程带来问题,例如指定“嘈杂的”模型和统计上不可靠的模型的风险。但是,这个风险对你来说可能是值得的。如果是这种情况,您可以使用以下两种解决方案。

解决方案 1 —使用 pmdarima 的 auto_arima 函数设置订单

从 pmdarima 使用 auto_arima 需要安装 pmdarima 软件包:

pip install pmdarima

完成后,您可以对时间序列调用函数,它会通过最小化信息标准(如 AIC)来快速找到最佳顺序,该信息标准用于测量模型的拟合度,但也会惩罚太多可能导致过度拟合的参数。我总是只使用训练集来做这一部分,这样我就不会泄漏数据和过度拟合。下面是一个使用相同数据集和 scalecast 流程进行预测的示例:

作者图片

作者图片

我们可以看到,从 auto_arima 中选择的 ARIMA 模型是(1,1,0)(0,1,0)[12],并且该模型似乎也很适合。您可以尝试向函数传递其他参数和不同的信息标准,看看是否会得到不同的结果。您还可以看到该模型的汇总统计数据:

f.regr.summary()

解决方案 2 —网格搜索 scalecast 中的最佳订单

如果您不喜欢 auto_arima 函数,并且仍然希望自动为您的系列找到最佳模型,scalecast 本身提供了另一种搜索最佳 arima 阶数的方法,那就是通过对数据的验证切片进行网格搜索。

这可能为 auto_arima 方法提供了优势,因为它实际上会在样本外数据上验证所选订单,而不是使用不完美的信息标准。网格搜索方法也不需要调用 Python 脚本的另一个库。缺点是它可能比 auto_arima 花费更长的时间,并且您不能快速地迭代通过这么多的模型。

以下是该过程的代码:

作者图片

该代码搜索 12 个 ARIMA 模型,通过测试最接近测试集观测值的 12 个观测值的每个误差,找到具有最佳阶数的模型。这 12 个观察值被排除在每次训练迭代之外。数字 12 可以在上面代码的第一行修改。正如我们所看到的,这种方法也产生了极好的效果!

选择模型并导出结果

我们在这个脚本中指定了四个 ARIMA 模型。哪个最好?我们可以比较 scalecast 中的几个误差和精度指标,包括 RMSE、MAE、MAPE 和 R2。我将选择测试集 MAPE 性能。首先,让我们将结果,包括测试集预测、预报和模型统计数据,导出到熊猫数据框架的字典中。

pd.options.display.max_colwidth = 100
results = f.export(to_excel=True,
                   excel_name='arima_results.xlsx',
                   determine_best_by='TestSetMAPE')

然后,我们使用以下代码选择一些信息来查看每个模型:

summaries = results['model_summaries']
summaries[
    [
         'ModelNickname',
         'HyperParams',
         'InSampleMAPE',
         'TestSetMAPE'
    ]
]

作者图片

不出所料,第一个没有选择订单的 ARIMA 显然是最差的。使用网格搜索找到订单的 ARIMA 在技术上是最好的(测试集上的误差为 3.6%),但其他三个模型都非常相似。它们都没有显示出过度拟合的迹象,因为它们的样本内和测试集指标非常接近。让我们一起来看看他们的预测。他们将按照他们的测试集 MAPE 性能从最好到最差排序。

作者图片

外部变量——下一次

我们已经概述了季节性 ARIMA (SARIMA)模型的使用,但该模型还有其他可用的变体。例如,使用外生变量,如假日或异常值,可以通过向对象添加所需的变量,并在manual_forecast()函数中指定Xvars参数,如果使用auto_forecast(),也可以在网格中指定。更多信息参见文档

结论

使用 scalecast 软件包指定和实现 ARIMA 的结果从未如此简单。我希望这些代码对您有用,并且您能够使用这种方法建立自己的 ARIMA 流程!

用 Python 预测大气 CO2

原文:https://towardsdatascience.com/forecasting-atmospheric-co2-concentration-with-python-c4a99e4cf142

如何用飞镖创建时间序列预测模型

安德里亚斯·费尔斯克在 Unsplash 上的照片

气候变化无疑是人类面临的最大挑战之一,专家认为这是对我们人类生存的威胁。2021 年,世界范围内记录了前所未有的热浪和野火,而洪水给欧洲和亚洲带来了破坏。根据政府间气候变化专门委员会(气专委)第六次评估报告,极端天气事件日益频繁与气候变化有关,需要在全球范围内采取严厉措施来解决这一问题。否则,数百万人将受到影响,他们的生活质量将在随后的几年中显著下降。

二氧化碳(CO2)和甲烷(CH4)等温室气体(GHG)将热量储存在大气中,从而使我们的星球保持温暖,有利于生物物种的生存。不管怎样,人类活动,如燃烧化石燃料,导致大量的 GHG 被排放,从而过度增加了地球的平均全球温度。因此,向可持续的全球经济过渡势在必行,这样我们才能减缓气候变化,确保我们人类的繁荣。在本文中,我们将对大气 CO2 浓度数据应用时间序列预测,从而有机会探索机器学习和气候变化的交集。

飞镖图书馆

飞镖标志—由 Unit8 提供的图像

darts 库开发者的目标是用 Python 简化时间序列分析和预测。Darts 支持多种预测方法,从 ARIMA 和指数平滑等经典统计模型,到基于机器学习和深度学习的新方法。此外,darts 包括各种功能,让我们了解时间序列的统计特性,以及评估预测模型的准确性。如果你想了解更多关于飞镖的知识,可以阅读本库的详细介绍,或者参考官方 API 文档。darts 库将让我们基于莫纳罗亚火山二氧化碳数据集创建各种预测模型。此外,我们将比较这些模型的准确性,并使用最佳模型来预测 2022 年的大气 CO2 浓度值。

莫纳罗亚二氧化碳数据集

莫纳罗亚天文台——照片由 NOAA 拍摄

莫纳卢阿火山是同名的莫纳罗亚天文台(MLO) 的所在地,这是一个自 1950 年以来一直监测大气的研究机构,其偏远的位置为记录气候数据提供了理想的条件。1958 年,查理斯·大卫·基林建立了 MLO 的 CO2 监测计划,并开始记录大气中 CO2 浓度迅速增加的科学证据。莫纳罗亚火山二氧化碳数据集是从斯克里普斯海洋研究所下载的,包括从 1958 年到 2021⁴.的每月大气二氧化碳浓度值,单位为百万分之一(ppm)此外,数据集还包含季节性调整和平滑版本的数据,但我们的分析将专门关注标准时间序列。

时间序列分析

在本节中,我们将提取一些关于数据集及其统计属性的见解。这将通过使用各种类型的绘图和其他时间序列分析技术来完成。

我们首先导入项目所需的 Python 库,包括 pandas、Matplotlib 以及 statsmodels 和 darts 库中的各种函数。之后,我们将 Matplotlib 图形 DPI 设置为 300,这样我们就可以获得本文的高分辨率图形,但这是可选的。

导入库之后,我们继续将数据集加载到 pandas 数据框架中。然后,我们对它应用一些数据预处理技术,比如删除不必要的列,设置每月日期时间索引并删除空值。

作者图片

在清理数据集之后,我们使用plot() pandas 函数创建一个简单的时间序列线图。显然,有一个明显的上升趋势,突出了大气 CO2 浓度在过去几十年中迅速增加的事实。此外,由于地球的自然碳循环,还有季节性,因为植物在 year⁵.的不同季节吸收和释放二氧化碳具体来说,当植物在春天开始生长时,它们通过光合作用从大气中去除二氧化碳。相比之下,当落叶树在秋天落叶时,二氧化碳会因呼吸作用而释放出来。

作者图片

我们使用seasonal_decompose() statsmodels 函数对时间序列进行分解,即提取趋势、季节和残差成分。在此之后,我们绘制这些组成部分,并更好地理解之前提到的趋势和季节性。在这种特殊情况下,我们已经可以通过目视检查线图来识别组件,但是季节分解在更复杂的情况下非常有用。

作者图片

我们使用plot_acf() statsmodels 函数来绘制时间序列的自相关函数(ACF ),即其滞后值之间的线性关系。由于时间序列趋势,我们可以看到自相关性对于小的滞后是高的,并且在滞后 5 之后开始逐渐降低。ACF 还应该突出时间序列的季节性成分,但在这种情况下是看不出来的。

时间数列预测法

在本节中,我们将在 CO2 数据集上训练各种预测模型,并比较它们的性能。在此之后,我们将选择最准确的模型,并基于它创建 2022 年的预测。

我们首先将 pandas 数据帧加载到一个 TimeSeries 对象,这是 Darts 库所需要的。之后,我们创建plot_backtest()print_metrics()效用函数,让我们绘制预测图并显示各种模型指标,包括 MAE、RMSE、MAPE、SMAPE 和 R。

创建一个简单的预测模型

设置基线精度是标准做法,因此我们将通过创建一个简单的模型来实现。这将有助于我们评估更复杂模型的性能,理论上,与基线相比,这些模型应该具有更高的准确性。在这种情况下,我们将创建一个简单的季节模型,该模型总是预测 K 步之前的值,K 等于季节周期。

作者图片

为了测试简单的季节性模型,我们使用了预测时间跨度为 12 个月的historical_forecasts()函数。此函数在扩展窗口中连续训练模型,默认情况下保留每个预测的最后一个值。这种技术被称为回溯测试或时间序列交叉验证,与典型的训练/测试方法相比,它更加复杂。生成预测后,我们使用效用函数显示图表和指标。如我们所见,朴素季节模型表现相当好,SMAPE 值为 0.59%。

创建指数平滑预测模型

既然我们已经设置了基线精度,我们将基于霍尔特-温特指数平滑法创建一个预测模型,这是一种自 1960s⁶.以来一直成功使用的经典方法

作者图片

如前所述,我们使用historical_forecasts()函数以及效用函数来评估模型性能。显然,指数平滑模型明显优于朴素模型,SMAPE 值为 0.1%。通过观察预测值与绘制的数据集值几乎相同,我们也可以直观地确认这一点。

创建线性回归预测模型

在过去的几年中,机器学习模型作为传统方法的一种替代方法被广泛应用于时间序列预测。通过简单地将滞后值作为特征添加到数据集中,我们可以将时间序列预测转换为回归 task⁷.因此,我们能够使用任何 scikit-learn 回归模型或其他具有兼容 API 的库,包括 XGBoost 和 LightGBM。在这种情况下,我们将基于 scikit-learn 库创建一个线性回归模型。

作者图片

通过使用historical_forecasts()和效用函数,我们测试线性回归模型并随后显示结果。我们可以看到,该模型性能优异,SMAPE 值为 0.11%。

创建时间卷积网络预测模型

近年来,深度学习在时间序列预测中变得流行,递归神经网络是研究和实际应用中的标准选择。无论如何,时间卷积网络是一种替代架构,提供了有前途的 results⁸,所以我们要测试它的性能。

作者图片

如前所述,我们使用historical_forecasts()和效用函数来测试时间卷积网络模型并显示结果。此外,我们还通过使用Scaler()类来规范化时间序列。显然,与基线相比,TCN 模型具有明显更好的性能,SMAPE 值为 0.11%。

创建预测

正如预期的那样,所有的模型都优于朴素的季节模型,提供了极好的准确性。无论如何,我们可以看到指数平滑提供了最好的性能,因此我们对 2022 年大气 CO2 浓度的预测将基于它。

作者图片

我们使用fit()函数在整个数据集上拟合指数平滑模型,然后显示结果。正如我们可以直观地观察到的,模型已经成功地识别了时间序列的组成部分。此外,我们可以将我们的结果与英国气象局(Met Office)2022 年的官方二氧化碳预测(T4)进行比较。正如所料,两个预测值几乎相同,从而验证了指数平滑模型的准确性。令人惊讶的是,指数平滑提供了最好的结果,而不是机器学习和深度学习模型。无论如何,我们应该记住,与经典方法相比,新模型并不一定在每种情况下都更好。此外,可以通过应用超参数调整来优化机器学习模型,这是一种耗时且复杂的技术,但可以提供显著的改进。

结论

在本文中,我们探索了数据科学可用于应对气候变化的方法之一,这是一个不断 grows⁹的蓬勃发展的多元化研究领域,因此我鼓励您了解更多信息。可悲的是,我们还发现今年大气中的二氧化碳浓度预计会增加,这突出表明必须减少排放,以确保我们人类的繁荣。最后,这篇文章的代码和数据可以在这个 Github 仓库获得,所以可以随意克隆它。我也鼓励你在评论中分享你的想法,或者在 LinkedIn 上关注我,我经常在那里发布关于数据科学、气候变化和其他主题的内容。你也可以访问我的个人网站或者查看我的新书,名为用 PyCaret 简化机器学习。

参考

[1]翟,等. IPCC 2021:气候变化 2021:物理科学基础.第一工作组对政府间气候变化专门委员会第六次评估报告的贡献。”(2021).

[2]约翰·霍顿。“全球变暖。”物理学进展报告 68.6 (2005): 1343。

[3]Daniel c . Harris,“查理斯·大卫·基林和大气二氧化碳测量的故事”分析化学 82.19(2010):7865–7870。

[4] Keeling,Charles D .等人,“1978 年至 2000 年大气 CO2 和 13CO2 与陆地生物圈和海洋的交换。一.全球方面。”(2001).

[5]查尔斯·d·基林,“农村地区大气中二氧化碳的浓度和同位素丰度”地球化学与宇宙化学学报 13.4(1958):322–334。

[6]彼得·温特斯,“用指数加权移动平均法预测销售”管理学 6.3(1960):324–342。

[7]托马斯·迪特里希,《时序数据的机器学习:综述》模式识别统计技术(SPR)和结构与句法模式识别(SSPR)IAPR 联合国际研讨会。施普林格,柏林,海德堡,2002。

[8]白、、j .、科尔特和弗拉德连科尔通。"用于序列建模的一般卷积和递归网络的经验评估."arXiv 预印本 arXiv:1803.01271 (2018)。

[9] Rolnick,David 等人,“用机器学习应对气候变化”美国计算机学会计算调查(CSUR)55.2(2022):1–96。

预测我未来的杂货账单

原文:https://towardsdatascience.com/forecasting-my-future-grocery-bills-59515b9348d3

根据我过去的食品杂货支出,使用平滑预测方法来估计我未来可能需要的食品杂货预算。

知道我们将来要花多少钱是一件非常困难的事情。今天,我们将看看一种可能的方法来预测和预计我们在未来的支出。对于预测,尤其是预算支出,有一点需要注意,由于通货膨胀、职业变化等原因,我们的预测可能与实际支出有很大差异。预测可以帮助我们深入了解未来的预期支出。

弗兰基·查马基在 Unsplash 上拍摄的照片

介绍

每月预算是非常重要的,以确保你不会超支。很多时候,预测你下个月要花多少钱会变得很有压力,会让很多人没有动力完成他们的月度预算。使用预测方法可以帮助人们估计他们在接下来的 1、6、12 等时间里会花多少钱。几个月。虽然预测不会给我们下个月要花多少钱的准确金额,但它可以帮助指导我们下个月应该为不同的账单分配多少钱,而这个价格在全年中是不固定的。

简单指数平滑

简单指数平滑(SES)采用指数衰减权重对数据进行加权移动平均。指数衰减权重使用系列中的所有累积数据。即使使用了所有的观察值,旧的观察值的权重也会以指数衰减的方式减少。SES 方法得出的预测是持平的。

方程:简单指数平滑(图片来自作者)

为了创建衰减,我们必须选择一个阿尔法参数。较大的 alpha 值意味着我们希望强调最近的数据而不是过去的数据。随着 alpha 值变小,我们开始更多地强调过去的数据。找到 alpha 最佳值的一种方法是执行搜索程序,看看哪个模型产生的均方误差(MSE)最小。SES 肯定不是最强的预测方法,但是,它可以为您的预测分析提供一个良好的基线。

霍尔特-温特斯

霍尔特-温特斯法是什么?它属于用于定量预测的族平滑方法。它不仅说明了数据集的趋势,还说明了它的季节性。趋势是数据集移动的长期方向(通常为正或负)。季节性是对一段时间内发生的规律性变化的衡量。

霍尔特-温特斯有两种方法:加法和乘法。这两种方法首先将使用简单回归来估计水平和趋势。第一个β系数(B0)最初是回归的截距。第二个β系数(B1)是回归的初始斜率估计值。季节性成分被估计为数据的平均值(通常,每个平均值使用 3-4 个观察值是可取的)。

加法需要一个固定的季节性因素。在这种情况下,静止意味着组件在数据集中是水平的。当水平、增长率和季节性可能发生变化时,我们将使用加法。

方程:霍尔特-温特斯加法(图片来自作者)

霍尔特-温特斯加法还有许多子成分。

等式:硬件添加水平估计值(图片来自作者)

第一个部分是在时间段 t 的水平估计,使用阿尔法平滑参数、增长值和季节性部分。

等式:硬件增加增长率估计值(图片来自作者)

第二部分是增长率的估计值,使用平滑常数γ。

等式:硬件加上季节性因素(图片来自作者)

第三个因素是季节变化,它有一个平滑常数δ。

等式:硬件添加点估计值(图片来自作者)

一旦我们计算了所有的成分,我们就可以开始对数据集进行点估计(预测)。

乘法说明了季节因素的增加,在应用中更常用。它的组成部分类似于加法,但在相互作用上有所不同。

等式:霍尔特·温特斯乘法(图片来自作者)

霍尔特-温特斯乘法方法与加法方法具有相同的组成部分,但变量的相互作用不同。

等式:硬件多组件(图片来自作者)

分析

我正在进行的分析将预测我在接下来的 4 个月应该为食品杂货支付多少(完整代码可以在这里找到)。很明显,这只是一个预测,根据我有限的数据,我可能想用新的观察结果来重新预测我未来的杂货账单。还有许多因素会对我的杂货账单产生巨大影响,我没有考虑这些因素,例如通货膨胀。

我计划今天用两种不同的平滑方法进行实验,看看哪种方法在为数据创建预测方面做得更好。我将使用的第一种平滑方法是简单的指数平滑法,因为它适用于呈现恒定趋势的数据。我还将研究霍尔特-温特斯方法,该方法通常处理显示趋势和季节性的数据。

首先,装入包:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.api import Holt

今天的大部分代码都改编自 statsmodels,专门用于预测,可以在这里找到。

资料组

我今天使用的数据集是我自己在过去 16 个月里获得的。你可以在这里找到数据集的副本。将数据集读入 Python:

#Read in the data
df = pd.read_excel('grocery_dataset.xlsx',index_col='PERIOD')
df.head()

图:数据集的前五行(图片来自作者)

读入数据集后,我们希望删除月份列,只关心案例中的时间段。

#At this point, I am only worried about the periods, not the months 
df.drop(['MONTH'],axis=1,inplace=True)
df.head()

图:清理过的数据集(图片来自作者)

绘制一段时间的成本(美元)图显示趋势相当平缓。虽然没有表现出巨大的趋势,但我怀疑如果我们在 18 个月后重新进行这一分析,我们将会由于通货膨胀而出现上升趋势。

图:时间成本(图片来自作者)

df.plot(title='Grocery Costs Overtime',xlabel='Period',ylabel="Cost (In Dollars $)")
plt.show()

简单指数平滑(SES)

回想一下,使用 SES 方法会给我们一个线性的预测。这可能不是预测未来每月杂货支出的最佳方式,但至少可以帮助预测下个月的支出。

首先,我们要符合模型。我选择使用 0.8 的 alpha。

ses_fit = SimpleExpSmoothing(df, initialization_method="estimated").fit(
    smoothing_level=0.8, optimized=False
)
ses_forecast = ses_fit.forecast(4).rename(r"$\alpha=0.8$")

一旦模型合适,我们就可以根据实际数据绘制预测图。

plt.figure(figsize=(12, 8))
plt.plot(df, marker="o", color="black")
plt.plot(ses_fit.fittedvalues, marker="o", color="red")
(line3,) = plt.plot(ses_forecast, marker="o", color="red")
plt.legend([line3], [ses_forecast.name])

图:SES 预测(图片来自作者)

如上所述,预测是线性的,这就是为什么 SES 可能不是预测你长期杂货账单的最佳方法,但它仍然可以给我们一些关于下个月预期花费的洞察力。让我们看看模型预测了什么。

ses_forecast

运行这段代码,该模型预测下个月的杂货支出大约为 380.67 美元。

霍尔特-温特斯

虽然 SES 有助于理解我下个月可能的杂货账单,但它本质上是线性的,因此我们还想实施 Holt-Winters 方法,看看我们是否可以获得未来几个月杂货支出的预测。

hw_add = ExponentialSmoothing(
    df,
    seasonal_periods=4,
    trend="add",
    seasonal="add",
    use_boxcox=True,
    initialization_method="estimated",
).fit()
hw_mul = ExponentialSmoothing(
    df,
    seasonal_periods=4,
    trend="mul",
    seasonal="mul",
    use_boxcox=True,
    initialization_method="estimated",
).fit()ax = df.plot(
    figsize=(10, 6),
    marker="o",
    color="black",
    title="Forecasts from Holt-Winters' multiplicative method",
)
ax.set_ylabel("Cost of Groceries ($)")
ax.set_xlabel("Period")
hw_add.fittedvalues.plot(ax=ax, style="--", color="red")
hw_mul.fittedvalues.plot(ax=ax, style="--", color="green")hw_add.forecast(12).rename("Holt-Winters (add-add-seasonal)").plot(
    ax=ax, style="--", marker="o", color="red", legend=True
)
hw_mul.forecast(12).rename("Holt-Winters (add-mul-seasonal)").plot(
    ax=ax, style="--", marker="o", color="green", legend=True
)plt.show()

绘制模型,我们可以看到他们是如何预测未来的。

图:硬件添加/多型号(图片来自作者)

这两个模型在创建数据预测方面都做得相当好。与 SES 方法相比,我们可以看到 Holt-Winters 是一种更好的方法,因为它不产生线性预测。在查看具体的模型指标之前,我可能会使用乘法方法作为我的预测模型,因为它似乎比加法方法预测的值要高一些。我选择这种模式的两个原因是,我理解由于通货膨胀,价格可能会在不久的将来上涨,我宁愿高估我的杂货账单,也不愿低估它。

results = pd.DataFrame(
    index=[r"$\alpha$", r"$\beta$", r"$\phi$", r"$\gamma$", r"$l_0$", "$b_0$", "SSE"]
)
params = [
    "smoothing_level",
    "smoothing_trend",
    "damping_trend",
    "smoothing_seasonal",
    "initial_level",
    "initial_trend",
]
results["Additive"] = [hw_add.params[p] for p in params] + [hw_add.sse]
results["Multiplicative"] = [hw_mul.params[p] for p in params] + [hw_mul.sse]

接下来,我们将获得模型度量来比较它们的性能。

图:模型度量(图片来自作者)

虽然加法方法确实有较低的 SSE,但我仍然使用我的判断来使用乘法方法进行预测。现在我们有了他们的方法,让我们看看模型预测未来 4 个月的食品支出。

hw_add.forecast(4)

图:硬件添加预测(图片来自作者)

加法模型预测我接下来 4 个月的账单是 $382.90、$333.96、$304.64 和$360.70。

hw_mul.forecast(4)

图:硬件倍增。预测(图片来自作者)

乘法预测我接下来 4 个月的账单是 $382.85,$335.83,$311.06 和$361.37。这一模型仍然非常接近加法模型,但略高一点。总的来说,这两个模型都给了我一个更好的主意,让我知道在接下来的 4 个月里,我应该如何支付我的食品杂货账单!

结论

今天我们看了两种方法,可以方便地预测我们未来的杂货账单(或任何账单!).虽然 SES 模型能够提供预测,但它仅创建线性预测,因此对于预测一个月以上逐月变化的账单,它对我们没有用处。如果使用 SES,他们可以在本月结束后预测下个月的账单。将当月数据添加到原始预测中。霍尔特-温特斯的模型做得更好,让我对未来 4 个月的预期支出有了一些了解。永远记住,预测从来都不是完美的,不应该只看表面。我用这些预测来帮助我了解未来 4 个月我应该为食品杂货分配多少钱,然而,我总是想记住,生活是不可预测的,如果事情没有按计划进行,你应该总是有 B 计划。

如果你喜欢今天的阅读,请给我一个关注!另外,在LinkedIn上加我,或者随时联系!感谢阅读!

来源

  1. 数据集是我自己收集的。
  2. 今天的部分代码改编自这里的。

使用递归神经网络的在线学习预测呼吸运动,用于更安全的肺部放射治疗

原文:https://towardsdatascience.com/forecasting-respiratory-motion-using-online-learning-of-rnns-for-safe-radiotherapy-bdf4947ad22f

UORO 在 Matlab 医疗时间序列预测中的应用

国家癌症研究所Unsplash 上拍摄的照片

T2020 年全球新增肺癌病例约 200 万例,肺癌患者 5 年相对生存率略高于 22% [ 12 ]。这推动了肺部放射治疗的研究,人工智能是实现这一目标的工具之一。

你有没有想过人工智能如何改善肺癌的治疗?为什么递归神经网络(RNN)特别有助于这项任务?还有,什么是在线学习,为什么它在这种情况下特别有用?

在这里,我将通过总结我最近发表在“生物医学中的计算机方法和程序”上的研究文章“使用经无偏在线递归优化训练的递归神经网络预测外部标记物的位置用于安全肺癌放疗”,并介绍其主要发现【3】,尽我所能回答这些问题。

除特别注明外,所有图片均为作者所有。

1.为什么我们在肺部放射治疗中需要时间序列预测

肺部肿瘤随着呼吸运动而移动。因此,精确地瞄准它们是具有挑战性的,因为在放射治疗过程中的实时成像具有局限性。

在一系列计算机断层扫描(CT)图像中,呼气相(左)和吸气相(右)末期的胰腺肿瘤。由于呼吸运动,肿瘤的质心上下移动,并且肿瘤形状也随着该运动而改变。【4】原图,经作者许可编辑。

外部标记是放置在胸部的金属物体,可以使用空间回归模型(也称为对应模型)来帮助推断胸部肿瘤的位置。

放射治疗系统(射波刀),使用外部标记引导放射束。经 Wiley 许可,改编自【5】,版权 2004 美国医学物理学家协会。

即使利用外部标记的红外跟踪,肿瘤的位置也难以精确估计,因为这些内部-外部对应模型固有的不精确性,而且因为治疗系统受到由于图像采集、机器人控制和辐射束激活引起的延迟。这种延迟会阻止有效的肿瘤照射,并导致对健康组织的过量剂量,从而导致肺炎等副作用,这种情况可能会危及生命。

由于总系统延迟δt 未得到补偿,健康肺组织受到过度照射。考虑到诸如治疗期间肿瘤形状变化的影响,这里用斜线表示的照射区域大于肿瘤尺寸。转载自【6】,版权 2021,经爱思唯尔许可。

为了解决系统延迟的问题,可以使用过去的呼吸数据来预测肿瘤位置。在如上所述的带有标记的外部引导的情况下,我们可以预测标记的位置。

使用对应模型从外部标记位置测量中导出肿瘤的未来位置。在这项研究中,我专门关注预测步骤,以补偿治疗系统的延迟。

2.我们的方法

2.1.数据集

在这项研究中,我使用了由九个序列组成的 ExtMarker 数据集,其中包含健康个体胸部三个外部标记的 3D 位置。采样频率为 10 Hz,脊柱方向的运动幅度在 6mm-40mm 之间。该数据集是公开的,更多信息可在【7】中找到。我们将每个序列的数据分为训练集、交叉验证集和测试集。

对于包含标记位置的 9 个序列中的每一个,将数据划分为训练集、交叉验证集和测试集。

预测呼吸运动是具有挑战性的,因为诸如振幅和周期的呼吸特征在每个记录中是变化的,即使它看起来主要是周期性的。此外,在大约一半的记录中,个人被要求说话或笑,这使得任务更加复杂。在现实世界中,也会发生突发的呼吸异常,如咳嗽、打嗝或打哈欠,这就是为什么挑战记录是有益的。

2.2.让我们进入 RNNs 的世界。

备注:本节中的方程最好在计算机显示器上查看。

RNNs 是一种特殊类型的神经网络,配备有反馈回路,使它们能够记住随着时间推移获得的信息。当我们进行预测时,标记在过去和未来的位置分别是网络的输入和输出。预测是一个自我监督的学习问题,因为不需要人工注释。这项研究使用了一个简单的香草架构,只有一个隐藏层。

预测标记位置的 RNN 的结构。输入向量 uₙ对应于过去的位置,输出向量 yₙ₊₁对应于预测的位置。经爱思唯尔许可,改编自【6】,版权 2022

rnn 通常使用时间反向传播(BPTT)进行离线训练。离线学习指定了一个学习过程,其中有两个独立的训练和推理步骤。在我们的研究中,我们使用在线学习,这意味着训练和推理是同时进行的。在放射治疗的情况下,这使得网络能够适应看不见的呼吸模式,尽管只看到有限的数据。事实上,由于有关个人信息保护的法规,人工智能医疗应用的数据收集通常很困难。这项研究调查了 UORO【8】(一种最近使用 RNNs 进行在线学习的方法)预测呼吸运动的效率。

下面简单讨论一下 UORO 是如何工作的。首先,让我们写出 RNN 方程。下面的第一个方程(状态方程)描述了内部状态向量 x ₙ的演化,第二个方程(测量方程)能够计算输出向量 y ₙ₊₁. θ ₙ和 u ₙ分别指定参数向量和输入向量。注意,参数向量依赖于 n ,因为我们执行在线学习。

我们寻求最小化的损失函数是:

在这个等式中, y ₙ *** 指定地面实况标签(我们想要预测的标记的位置)。我们可以使用 y ₙ的定义和链式法则计算损失相对于 θ 的导数:

类似地,通过将链式法则与测量方程结合使用,我们可以推导出以下方程:

第一个等式能够计算作为影响矩阵∂xₙ/∂ θ的函数的损失梯度。第二个方程让我们在每个时间步递归更新影响矩阵。对于我们的特定网络,所有其他偏导数都可以使用损耗 L 的定义和选择的函数 F ₒᵤₜ和 F ₛₜ 来计算;在我们的工作中,后者分别是线性函数和单元双曲正切。这种在每个时间步计算损失梯度的方法被称为实时递归学习(RTRL)。RTRL 的主要缺点是计算量大,因为更新成本是o(q⁴,其中 q 是隐藏单元的数量。这特别是由于影响矩阵的大小,它有许多条目,我们需要在每个时间步更新。UORO 的主要思想是将矩阵估计为两个随机向量的乘积(换句话说,它由无偏的秩为 1 的矩阵估计器近似),并在每个时间步长更新它们:

同样,我不会在这里详细描述这两个向量是如何更新的。重要的一点是,有一种有效的方法来压缩包含在影响矩阵中的信息,使得能够将计算成本降低到 O(q )

我将在文章结尾的附加部分描述 UORO 核心的数学属性,所以请务必跟上:)

在这项研究中,我对每个序列独立进行预测,并使用网格搜索进行交叉验证。

2.3.在 Matlab 中实现 UORO

我们来试着了解一下 UORO 在 Matlab 中的基本实现。这里我将描述一下我的论文对应的 Github 库的rnn_UORO功能。如果您对 Python 有所了解,那么这里没有什么好害怕的,因为这两种语言的语法非常接近。如果您对代码不感兴趣或者喜欢直接分析结果,可以跳过这一部分。

顺便提一下,我还想提一下 realpython.com 的深度指南,题为“Matlab 与 Python:为什么以及如何进行转换”,它比较了 MATLAB 和 Python,并从我在 MATLAB 的经历中帮助我学习 Python(反正它们是非常相似的语言)。如果你精通 Python 并且想更好地理解 Matlab 语法,你也可以阅读。

首先,我定义了一个辅助函数来根据状态方程实现正向传播(参见 2.2 节)。Matlab 中的函数是使用function关键字定义的,而def关键字是在 Python 中使用的。在这段代码中,myRNN是一个包含 RNN 参数的 Matlab 结构数组(这或多或少相当于 Python 中的字典和 C 语言中的结构)。我选择激活函数phi作为双曲正切函数。它是使用一个匿名函数实现的,这个函数相当于 Python 中的 lambda 函数

Matlab 中的状态方程带有线性状态函数 F ₛₜ和双曲正切激活函数

我还使用了另一个辅助函数来执行一个梯度下降步骤,在每个时间步进行剪辑。这里,theta是 RNN 参数向量,dtheta是损耗梯度。通过实验,我发现削波有助于稳定 RNN 的行为并获得更好的性能。如果你在 GitHub 库中查看我的代码,你会注意到我也实现了自适应矩估计(ADAM)。然而,我在实验中注意到,后者并没有提高预测的准确性。因此,在我的文章中,我只报告了用梯度下降法获得的结果。

Matlab 中带裁剪的梯度下降步长

用于训练 RNN 的主函数将分别包含 RNN 参数和预测超参数的结构myRNNpred_par以及包含输入和输出数据(标记的位置)的 2D 数组XdataYdata作为输入。

UORO 在 Matlab 中的实现

首先,我们定义包含有用信息的变量,比如数组的大小(第 3 行到第 11 行)。

然后,我们在时间步长上循环,并使用上面的辅助函数RNNstate_fwd_prop执行正向传播(第 13 到 21 行)。这些行包括使用测量方程(带有线性输出函数)和瞬时误差(参见第 2.2 节)计算的 y ₙ₊₁。 tic 功能用于告诉计算机“启动秒表”测量当前时间步长对应的计算时间。

第 23 行和第 45 行之间的代码部分非常数学化。我不会在这里深入细节,但我会从整体的角度解释发生了什么。首先,您可以看到有一个使用 rand 函数随机初始化的向量nu(第 30 行)。这里重要的一点是两个向量myRNN.xtildemyRNN.theta_tilde是从那个随机向量中计算出来的(第 44 和 45 行)。这些对应于我们在 2.2 节中提到的两个随机向量,其乘积是影响矩阵∂xₙ/∂ θ 的无偏估计。你可以看到它们在每个时间步递归更新。另一个重要的行是从这两个向量计算损失梯度估计值gtilde(第 27 行)。如果你对底层数学感兴趣,我想让你参考一下关于 UORO【8】的原始论文和我的文章【3】,它首次提供了香草 RNNs 情况下myRNN.dthetamyRNN.dtheta_gdx的闭合形式表达式。这些在该实现中用于计算损耗梯度。此外,在这个故事的结尾有一个额外的部分,涵盖了 UORO 所依赖的基本数学属性,所以请继续关注我:)

最后,我们用上面描述的update_param_optim辅助函数更新梯度估计(第 51 行)。我使用了

目录

我的 Github 库中的代码略有不同,因为它也处理 GPU 计算,但是为了解释简单,我跳过了这一部分。此外,我通过实验发现,使用我的 GPU 会使 UORO 计算变慢。所以我这篇文章只报道 UORO 对应 CPU 计算的性能。

3.结果

3.1.预测行为的定性观察

我们先来定义这里的基本术语。我们称之为“水平”,用 h 表示进行预测的提前时间间隔,信号历史长度(SHL)表示时间间隔,其信息用于在给定的时间步长进行单次预测。

预测 1D 位置信号。SHL 和 horizon 分别用 h 和 l 表示,转载自【3】,版权 2022,经爱思唯尔许可。

让我们首先在视觉上定性地检查序列 1 中标记 3 的预测运动(不规则呼吸)沿着脊柱方向对于不同的水平值。

一个对象胸部上的三个标记随着呼吸运动而移动。

下图似乎表明 UORO 比 RTRL 和最小均方误差(LMS)更精确。事实上,我们限制了与 RTRL 关联的隐藏单元和 SHL 的数量,以保持可接受的计算时间。此外,LMS 是自适应线性滤波器,不具有 RNNs 的代表能力。

用 UORO 训练的 RNN 预测序列 1 中标记 3 的 z 坐标(脊线轴)。转载自【3】,版权 2022,经爱思唯尔许可。

用 RTRL 训练的 RNN 预测序列 1 中标记 3 的 z 坐标(脊柱轴)。转载自【3】,版权 2022,经爱思唯尔许可。

我们观察到 LMS 给出的预测具有高抖动,即,它在地面实况标记坐标周围振荡很多。

用 LMS 预测序列 1 中标记 3 的 z 坐标(脊线轴)。经爱思唯尔许可,转载自【3】,版权 2022。

3.2.数值预报精度

我承认我选择了上面的例子,因为它使 UORO 看起来很好。尽管如此,在某些情况下,UORO 的表现不如其他方法。让我们仔细看看,当我们对 0.1s 和 2.0s 之间的水平值和九个序列的数值精度指标进行平均时,会发生什么情况。事实上,之前有报道称,放射治疗中的潜伏期范围在 0.1 秒和 2.0 秒之间【9】

各算法预测性能的比较。每个误差值对应于测试集在所考虑的序列和 0.1s 到 2.0s 之间的水平值上的给定性能测量的平均值。改编自【3】,版权 2022,经 Elsevier 许可。

我报告了几个指标,因为它们给出了关于每个算法的预测行为的不同信息。从严格意义上讲,平均误差(MAE)是一段时间内的平均绝对误差,均方根误差(RMSE)惩罚更高的偏差,抖动衡量预测信号的振荡幅度。振荡太多的预测是不希望的,因为它们使得控制引导辐射束的机器人更加复杂。

当查看九个序列的平均误差度量时,我们观察到 RTRL 在 MAE 方面优于 UORO,但是当查看 RMSE 时,情况正好相反。LMS 精度相对较好,但抖动较大。当然,当仅考虑对应于规则呼吸的序列时,误差较低,而当观察对应于不规则呼吸的序列时,误差较高。

但是不同的系统有不同的延迟时间。在每种情况下,最佳算法的选择取决于所考虑的放射治疗系统的等待时间。因此,我将预测误差绘制成水平值的函数 h.

根据预测范围预测每个算法的性能。每个点对应于 9 个序列中测试集的一个性能测量的平均值。经爱思唯尔许可,转载自【3】,版权 2022。

因为超参数优化导致对于不同的 h 值的不同超参数选择,所以图表看起来具有局部不规则性。然而,随着 h 的增加,误差总体上趋于增加。我们观察到,对于低的 h 值,线性预测实际上是最准确的预测算法。LMS 对于中间值 h 也很有效。

下图总结了每种算法在 RMSE 和抖动方面的性能。顺便提一下,在我的原始文章中,我使用了一个类似的图表,用“归一化 RMSE”来描述性能,缩写为 nRMSE,而不是 nRMSE 前者是在评估性能时额外考虑信号幅度的措施。我们再次观察到,对于对应于不规则呼吸的信号,RMSE 和抖动都较高。给定可接受的抖动水平和特定的地平线值,这种 2D 表示可以帮助找到具有最高预测准确度的算法。

每个算法在 RMSE 和抖动方面的预测性能。每个点对应于对于单一水平值的规则或不规则呼吸序列的测试集的给定算法的 RMSE 和抖动的平均值。对应于具有高层位值的线性回归的数据点由于对应于高 RMSE 值而没有显示出来。

3.3.超参数优化

先说超参数优化。我们之前指出,我们使用交叉验证来选择网络超参数,以获得最佳性能。我发现在这项研究中,学习速度和 SHL 是对表现影响最大的因素。因此,让我们仔细看看他们的选择如何影响预测的准确性。

对于
不同的水平值,交叉验证集 UORO 的预测 nRMSE 作为学习率和 SHL 的函数。黑色虚线对应 0.1 秒至 2.0 秒范围内的 nRMSE 最小值平均值,相关误差条对应这些范围内的标准偏差。转载自【3】,版权 2022,经爱思唯尔许可。

我们再次观察到,地平线 h 越高,预测误差越高。在高值的 h 的情况下,网络看起来更有效,具有更高的学习率和更低的 SHL。换句话说,在这种情况下,当只考虑最近的信号并在输入呼吸模式发生变化时更快地调整权重时,网络工作得更好。

3.4.时间性能

计算时间是评估我们算法的最重要的标准之一。我们观察到预测时间随着 SHL 的增加而增加,SHL 与输入层的大小和隐藏单元的数量成比例。线性回归似乎是最快的预测方法,其次是 LMS。UORO 能够在我的机器上执行预测的速度比 RTRL 快大约 100 倍,这是他们各自计算复杂度 O(q )O(q⁴) 的直接结果。

计算时间(戴尔英特尔酷睿 i9–9900k 3.60 GHz CPU 32Gb RAM,带 Matlab)。改编自【3】,版权 2022,经爱思唯尔许可。

4.总之

这项工作是首次将 UORO 训练的 RNNs 应用于放射治疗中的呼吸运动补偿。UORO 是一种在线学习算法,能够学习以流方式到达的数据,这使得对不规则呼吸具有高鲁棒性,并且比传统学习算法需要更少的数据。因此,它有可能减少对健康组织的辐射,从而避免气胸等副作用。

据我们所知,我们使用最广泛的水平值范围 h 来比较关于呼吸运动预测的文献研究中的不同预测算法。我们发现线性回归在 0.1s ≤ h ≤ 0.2s 时 RMSE 最低,其次是 0.3s ≤ h ≤ 0.5s 时的 LMS,以及 h ≥ 0.6s 时的 UORO,呼吸信号采样频率为 10Hz。

如果您对这个故事感兴趣,请不要犹豫阅读最初的研究文章,在那里我更广泛地涉及了这个主题并讨论了数学细节(这篇文章也是第一篇提供与 UORO 中的损失梯度相关的一些表达式的详细计算的文章)。数据集和 Matlab 代码是公开可用的,所以如果你愿意,你可以用它们来做你自己的实验。如果你愿意的话,也请考虑引用我的研究文章。

非常感谢您花时间阅读本文。我真诚地希望你能喜欢学习一些有趣的东西。请不要犹豫,在评论区告诉我你的想法。😃

5.奖金:排名第一的把戏

我想提一下作为 UORO 基础的核心数学性质,它是在【10】中首次介绍的。这是:

给定矩阵的分解 A 为秩 1 外积的和

·独立随机符号 ν {-1,1},和一个向量 ρn 正实数,以下量

满足:

此外,选择

最小化近似值的方差。

这个性质的第一部分相对容易证明,它是由期望算子的线性性产生的。方差最小化部分需要更详细的分析。虽然我不会在这里对后者做出评论,但我想在 2.3 节中与 Matlab 实现做一个链接。您可以查看代码,看到我的实现中的变量nu对应于上面的向量ν(第 30 行)。变量rho0rho1(第 40 和 41 行)对应于上述公式中的量 ρ ᵢ。还要注意后者是如何表示为代码中两个向量的范数的平方根的。出现在这些分数周围的变量pred_par.eps_normalizer是为了保证数值的稳定性。

为了推导 UORO 算法,秩 1 技巧实际上结合描述影响矩阵∂xₙ/∂ θ 的递归更新的方程应用了两次(参见第 2.2 节)。证明细节在原始 UORO 文件【8】中。

参考

  1. Sung Hyuna、Jacques Ferlay、Rebecca L. Siegel、Mathieu Laversanne、Isabelle Soerjomataram、Ahmedin Jemal 和 Freddie Bray,2020 年全球癌症统计:全球 185 个国家 36 种癌症发病率和死亡率的全球估计数 (2020 年),ca:临床医生癌症杂志
  2. 国家癌症研究所-监测、流行病学和最终结果项目,癌症统计
    事实:肺癌和支气管癌
    (2022)[2021 年 6 月 15 日获取]
  3. Pohl Michel、Mitsuru Uesaka、Hiroyuki Takahashi、Kazuyuki Demachi 和 Ritu Bhusal Chhatkuli,使用经过无偏在线递归优化训练的递归神经网络预测外部标记的位置,用于安全肺癌放射治疗 (2022),《生物医学中的计算机方法和程序》
  4. Ritu Bhusal Chhatkuli,利用主成分分析和多通道奇异光谱分析开发无标记肿瘤预测系统,并在放射治疗中进行实时呼吸相位识别 (2016),东京大学博士论文
  5. Schweikard Achim,Hiroya Shiomi 和 John Adler,放射外科中的呼吸跟踪 (2004),医学物理学
  6. Pohl Michel、Mitsuru Uesaka、Kazuyuki Demachi 和 Ritu Bhusal Chhatkuli,使用经过实时递归学习训练的递归神经网络预测胸部内部点的运动,用于肺癌放射治疗中的潜伏期补偿 (2021),计算机化医学成像和图形
  7. Krilavicius Tomas、Indre Zliobaite、Henrikas Simonavicius 和 Laimonas Jaruevicius,预测放射治疗中实时肿瘤跟踪的呼吸运动 (2016),ArXiv.org
  8. Corentin Tallec 和 Yann Ollivier,无偏在线递归优化 (2017),ArXiv.org
  9. 维尔马·普纳姆、吴焕梅、马克·兰格、因陀罗·达斯和乔治·桑迪森,调查:用于图像引导放射治疗的实时肿瘤运动预测 (2010),科学计算&工程
  10. Ollivier Yann、Corentin Tallec 和 Guillaume Charpiat,在线无回溯训练循环网络 (2015),ArXiv.org

用深度学习中的线性模型预测不确定性

原文:https://towardsdatascience.com/forecasting-uncertainty-with-linear-models-like-in-deep-learning-bc58f53938

将任意的和认知的不确定性纳入预测区间

Unsplashengin akyurt 拍摄的照片

通常,机器学习行业的应用程序不会考虑如何产生不确定性估计。在许多现实世界的任务中,不仅需要做出准确的预测。提供模型结果的置信度对于做出最有效的决策可能至关重要

除了一些深度学习技术或者其他特殊情况,产生信心估计并不是免费的午餐。我们可以把产生置信度得分的所有解决方案看作是标准训练和预测阶段的一个独立步骤。最简单和直观的方法在于使用残差自举作为估计观测值和预测值之间不确定性的方法基于 Bootstrap 的技术是接近不确定性领域的起点

使用 bootstrapping,我们可以用较少的努力和简单的假设为我们的预测建立一个置信度。另一方面,与盲目使用 bootstrap 规则相比,产生可靠和校准的估计可能需要更多的关注和验证。

首先要记住的是,我们不想优化预测模型来产生最佳的逐点预测。有准确的预测是可取的,但提供可靠的界限,其中我们有信心观察值下降,将是梦幻般的。换句话说,有好的预测是一个起点,但是建立信心评估在大多数时候是一项单独的任务,应该使用适当的评分方法进行验证。

其次,我们应该记住存在两种不同的不确定性来源。我们分别指的是任意的和认知的不确定性。可以确定的是,所有的 AI 预测总是不确定的。由于这个原因,一个好的置信度应该包含任意的和认知的不确定性

在这篇文章中,我们介绍了一种在回归任务中为我们的机器学习模型提供置信度估计的方法。所提出的方法只是作为预测之后的一个附加步骤。它适合一个单独的线性模型,通过最小化一个“特别的”损失函数来专门预测样本的不确定性。为了对这个主题有一个更技术性的概述,我建议这篇文章也适用于神经网络生态系统之外。

实验设置

所提出的解决方案的先决条件可以概括为两个一般概念:

  • 适应性:作为流程中的一个附加步骤,我们的解决方案应该适用于每个预测解决方案的末尾。
  • 完备性:该方法应该足够好,能够涵盖任意的和认知的不确定性场景。

作为起点,我们必须获得一个经验表示来对预测模型的方差进行数值估计。在这个意义上,高斯负对数似然(NLL)损失是一个完美的候选。

高斯负对数似然公式[图片由作者提供]

在上述损失中,主要涉及 3 个条款:

  • yᵢ: 我们用来建立预测模型的目标值。
  • μ ( xᵢ ):预测模型做出的逐点预测。
  • (xᵢ):估计的不确定性,确切地代表了我们想要预测的东西。

为了提供不确定性的样本估计,我们认为给出了 yᵢμ ( xᵢ )。这意味着我们已经拟合了我们选择的预测模型。为了最小化 NLL 损失,我们拟合了一个线性模型,该模型试图预测 σ ( xᵢ )的最佳值。在最小化结束时,我们有一个线性模型,它接收我们处理的特征作为输入,并返回每个观察值的不确定性预测

预测不确定性

让我们来看看所提议的方法在起作用。

我们在固定域中生成正弦数据,在一个方向上增加高斯噪声。我们明确地选择了这些数据,以观察一个模型在其知识领域之外可以预测什么。尽管数据非常简单,但我们并不期望我们的模型能在列车边界之外很好地推广。

原始正弦曲线(左)。噪音增加(右侧)。[图片由作者提供]

原始正弦曲线加上不断增加的噪声。[图片由作者提供]

另一方面,我们的不确定性估计应该概述我们的模型在其知识领域之外做出预测的困难

在下图中,我们描述了使用多层感知器回归器的预测,通过建议的方法获得的不确定性。生成的西格玛间隔的宽度随着数据中噪声的增加而增加。这是正确的,这意味着我们可以将数据中存在的噪声捕捉为一种不确定性(任意不确定性)。

包含任意不确定性的适马区间。[图片由作者提供]

下一步,我们想看看我们是否能把模型预测的不准确性(认知不确定性)作为一个额外的不确定性来源。在这种意义上,我们期望在训练边界之外看到两个方向上的置信带增长,因为模型不适合预测该范围之外的数据。

包含任意和认知不确定性的适马区间。[图片由作者提供]

在上图中,我们观察到的正是我们的目标。我们的 sigma 区间同时封装了来自数据和模型结果的不确定性。我们通过在系综格式中应用我们的方法简单地获得了这个结果。换句话说,我们拟合多个多层感知器回归器,并将它们组合以构建高斯混合。混合物的平均值和标准偏差分别作为真实值和不确定性的预测返回。

仔细观察两种方法中的西格玛预测,有助于区分两种不确定性来源的考虑方式。

只有任意不确定性(红色)和任意加认知不确定性的适马区间宽度。(蓝色)[图片由作者提供]

摘要

在这篇文章中,我们介绍了一种评估模型不确定性的方法。我们提供了一个奇特而巧妙的实现,它包括拟合一个线性回归来预测每个逐点预测的标准偏差。我们还发现了如何将任意的和认知的来源结合到我们的信心预测中。作为一个附加值,建议的实现似乎适用于所有具有内置外推能力的模型。

查看我的 GITHUB 回购

保持联系: Linkedin

参考

马蒂亚斯·瓦尔登格罗-托罗;丹尼尔·萨罗莫·莫里。( arXiv 2022) 对任意性和认知不确定性的深入研究

用决策树和随机森林进行预测

原文:https://towardsdatascience.com/forecasting-with-decision-trees-and-random-forests-ac081ff451ec

对于表格数据,随机森林灵活而强大。它们也适用于时间序列预测吗?让我们找出答案。

约翰·西门子在 Unsplash 上拍摄的照片

介绍

今天,深度学习主导了现代机器学习的许多领域。另一方面,基于决策树的模型仍然大放异彩,尤其是对于表格数据。如果你查找各个 Kaggle 挑战的获胜方案,很有可能其中就有一个树模型。

树方法的一个主要优点是,它们通常不需要太多的微调来获得合理的结果。这与深度学习形成了鲜明的对比。这里,不同的拓扑和架构会导致模型性能的显著差异。

对于时间序列预测,决策树不像表格数据那样简单,但是:

树木和森林对预测的挑战

你可能知道,拟合任何基于决策树的方法都需要输入和输出变量。然而,在单变量时间序列问题中,我们通常只将我们的时间序列作为目标。

为了解决这个问题,我们需要增加时间序列,使其适合树形模型。让我们先来讨论两种直观但错误的方法,以及它们失败的原因。显然,这些问题适用于所有决策树集成方法。

作为时间回归的决策树预测

也许最直观的方法是将观察到的时间序列视为时间本身的函数,即

(图片由作者提供)

带有一些独立同分布随机附加误差项。在之前的一篇文章中,我已经说过为什么逆时间回归是有问题的。对于基于树的模型,还有另一个问题:

决策树用于回归反对时间不能外推未来

通过构造,决策树预测是训练数据集子集的平均值。这些子集是通过将输入数据的空间分割成轴平行的超矩形而形成的。然后,对于每个超矩形,我们取这些矩形内所有观察输出的平均值作为预测。

对于相对于时间的回归,这些超矩形仅仅是时间间隔的分割。更确切地说,这些区间是互斥的,完全穷尽的。

预测是这些时间间隔内时间序列观测值的算术平均值。从数学上来说,这大致可以解释为

(图片由作者提供)

现在考虑使用这个模型来预测未来某个时间的时间序列。这将上述公式简化为:

(图片由作者提供)

换句话说:对于任何预测,我们的模型总是预测最终训练区间的平均值。这显然是没用的…

让我们用一个简单的玩具例子来想象这个问题:

对于简单的线性趋势,使用决策树将时间序列建模为时间的函数是非常失败的。(图片由作者提供)

季节性模式显然也存在同样的问题:

对于季节性时间序列,针对时间的决策树回归也不起作用。(图片由作者提供)

用一句话概括以上内容:

决策树对非分布数据失效,但在对时间的回归中,未来每个时间点都是非分布的。

因此,我们需要找到一种不同的方法。

自回归预测的决策树

一个更有前途的方法是自回归法。在这里,我们简单地将随机变量的未来视为依赖于其过去的实现。

(图片由作者提供)

虽然这种方法比按时回归更容易处理,但它也不是没有代价的:

  1. 时间序列必须在等距离的时间戳处观察:如果你的时间序列是在随机时间测量的,你不能使用这种方法而不做进一步的调整。
  2. 时间序列不应包含缺失值:对于许多时间序列模型,这一要求不是强制性的。然而,我们的决策树/随机森林预测器需要一个完全观察到的时间序列。

由于这些警告对于大多数流行的时间序列方法来说是常见的,所以它们不是太大的问题。

现在,在进入示例之前,我们需要再看一下之前讨论过的问题:基于树的模型只能在训练数据的范围内进行预测。这意味着我们不能仅仅用决策树或随机森林来模拟自回归依赖性。

为了举例说明这个问题,我们再举一个例子:

自回归决策树无法预测具有线性趋势的时间序列。(图片由作者提供)

还是那句话,一点用都没有。要解决最后一个问题,我们需要首先移除趋势。然后,我们可以拟合模型,预测时间序列,并对预测进行“重新趋势化”。

对于去趋势化,我们基本上有两种选择:

  1. 拟合线性趋势模型 —这里我们用线性回归模型对时间序列进行回归。然后从训练数据中减去它的预测,以创建一个平稳的时间序列。这消除了一个不变的、确定的趋势。
  2. 使用一阶差分 —在这种方法中,我们通过一阶差分来转换时间序列。除了确定性趋势,这种方法还可以消除随机趋势。

由于大多数时间序列是由随机性驱动的,第二种方法似乎更合理。因此,我们现在的目标是预测转换后的时间序列

(图片由作者提供)

通过自回归模型,即

(图片由作者提供)

显然,差异和滞后从我们的训练数据中移除了一些观察值。应该注意不要用这种方式删除太多信息。也就是说,如果数据集很小,不要使用太多滞后变量。

为了获得原始时间序列的预测,我们需要通过以下方式重新转换差分预测

(图片由作者提供)

并且,递归地进行进一步的预测:

(图片由作者提供)

对于我们的运行示例,这最终导致了一个合理的解决方案:

基于差分时间序列的决策树预测最终奏效。(图片由作者提供)

从树木到随机预测预报

现在让我们将上述方法应用于真实世界的数据集。我们使用圣路易斯联邦储备银行数据库中的酒类销售数据。为了评估,我们使用过去四年作为维持集:

圣路易斯联邦酒精销售数据-培训和抵制集。(图片由作者提供)

生长用于预测的自回归随机森林

由于单个决策树往好里说会令人厌烦,往坏里说会不准确,所以我们将使用随机森林来代替。除了典型的性能改进,随机森林允许我们生成预测间隔。

要创建随机森林预测间隔,我们按如下步骤进行:

  1. 训练一个自回归随机森林:这一步相当于像以前一样拟合决策树
  2. 在每个预测步骤中使用随机抽取的决策树:我们让一个随机抽取的单一决策树来执行预测,而不是只使用forest.predict()。通过多次重复此步骤,我们创建了一个决策树预测示例。
  3. 从决策树样本中计算感兴趣的数量:这可以是中值到标准差或更复杂的目标。我们主要对平均预测和 90%的预测区间感兴趣。

下面的 Python 类完成了我们需要的一切:

由于我们的数据严格为正,具有趋势和年度季节性,我们应用以下转换:

  • 对数转换:我们的预测需要通过指数转换重新转换。因此,取幂的结果也将严格为正
  • 第一个差异:如上所述,这消除了数据中的线性趋势。
  • 季节性差异 : 季节性差异的工作方式类似于具有更高滞后订单的第一差异。此外,它允许我们消除确定性和随机性的季节性。

所有这些变换的主要挑战是正确地将它们的逆变换应用于我们的预测。幸运的是,上面的模型已经实现了这些步骤。

评估随机森林预测

使用数据和模型,我们在测试期间得到以下结果:

随机森林预测、训练和测试数据。(图片由作者提供)

这个看起来很不错。为了验证我们不仅仅是幸运,我们使用了一个简单的基准测试进行比较:

简单基准模型的比较预测。(图片由作者提供)

显然,基准区间比随机森林差得多。平均预测开始时还算合理,但在几个步骤之后就迅速恶化了。

让我们在一张图表中比较这两种平均预测:

平均预测随机森林与基准。(图片由作者提供)

很明显,随机森林在长期预测方面要优越得多。事实上,随机森林有一个909.79RMSE ,而基准的 RMSE 是9745.30

结论

希望这篇文章能让你对使用树模型进行预测的注意事项有所了解。虽然单个决策树有时可能有用,但随机森林通常更有效。也就是说,除非你的数据集非常小,在这种情况下,你仍然可以减少森林树木的max_depth

显然,您可以轻松地在任一模型中添加外部回归变量,以进一步提高性能。例如,在我们的模型中加入月度指标可能会产生比现在更准确的结果。

作为随机森林的替代方案,可以考虑梯度推进。Nixtla 的 mlforecast 软件包有一个非常强大的实现——除了他们所有其他的预测工具。但是请记住,我们不能将预测区间的算法转换为梯度推进。

另一方面,请记住,使用高级机器学习进行预测是一把双刃剑。虽然在表面上很强大,但时间序列的 ML 比横截面问题的 ML 更容易过度拟合。但是,只要您根据一些基准正确地测试您的模型,它们也不应该被忽略。

PS:你可以在这里找到这篇文章的完整笔记本。

参考

【1】布雷曼·利奥。随机森林。机器学习,2001 年,45.1,第 5–32 页。

【2】brei man,Leo 等《分类与回归树》。Routledge,2017。

【3】汉密尔顿,詹姆斯·道格拉斯。时间序列分析。普林斯顿大学出版社,2020 年。

[4]美国人口普查局,商业批发商,制造商销售分支机构和办事处除外:非耐用品:啤酒、葡萄酒和蒸馏酒精饮料销售[S4248SM144NCEN],从圣路易斯美联储银行检索;https://fred.stlouisfed.org/series/S4248SM144NCEN(CC0:公共领域)

原载于 2022 年 9 月 19 日 https://www.sarem-seitz.comhttps://www.sarem-seitz.com/forecasting-with-decision-trees-and-random-forests/

霍尔特线性趋势指数平滑的时间序列预测

原文:https://towardsdatascience.com/forecasting-with-holts-linear-trend-exponential-smoothing-af2aa4590c18

指数平滑模型中的趋势补偿

杰瑞米·托马斯Unsplash 上拍照

背景

在我的上一篇文章中,我们介绍了 指数平滑 的思想来构建预测模型。指数平滑法的要点是对最近的观测值给予更多的权重,而对更多的历史观测值给予更少的权重。

我们介绍的第一个模型是简单指数平滑。“简单”部分是指模型不考虑趋势季节性,只预测水平。这导致这种特殊的模型经常对大多数时间序列提供不充分的预测。因此,我们需要从这个简单的方法进一步迭代。

在来了 霍尔特的线性趋势法 (也称为双指数平滑),顾名思义,就是在简单的指数平滑模型中加入一个(线性)趋势成分。在这篇文章中,我们将介绍霍尔特线性趋势模型的理论和实际应用。

霍尔特的线性趋势模型理论

简单指数平滑

让我们首先回顾一下简单的指数平滑方程:

作者在 LaTeX 中生成的方程。

这里【ŷ_{t+1}】是我们预测的时间步长, y_t 是时间序列的最近观测值, ŷ_{t-1} 是之前的预测, α 是取值为 0 ≤ α ≤ 1 的平滑因子。 我们看到,预测仅仅是对以前观测值的加权平均,这就是指数平滑的本质。

上述等式可以改写为分量形式:

作者在 LaTeX 中生成的方程。

其中 h 是我们预测的未来时间步,我们让l _ t=【ŷ_{t+1}】来明确地证明这是水平分量。如果你想了解更多关于简单指数平滑的知识,请参考我以前的文章:

添加趋势

如前所述,该模型没有趋势或季节成分,并导致持平预测(所有预测将相同并等于最近观察到的值)。因此,1957 年 Charles Holt 扩展了这个模型,加入了一个趋势成分, b_t :

作者在 LaTeX 中生成的方程。

其中 b_t 为预测趋势分量,b _ { t-1 }为之前的预测趋势, β 为可取值为 0 ≤ β ≤ 1 的趋势平滑因子。

趋势方程是根据水平分量的逐级变化计算的。此外,从总体方程来看,趋势分量现在正乘以时间步长 h ,因此预测不再是平坦的,而是h 的线性函数。因此,模型的名称为霍尔特线性趋势法。**

对于这个模型,我们需要为趋势分量设置一个初始值,常见的选择是:

作者在 LaTeX 中生成的方程。

其中 t 是时间序列中的某个任意时间步长。这基本上只是一个平均预测模型

抑制

目前这种公式的一个问题是,预测在未来会任意增加或减少。事实上,没有什么东西会无限增长或衰减。因此,通常会添加一个抑制性术语【ϕ】,以缩减长期预测:

作者在 LaTeX 中生成的方程。

其中 ϕ 的值可以是 0 < ϕ < 1 。不能是 01 的原因是为了确保确实发生一些阻尼。如果 ϕ=1 那么这个模型就是普通的霍尔特线性趋势法。

现在让我们用 Python 实现这个新的指数平滑模型!

Python 实现

下面是在一个非常简单的数据集上使用stat models包实现 Holt 线性趋势方法的代码片段:

这些数据是美国航空公司的乘客数据集,来自拥有 CC0 执照的 Kaggle。

作者 GitHub 要点。

作者用 Python 生成的图

我们观察到霍尔特的方法捕捉到了趋势,而简单的指数平滑模型只是一个像我们预期的平面预测。然而,房间里仍然有一头大象。我们的时间序列具有相当高且明显的季节性,这是霍尔特的模型所没有捕捉到的。我将在下一篇文章中讨论如何处理季节性,但是现在我们可以得出结论,霍尔特的方法只适用于没有季节性的数据。

通过对拟合的模型对象调用以下方法,可以进一步诊断拟合的霍尔特模型:

**model_holt.summary()**

作者用 Python 生成的图像。

这里的smoothing_level是指平滑电平参数 α ,非常大,表示电平变化非常频繁。然而,smoothing_trend指的是平滑趋势参数【β,非常低,意味着趋势几乎没有太大变化。最后,damping_trend,也就是【ϕ】,很大,说明我们需要预测未来很长一段时间,才能观察到上升趋势的衰减。

总结和进一步的思考

在本文中,我们展示了 Holt 的线性趋势方法在将趋势合并到指数平滑中的数学原理,以及如何用 Python 实现该模型。当数据有趋势但没有季节性成分时,Holt 的方法产生最好的预测。在以后的文章中,我们将回顾一下 Holt Winters 模型,该模型增加了季节性。

本文中使用的完整代码可以在我的 GitHub 中找到:

**https://github.com/egorhowell/Medium-Articles/blob/main/Time Series/Exponential Smoothing/holts_forecasting.py

参考资料和进一步阅读

和我联系!

(所有表情符号由 OpenMoji 设计——开源表情符号和图标项目。许可证: CC BY-SA 4.0**

简单指数平滑的时间序列预测

原文:https://towardsdatascience.com/forecasting-with-simple-exponential-smoothing-dd8f8470a14c

指数平滑的介绍以及用 Python 实现指数平滑进行预测。

瑟奎拉在 Unsplash 上拍摄的照片

背景

预测领域包含各种各样的模型。在我之前的帖子中,我们讨论了一些基本预测技术,它们通常为迭代和构建更复杂的模型提供了良好的基础。这些基本模型的一个自然迭代是 指数平滑

指数平滑的想法起源于 20 世纪 50 年代,基本上是一种奇特的方式,表明我们将更加重视最近的观察结果。较老的观测值将会以指数衰减的速率得到较少的权重。因此,它被称为指数平滑。

您可以将它看作是简单预测模型和平均预测模型之间的中间值,在简单预测模型中,我们将预测值设置为等于最近的观测值,在平均预测模型中,我们将预测值设置为等于所有历史观测值的平均值。如果你想了解更多关于天真和平均预测模型的内容,请点击这里查看我以前的帖子:

在本文中,我想介绍最基本的指数平滑模型,以及如何用 Python 实现它。

什么是简单指数平滑?

概观

如上所述,这篇文章是关于指数平滑家族中最基本的模型,简单指数平滑。这个模型简单的原因是因为它没有考虑趋势或季节性。然而,这是一个很好的起点,可以在以后添加进一步的复杂性。如果你想了解更多的趋势和季节性,请参考我以前的文章:

一般来说,当您的数据没有任何趋势或季节性时,这个简单的模型是最好的。或者,您可以通过执行差分和 Box-Cox 变换来转换数据,使其保持平稳,从而消除其趋势和季节性。要了解更多关于这些转换的信息,请点击这里查看我以前的帖子:

理论

(简单)指数平滑的一般数学公式如下:

作者在 LaTeX 中生成的方程。

其中【ŷ_{t+1}】为预测值, y_t y_{t-1}y _ { t-2 }为时间序列的前一次观测值, α 为平滑参数,取值 0 ≤ α ≤ 1 平滑参数是我们在拟合这个模型时需要估计的主要参数。该参数通常由最小二乘法的 来计算,幸运的是大多数计算软件包已经为我们完成了!

平滑参数也可以通过预测者的直觉来选择。

α 的值越高,越重视最近的观察,反之亦然,对于 α 的值越低。如果 α=1, 那么我们恢复原始预测模型,其中所有预测都等于最近的观察。

我们可以简化上面的公式,用之前的预测来表达,【ŷ_{t-1}】:**

作者在 LaTeX 中生成的方程。

这种重新表述的证明相当琐碎,但有点详尽,所以我在这篇文章中省略了它。不过有兴趣的读者可以在这里找到推导

像所有的时间序列一样,指数平滑法通常被分解成它们的组成部分,如水平趋势季节性。由于简单的指数平滑法既不考虑趋势也不考虑季节性,因此它只包含水平分量, l_t:

作者在 LaTeX 中生成的方程。

其中 h 是我们正在预测的未来时间步,我们已经使l _ t=【ŷ_{t+1}】明确声明这是级别组件。这种简单指数平滑的数学表示可能看起来没有用,因为只有一个组件,但是当添加更多组件(如趋势和季节性)时,它变得非常强大。

现在这些理论已经足够了,让我们来看一个 Python 中的真实例子!

在 Python 中实现指数平滑

下面是使用 statsmodel 包对美国航空公司乘客数据集进行简单指数平滑的实现:

数据来源于拥有 CC0 许可证的 Kaggle

作者 GitHub 要点。

作者用 Python 生成的图

预测显然不太好,但这确实是最简单的指数平滑模型。需要注意的一点是,预测是平的,这显然是由于模型没有趋势或季节性成分。还有进一步的模型,如霍尔特·温特的https://en.wikipedia.org/wiki/Exponential_smoothing#Triple_exponential_smoothing_(Holt_Winters)***,我将在后面的帖子中讨论,这些模型对这些额外的组件进行建模。*****

我们可以通过运行以下方法提取拟合模型的详细信息:

*****model.summary()*****

作者用 Python 生成的图像。

该模型发现平滑参数的最佳值 α、 ( smoothing_level)为 0.995 。这是非常高的,表明趋势相当不稳定,变化频繁。预测者(或读者)也可以根据自己的判断对模型进行诊断。

总结和进一步的思考

在这篇文章中,我们介绍了指数平滑的概念。这一系列中最基本的模型是简单指数平滑,因为它不包含趋势或季节成分。它只是增加了最近观察值的权重,减少了历史观察值的权重,这个权重由一个平滑参数决定。在 Python 示例中,该模型没有提供良好的预测,因为数据表现出明显的季节性和明显的趋势。

本文使用的完整代码可以在我的 GitHub 中找到:

*****https://github.com/egorhowell/Medium-Articles/blob/main/Time Series/Exponential Smoothing/simple_exp_smoothing.py

参考资料和进一步阅读

和我联系!

(所有表情符号由 OpenMoji 设计——开源表情符号和图标项目。许可证: CC BY-SA 4.0*****

利用大规模合成数据进行预测(Nixtla & YData)

原文:https://towardsdatascience.com/forecasting-with-synthetic-data-at-scale-nixtla-ydata-404b65600876

制作合成时间序列数据,然后用深度学习模型进行预测

http://github.com/nixtla*和* YData 组成。 费德里科·加尔萨·拉米雷斯 马克斯·梅根塔尔

介绍

在这篇文章中,我们解释了如何使用 nixtlatsydata-synthetic ,开源和免费的 python 库允许您生成合成数据来训练最先进的深度学习模型,而不会明显损失数据质量。我们开发了一个不直接访问原始数据的深度学习预测管道,并表明合成数据对模型的性能影响最小。

动机

在过去的十年中,基于神经网络的预测方法在大规模预测应用中变得无处不在,超越了行业界限,进入了学术界,因为它在许多实际任务中重新定义了最先进的技术,如需求规划、电力负荷预测、逆向物流、天气预测,以及 M4 和 M5 等预测比赛。

然而,对于那些对创建预测感兴趣的人来说,一个问题是不使用原始数据的模型开发或软件测试;这可能是因为实际数据需要时间来收集,对其使用有限制,或者数据根本不存在。在许多应用程序中,用户不希望模型能够访问实际数据,特别是如果模型训练是在云中或基础设施之外进行的。上述情况极大地限制了实践,阻止了使用可用云对大型数据集的模型进行缩放。

这篇文章展示了如何使用nixtlatsydata-synthetic来解决这个问题。首先,用户可以使用ydata-synthetic创建合成数据;合成数据是人工创建的,并保留原始数据属性,在合规的同时确保其商业价值。随后,用户可以使用nixtlats训练最先进的神经预测算法,而无需访问原始数据。一旦模型经过训练,就可以将模型发送给原始数据的所有者,并对其基础设施的安全性进行推断。下图描述了该过程。

作者图片

我们评估并显示,与原始模型的预测相比,合成模型的预测性能保持不变。

图书馆

nixtlatsydata-syntheticPyPI 中可用,所以你可以使用pip install nixtlatspip install ydata-synthetic来安装它们。

数据

为了评估管道,我们考虑每年的 M4 竞赛数据集。数据集最初是公开发布的,它是带着完全开放访问许可发布的。M4 主要预测竞赛引入了一种新的多变量时间序列模型,称为指数平滑递归神经网络(ESRNN),它在基线和复杂时间序列集合上以较大优势获胜。

我们将使用nixtlats库来方便地访问数据。

在本例中,我们使用 1000 个年度时间序列。

M4.load方法返回训练集和测试集,所以我们需要将它们分开。该库还提供了各种各样的数据集,参见文档

nixtlats需要虚拟测试集来进行预测,因此我们将训练数据与零值测试数据相结合。

管道

使用 ydata-synthetic 创建合成数据

在本节中,我们使用来自ydata-syntheticTimeGAN模型合成由Y_df_train定义的训练数据。查看后的合成时间序列数据:GAN 方法,您可以了解更多关于TimeGAN模型的信息。

以下线路训练TimeGAN模式,

因此,对象synth_data包含合成训练数据。为了使用nixtlats,我们需要将synth_data转换成熊猫数据帧。使用下面几行代码可以很容易做到这一点。

使用 nixtlats 训练深度学习模型

在本节中,我们使用以前的合成数据来训练 M4 竞赛的获胜者 ESRNN 模型。这种模式是混合的;一方面,它通过指数平滑模型局部拟合每个时间序列,然后使用递归神经网络训练水平。您可以通过使用 ESRNN 模型查看 Python 中的预测来了解关于此模型的更多信息。

模型培训的流程遵循 PyTorch 的通用实践。首先必须实例化一个DatasetTimeSeriesDataset类允许在每次迭代中返回完整的序列,这对于 ESRNN 之类的递归模型很有用。为了进行实例化,该类接收目标系列Y_df作为包含列unique_iddsy的 pandas dataframe。此外,还可以包括临时外生变量X_df和静态变量S_df。在这种情况下,我们只使用原始模型中的静态变量。

接下来我们需要做的是定义nixtlats中包含的 ESRNN 模型如下:

然后我们可以这样训练它,

用真实数据训练的模型

为了比较提供相似结果的两种解决方案,在本节中,我们使用原始数据训练模型。

然后我们可以这样训练它,

比较预测

最后,我们使用原始数据对两个模型进行预测,model_synth用合成数据训练,model用原始数据训练。首先,我们定义测试数据集和加载器。

以下几行用综合模型获得预测,

同样,下面几行通过用真实数据训练的模型获得预测,

现在,我们使用平均百分比误差(MAPE)及其对称版本(SMAPE)将两个模型的性能与真实值进行比较。nixtlats提供的功能可以轻松做到这一点。

正如我们所见,考虑到 MAPE 损失,即使是用ydata-synthetic生成的合成数据训练的模型也能产生更好的预测。

结论

合成数据有广泛的应用。在这篇文章中,我们展示了创建合成数据的完整管道,并使用它来训练最先进的深度学习模型。正如我们所看到的,性能没有受到损害,甚至对于某些指标来说,性能甚至更好。

用树预测:时间序列的混合分类器

原文:https://towardsdatascience.com/forecasting-with-trees-hybrid-classifiers-for-time-series-b2509abf15f8

构建用于时间序列预测的有效混合随机森林分类器

Unsplash 上由 Karthikeyan Perumal 拍摄的照片

当处理时间序列时,我们习惯于考虑传统的预测任务,其中我们必须逐点预测一些感兴趣的 KPI 的未来值。还有一些情况下,我们必须预测系统的状态。这种状态可能采用离散值,并且是已知回归变量之间的一些组合的结果。换句话说,我们必须在依赖于时间的场景中解决分类任务。

时间序列分类问题的建模与标准分类任务的建模非常相似。我们只需要关注我们所掌握的数据的时间动态。在这个意义上,基于树的算法的使用在一些基本假设下可能是最佳的。基于树的模型的威力是众所周知的,并且有文献记载。最近,我们注意到在非标准表格任务中,这类模型的采用也有所增加。我们特别指的是所有关于时间序列预测的应用。这项工作在该领域做出了显著贡献,与深度学习解决方案相比,梯度推进模型表现出色。

在之前的帖子中,我们开始研究这个领域,并发现了如何在时间序列预测场景中构建有效的基于混合树的预测器。随着我们的发现的成功,我们试图在分类环境中复制类似的方法。我们希望通过混合线性模型和随机森林来测试不同的混合分类器解决方案。

这部作品中介绍了优秀的杂交候选人。它旨在引入各种加权随机森林版本,以利用集成学习的有效性。特别是,我们对基于堆叠的随机森林感兴趣,该森林对每个决策树做出的出袋(OOB)预测进行逻辑回归训练。

另一个有趣的混合随机森林架构在这里展示。作者提出了两步学习过程,其中,在对原始数据拟合线性模型之后,对从上一步获得的残差拟合随机森林(为了方便起见,我们将其称为“线性森林”)。在 线性树 包中提供了一个有效且兼容 scikit-learn 的线性森林实现。这个库提供了混合线性模型和决策树的学习能力的算法的实现(如 LinearTreeLinearForestLinearBoosting )。

在本文中,我们试图通过从头开始构建我们的混合随机森林预测器来解决一个超越概率问题。我们的任务是提前预测时间序列超过固定阈值的概率。我们专注于二进制分类,这里我们只登记两种状态:高于低于阈值。

实验设置

我们从一些简单的随机漫步开始产生一些人工序列。我们认为它们是在预测时观察到的,即它们是我们可以用来构建预测算法的数据。预测的目标是已知随机游走的线性组合,其中每个序列的贡献大小是从指数分布中随机生成的。

输入和目标生成流程示例(图片由作者提供)

从这些方面来说,我们似乎正在准备执行一项回归任务。我们对预测目标的准时值不感兴趣。取而代之的是,我们专注于从两个连续的时间步骤中检测目标何时可能记录到突变

突变的定义取决于领域,根据应用领域可以有不同的解释。在我们的实验环境中,假设我们对预测目标与前一步相比何时出现负变化感兴趣。我们需要区分我们的目标系列,并应用一个固定的阈值(以零为中心的水平线)来区分正负变化。

阈值等于 0%的二进制标签生成(图片由作者提供)

我们的预测模型必须被训练以区分这两个创建的类。有了这种数据和以零为中心的固定阈值,我们面临的分类问题就产生了一种近乎完美的平衡状态。为了让事情更刺激,我们可以想象在不平衡的环境下工作。假设我们也对预测目标何时记录到显著的负变化感兴趣。这可以通过设置低于零的阈值来实现。

作为阈值水平函数的二进制标签生成(图片由作者提供)

这样,阈值越低,要预测的感兴趣的负变化就越高(即标签分布越不平衡)。

结果

至此,我们已经为建模做好了准备。我们选择了三种不同的预测架构来测试我们的二元分类问题。我们从 scikit-learn 中提供的标准随机森林分类器开始。然后,作为的混血儿,我们要测试一个加权随机森林和线性森林。提醒一下:

  • 加权随机森林是堆叠模型的一个特殊版本,其中估计器是使用袋外预测来拟合的。
  • 线性森林反复提供混合线性和随机森林结果的预测。

我们生成 100 对不同的特征(X)和目标(y ),以模拟更多不同的场景。对于每个场景,我们尝试使用不同阈值级别的所有提到的分类器来理解不平衡上下文中的行为。作为预处理,我们对输入特征进行简单的一阶微分。

标准随机森林的测试数据结果(图片由作者提供)

加权随机森林的测试数据结果(图片由作者提供)

线性森林的测试数据结果(图片由作者提供)

对于所有测试的算法,当不平衡变得更强时(即阈值越低),我们记录到性能下降(如预期的那样)。

目标变化阈值固定为 0%的测试数据结果(图片由作者提供)

目标变化阈值固定为-0.1%的测试数据结果(图片由作者提供)

目标变化阈值固定为-0.2%的测试数据结果(图片由作者提供)

目标变化阈值固定为-0.3%的测试数据结果(图片由作者提供)

目标变化阈值固定为-0.4%的测试数据结果(图片由作者提供)

查看每个阈值设置中的性能,我们会注意到不同的模式。标签越平衡,线性森林记录的性能(在精确度和召回率方面)越好。标签分布越不平衡,加权随机森林的召回率越高。相反,标准随机森林可能总是在不平衡的上下文中达到最佳精度。

摘要

在这篇文章中,我们从一个标准的时间序列预测问题切换到一个更有趣的分类任务。尽管有这种变化,我们还是采用了所有对用基于树的算法建模时间相关系统有用的预防措施。在这个意义上,我们测试了不同的混合随机森林体系结构,这些体系结构显示出是更经典的随机森林分类器的有价值的替代方案。

查看我的 GITHUB 回购

保持联系: Linkedin

用树预测:时间序列的混合建模

原文:https://towardsdatascience.com/forecasting-with-trees-hybrid-modeling-for-time-series-58590a113178

构建有效混合预测器的简单步骤

梁杰森Unsplash 上的照片

基于树的算法在机器学习生态系统中众所周知。到目前为止,他们以主导每个表格监督任务的方法而闻名。给定一组表格式的特性和一个要预测的目标,它们可以获得令人满意的结果,而不需要太多的努力或特殊的预处理。在他们学习过程的基础上,分裂标准只关注相关特征和有用值的范围是有效的。

他们的有效性是有据可查的,而且似乎在各种意想不到的领域逐年增加。非常有趣的是在这部作品中概述的证据。它旨在说明梯度推进模型在时间序列预测领域取得的成就,以及它们如何优于深度学习方法。这听起来很奇怪,因为基于树的算法在建模依赖于时间的现象方面名声不佳(至少直到今天)。

基于树的模型的弱点是,从技术上讲,它们不能根据比训练数据中看到的更高/更低的特征值进行推断。对于他们来说,预测可见区间之外的值几乎是不可能的。相反,经典线性回归可能较少受到数据动态行为的影响。既然线性回归擅长外推趋势,而梯度推进擅长学习交互,为什么不把它们结合起来呢?我们的目标是创造“混合”预测器,结合互补的学习算法,让一个的优势弥补另一个的劣势。

在处理深度学习时,更容易想到“混合模型”。神经网络的无限架构组合和个性化训练过程在定制方面提供了很大的好处(例如,指数平滑叠加 LSTM )。用树模型开发定制的混合解决方案更加棘手。在这种意义上,一个很好的资源由 线性树 : 表示,这是一个 python 包,它提供了混合模型架构,混合了基于树的模型和线性模型的学习能力。不仅如此, LGBMXGBoost 还引入了在树叶中使用线性近似来拟合梯度增强的能力。

在本帖中,我们试图从零开始构建我们的混合预测器。我们需要做的就是遵循两步走的方法来学习下划线系统模式。

实验设置

为了设计有效的混合体,我们需要对时间序列是如何构造的有一个总体的了解。许多时间序列可以通过将仅仅三个成分 ( 趋势季节周期)加在一起加上一个本质上不可预测的项(误差)来精确描述。

series = trend + seasons + cycles + error

学习时间序列成分可以被理解为一个迭代过程:

  • 首先学习趋势,将其从原始序列中减去,得到残差序列;
  • 其次,从去趋势残差中学习季节性,减去季节;
  • 最后,学习周期并减去周期。

换句话说,我们使用一种算法来拟合特定的分量序列,然后使用第二种算法来拟合残差序列。最终预测是各种模型组件预测的相加。

为了试验混合模型的构建,我们开始生成一些具有双重季节性模式和趋势成分的模拟序列。

合成时间序列生成(图片由作者提供)

未知趋势是通过拟合随机行走序列的三次多项式获得的。结果是一条平滑的趋势线,该趋势线被添加到季节性成分中,以获得要预测的最终时间序列。我们以这种方式生成多个时间序列,并尝试预测它们,为各种解决方案设定基准。

合成时间序列(图片由作者提供)

我们尝试了四种不同的方法:

  • 在选择最佳多项式基之后,拟合一个简单的线性模型;
  • 用差分变换变换目标以使其静止;
  • 混合添加剂。首先,用最佳多项式基拟合一个线性模型来推断趋势。其次,用梯度推进对去趋势序列建模;
  • 杂交包容。拟合梯度增强,还包括外推趋势(通过用最佳多项式基拟合线性模型获得)作为特征。

除了简单的解决方案,所有的方法都使用一些样条变换作为特征。他们很容易通过观察序列来捕捉季节性模式。通过在训练数据上搜索最佳多项式基来选择最佳趋势。拟合梯度增强,同时使用时间交叉验证策略搜索一些最佳参数配置。

结果

对于我们处理的每个系列,我们尝试所有提到的方法,并将结果存储在测试数据中。

记录在测试数据上的均方误差(图片由作者提供)

混合包含方法获得最低的平均测试误差,其次是差分方法。混合添加剂的表现低于我们的预期,因为它记录的误差几乎是差分法的三倍。一般来说,在对动态系统建模时,区分目标总是一个很好的基准(就像我们实验中展示的那样)。最令人惊讶的是混合方法之间的巨大性能差异。让我们直观地检查一些结果,看看会发生什么。

预测对比(图片由作者提供)

预测对比(图片由作者提供)

预测对比(图片由作者提供)

从上面的一堆图片中,我们可以更好地理解加法的“失败”。它的表现与之前的趋势拟合度密切相关。如果趋势估计不准确,最终的预测也是无用的。由于趋势作为一个特征包含在模型中,这种行为通过包容性混合方法得以缓解。因此,梯度提升可以校正/减轻最终预测误差。

摘要

在这篇文章中,我们介绍了构建时间序列混合预测模型的不同方法。需要强调的重要一点是,除了这里显示的方式之外,还有许多方式可以组合机器学习模型。尽管如此,如果我们清楚地知道标准算法是如何工作的,就更容易调试或理解我们混合创造的可能的缺点或陷阱。

作为下一个阅读的话题,我建议 用树预测:时间序列的混合分类器

查看我的 GITHUB 回购

保持联系: Linkedin

忘记算法和模型——首先学习如何解决问题

原文:https://towardsdatascience.com/forget-about-algorithms-and-models-learn-how-to-solve-problems-first-c791fde5842e

意见

有抱负的开发人员和数据科学家经常本末倒置

解决问题是真正的脑力劳动。作者图片

A A 几乎每周都有朋友或熟人问我,我想学编码;我应该从哪种语言开始?差不多两周一次,我在 LinkedIn 上收到一条 DM,开头是我儿子应该开始编程了;对他来说最好的语言是什么?

不仅仅是以前没编过代码的人。我经常从有几年编码经验的人那里得到这些信息。

我说这些不是为了抱怨。

我在 Medium 上研究不同编程语言、框架和人工智能模型的优缺点,以此谋生。人们提出这样的问题让我受益匪浅。

问题很直观。毕竟,每个人都希望用最好的工具工作,并尽可能快地建立他们的软件技能。

当你观察到每个开发人员似乎使用不同的技术堆栈时,想知道哪一个是正确的是完全有意义的。

问题是,这完全取决于手头的问题。

没有技术本身是好的或坏的;这取决于你想解决什么类型的问题。归根结底,编程就是:通过使用计算机来解决问题。

所以,对于想开始编程或者想提升软件开发或数据科学技能的人来说,问题不应该是我该用什么, Python 还是 Julia 问题应该是我怎样才能更好地解决软件问题?

如何解决问题

完全公开,我的职业不是计算机科学家。我是一名粒子物理学家,碰巧使用编程和数据科学的概念,因为我处理来自粒子对撞机的海量数据。

也就是说,物理学家和计算机科学家一样受欢迎。这不是因为他们对中微子或黑洞的了解;是因为他们解决问题的能力。

《T21》引用亚伯拉罕·林肯的话说,“给我六个小时去砍树,我会用前四个小时去磨斧子。”。

对于程序员和数据科学家来说,这意味着在开始编码之前,要花时间理解问题并找到高层次的解决方案。在一般的编码面试中,候选人预计花在实际编写代码上的时间不到一半,其余时间用于理解问题。

1-理解问题

永远不要跳过这一步!

知道你是否理解一个问题的关键是你是否能向不熟悉它的人解释它。尽量用通俗易懂的英语或者母语写下来;画一个小图;或者告诉一个朋友。如果你的朋友不明白你在说什么,你需要回到问题陈述。

要问的关键问题是:

  • 什么是输入?期望的输出是什么?
    例如,输入可能是一组数据,输出可能是对数据的线性回归。
  • 问题背后的假设是什么?
    例如,你可能假设你的数据中(几乎)没有测量误差。
  • 是什么让这个问题变得复杂?
    例如,您拥有的数据可能不完整,或者数据集可能太小,无法得出明确的结论。

2-分解问题

每个大问题都由许多小问题组成。鉴于我们之前的线性回归示例,您可能需要考虑以下子问题:

  • 清理数据
  • 找出数据中哪些变量对回归有意义,哪些变量可以安全地忽略
  • 寻找合适的工具来进行回归(这就是关于编程语言和框架的老问题发挥作用的地方)
  • 评估您的结果并检查错误

把问题分解有助于你为工作制定一个合适的计划。

这也更有激励性,因为你会在前进的道路上实现小而重要的里程碑。这比坐在堆积如山的工作面前感觉自己没有前进要令人满意得多。

3-从一个例子开始

魔鬼总是在细节中。

不要从整个项目开始,取其中的一小部分。试试你的计划是否可行,或者你是否因为不可预见的困难而不得不修改它。

这有助于你理解困难的部分。许多问题听起来很简单,但是当你开始构建它们时,就会遇到一个接一个的障碍。

在我们的例子中,我们可以先对几个变量进行线性回归,而不是使用所有相关变量。这不会给你任何项目完成的分数;然而,当您仍在处理少量数据时,发现脚本中的错误可以挽救生命。

当你把所有的数据都扔进机器,运行几个小时,然后然后回来意识到脚本中途挂起,你会非常沮丧。

相信我,这种事经常发生!

首先运行小测试,并确保您的解决方案如您所设想的那样工作。

4-执行

这是肉多的部分。现在,您可以为您的大问题构建解决方案了。

把你所有的数据都扔给代码。运行一个奇特的模型。想做什么就做什么。

完成前面的三个步骤后,这应该会非常顺利!

如果有错误,您可能必须返回到步骤 1-3,看看您是否已经理解了所有内容,并且没有忽略任何错误。

5-反射

仅仅因为你找到了一个解决方案,并不意味着你找到了 T2 最好的解决方案。不要跑掉,收工;思考如何优化您的解决方案,以及如何以不同的方式实现它。

你可能想和你的同事交流,问他们如何解决这个问题。他们的方法和你的不同吗?

您还可以尝试确定解决方案中最大的瓶颈,即执行时花费最多时间和资源的部分。你如何改进它们?

最后,思考您的解决方案在未来可能会如何发展。新的软件框架或人工智能的使用会让你的解决方案更好吗?你的解决方案如何有助于解决其他更复杂的问题?

吹牛

包括我自己在内的人们,倾向于沉迷于不同的编程语言和最新的框架,这些语言和框架可能会使所有事情的效率提高 1000 倍。

值得提醒自己的是,这还不到成为一名优秀程序员所需的一半。另一半是解决问题。

你不会在一夜之间获得解决问题的技能。

但是如果你采用这些步骤,问正确的问题,并且经常这样做,你就走在了让你的职业生涯从优秀走向卓越的正确道路上。

成为 中等会员 可完全访问我的内容。

别管路线图了!以下是如何快速掌控数据科学堆栈的方法

原文:https://towardsdatascience.com/forget-roadmaps-here-is-how-to-dominate-the-data-science-stack-blazingly-fast-a49e62f2bcf

以前所未有的方式学习新软件包

为什么要这么做?

在生产率大师中,有一个术语叫做“流动状态”。当你全神贯注于你正在努力做的事情,以至于进入某种恍惚状态时,就会发生这种情况。你不能让你的手指离开键盘。他们开始按照自己的意愿工作,一次不用思考几个小时就能写出质量最好的代码。

你正在做所谓的“深度工作”——在这段时间里,你最有生产力和创造力,并且处于“心流”中。一周内你进入心流的次数越多,你的高质量工作就越多。唯一的要求——不能分心。

即使是最微小的事情,比如检查你的电子邮件,偷看你的通知,或者对我们程序员来说最重要的事情——谷歌一下。都是摩擦。他们扼杀创造力扰乱你的思维过程。深度工作就像睡眠——一旦你醒来,你就不能从你停止的地方继续。你得回去睡觉了。再一次。

那么,你如何将注意力分散、重复的谷歌搜索降到最低呢?(不幸的是,你无法消除它们)。

很简单——您必须对所需的工具、库和框架了如指掌。

今天很少去 Sklearn 或者熊猫文档。作为用户,我对这些库的结构和 API 设计了如指掌。即使我忘记了一个类名或一个参数,我也知道 Jupyterlab 和 PyCharm 的相关快捷方式,这些快捷方式可以在一秒钟内显示我需要的内容。

我是如何在这些图书馆(以及其他许多图书馆)变得如此优秀的?我利用自己设计的成熟的学习框架,在每一个项目上都投入了大量的时间。使用它,您可以在几个小时或一两天内学习任何中小型库(如果库非常庞大的话)(如 Tensorflow 或 NumPy)。

在整篇文章中,我将详细概述框架的每个步骤。最后,您将有一个强大的公式来深入学习任何新的包,这样您就很少需要回到文档中。

https://ibexorigin.medium.com/membership

获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:

https://alphasignal.ai/?referrer=Bex

飘飘欲仙

不同的包在依赖性或计算开销方面有不同的要求。正如您将对许多包重复这一学习过程一样(我希望如此),让您的本地环境充满新的 Conda envs 不是一个选项。

此外,大多数数据科学软件包,如 SHAP 和 UMAP,都需要大 CPU 和 GPU,所以云环境应该是你的首选。最好的选择是谷歌 Colab 或者 Kaggle 内核。两者都在编码环境和硬件方面提供了大量的好处。

此外,它们都运行在 Linux 上,如果你有一台 Linux 机器,你会遇到的一些库更容易安装。试图在 Windows 上安装 Tensorflow GPU 的人知道我在说什么😉。

从快速入门开始

您访问新软件包文档的第一页是快速入门。如果没有,可能会有一个安装页面,或者只有主页就可以了。您希望形成学习该软件包是否值得的初步印象,如果值得,学习如何安装它。

除非软件包需要第三方软件,否则简单的pipconda安装就足够了。从现在开始,我将使用 SHAP 图书馆的文档给出例子——一个极好的框架来解释黑盒模型和深度神经网络。

SHAP 文档主页。

N-tab 规则

安装完成后,您可以离开文档并在 Google 上搜索“your _ package _ nametutorial”

你在不同的标签页中打开一些文章的链接,开始专心阅读它们。你不希望 YouTube 视频的一半时间都花在看那个人输入代码上,浪费你的时间在填充语句上。

通过阅读一些关于您的软件包的文章,您可以对它所解决的问题的上下文以及它是如何解决问题的有所了解。您还会接触到它的基本 API。此外,一个好的教程会解释大多数与您的软件包相关的术语和概念,所以当文档中突然提到它们时,您不会迷路。

最后,你选择一两篇文章来最好地解释这个包,如果你在接下来的步骤中需要的话,把它们的标签打开。

转到教程部分或用户指南

现在,是时候打开文档的教程页面了。您的软件包文档可能没有单独的教程部分。在这种情况下,您可以搜索“示例”页面,或者在文档的搜索栏中输入“示例”一词。搜索“用户指南”也可以。

如果你运气好,并且你的包被广泛传播,很有可能它会有一个全面的教程页面。例如,Matplotlib 将其教程分为三个难度级别。它的所有代码示例都是由包创建者编写的,运行速度很快,允许您快速试验代码:

Matplotlib 文档的屏幕截图

你学习的核心部分就发生在这个阶段。您应该一个接一个地打开每个教程,仔细阅读解释,并将每个代码块复制到您的环境中。

我建议每块至少花 2-5 分钟。首先,逐行运行它,对它们进行注释和取消注释。然后,更改不同参数的值,看看它们对输出有什么影响。我在 Jupyter Lab 上使用“Shift + Tab ”,它在弹出窗口中显示类或函数 docstring 以获得可能的想法。

尽管前几个教程要花几个小时,但当您转到后续页面时,花费的时间会更少,因为函数和类会开始重复。

API 参考

现在,我们进行最后的润色。API 参考页面通常在文档的末尾,它显示了包中每个模块、类和函数的列表。

将有数百个参考,所以你只需要探索你需要的功能。您已经知道自己想要什么,因为您已经花了相当长的时间来了解这个软件包,注意到了它的优点和缺点,并且对如何使用它有了很好的想法。

Sklearn API 参考页面

找到将来可能会用到的函数后,打开它们的文档,试着用这些例子做几次。我还建议至少读一遍其他函数和类名。通常,您会遇到一些边缘情况,这些情况可能会被隐藏在 API 参考中的某个函数很好地解决。

CLI 呢?

如果您的软件包也有一个 CLI 工具呢?

通常情况下,CLI 适用于对命令行有经验并且已经知道核心包 API 的人。这就是为什么你需要在最后阶段学习它。

如果您的软件包有 CLI 工具,它也会有单独的 CLI 参考页面。您应该在一个单独的选项卡上打开它,并像在 API 参考部分那样阅读它。

我还大量使用了--help-h标志。例如,在conda之后使用-h会显示 Anaconda CLI 的所有子命令:

作者图片

我主要对createinstallremove子命令感兴趣,因为我只需要这些子命令来管理环境。在将每个子命令与conda关键字组合后,我再次添加-h标志,得到另一组子命令。我试用我需要的那些,并继续添加-h旗帜,直到我对整个过程感到满意。

这就是你如何成为命令行忍者!

摘要

数据科学堆栈势不可挡。你需要学习和练习的东西太多了。为了最大限度地利用你的时间,你需要聪明地学习,尽可能让每一次学习都富有成效。主要思想是全面地学习每一个包、库和框架,这样你就不必浪费宝贵的时间重新学习了。

此外,对所需的包和工具了如指掌,使您能够深入、不间断地工作,极大地提高了您的输出能力。

为 IEEE 交易日志格式化 Jupyter 笔记本

原文:https://towardsdatascience.com/formatting-a-jupyter-notebook-for-ieee-transactions-journals-4453d5b3c633

Quarto 是一个用于 Jupyter 笔记本的开源技术发布系统,可以使用模板进行扩展。本文介绍了一个用于生成 IEEE 格式文章的模板。

尼克·扬森CC BY-SA 3.0pix4 free的研究论文

“文档即代码”范式中,技术作者使用软件开发工具来编辑、协作和发布技术文档。除了它在记录软件项目方面的主场之外,它特别适合于工程师和科学家,写数据丰富的文章。类似于软件项目,这样的文章依赖于重要的计算机代码。因此,在同一个环境中管理代码和相应的文章可以提高生产率,并有利于更深入的协作,因为代码体现了这种方法。相比之下,仅仅在手稿和结果上进行合作,而没有解释我们如何到达那里的代码,会混淆该方法的关键元素。

当文章采用计算笔记本的形式时(相对于静态手稿),就实现了额外的集成。文章可以直接调用代码,以便重新运行一些分析并动态更新图表。这进一步提高了再现性。只要代码和支持材料(数据和软件依赖)是共享的,任何人都可以复制文章的计算部分。

正如[1]所指出的,一个可重现的工作流程的主要目的不是处理赤裸裸的欺诈。相反,它是为了获得更好的奖学金。这是因为再现性灌输信任,这是合作的先决条件。在同一参考文献中,还指出了一些作品不需要被复制,并且在结果的可复制性和再利用之间存在连续体。同样,我们不提倡不适当的一揽子要求,即每一个单独的图或表都应该从头开始可复制。

尽管如此,在许多情况下,这是可取的。例如,数据丰富的分析涉及复杂的数学和容易出错的代码。新的数据,新的假设,新的发现可能会出现,可能会挑战或加强先前的结论。拥有一个可重现的分析管道来按需重新运行分析被认为是一个好的实践。如果文章是计算笔记本,更新的结果可以容易地合并到更新的文章中。正如在[2]中所提到的,努力追求再现性是一种纪律,可以带来更好的工作。

Jupyter 笔记本非常适合作为一篇成熟的学术文章——尤其是当文章依赖于数据分析时。它使叙述性故事和为分析提供动力的代码之间紧密联系起来。然后,完整的文章被视为代码。这种“article-as-code”范式已经在科学和工程的各个领域流行开来。

例如,参考文献。[3]是一篇化学论文,并提供了一个完整的同伴笔记本,可在 Google Colab 上免费获得。笔记本完整地复制了期末论文,同时也包含了代码和解释。

参考文献。[4]提供了在 Jupyter 笔记本中编写和共享计算分析的十条规则。这篇论文发表在生物学杂志上,但是这些规则更普遍适用。以下是十条规则的浓缩版

  • 讲一个故事,解释过程,而不仅仅是结果。
  • 使用编码最佳实践:模块化代码,明确依赖关系,使用版本控制,共享数据,构建分析管道。
  • 设计您的笔记本有三个目的:阅读、运行和探索。

除了正式的学术文章,工程设计和分析报告也受益于计算笔记本格式。工程设计通常依赖于方程、快速内联计算、用于算法描述的伪代码以及探索权衡的图表。这些元素在笔记本上都是一等公民。此外,可能需要重要的代码来配置支持工具(,例如 CAD、FEM 模拟),这些代码通常对再现设计至关重要。

在工程分析阶段,来自模拟或实验的大型数据集、原型被处理并总结在表格和图表中。笔记本电脑非常适合用作自动分析管道。

这个概念在芯片设计中被拉长了。例如,“Code-a-chip”授予前往最大的半导体芯片设计会议,并展示创新的笔记本驱动设计。

Quarto 是 Jupyter 笔记本的发布系统。它是专为撰写精美的学术文章而设计的。我们在以前的文章[5]中探讨了它的功能。

特别是为 7 家期刊和出版社(计算机械协会、美国化学学会、爱思唯尔期刊……)提供了内置模板。它还提供了从 Latex 类添加新模板的扩展机制。

IEEE 期刊没有内置模板。IEEE 出版了全球近三分之一的电气工程、计算机科学和电子技术文献。

这里我们介绍作者开发的 ieeetran 模板。从一个 Jupyter 笔记本中,它产生了一篇根据 IEEE class“IEEEtran”格式化的 PDF 文章。该模板支持 IEEE 规范,即两列格式以及跨越两列的figure*table*环境。除了用一个命令来处理双列表之外,编写器不需要任何 Latex 命令。

实际模板的开发包括调用现有类在 Latex 中填充模板文章。虽然模板开发需要 Latex 知识,但这对作者来说是透明的。

通过解压文档目录中的内容来安装模板。要使用该模板,运行下面的单个命令,调用输出格式为ieeetran-pdf的 Quarto。

quarto render article.qmd --to ieeetran-pdf

下图说明了工作流以及示例输入和输出文档。

从 Jupyter 笔记本到完全格式化的 IEEE 纸的转换只需一个命令。图片作者。

总之,我们开发了一个 Quarto 扩展,可以无缝地将 Jupyter 笔记本转换成 PDF 格式的 IEEE 论文。尽管如此,我们还是建议在文章起草过程中使用 HTML 输出(使用参数--to ieeetran-html)。起草也可以完全在通常的笔记本编辑器中进行,保证最终的格式化只需要一个命令。编写器不需要任何 Latex 命令。一个限制是该模板还不支持会议格式。

Quarto 正在降低采用“文档即代码”范式的门槛。有了模板扩展机制,就可以很容易地开发出基于现有 Latex 类的模板。格式化一篇论文成为一项常规操作,节省了关注文章内容的宝贵时间。

参考

[1] J. Baillieul 等人,“关于研究监管和研究可再现性的未来的第一次 IEEE 研讨会的报告。”2016【上线】。可用:https://www . IEEE . org/content/dam/IEEE-org/IEEE/web/org/IEEE _ reproducibility _ workshop _ report _ final . pdf

[2] L. A. Barba,“定义开源软件在研究再现性中的作用”,计算机,第 55 卷第 8 期,第 40–48 页,2022 年 8 月【在线】。可用:http://dx.doi.org/10.1109/MC.2022.3177133

[3] M. Ziatdinov,C. Nelson,R. Vasudevan,D. Chen,S. Kalinin,“自下而上构建铁电体:原子尺度铁电畸变的机器学习分析”, ChemRxiv ,2019 年 4 月【在线】。可用:https://chemrxiv . org/engage/chemrxiv/article-details/60c 74146567 dfe 2305 ec3d 40

[4] A. Rule et al. 《在 jupyter 笔记本上书写和分享计算分析的十个简单规则》, PLoS Comput。生物。第 15 卷第 7 期第 e1007007 页 2019 年 7 月【在线】。可用:http://dx.doi.org/10.1371/journal.pcbi.1007007

[5] G. Close,“python 中的信号链分析:硬件工程师案例研究”2021 年 2 月【上线】。可用:https://towardsdatascience . com/signal-chain-analysis-in-python-84513 fcf 7 db 2

Forrester 改变了他们对数据目录的看法,以下是您需要了解的内容

原文:https://towardsdatascience.com/forrester-changed-the-way-they-think-about-data-catalogs-and-heres-what-you-need-to-know-139e13a5869d

这是我们对元数据的看法发生重大转变的最新迹象

萨拉·科恩在 Unsplash 上的照片

正如我们在今年年初预测的,元数据将在 2022 年成为热点——而且只会越来越热。

但这不是我们都知道和讨厌的老派元数据概念。我们谈论的是那些需要 18 个月才能建立起来的 IT“数据清单”,只有在独裁者般的数据管理员统治下才能工作的整体系统,以及孤立的数据目录,这是您在处理数据仪表板或管道时最不想打开的东西。

数据行业正处于我们对元数据的看法发生根本性转变的过程中。在过去的一两年里,我们已经看到了许多全新的想法涌现出来,以捕捉元数据的这一新概念,例如指标层现代数据目录活动元数据,所有这些都得到了数据领域主要分析师和公司的支持。

现在我们有了这种转变的最新迹象。今年夏天,Forrester 放弃了关于“机器学习数据目录”的 Wave 报告,为关于“数据操作的企业数据目录”的报告让路。这里是您需要知道的关于这种变化来自哪里、为什么发生以及它对现代元数据意味着什么的一切。

元数据的快速历史

在大数据的早期,公司面临的最大挑战是简单地跟踪他们现在拥有的所有数据。IT 团队的任务是创建一个“数据清单”,列出公司存储的数据及其元数据。但是在这个数据目录 1.0 时代,公司花在实现和更新这些工具上的时间比实际使用它们的时间还多。

在 2010 年代初,出现了一个重大转变——出现了数据目录 2.0 时代。这带来了对数据管理和将数据与业务环境集成的更大关注,以创建超越 IT 团队的单一事实来源。至少,计划是这样的。这些数据目录带来了一系列问题,包括僵化的数据治理团队、复杂的技术设置、冗长的实施周期以及较低的内部采用率。

今天,元数据平台变得更加活跃,数据团队变得比以往更加多样化,元数据本身也正在成为大数据。这些变化为我们带来了 数据目录 3.0 ,新一代数据治理和元数据管理工具,有望克服过去的编目挑战,并为现代企业增强元数据的力量。

去年, Gartner 放弃了他们旧的数据目录分类,转而采用一种反映我们对元数据看法的根本性转变的分类。现在,Forrester 已经采取行动,用自己的术语来定义这一新类别。

Forrester:从机器学习数据目录转移到数据运营的企业数据目录

数据目录 2.0 的最大挑战之一是采用——无论它是如何建立的,公司发现人们很少使用他们昂贵的数据目录。有一段时间,数据世界认为机器学习是解决方案。这就是为什么直到最近,Forrester 的报告都专注于评估机器学习数据目录。

然而,在 2022 年初, Forrester 在其Now Tech report中放弃了机器学习。它解释说,即使基于 ML 的系统变得无处不在,它们要解决的问题仍然存在。虽然机器学习允许数据架构师更清楚地了解他们组织内的数据,但它并没有完全解决围绕数据管理和配置的现代挑战。

关键的变化——仅仅通过数据维基“概念性的数据理解”已经不够了。相反,数据团队需要构建一个目录来支持数据运营。这需要深入了解和控制他们的数据,以“构建数据驱动的应用程序并解决数据流和性能问题”。

“在分布式云、边缘计算、智能应用、自动化和自助服务分析使用情形下,调配数据变得更加复杂……数据工程师需要一个数据目录,它不仅仅是生成关于数据和元数据的 wiki。”

—Forrester Now Tech:2022 年 Q1 数据运营企业数据目录

什么是 DataOps 的企业数据目录?

那么,什么是 DataOps (EDC)的企业数据目录呢?

Forrester 称,“[企业]数据目录创造了数据透明度,使数据工程师能够实施数据运营活动,开发、协调和编排数据策略和控制的配置,并管理数据和分析产品组合。”

有三个关键想法将 EDCs 与早期的机器学习数据目录区分开来。

处理现代数据和元数据的多样性和粒度

“我们的数据环境非常混乱,包括云原生功能、异常检测、同步和异步处理以及边缘计算。”

—Forrester Now Tech:2022 年 Q1 数据运营企业数据目录

如今,公司的数据不仅仅是由简单的表格和图表组成。它包括广泛的数据产品和相关资产,如数据库、管道、服务、策略、代码和模型。更糟糕的是,这些资产都有自己的元数据,而且越来越详细。

EDC 是为这种复杂的数据和元数据组合而构建的。EDC 不仅仅是存储这些数据的“wiki ”,而是充当“记录系统”,在数据产品生命周期内自动捕获和管理公司的所有数据。这包括同步上下文并支持跨数据工程师、数据科学家和应用程序开发人员的交付。

这一原则的应用示例

例如,我们与一个每天接收 1.2 TB 事件数据的数据团队合作。他们没有尝试手动管理这些数据和创建元数据,而是使用 API 来评估传入的数据并自动创建其元数据。

  • 自动分配所有者:他们扫描查询日志历史和定制元数据,以预测每个数据资产的最佳所有者。
  • 自动附加列描述:这些是由机器人通过扫描与该资产的交互而推荐的,并且由人来验证。
  • 自动分类:通过扫描资产的列以及相似资产的分类方式,他们可以根据 PII 和 GDPR 的限制对敏感资产进行分类。

提供数据流和交付的深度透明度

“data ops 采用 CI/CD 实践需要数据移动和转换的详细情报。”

—Forrester Wave:Q2 2022 年数据行动的企业数据目录

DataOps 的一个关键理念是 CI/CD ,这是一个通过持续集成和交付来提高协作、生产率和速度的软件工程原则。对于数据,实施 CI/CD 实践依赖于对数据如何在整个公司内移动和转换的准确理解。

EDC 通过列级沿袭、影响分析、根本原因分析和数据策略合规性等特性,提供粒度数据可见性和治理这些应该是程序化的,而不是手动的*,带有自动标记、警报和/或建议,以帮助用户掌握复杂、快速移动的数据流。*

这一原则的应用示例

例如,我们与一个处理数百个元数据更改事件的数据团队合作(例如,模式更改,如添加、删除和更新列;或者分类更改,如删除 PII 标签),每天会影响 100,000 多张表。

为了确保他们总是知道这些变化的下游影响,该公司使用 API 来自动跟踪和触发模式和分类变化的通知。这些元数据更改事件还会自动触发数据质量测试套件,以确保只有高质量的合规数据才能进入生产系统。

围绕现代数据操作和工程最佳实践而设计

“并非所有的数据目录都是为数据工程师设计的……[看看]除了复选框技术功能以外,还要使工具功能与您的数据操作模型的运行方式保持一致。”

—Forrester Now Tech:2022 年 Q1 数据运营企业数据目录

随着数据的增长超出了 IT 团队的范围,数据工程工具不再仅仅关注数据仓库和湖。DataOps 融合了来自数据和开发人员世界的最佳实践和经验,帮助不同的数据人员更好地合作。

EDC 是连接“数据和开发环境”的重要方式。双向沟通、协作和双向工作流等特性使得跨团队和跨职能的数据交付更加简单、快速。

这一原则的应用示例

例如,我们与一个数据团队合作,该团队使用这种想法来减少跨团队的意外并主动解决问题。他们使用 API 来监控管道的健康状况,当一个进入 BI 仪表板的管道发生故障时,就会发出警报。

如果发生这种情况,他们的系统首先创建一个全团队公告,例如“上游管道有一个活动问题,所以不要使用这个仪表板!”—在数据消费者使用的 BI 工具中自动发布。接下来,系统归档一个吉拉票证,标记给正确的所有者,以跟踪并启动关于此问题的工作。这个自动化的过程使数据团队不会对那个糟糕的松弛消息感到惊讶,“为什么这个仪表板上的数字看起来不对?”

活动元数据在数据操作的企业数据目录中的作用

“企业数据目录采用主动方法将控件和数据产品库转化为服务,用于将数据连接到应用程序的部署。”

**—Forrester Now Tech:2022 年 Q1 数据运营企业数据目录

虽然不是他们最初的 EDC 定义的一部分,但 Forrester 在评估不同目录时多次提到了“主动方法”和“主动元数据”。这是因为活动元数据是现代 EDCs 的关键部分。

与其他现代概念(如数据网格和数据结构)一样,DataOps 从根本上基于收集、存储和分析元数据的能力。然而,在一个元数据正在接近“大数据”并且其用例增长速度更快的世界中,存储元数据的标准方式已经不够了。

解决方案是“活动元数据”,它是 DataOps 的企业数据目录的关键组件。主动元数据使元数据的双向移动成为可能,而不仅仅是从数据栈的其余部分收集元数据并将其带回被动数据目录。它将丰富的元数据和统一的上下文发送回数据堆栈中的每个工具,并通过自动化实现强大的编程用例。

虽然元数据管理并不新鲜,但它在最近几年经历了令人难以置信的变化。我们正处于元数据领域的一个转折点,在这个时刻,我们正集体远离老派的数据目录,拥抱元数据的未来。

看到这种变化令人着迷,尤其是当它以 Forrester 的这种重大转变为标志时。考虑到它们在过去几个月里已经走了多远,我们迫不及待地想看看 EDCs 和活动元数据在未来几年将如何继续发展!

觉得这个内容有帮助?在我的时事通讯《元数据周刊》上,我每周都写关于活动元数据、数据操作、数据文化和我们的学习建设的文章。 在此订阅。

卷积神经网络中的前向和后向传播

原文:https://towardsdatascience.com/forward-and-backward-propagation-in-convolutional-neural-networks-64365925fdfa

理论与代码

https://unsplash.com/@fabioha

介绍

如果你在这里,你一定听说过卷积神经网络,也许你现在正试图了解它们是如何工作的。在这种情况下,本文将通过使用一些可视化的例子来帮助您了解 CNN 中的向前和向后传球是如何执行的。
我假设你熟悉 CNN 中的填充和步长,并且对反向传播和链式法则有一些基本的了解。

正向传播

我们从形状为 5x5一个输入和形状为 3x3一个滤波器开始。我们假设通道数 C = 1stride = 1

按作者分类的图像—输入和过滤器权重(图 1)

我们用 pad = 1 进行填充,使卷积运算后的输出和输入的形状相同。

按作者分类的图像—填充输入(图 2)

实际上,我们可以使用下面的公式来计算输出形状:
H _ out = floor(1+(H+2 * pad-HH)/stride)
W _ out = floor(1+(W+2 * pad-WW)/stride)

其中 H 是输入的高度, HH 是滤波器的高度
W 是输入的宽度

在我们的例子中我们得到:
H _ out= floor(1+(5+2 * 1–3)/1)= 5
W _ out= floor(1+(5+2 * 1–3)/1)= 5

卷积运算中的前向传递由输入上的重叠滤波器权重组成,将它们相乘并将结果相加以获得输出。下面的动画显示了对 y ₁₁、y₁₂t44】和 y ₅₅的计算(类似地,其他输出yᵢ也被计算)**

作者提供的图像—正向卷积运算(图 3)

在代码中:

反向传播

我们需要计算输出 Y 相对于输入 X、滤波器 W 和偏置 b 的导数,计算相对于偏置 b 的导数很容易,我建议您在阅读完本教程后亲自尝试一下,您一定能够做到!现在让我们最后从y/x开始:

让我们先看看输出 y 对 x 的第一个元素的导数是什么,也就是说,我们要计算 ∂Y/ ∂x₁₁ 。我们可以注意到 x₁₁影响了 y ₁₁ ,y ₁₂ ,y₂₁y ₂₂,因为它参与了这些输出单元格的计算。于是我们得到
y/x₁₁=y₁₁/x₁₁+y/x【t34* ∂x₁₁= w₂₂+w₂₁+w₁₂+w₁₁
以类似的方式我们可以计算 x 的元素的其他偏导数
*

还要注意,∂X 的形状与未填充的 X 的形状相同,所以我们不需要计算关于填充元素的导数

**

来自作者的图像-输入反向传播(图 4)

还有更多的内容。当进行反向传播时,当我们按照链式法则进行反向传播时,我们通常有来自下一层的输入梯度。在这种情况下,我们假设卷积层之后是,我们将得到 y 相对于损耗 l,∂L/∂Y.的入射梯度,它与 y 的形状相同,这是我们唯一需要知道的,因为我们不会在本文中计算这一导数。

作者图片— ∂L/∂Y 衍生矩阵(图 5)

因此,当计算∂Y/∂x11 时,我们还需要将∂Y/∂x 乘以相应的引入导数∂l/∂y:
(1)∂
y/x₁₁=l/yt73】yx₁₁+l/y₂₁y₂₁/x+l/* w₂₁+dy₂₁** w₁₂+dy₂₂** w₁₁*

如果我们在通过所有宽度( W1 )和高度( H1 )的第一次循环后打印出 w_idxsy_idxs ,我们将得到 w 和∂L/∂Y 矩阵的相应单元格,以计算∂ Y/x ₁₁:

*w_idxs = [(1, 1), (1, 0), (0, 1), (0, 0)]
y_idxs = [(0, 0), (0, 1), (1, 0), (1, 1)]*

图 4* 中,我们从 1 开始索引,因此如果我们将 1 加到 w_idxsy_idxs 中,我们会看到,在最后一行中,为了获得关于 x,dx₁₁的第一个元素的导数,我们将与上面公式中完全相同的元素相乘。*

为了计算 ∂Y/ ∂W ,我们以类似的方式进行(注意,为了避免图像过多,我们没有显示所有的操作):

**

作者图片—权重反向传播(图 6)

查看 dY/dw ₁₁ 我们可以看到w₁₁t29】的变化会影响所有 yᵢ 的每一个 i,j 。现在,我们需要考虑 X_padded,而不是从 1 到 5 进行索引,X_padded 将从 0 到 6 进行索引。**

(2)∂y/w₁₁=l/y₁₁y₁₁/w+ly₄₅y₄₅/w₁₁+l/yt89】∂*y**

正如我们可以看到的,因为每一个权重影响每一个输出所有元素传入的导数∂ L/Y 被用来计算∂y/wᵢⱼ

如果我们查看被求和以计算 dw[0,0] (忽略滤波器和通道):
给定 padded_x :

作者图片—填充输入具体示例(图 7)

dout 导数:

图片由作者—∂L/∂Y 具体举例(图 8)

我们将从 padded_xx₀₀x₄₄ 中选择的元素相乘

按作者分类的图像—选定的填充输入(图 9)

的所有元素对进行 dout,然后像(2)中那样对所有元素求和:

作者提供的图片—渐变结果 W 矩阵的第一个元素(图 10)

请注意,我们已经看到了假设 1 个通道和 1 个滤波器的向前和向后传递,但是代码能够处理多个通道和滤波器,并且到目前为止您已经阅读的解释很容易推广。

结论

你现在应该很好的理解了在 CNN 中向后和向前传球是如何完成的。显然,你不需要手动实现它们——在上面的代码片段中,我们使用的循环很容易理解,但是非常慢而且效率很低!不过不要担心,有很多包可以使用非常高效的实现为您完成所有工作。

参考

http://cs231n.stanford.edu/

卷积神经网络中最大池层数的前向和后向传播

原文:https://towardsdatascience.com/forward-and-backward-propagation-of-pooling-layers-in-convolutional-neural-networks-11e36d169bec

理论与代码

介绍

在上一篇文章中,我们看到了如何在 CNN 中进行卷积运算的前向和后向传播。发现在卷积层之后应用汇集层提高了性能,有助于网络更好地概括并减少过拟合。这是因为,给定一个特定的网格(池高 x 池宽),我们只从中采样一个值,忽略特定的元素并抑制噪声。此外,因为池化减少了来自前一层的特征图的空间维度,并且它不添加任何要学习的参数,所以它有助于降低模型复杂性、计算成本并导致更快的训练。

正向传播

我们假设在卷积运算之后,我们得到形状为 4x4 的输出。然后我们要做最大汇集,使汇集高度汇集宽度步幅都等于 2。池化类似于卷积,但不是在权重和输入中的区域之间进行元素级乘法,然后对它们求和以获得输出矩阵中某个单元的元素,而是简单地从该区域中选择最大元素。以下可视化将阐明:

按作者分类的图像-正向传播

池化操作后的输出形状使用以下公式获得:
H _ out = floor(1+(H-pool _ height)/stride)
W _ out = floor(1+(W-pool _ width)/stride)

其中 H 是输入的高度, pool_height 是池化区域的高度
W 是输入的宽度,

在我们的例子中我们得到:
H _ out= floor(1+(4–2)/2)= 2
W _ out= floor(1+(4–2)/2)= 2

这是它在代码中的实现方式:

反向传播

与卷积运算不同,我们不必在这里计算权重和偏差导数,因为合并运算中没有参数。因此,我们需要计算的唯一导数是关于输入的,**∂y/我们知道,关于输入的导数将具有与输入相同的形状。再来看第一个元素y/x—y/x₁₁.**

按作者分类的图像-汇集第一个元素

*很明显,只有当 x ₁₁是关于第一区域的第一汇集操作中的最大元素时,∂y/x₁₁=∂y₁₁/∂x₁₁的导数才不为零。假设第一区域中的最大元素是 x ₁₂,∂y₁₁/∂x₁₂=∂x₁₁/∂x₁₂= 1,并且在第一池区域中相对于其他 x ⱼ的导数为零。同样,因为我们有一个来自下一层的引入导数,我们需要按照链式法则将局部梯度乘以引入梯度。因此,假设 dy₁₁to 是引入的导数,对于第一个区域,除了∂x₁₁/∂x₁₂= 1 dy₁₁= dy₁₁.,所有的梯度都为零

按作者分类的图像—反向传播

在代码中:

因此,假设以下输入和输入导数:

按作者分类的图像—输入

按作者分类的图片—即将推出的衍生产品

输出和相对于输入的梯度将是:

按作者分类的图像-输出

按作者分类的图像—输入渐变

结论

如果您阅读了我的前一篇关于卷积运算的前向和后向传播的文章,我相信这篇文章对您来说是小菜一碟!
池化操作对于更好的模型泛化、降低复杂度和提高训练速度非常重要。然而,有人声称这在某些情况下可能是不好的,因为通过下采样特征,我们可能会丢失一些重要的信息,这些信息对于正确地分类对象是至关重要的。然而,在构建 CNN 模型时,池层的优势足够大。

参考

http://cs231n.stanford.edu/】

基础 RL:动态编程

原文:https://towardsdatascience.com/foundational-rl-dynamic-programming-28f96f6fb40e

强化学习之路

作者使用人工智能工具 Midjourney 生成的封面照片(许可为知识共享非商业性 4.0 资产许可证)

通过前面两篇文章:(1) 马尔可夫状态、马尔可夫链和马尔可夫决策过程,( 2)求解马尔可夫决策过程,我为开发强化学习(RL)的详细概念奠定了基础。RL 问题被公式化为马尔可夫决策过程(MDP ),其可以被求解为最优策略(即,代理需要采取什么行动来实现目标)。解决方案可以通过扫描整个状态空间来实现,也可以通过其他一些技术来实现,这些技术认为解决方案可以通过解决较小的问题来递归实现。后者是动态编程的话题。

动态规划

动态编程是一种通过将复杂问题分解成更小、更简单的子问题来解决它们的方法。这是一种自下而上的方法,首先解决较小的子问题,然后向上组合解决方案来解决较大的子问题。

为了使用动态编程,一个问题必须有两个具体特征:

  1. 最优子结构:将最优解和更小的子问题组合起来,可以找到整体问题的最优解。
  2. 重叠子问题:较大问题中的较小子问题相互重叠,这意味着它们共享一些共同元素。这使得我们可以重用已经解决的子问题的解决方案,而不必多次解决它们。

通过使用这两个特征,我们可以有效地解决问题,并使用动态规划找到最有效的解决方案。

我们可以在自动驾驶纵向控制的背景下理解这一点,目标是最大限度地降低油耗。考虑车辆的状态由 (v,a),即速度和加速度组成。我们可以将可能的动作定义为加速、减速或保持恒定速度。 f(v,a) 可以将油耗作为速度和加速度的函数。这是一个非常合理的油耗函数(见【4】)。

我们可以使用动态规划来寻找在给定时间范围内最小化总燃料消耗的最佳行动顺序。

我们可以定义成本函数 C(t,s) ,其表示在时间 t 达到状态 s 的最小燃料消耗,并使用以下递归:

等式 1。价值函数

其中 ss 的前身。

我们可以通过跟踪在每个时间步采取的行动来获得最佳的行动顺序,以达到当时的最低成本。例如,在时间 t 处,车辆处于状态 s = (v,a) 并且达到该状态的最小成本是 C(t,(v,a)) 。如果通过在时间 t-1 采取“加速”动作达到了最小成本,那么最佳动作序列包括在时间 t-1 的“加速”。

上例的 pythonic 化伪代码可以在https://gist . github . com/rahulbhadani/a 119916d 26 e 251 b 939 b 817 DDE 03 ad 032找到。

近似动态规划

近似动态规划(ADP)用于解决有时很大很复杂的问题,并且通常(但不总是)是随机的。

ADP 用于克服通常在贝尔曼方程中观察到的维数灾难。

ADP 是关于学习什么和如何学习,以便随着时间的推移做出更好的决定。

在 ADP 中,真值函数 Vπ(s) 被其统计近似值 (s)代替。此外,ADP 在时间上向前迈进,但有一些变化。近似函数 (s) 可以通过多种方式确定,例如:

  1. 查找表格
  2. 参数模型
  3. 非参数模型

或者,蒙特卡罗方法可用于近似期望值。

许多方法如离散模型、神经网络和核回归可以是上述三种策略的组合。我们需要一个单独的讨论线程来详细讨论价值函数的近似值。

读者应该注意,对于 ADP,策略可能不是最优的,我们需要与次优策略妥协。

结论

在本文中,我以自动驾驶中的纵向控制为例讨论了动态编程的细节。动态规划是解决处于强化学习问题核心的 MDP 的有效方法。在本系列的下一篇文章中,我将讨论价值迭代和策略迭代。即将到来的未来文章将致力于用于近似价值函数的策略。

如果您尚未阅读基础 RL 系列的前两篇文章,请务必阅读:

  1. https://towards data science . com/fundamental-rl-Markov-States-Markov-chain-and-Markov-decision-process-be 8 CCC 341005
  2. https://towards data science . com/fundamental-rl-solving-Markov-decision-process-d 90 b 7 e 134 c 0b

你喜欢这篇文章吗?给我买杯咖啡

喜欢我的作品吗?加入我的邮件列表

想了解更多 STEM 相关话题?加入介质

参考

  1. 深度强化学习,莫希特·塞沃克,https://doi.org/10.1007/978-981-13-8285-7
  2. 深度强化学习,Aske Plaat,https://doi.org/10.1007/978-981-19-0638-1,施普林格新加坡
  3. 强化学习和随机优化:连续决策的统一框架。),威利(2022)。精装本。ISBN 9781119815051。
  4. Lee,J.W .,Gunter,g .,Ramadan,r .,Almatrudi,s .,Arnold,p .,Aquino,j .,Barbour,w .,Bhadani,r .,Carpio,j .,Chou,F.C .和 Gibson,m .,2021 年 5 月。飞行器动力学、不稳定性、能量模型和稀疏流平滑控制器的综合框架。在数据驱动和智能信息物理系统研讨会论文集(第 41–47 页)。https://doi.org/10.1145/3459609.3460530
  5. 《你应该知道的近似动态规划》海军研究后勤(NRL) 56,第 3 期(2009 年):239-249 页。https://doi.org/10.1002/nav.20347
  6. 布索尼乌、卢西恩、巴特·德舒特和罗伯特·巴布斯卡。"近似动态规划和强化学习."在交互式协作信息系统中,第 3–44 页。施普林格,柏林,海德堡,2010。https://www.dcsc.tudelft.nl/~bdeschutter/pub/rep/10_028.pdf

基础 RL:马尔可夫状态、马尔可夫链和马尔可夫决策过程

原文:https://towardsdatascience.com/foundational-rl-markov-states-markov-chain-and-markov-decision-process-be8ccc341005

强化学习之路

作者使用人工智能工具 Dreamstudio 生成的封面照片(授权为https://creativecommons.org/publicdomain/zero/1.0/

R 强化学习(RL)是一种机器学习,在这种机器学习中,代理通过试错来学习与其环境进行交互,以使回报最大化。它不同于监督学习,在监督学习中,代理根据标记的示例进行训练,也不同于无监督学习,在无监督学习中,代理学习识别未标记数据中的模式。在强化学习中,代理人学习在一个环境中采取行动以获得最大的回报,如赢得分数或赢得游戏。

强化学习对于广泛的应用是有用的,包括机器人、自然语言处理和游戏。

在本文中,我构建了一些基本概念来理解强化学习。在的下一篇文章中,我会谈到马尔科夫决策过程的解决方案。

代理人、行动、奖励和目标

在 RL 中,我们有一个代理,我们使用某种算法训练它采取某些行动,使回报最大化,以达到最终目标。最终目标可能是非常遥远的未来,或者不断变化(就像自主导航一样)。

马尔可夫状态

在强化学习中,状态是指主体所处的当前情境或环境。它表示代理在给定时间点拥有的关于其环境的信息。例如,自主车辆的位置和速度可以是 RL 问题中的状态。代理使用状态信息来决定下一个时间步采取什么行动来最大化奖励。

在 RL 中,我们关心马尔可夫状态,其中状态具有所有未来状态仅依赖于当前状态的性质。这意味着主体不需要记住它与环境交互的全部历史来做决定。相反,它可以简单地关注当前状态并据此采取行动。这使得学习过程更加有效,因为代理不必存储和处理大量信息。此外,它使代理的行为更可预测,因为它完全由当前状态决定。这在许多应用中都很有用,比如机器人和控制系统。

我们可以将车辆的马尔可夫状态编码如下:

# define the states of the vehicle
STOPPED = 0
MOVING_FORWARD = 1
MOVING_BACKWARD = 2

# define the actions of the vehicle
STOP = 0
MOVE_FORWARD = 1
MOVE_BACKWARD = 2

# define the Markov state of the vehicle
class VehicleMarkovState:
  def __init__(self, state, action):
    self.state = state
    self.action = action

# define a function to encode the Markov state of the vehicle
def encode_markov_state(vehicle_state, vehicle_action):
  return VehicleMarkovState(vehicle_state, vehicle_action)

# example: encode the Markov state of a vehicle that is moving forward
markov_state = encode_markov_state(MOVING_FORWARD, MOVE_FORWARD)
print(markov_state.state)  # prints 1 (MOVING_FORWARD)
print(markov_state.action)  # prints 1 (MOVE_FORWARD)

马尔可夫链

马尔可夫链是一个有限状态机,其中每个状态都是一个马尔可夫状态。马尔可夫链由许多状态组成,具有从一个状态到另一个状态的转移概率。在马尔可夫链中,转移到特定状态的概率取决于当前状态和经过的时间,而不用担心过去发生了什么。

马尔可夫链不同于随机过程,因为在随机过程中,现在发生的事情取决于过去发生的事情,而不仅仅是最近发生的事情。

让我们考虑一个例子:

图一。一个马尔可夫链,图片由作者提供

我们有两个马尔可夫状态 A,和 B,从 A 到 B 的转移概率是 0.7,从 B 到 A 的转移概率是 0.9,从 B 到 B 的转移概率是 0.1,从 A 到 A 的转移概率是 0.3。这个想法如图 1 所示。我们可以用 Python 对此进行编码,如下所示:

# define the states of the Markov chain
A = 0
B = 1

# define the transition probabilities
transition_probs = [[0.3, 0.7],  # transition probabilities from A
                    [0.9, 0.1]]  # transition probabilities from B

# define a class to represent the Markov chain
class MarkovChain:
  def __init__(self, states, transition_probs):
    self.states = states
    self.transition_probs = transition_probs

# define a function to encode the Markov chain
def encode_markov_chain(markov_states, markov_transition_probs):
  return MarkovChain(markov_states, markov_transition_probs)

# example: encode the Markov chain
markov_chain = encode_markov_chain([A, B], transition_probs)
print(markov_chain.states)  # prints [0, 1]
print(markov_chain.transition_probs)  # prints [[0.3, 0.7], [0.9, 0.1]]

马尔可夫决策过程

马尔可夫决策过程或 MDP 是马尔可夫链的延伸。在 MDP,根据某种动作,状态从一个马尔可夫状态转换到另一个马尔可夫状态。这种转变会带来相应的回报。MDP 是一个 4 元组模型(𝓢、𝓐、𝓟、𝓡),其中 s ∈ 𝓢是一个状态, a ∈ 𝓐是当代理是一个状态 s 时采取的一个动作,𝓟 (s' | s,a) 是在动作 a (或一些动作)的影响下从 s 转移到状态s’的转移概率矩阵

策略函数:在 RL 文献中通常用π表示的策略函数规定了从状态空间𝓢到动作空间𝓐.的映射

MDP 可以用来模拟自动驾驶汽车的决策过程。在这种情况下,MDP 的状态可能表示汽车和环境中其他对象(如其他汽车和障碍物)的不同位置和速度。MDP 的动作可能代表自动驾驶汽车可以采取的不同动作,例如加速、刹车或转弯。MDP 的奖励可能代表不同行为的价值或效用,例如避免碰撞或快速到达目的地。使用 MDP,自动驾驶汽车可以学习采取最大化其回报的行动,例如避免碰撞和快速到达目的地。

本文为开始强化学习提供了第一手基础。在下一篇文章中,我将讨论更多的概念,比如价值函数、动态规划、如何求解马尔可夫决策过程以及部分可观测性 MDP。

第二部分:https://towards data science . com/fundamental-rl-solving-Markov-decision-process-d 90 b 7 e 134 c0b

参考

  1. 罗纳德·霍华德(1960)。 动态规划和马尔可夫过程 (PDF)。麻省理工学院出版社。
  2. 强化学习和随机优化:连续决策的统一框架。),威利(2022)。精装本。ISBN 9781119815051。

你喜欢这篇文章吗?给我买杯咖啡。

喜欢我的作品吗?加入我的邮件列表

想了解更多 STEM 相关话题?加入介质

基础 RL:求解马尔可夫决策过程

原文:https://towardsdatascience.com/foundational-rl-solving-markov-decision-process-d90b7e134c0b

强化学习之路

作者使用人工智能工具 Dreamstudio 生成的封面照片(授权为https://creativecommons.org/publicdomain/zero/1.0/

的第一部分中,我讨论了一些基本概念来为强化学习(RL)建立基础,如马尔可夫状态、马尔可夫链和马尔可夫决策过程(MDP)。强化学习问题是建立在 MDP 之上的。

MDP 是一个 4 元组模型(𝓢、𝓐、𝓟、𝓡),其中 s ∈ 𝓢是一个状态, a ∈ 𝓐是当代理是一个状态 s 时采取的一个动作,𝓟(s“| s,a) 是在动作 a 的影响下从 s 转移到状态s’的转移概率矩阵(或者

策略函数:策略函数,在 RL 文献中通常用π表示,规定了从状态空间𝓢到动作空间𝓐.的映射

MDP 的目标是找到一个最大化长期回报的最优策略。

贴现因素

MDP 需要一个离散时间的概念,因此 MDP 被定义为一个离散时间随机控制过程。在 RL 的上下文中,每个 MDP 由折扣因子γ (gamma)来参数化,该折扣因子γ确定未来奖励相对于当前奖励的重要性。换句话说,这是一个衡量未来的回报相对于现在的回报对代理人有多大价值的指标。折扣因子是一个介于 0 和 1 之间的值,其中值 0 意味着代理人只关心眼前的奖励,并将完全忽略任何未来的奖励,而值 1 意味着代理人将把未来的奖励视为与现在获得的奖励同等重要。

例如,在代理人在两个行动 A 和 B 之间做出决定的情况下,这两个行动将导致不同的未来奖励序列,折扣因子可以用于确定这些不同奖励序列的相对值。如果折扣因子低,那么代理人将更有可能选择导致即时奖励的行动,而如果折扣因子高,代理人将更有可能选择导致更大金额的未来奖励的行动。

一般来说,折扣因子是 MDP 中的一个重要考虑因素,因为它允许代理人用长期奖励来换取短期奖励,并平衡即时满足的需求和延迟满足的潜在好处。

因此,在 RL 环境中的 MDP 是(𝓢,𝓐,𝓟,𝓡,γ)。

奖励公式

考虑到贴现因子γ,在时间 t 的回报可以写成

等式 1。时间步长 t 的奖励。

总奖励,即所有时间的累积奖励是

等式 2。累积奖励

最大化累积回报的π策略是我们的 MDP 问题的解决方案。

我们来看一个例子(自动驾驶代理):

在自动驾驶的情况下,可以使用马尔可夫决策过程来对车辆的决策过程进行建模,以便在长期内最小化燃料消耗。在这种情况下,系统的状态可以由车辆的当前速度和加速度来表示,目标是找到将最小化燃料消耗的最佳动作序列。

车辆的瞬时燃料消耗可以建模为函数 g(v,a) ,其中 v 是当前速度,而 a 是当前加速度。该函数可用于评估给定状态下每个可能行动的成本,马尔可夫决策过程可用于寻找将最小化车辆长期燃料消耗的最佳行动序列。

例如,在当前速度为 v 且当前加速度为a的给定状态下,MDP 可以考虑一系列可能的动作,例如加速、减速或保持当前速度。可以使用函数 g(v,a) 来评估每项措施的成本,并根据哪项措施将导致长期最低的燃油消耗来选择最佳措施。然后,可以在每个后续状态下重复该过程,以找到最大限度降低燃油消耗的最佳行动顺序。

需要强调的重要一点是,在上面的例子中,我没有考虑转移概率𝓟,这是为了让我们的例子更容易理解,也更符合实际。

一些 python 代码:

考虑一个 MDP,其中状态为速度和加速度,速度的最小值为 0 米/秒,最大值为 50 米/秒,加速度的最小值为-4.5 米/秒,最大值为 3.0 米/秒,它们中的每一个都以 0.1 量化。我们可以考虑以下构造函数中的 MDP 类:

def __init__(self, velocity_min, velocity_max, acceleration_min, acceleration_max, velocity_step, acceleration_step, acceleration_min_accelerate, acceleration_max_accelerate):
        # Define minimum and maximum values for velocity and acceleration
        self.VELOCITY_MIN = velocity_min
        self.VELOCITY_MAX = velocity_max
        self.ACCELERATION_MIN = acceleration_min
        self.ACCELERATION_MAX = acceleration_max

        # Define quantization step for velocity and acceleration
        self.VELOCITY_STEP = velocity_step
        self.ACCELERATION_STEP = acceleration_step

        # Define minimum and maximum values for acceleration when accelerating or decelerating
        self.ACCELERATION_MIN_ACCELERATE = acceleration_min_accelerate
        self.ACCELERATION_MAX_ACCELERATE = acceleration_max_accelerate

        # Calculate number of possible values for velocity and acceleration
        self.num_velocity_values = int((self.VELOCITY_MAX - self.VELOCITY_MIN) / self.VELOCITY_STEP) + 1
        self.num_acceleration_values = int((self.ACCELERATION_MAX - self.ACCELERATION_MIN) / self.ACCELERATION_STEP) + 1

        # Create list of possible values for velocity and acceleration
        self.velocity_values = [self.VELOCITY_MIN + i * self.VELOCITY_STEP for i in range(self.num_velocity_values)]
        self.acceleration_values = [self.ACCELERATION_MIN + i * self.ACCELERATION_STEP for i in range(self.num_acceleration_values)]

期望的动作可以是加速、减速或保持车辆的恒定速度。

 # Function to calculate available actions in a given state
    def calculate_actions(self, v, a):
        # Initialize list of available actions
        actions = []

        # If current velocity is less than maximum, add option to accelerate
        if v < self.VELOCITY_MAX:
            for a_new in self.acceleration_values:
                if self.ACCELERATION_MIN_ACCELERATE <= a_new <= self.ACCELERATION_MAX_ACCELERATE:
                    actions.append((v, a_new))

        # If current velocity is greater than minimum, add option to decelerate
        if v > self.VELOCITY_MIN:
            for a_new in self.acceleration_values:
                if -self.ACCELERATION_MAX_ACCELERATE <= a_new <= -self.ACCELERATION_MIN_ACCELERATE:
                    actions.append((v, a_new))

        # Add option to maintain current velocity and acceleration
        actions.append((v, a))

         return actions

接下来,我们可以定义一个函数来计算预期油耗:

# Function to evaluate the expected fuel consumption for a given state and action
    def evaluate_fuel_consumption(self, v, a, v_new, a_new):
        # Calculate expected fuel consumption for current state and action
        fuel_current = self.fuel_consumption(v, a)
        fuel_new = self.fuel_consumption(v_new, a_new)
        return fuel_current + fuel_new

计算最优策略的一种简单方法是扫描整个状态空间:

# Function to find the optimal action in a given state, based on minimizing expected fuel consumption
    def find_optimal_action(self, v, a):
        # Calculate available actions in current state
        actions = self.calculate_actions(v, a)

        # Initialize minimum expected fuel consumption
        min_fuel = float("inf")

        # Initialize optimal action
        optimal_action = None

        # Iterate over available actions and find action with minimum expected fuel consumption
        for v_new, a_new in actions:
            fuel = self.evaluate_fuel_consumption(v, a, v_new, a_new)
            if fuel < min_fuel:
                min_fuel = fuel
                optimal_action = (v_new, a_new)

        return optimal_action

    # Function to calculate the optimal policy for the MDP
    def calculate_optimal_policy(self):
        # Initialize dictionary to store optimal policy
        optimal_policy = {}

        # Iterate over all possible states and calculate optimal action for each state
        for v in self.velocity_values:
            for a in self.acceleration_values:
                optimal_policy[(v, a)] = self.find_optimal_action(v, a)

        return optimal_policy

以上代码片段的测试实现如下:

# Create MDP instance
mdp = MDP(VELOCITY_MIN, VELOCITY_MAX, ACCELERATION_MIN, ACCELERATION_MAX, VELOCITY_STEP, ACCELERATION_STEP, ACCELERATION_MIN_ACCELERATE, ACCELERATION_MAX_ACCELERATE)

# Calculate optimal policy for the MDP
optimal_policy = mdp.calculate_optimal_policy()

# Print optimal policy for the first few states
for i in range(10):
    for j in range(10):
        print(optimal_policy[(mdp.velocity_values[i], mdp.acceleration_values[j])])

上面例子的完整代码可以从https://gist . github . com/rahulbhadani/92d 3 be 52529 a 64372 c 796 ca 5 e 7 CB 3770下载。

现在,我们可能会问一个问题:上述实现是否高效?

我们清楚地看到,上面的实现扫描了整个状态空间,效率不是很高。为了提高这种实现的效率,我们可以使用动态编程来存储每个状态的最佳动作,然后使用存储的值来计算整个 MDP 的最佳策略,而不需要迭代所有可能的状态。通过利用任何给定状态的最优策略仅取决于从当前状态可以到达的状态的最优策略的事实,这将允许更有效地计算最优策略。

另一种选择是使用函数逼近来逼近最优策略,这比显式计算所有可能状态的最优策略更有效。这可以通过在状态空间的代表性样本上训练诸如神经网络(深度 RL)的模型,然后使用训练的模型来预测任何给定状态的最佳动作来完成。

在这一点上,我们应该可以轻松地讨论贝尔曼方程、动态规划和 Q 函数。

贝尔曼方程在控制理论或控制工程中也称为汉密尔顿-雅可比方程。

贝尔曼方程:价值函数和 Q 函数

在上述定义的 MDP 中,目标是长期最小化燃料消耗,贝尔曼方程在确定最优策略中起着重要作用。贝尔曼方程提供了状态值和从当前状态可达到的状态值之间的递归关系,可用于通过找到导致具有最高值的状态的动作来确定最佳策略。

这种递归使用迭代计算机科学算法来解决,如动态规划和线性规划。它们的变化导致了多种 RL 训练算法。

状态值和状态-动作值的贝尔曼方程分别称为值函数和 Q 函数。

价值函数

在 MDP,状态的值被定义为从当前状态开始的长期的预期燃料消耗。我们称之为价值函数。

数学上,价值函数可以写成

等式 3。价值函数。

其中 P( s(t),a(t) ) 在动作 a(t) 的影响下,从状态 s(t)s(t +1 ) 的转移概率。等式 3 的定义是从 1 得到的值函数的修改形式。

价值函数可用于通过将当前状态下的预期燃料消耗与下一状态下的预期燃料消耗相加来计算状态的价值,其中下一状态下的预期燃料消耗通过在当前状态下可采取的所有可能行动中取最大值来计算。这个过程可以递归地重复,以计算每个状态的值,从初始状态开始,向后工作到最终状态。

一旦使用贝尔曼方程计算了所有状态的值,就可以通过找到导致每个状态具有最高值的状态的动作来确定最优策略。然后,该最佳策略可用于确定在每个状态下采取的最佳行动,以便在长期内将燃料消耗降至最低。

q 函数

在上面定义的 MDP 的情况下,目标是在长期内最小化燃料消耗,Q 函数是将每个状态和动作对映射到实数的函数,表示从该状态开始并采取该动作的长期内的预期燃料消耗。

数学上,Q 函数可以写成

等式 4。q 函数

这是在[ 1 中使用的 Q 函数的修改形式。

可以使用贝尔曼方程来计算 Q 函数,该方程提供了状态值和从当前状态可以达到的状态值之间的递归关系。可以通过将当前状态下的预期燃料消耗与下一状态下的预期燃料消耗相加来计算 Q 函数,其中下一状态下的预期燃料消耗是通过采取当前状态下可以采取的最大总可能行动来计算的。

一旦使用贝尔曼方程计算出所有状态-动作对的值,就可以通过找到使每个状态的 Q 函数最大化的动作来确定最佳策略。然后,该最佳策略可用于确定在每个状态下采取的最佳行动,以便在长期内将燃料消耗降至最低。

让我们看看他们如何寻找本文中的例子。

 class MDP:
    # ...

    # Function to calculate the value function for the MDP
    def calculate_value_function(self):
        # Initialize dictionary to store values of each state
        values = {}

        # Iterate over all possible states and calculate value of each state
        for v in self.velocity_values:
            for a in self.acceleration_values:
                values[(v, a)] = self.evaluate_value(v, a, values)

        return values

    # Function to evaluate the value of a state using the Bellman equation
    def evaluate_value(self, v, a, values):
        # Check if value of current state has already been calculated
        if (v, a) in values:
            return values[(v, a)]

        # Calculate available actions in current state
        actions = self.calculate_actions(v, a)

        # Initialize maximum expected fuel consumption
        max_fuel = float("-inf")

        # Iterate over available actions and find action with maximum expected fuel consumption
        for v_new, a_new in actions:
            fuel = self.evaluate_fuel_consumption(v, a, v_new, a_new)
            if fuel > max_fuel:
                max_fuel = fuel

        # Return maximum expected fuel consumption
        return max_fuel
class MDP:
    # ...

    # Function to calculate the Q-function for the MDP
    def calculate_q_function(self):
        # Initialize dictionary to store values of each state-action pair
        q_values = {}

        # Iterate over all possible states and actions
        for v in self.velocity_values:
            for a in self.acceleration_values:
                for v_new, a_new in self.calculate_actions(v, a):
                    q_values[((v, a), (v_new, a_new))] = self.evaluate_q_value(v, a, v_new, a_new, q_values)

        return q_values

    # Function to evaluate the Q-value of a state-action pair using the Bellman equation
    def evaluate_q_value(self, v, a, v_new, a_new, q_values):
        # Check if Q-value of current state-action pair has already been calculated
        if ((v, a), (v_new, a_new)) in q_values:
            return q_values[((v, a), (v_new, a_new))]

        # Calculate expected fuel consumption in current state
        fuel = self.evaluate_fuel_consumption(v, a, v_new, a_new)

        # Calculate expected fuel consumption in next state by taking maximum over all possible actions
        max_fuel = float("-inf")
        for v_next, a_next in self.calculate_actions(v_new, a_new):
            fuel_next = self.evaluate_q_value(v_new, a_new, v_next, a_next, q_values)
            if fuel_next > max_fuel:
                max_fuel = fuel_next

        # Return expected fuel consumption in current state plus expected fuel consumption in next state
        return fuel + max_fuel

当然,该示例忽略了转移概率的概念,因此用于优化燃料消耗的示例的值函数和 Q 函数比用于一些现实世界的实际问题要简单得多。

在本文中,我讨论了通过求解 MDP 来获得最优策略、关联值函数和 Q 函数,这些函数可用于以最优方式求解 MDP。

在下一篇文章中,我将在强化学习问题的背景下讨论动态编程。在以后的文章中,我将讨论深层的 RL 概念以及在这种场景中引入神经网络,并给出一些仿真示例。加入我的电子邮件列表,让我未来的文章直接发送到你的收件箱。

如果你还没有看完基金会 RL 系列的第一篇文章,请一定要去看:https://towards data science . com/fundamental-RL-Markov-States-Markov-chain-and-Markov-decision-process-be 8 CCC 341005

你喜欢这篇文章吗? 给我买杯咖啡

爱我的文字?加入我的 邮箱列表

想了解更多 STEM 相关话题?加入 中等

参考

  1. 深度强化学习,Aske Plaat,https://doi.org/10.1007/978-981-19-0638-1,施普林格
  2. 强化学习和随机优化:连续决策的统一框架。),威利(2022)。精装本。ISBN 9781119815051。
  3. 罗纳德·霍华德(1960)。 动态规划与马尔可夫过程 (PDF)。麻省理工学院出版社。

探索性数据分析中要避免的四个常见陷阱

原文:https://towardsdatascience.com/four-common-pitfalls-to-avoid-in-exploratory-data-analysis-85d822dd5e34

用数据讲述可操作的故事

凯尔·格伦在 Unsplash 上的照片

介绍

在本文中,我将分享数据探索中的四个常见错误以及如何避免它们。

探索性数据分析(EDA)是使用图形表示和汇总统计来发现数据中的趋势和模式。EDA 的流行图形包括散点图条形图直方图圆环图热图。此外,作为描述数据集的措施的汇总统计包括计数、平均值、中值、标准差和偏斜度

作者图片

EDA 是数据科学项目生命周期中的关键步骤之一,有助于在机器学习建模之前更好地理解数据。它还可以通过可操作的见解产生一些快速的胜利,为企业创造价值。

作者图片

EDA 的主要目标是识别数据中的错误,更好地理解数据,检测异常值,并揭示变量之间的关系。

探索性数据分析中的陷阱

为了实现 EDA 的既定目标,数据从业者在数据探索过程中必须避免以下陷阱:

  1. 不明确的业务问题

每个数据科学项目的核心都是需要解决的业务问题。如何规划季节性销售?应该向不同的客户提供什么类型的促销?这些是可能会提出的一些问题。然而,数据从业者不一定精通业务,经理也不一定是数据专家,这会导致不良的业务问题。此外,现有数据可能不足以回答相关问题。

避免这个陷阱的技巧:

  • 尽早从利益相关者那里获得反馈。
  • 尽快明确需求。
  • 将 EDA 视为一个迭代过程,需要频繁地循环返回给涉众。

2。浅薄的见解

快速发现的愿望并不完全错误。然而,你认为伟大的洞察力可能只是对利益相关者“陈述显而易见的事情”。问题“那又怎样?”非常常见,数据从业者必须在分析期间为此做好准备。想象一下,告诉销售团队,客户 A 是今年花钱最多的人。是的,这是真的,但他们可能已经知道这一点。

避免这个陷阱的技巧:

  • 根据业务问题以及如何增加价值来定制您的发现。
  • 回答“那又怎样?”事先提问(确保见解可行)。
  • 当你发现见解时,对初步结果进行一些早期讨论。

3。错误的推论

数据从业者可能会得出错误的结论,原因有几个。常见的包括缺乏领域知识,将相关性视为因果关系,以及忽略混淆变量

避免这个陷阱的技巧:

  • 扩展你的业务领域知识。
  • 提高你的统计技能。
  • 在分析过程中咨询业务涉众。

4。可视化效果不佳

可视化会出什么问题?很多!仅举几个:图形选择不好,轴比例误导,使用太多颜色,对观众中的一些色盲不敏感,显示错误的单位。关于如何正确地进行数据可视化,有大量的资源。因此,本节的提示是帮助提高数据可视化技能的教程链接。

资源:

结论

在本文中,我们讨论了数据探索中的常见陷阱以及如何避免它们。此外,我们强调了 EDA 的目标,并提供了一些资源来提高您的数据可视化技能。

我希望你喜欢这篇文章,直到下次。干杯!

有什么更有趣的?你可以通过下面我的推荐链接订阅 Medium 来获得更多我和其他作者的启发性文章,这也支持我的写作。

https://aolaoye.medium.com/membership

四个自定义 SHAP 图

原文:https://towardsdatascience.com/four-custom-shap-plots-8605d73b4570

超越 Python 包,创建 SHAP 值的定制可视化

来源:作者

SHAP 值是理解模型如何进行预测的重要工具。SHAP 软件包提供了许多可视化,使这个过程更加容易。话虽如此,我们不必完全依赖这个一揽子方案。通过创建我们自己的 SHAP 图,我们可以进一步了解模型是如何工作的。在这篇文章中,我们将解释四个定制的 SHAP 情节,以及你能从中学到什么。你可以在 GitHub 上找到用来创建图的代码。

如果你刚到 SHAP,那么看看下面的视频如果你想要更多,那就来看看我的 SHAP 课程 注册我的 简讯 :)即可免费获取

讨论的一个图是图 1 中的瀑布图。这是可视化单个预测的 SHAP 值的好方法。你的模型做出的每一个预测都会有自己的瀑布图。它可用于准确解释每个特征对最终预测的影响。例如,这只鲍鱼的壳重使预测的年轮数减少了 1.82。

图 1:使用鲍鱼数据集和 XGBoost 创建的瀑布图

为了创建这个图,我们首先必须使用 SHAP 包计算 SHAP 值。然后,我们将这些值传递给提供的瀑布图函数。还有许多其他可用的情节,但我们不一定要使用它们。一旦我们有了 SHAP 价值观,我们就可以自由地创造我们自己的观想。现在,让我们开始我们的第一个。

地块 1: SHAP 对比热图

正如我们在瀑布图中看到的,对于给定的预测,模型中的每个要素都有一个 SHAP 值。我们能够计算出这些 SHAP 值在所有预测中的相关性。对每一个成对的特征组合都这样做,我们可以构建一个如图 2 所示的 SHAP 相关热图。这里我们可以看到,例如,鲍鱼壳的直径高度的 SHAP 值的相关性为 0.4。

图 2: SHAP 相关矩阵

我们可以将其与图 3 所示的标准关联热图进行比较。这是使用特征值创建的,可以告诉我们这些特征是否相关。换句话说,它可以告诉我们两个特征是否倾向于向相同或相反的方向移动。另一方面,SHAP 值给出了要素对预测的贡献。因此,SHAP 相关将告诉我们两个特征是否倾向于在相同的方向上移动预测。

图 3:标准相关矩阵

我们已经可以看到一些不同之处。请注意,在图 3 中,总重量和去皮重量呈正相关(1)。相比之下,这些特征的 SHAP 值是负相关的(-0.5)。即使这些特征以相同的方向移动,它们的贡献也倾向于以相反的方向移动预测。直觉上,这似乎很奇怪。一个潜在的原因是这两个特征之间存在相互作用。

另一个区别是 SHAP 相关可以计算连续变量和分类变量。这是因为 SHAP 值将始终是连续的,无论它们是针对哪个要素计算的。这就是为什么三个二元变量(即性别。我,性。m 和性。f)包括在 SHAP 关联热图中,但不包括在标准关联热图中。

2:调整后的瀑布图

下一个情节更多的是对现有的 SHAP 情节的补充。在本文的开始,在图 1 中,我们看到了一个瀑布图。通过包含一些附加信息,我们可以了解更多关于预测的信息。查看图 4,我们添加了鲍鱼的实际环数(即 y=7)。我们还为每个特性添加了一个分布图。红线给出了该特征的平均值。虚线给出了用于进行预测的实际特征值。

图 4:带有特性分布的瀑布图

图 5 是可视化该信息的另一种方式。这里,特征被颜色块包围,颜色由特征的值决定。我们坚持 SHAP 一揽子方案使用的惯例。如果某个特性的值相对于该特性的平均值较低,则该块会更蓝。如果特征值较高,该块将为红色。在图 4 中,我们可以看到大多数特征值都低于平均值(即红线左侧)。这对应于您在图 5 中看到的所有蓝色块。

图 5:带有特征颜色的瀑布图

通过在上面的图中包括实际的年轮数 y,我们可以看到这个模型对这种鲍鱼有多精确。如果我们看到预测值与实际值非常不同,我们对 SHAP 值的解释可能会改变。在我们的例子中,预测值与实际值非常接近。如果它们不同,我们可以查看瀑布图,以了解是否有任何特征导致了不正确的预测。

通过包括分布图或色块,我们给出了特征值的上下文。这让我们能够更全面地解释为什么模型会做出这样的预测。在我们看到哪个因素对预测贡献最大之前。现在,我们可以开始理解为什么这个特性如此重要。为了更好地理解这一点,我们来举一个银行业的例子。

来源: flaticon

假设我们建立一个用于接受/拒绝贷款申请的模型。月收入可能是一个重要因素。随着这一特征的减少,你更有可能拖欠贷款。假设模型拒绝了一个应用程序。在我们能够说“我们拒绝了你的申请,你的月收入是这个决定的主要驱动力”之前。现在有了一个调整后的瀑布图,我们可以说,“我们拒绝了你的申请。这是因为你的月收入远低于我们一般客户。”

图 3:交互热图

接下来我们要看的两个图是 SHAP 相互作用值的可视化。如果你不熟悉这些,我建议你阅读下面的文章。我们将深入探讨如何解释这些价值。总而言之,对于一个给定的预测,我们将有一个 SHAP 相互作用值的矩阵。矩阵的对角线给出了主要效应,而非对角线给出了交互效应。每个预测都有一个这样的矩阵。

为了创建第三个图,我们首先计算所有相互作用值矩阵中每个细胞的绝对平均值。相互作用的影响减半,因此我们也将非对角线乘以 2。然后,我们可以将其显示为热图,如图 6 所示。热图将围绕对角线对称,因此我们只显示下半部分。

图 6: SHAP 互动价值观热图

该图类似于均值 SHAP 图,因为它可以突出重要的特征。除了现在,我们可以突出重要的主效应和交互效应。例如,我们可以看到,平均主效应对于体验、程度、性能和销售都很大。这告诉我们,这些特征往往会对模型的预测产生重大影响。同样,我们可以看到,体验程度和绩效销售互动的影响是显著的。

剧情 4:互动瀑布剧情

我们的最后一个图是 SHAP 互动值的瀑布图。这可以用与普通瀑布图相同的方式来解释。除了现在我们不能看到主效应和交互效应是如何对预测起作用的。例如,体验主效果增加了 35.99 美元的预测奖金。类似地,体验度互动效应增加了 13.76 美元的预测奖金。

图 7:交互瀑布图

如果你看看用来创建这个情节的代码,你会发现我们已经相当狡猾。问题是 SHAP 软件包没有提供一种方法来可视化预测的相互作用值。通过一点工作,我们能够使用为正常 SHAP 值创建瀑布图的函数。如图 8 所示,这涉及到将二维矩阵中的交互值转换为一维数组。一旦我们有了这个数组,我们可以把它传递给瀑布函数,SHAP 会把它当作一组正常的 SHAP 值。

图 8:交互矩阵到交互数组

支持我成为我的 推荐会员 😃

https://conorosullyds.medium.com/membership

|Twitter|YouTube|时事通讯 —注册免费参加 Python SHAP 课程

图像来源

所有图片都是我自己的或从www.flaticon.com获得的。在后者的情况下,我拥有他们的保费计划中定义的“完全许可”。

参考

南伦德伯格, SHAP 蟒包 (2021) https://github.com/slundberg/shap

南伦德伯格和 s .李,解释模型预测的统一方法 (2017),https://arxiv.org/pdf/1705.07874.pdf

2022 年 1 月要读的四篇深度学习论文

原文:https://towardsdatascience.com/four-deep-learning-papers-to-read-in-january-2022-fbe183e6bf6c

从自举元学习到深度学习的时间序列预测,外推&泛化与利用 Ridge Rider 探索多样性最优的关系

欢迎来到一月份的【T4:机器学习-拼贴】系列,在这里我提供了不同深度学习研究流的概述。那么什么是 ML 拼贴呢?简单地说,我为我最近最喜欢的一篇论文起草了一张幻灯片的视觉摘要。每一周。在月底,所有由此产生的视觉拼贴都被收集在一个摘要博客帖子中。因此,我希望给你一个视觉和直观的深入了解一些最酷的趋势。所以,废话不多说:这里是我最近阅读的四篇最喜欢的论文,以及为什么我认为它们对深度学习的未来很重要。

“自举元学习”

作者:Flennerhag 等人(2021) |📝论文

一段总结:元学习算法旨在自动发现归纳偏差,这允许跨许多任务的快速适应。经典的例子包括 MAML 或 rl2。通常,这些系统在双层优化问题上被训练,其中在快速内部循环中,人们仅考虑单个任务实例化。在第二个更慢的外部循环中,系统的权重然后通过在许多这样的单独任务上分批来更新。因此,系统被迫发现并利用任务分布的底层结构。大多数时候,外部更新必须通过内部循环更新过程来传播梯度。这会导致两个问题:如何选择内环的长度?目光短浅允许更容易的优化,同时可能是短视的。此外,元目标可能表现不稳定,遭受消失和爆炸梯度。那么,我们如何克服这种短视的优化困难呢?自举元学习(Bootstrapped meta-learning)提出通过将内部循环运行得更长一点来构建所谓的自举目标。然后,我们可以使用由此产生的网络作为短期学生的老师。类似于 DQNs,引导目标从计算图中分离出来,并简单地作为损失计算中的固定量。因此,我们本质上是向前推动元代理。用于比较专家和学生的度量可以进一步控制元目标的曲率。在一组 toy RL 实验中,作者表明自举可以允许快速探索适应,尽管时间跨度较短,并且它优于时间跨度较长的普通元梯度。与 STACX 元梯度代理一起,自举元梯度提供了一个新的雅达利 SOTA,也可以应用于多任务少镜头学习。总之,这项工作为如何积极地操纵元学习问题公式化打开了许多新的视角。

ML-Collage [37]:作者的数字。|📝论文

“N-Beats:可解释时间序列预测的神经基础扩展分析”

作者:Oreshkin 等人(2020) |📝纸张 |🤖代码

一段话总结:传统的时间序列预测模型,如 ARIMA,来自金融计量经济学领域,依靠拟合的移动平均线来预测趋势和季节性成分。它们往往只有很少的参数,同时保持清晰的可解释性。最近,混合模型,结合递归神经网络和微分预测已经变得越来越流行。这允许灵活的函数拟合,同时保持更经典方法的归纳偏差。但是,是否也有可能训练有竞争力的预测者,这些预测者基于纯粹的深度学习方法?在 N-Beats 中,作者介绍了一种用于单变量时间序列预测的新网络架构,该架构在 M4 M3&旅游基准上建立了一个新的 SOTA。该体系结构由多个残差块栈组成,它们同时执行预测和回溯。单个堆栈的部分预测被组合成所考虑的时间范围的最终预测。此外,单个块预测的基础可以被学习或固定为合适的和可解释的函数形式。例如,这可以是捕捉趋势的低维多项式或季节性成分的周期性泛函。作者将他们的方法与集成技术相结合,融合了基于不同度量、输入窗口和随机初始化训练的模型。此外,它们还表明,随着更多叠加的增加,性能增益达到饱和,并直观地分析了固定基础叠加预测确实是可解释的。

ML-Collage [38]:作者的数字。|📝论文

‘高维学习总是等于外推’

作者:Balestriero 等人(2021) |📝论文 | 🗣 播客

一段话总结:神经网络(NNs)只能学习插值吗?Balestriero 等人认为,为了解决高维任务,神经网络必须进行外推。他们的推理依赖于插值的简单定义,也就是说,每当数据点落入观察到的训练数据的凸包中时,就会发生插值。随着原始输入空间的维数线性增长,该空间的体积以指数速率增长。我们人类挣扎于三维空间之外的几何直觉的可视化,但是这种现象通常被称为维度的诅咒。但如果数据位于一个更低维度的流形上呢?那么,是否有可能避开维数灾难,只用几个样本就能获得插值?在一组合成实验中,作者表明,实际上重要的不是流形的原始维度,而是所谓的内在维度,即包含数据流形的最小仿射子空间。他们表明,对于常见的计算机视觉数据集;随着所考虑的输入维数的增加,测试集样本包含在训练集的凸包中的概率迅速降低。作者还强调,这种现象存在于神经网络嵌入或不同的降维技术中。在所有情况下,插值百分比随着更多输入维度的考虑而降低。那么这能告诉我们什么呢?为了使无核武器国家成功地解决一项任务,他们必须在“外推法”制度下运作!但并不是所有的人都像其他人一样一般化。所以这就提出了新的问题,关于外推和更普遍的概括之间的关系。例如,数据扩充和规范化扮演什么角色?

ML-Collage [39]:作者的数字。|📝论文

“脊骑士:通过遵循黑森的特征向量寻找不同的解决方案”

作者:帕克-霍尔德等人(2020) |📝论文 | 🗣 谈话 |🤖代码

一段话总结:现代深度学习问题往往要处理很多局部最优。梯度下降已被证明偏向于简单的高曲率解。这个问题的经典例子包括计算机视觉中的形状与纹理优化,或者不能推广到新玩家的自我游戏策略。其中优化过程结束的局部最优可能取决于许多任意因素,例如初始化、数据排序或诸如正则化的细节。但是,如果我们不是试图获得一个单一的最优值,而是同时探索一组不同的最优值,那会怎么样呢?脊骑士算法旨在这样做,通过迭代地跟随具有负特征值的 Hessian 的特征向量——所谓的脊。作者证明,只要特征向量沿轨迹平滑变化,这个过程就是局部损失减少的。通过遵循这些不同的脊,脊骑士能够在列表 RL 和 MNIST 分类的上下文中覆盖许多不同的局部最优。作者表明,脊骑士也可以帮助发现最佳零射击协调政策,而不必访问潜在的问题对称性。总之,Rider 将连续优化问题转化为在不同山脊上的离散搜索。它为稳健优化开辟了一个有希望的未来方向。但是在该方法的可扩展性方面还存在许多未解决的问题,包括有效的特征分解和多个特征向量的同时探索。

ML-Collage [40]:作者的数字。|📝论文

这是这个月的🤗让我知道你最喜欢的论文是什么。如果你想获得一些每周的 ML 拼贴画输入,查看 Twitter 标签 #mlcollage ,你也可以在最后的摘要中看到更多的拼贴画📖博客帖子:

你应该在 2022 年使用的四种可解释的算法

原文:https://towardsdatascience.com/four-interpretable-algorithms-that-you-should-use-in-2022-56b76974889b

为了做出最好的决定,我们必须了解他们

图片来自 Pixabaygeralt 拍摄。

新的一年开始了,是时候下好决心了。其中之一可能是使决策过程更具可解释性。为了帮助你做到这一点,我提出了四个可解释的基于规则的算法。这四种算法都使用决策树作为规则生成器(如随机森林、AdaBoost、梯度提升等)。).换句话说,这些可解释的算法中的每一个都通过拟合黑盒模型并生成可解释的规则集成模型来开始其过程。

尽管它们都被宣称是可解释的,但它们是用不同的概念可解释性开发的。你可能知道,这个概念在数学上很难被很好地提出。因此,作者用他们自己的可解释性定义设计了可解释的算法。

为了避免详述,这里我假设数据由 d 数量特征组成,并且如果观察位于特征空间 ℝᵈ 的超矩形内,则规则是值为 1 的二元变量。换句话说,规则 rₙ 定义如下:

其中x【j】是的值,观察值 x 为第特征, Iⱼ,ₙ 为ℝ.的区间规则和区间由 n、索引,n 是样本 Dₙ.中的观察次数规则中的 长度 是规则定义中的区间数 s Iⱼ,ₙ ≠ℝ,可能与 d 不同。例如,条件“ If feature_1 in [1,3] AND feature_3 in [0,2] ”定义了超矩形 [1,3] × ℝ × [0,2] × ℝᵈ⁻中长度为 2 等于 1 的规则。**规则可以写成如下:

1.RuleFit (2008 年)。

第一种算法是最流行的,也是最古老的。RuleFit 是在[1]中引入的。它生成一个稀疏线性模型,该模型以决策规则的形式包含选定的交互影响。

动机:这个算法的思想来源于双重观察:基于规则的模型的主要缺点是难以捕捉线性依赖;另一方面,线性模型不能捕捉特征之间的相互作用。因此,RuleFit 的思想是通过创建一个稀疏线性模型,并以决策规则的形式添加交互效果,将这两种类型的算法结合起来。

“使用集成学习,不要求基函数必须从同一个参数基学习器生成。”

该算法包括两个步骤。第一个是规则生成,第二个是规则选择。

规则生成:作者已经提出使用集成树算法,例如随机森林、AdaBoost 和梯度提升。然后,作为规则,以上述形式提取每棵树的所有节点(内部和末端)。

规则选择:这些提取的规则,连同原始的输入特征,然后被输入到一个 L1 正则化的线性模型,也称为 Lasso,它估计每个规则和每个变量对输出目标的影响,同时估计这些影响中的许多为零。为了使每个线性项具有与典型规则相同的先验影响,作者建议对原始输入特征进行归一化。

可解释性:众所周知,套索惩罚是有选择性的。因此,使用这种正则化,作者期望许多系数具有零值,从而生成一个可解释的模型。

出于解释的目的,希望集合由“简单”的规则组成,每个规则由少量变量定义。

此外,作者提出了许多公式来计算规则拟合模型中任何预测因子的重要性,并研究交互效应。所有这些指标都是为了帮助统计学家理解生成的模型。**

备注:在实践中,RuleFit 很难解释。由于被激活的规则所暗示的预测来自套索拟合,所以规则可以覆盖正面的例子,但是仍然具有负面的预测。此外,有时生成的模型具有太多非零权重的规则,因此变得无法为人所理解。

包: RuleFit 在以下 GitHub 项目中用 Python 实现: imodelsrulefit 。RuleFit 也有几个 R 软件包。

2.节点收获(2010 年)。

这种算法已在[2]中提出。该算法将随机森林的所有节点和树叶视为规则,并解决线性二次问题以拟合每个节点的权重。因此,估计器是节点的凸组合。

动机:节点收获算法(NH)背后的思想是在给定一组 q 规则 Q 的情况下选择特征空间的最佳划分。最小化问题可以正式写成

其中 wₖ 是规则的权重 rₖpₖ 是规则的预测 rₖ (通常是经验条件期望 Y 给定 X ∈ rₖ).

不幸的是,这个方程很难求解,特别是因为对于 k ∈ {1,…,q} 的约束 wₖ ∈ {0,1} 并不对应于凸可行域。在本文[2]中,作者提出了一种方法来适应这个优化问题,使其可解。

“NH 的主要思想是,如果权重仅被约束为非负,则解决最佳经验划分问题在计算上变得可行。”

规则生成:在本文中,作者推荐使用随机森林算法,并用大小为 n/10 的数据的子样本来拟合每棵树,而不是通常的 bootstrap 样本,以加快计算速度并增加规则集的多样性。然后,满足最大交互顺序(规则长度)的给定条件和最小尺寸(规则覆盖的观察数量)的给定条件的每棵树的所有节点被添加到规则集 Q 。最后,如果 Q 中的规则数量低于选定数量 q ,则重复该过程。

规则选择:为了解决优化问题,作者对原规则做了两处修改。第一个是用经验分割条件来代替分割特征空间的条件。这意味着每个观察必须恰好被一个选择的规则覆盖。第二个修改是通过允许权重向量取区间[0,1]中的值而不是二进制集{0,1}来放松对权重向量 w 的约束。由于这些变化,新的最佳经验分割问题可以用二次规划求解器来解决。**

****可解释性:可解释性由交互次数的选择来保证。它必须小于或等于 3。事实上,很难理解一个包含三个以上特征的规则。而且,即使 Q 中的规则数量很大,优化问题的求解也要给大多数规则赋予 0 的权重。

“绝大多数节点[..]将接收零权重,除了通过经验划分的约束之外,稀疏性没有被明确地实施。”

然而,根据我的经验,选择的规则数量仍然太高,无法创建一个可解释的模型,然而,该模型仍然相当准确。

****备注:在本文中,作者提出了一种在 Q 的大小大于观测值数量时,更快解决优化问题的降维方法。他还提出了在实践中运行良好的 NH 参数的缺省值。最后,他建议规范化,以提高可解释性。

****包:节点收割已经在节点收割包的 R 中实现。

3.SIRUS (2021)。

SIRUS 算法已在[3]中引入,并在[4]中扩展。它使用修改的随机森林来生成大量规则,然后使用大于调整参数 p₀ 的冗余来选择这些规则。为了确保生成的规则中有冗余,特征被离散化。

****动机:这个算法的主要优点是稳定。对于来自同一分布的两个独立样本,选择的规则集几乎相同。事实上,对作者来说,稳定性被视为可解释性的关键特征之一。

规则生成: SIRUS 使用稍微修改过的随机森林。首先,使用边际分布的经验 q 分位数将特征离散化到 q 个箱中。离散化是稳定性的基础,只要 q 不太小,就能保证精度。为了加强可解释性,树的深度被限制为 2,而通常的随机森林算法推荐深度树。这意味着 SIRUS 规则生成过程的每棵树最多有 6 个节点(根节点不算)。最后,每棵树都被一个 aₙ 观测值的子样本代替,其中 aₙ 是 SIRUS 的一个参数。

规则选择: SIRUS 生成了许多树(通常有 10000 棵),它选择了其中很大一部分共享的规则。为了能够识别这些规则,SIRUS 计算了森林中的一棵树包含特定路径的经验概率。然后,它选择经验概率大于或等于选定参数 p₀ ∈ (0,1) 的所有规则。不幸的是,这些方法在选择的规则列表中产生大量冗余,并且选择过程需要后处理。如果规则 r 是具有在林中具有较高频率的路径的规则的线性组合,则 r 从所选规则集中移除。

****可解释性:选择过程中的后处理确保只保留长度最短的规则。的确,一个规则只能是长度更短的规则的线性组合。

“在我们的例子中,恰好 p₀参数本身就足以控制稀疏度”

****备注:离散化过程在 SIRUS 中是强制性的。首先,它增加了算法的稳定性。其次,它能够创建具有通用规则的决策树。事实上,如果特征是连续的,决策树算法在两个独立的样本中为相同的规则切割相同的精确值的概率是零。
作者还证明了路径概率估计的一致性和渐近收敛到完全稳定(即对于从同一分布中抽取的两个独立样本,选择的规则集完全相同)。最后,作者给出了一些参数调整的技巧。

包: SIRUS 已经在 sirus 包中的 R 中实现。

4.覆盖算法(2021)。

这种算法已在[5]中提出。该算法提取一个稀疏规则集,该规则集考虑了树集合的所有节点和叶子,这些节点和叶子是根据它们的统计特性选择的,以形成“准覆盖”(这意味着覆盖是渐近的)。然后,使用所谓的分割技巧将覆盖转化为分割,以创建回归函数的一致估计量。
我在之前的一篇文章《如何处理重叠规则》中已经讲过分区的诀窍了?”。

动机:**想法是生成回归函数的可解释的、一致的基于规则的估计量。在文献中,一致的估计量必须由长度规则 d 产生。而对于一个大的 d ,规则就变得无法解释了。为了绕过这个限制,作者引入了重要不重要规则的概念。重要规则可以被认为是目标变量表现出与其平均行为显著不同的行为的规则。并且无关紧要的规则可以被认为是目标变量具有非常低的方差的规则。作者还取消了有覆盖物的条件。这意味着所选规则的集合不必覆盖特征空间;这一限制被更灵活的准覆盖限制所取代。覆盖算法是这些概念的一个例子。**

****规则生成:如同在 RuleFit 中一样,集成树算法如随机森林、AdaBoost 和梯度增强可以用作规则生成器。然后,提取每棵树的所有节点(内部和终端)作为规则,仅保留长度小于或等于指定参数值(通常为 3)的规则。

****规则选择:选择过程分为两步。首先,该算法提取重要的规则,按照覆盖率递减的方式对它们进行排序。该算法试图通过排除那些重叠太多的规则来用这些规则覆盖经验特征空间。第二,如果覆盖不完整,该算法试图通过添加无关紧要的规则来扩展覆盖,通过增加方差来排序它们

****可解释性:可解释性由所选规则集中少量的短规则来保证。事实上,如果一个决定是基于两个或三个长度相等的规则,那么它就更容易理解。此外,重要和不重要规则的概念有助于用户识别感兴趣的领域。无关紧要的规则表示发生不多的领域,与突出感兴趣的领域的重要规则形成对比。

****备注:本文的主要结果是由一组显著和不显著规则构造的回归函数的可解释估计量的相合性证明。如上所述,该算法只是一个示例,用于显示如何在预测算法中使用重要和不重要的规则。事实上,它不能产生一致的估计量,因为规则生成器不能完全满足某些条件。这种算法目前是积极研究的主题。

包: CoveringAlgorithm 已经在 Python 中开发了 GitHub 包 CoveringAlgorithm

结论

有趣的是,每个算法都有不同的目标:规则匹配和节点收获的准确性,SIRUS 的稳定性和覆盖算法的简单性。受益于这些先前的研究,我正致力于一种定量的方法来衡量基于这三联体的可解释性:可预测性、稳定性和简单性。第一种致力于基于树的算法和基于规则的算法的方法发表在 MDPI 人工智能杂志(TDS 版本已经发布在这里)。我目前正在对这种方法进行扩展,使其独立于算法,并能够比较任何预测算法的可解释性。

关于我们

Advestis 是一家欧洲合同研究组织(CRO ),对统计学和可解释的机器学习技术有着深刻的理解和实践。Advestis 的专长包括复杂系统的建模和时间现象的预测分析。

领英:【https://www.linkedin.com/company/advestis/】T4

参考

[1]j . h .和波佩斯库,2008 年。通过规则集成的预测学习。《应用统计学年鉴》2 (3),第 916–954 页。

[2]2010 年,纽约州梅因绍森市。节点收获。《应用统计学年鉴》,第 2049–2072 页。

[3]c . bénard,g . Biau,Veiga,s .和 s .和 s . swoon et,e .,2021 年。SIRUS:稳定且可解释的分类规则集。电子统计杂志15 (1),第 427–505 页。

[4]c . bénard,g . Biau,Veiga,s .和 s .和 s . o . s .,2021 年 3 月。通过规则提取的可解释随机森林。在人工智能和统计国际会议中,第 937–945 页。PMLR。

[5] Margot,v .,Baudry,J.P .,Guilloux,f .和 Wintenberger,o .,2021 年。使用数据相关覆盖的一致回归。电子统计杂志15 (1),第 1743–1782 页。

四种强大的算法,近距离观察

原文:https://towardsdatascience.com/four-powerful-algorithms-up-close-bbf78426688b

也许你正在迈出机器学习的第一步。也许你已经探索前沿研究很多年了。无论如何,我们认为你会喜欢这个星期的算法为中心的版本的变量。毕竟,熟悉模型的内部运作对于数据科学家和 ML 从业者来说都是一项至关重要的技能——而且你可以继续磨练多年。让我们开始吧。(如果你想尝试一些完全不同的东西,可以看看下面其他一些优秀的文章。)

  • 蜂群能告诉我们关于商业结果的什么? 在他的第一篇 TDS 文章中, Naresh Ram 展示了对进化算法——在这种情况下是人工蜂群(ABC)算法——和商业决策的交叉的有趣的深入探究。这是一个全面的理论介绍,还包括一个完整的代码实现,供你们中间的修补者使用。
  • 用一个医疗用例 观察一个随机的森林。分类和预测是机器学习在医疗保健领域的两个关键应用。Lopamudra Nayak——另一位首次获得 TDS 的作者!—分享了一个精辟的实践教程,展示了如何基于随机森林分类器构建糖尿病预测模型。

照片由弗兰蒂塞克·杜里斯Unsplash 上拍摄

  • 获取层次聚类的基础知识对 。有时,在一头扎进所有的编码、测试和微调之前,慢慢进入一个主题会有所帮助。例证:Shreya Rao 的初学者友好算法介绍的最新补充。它耐心地引导读者了解层次聚类的具体细节(并配有整洁的手工插图)。
  • 关于魏斯费勒-雷曼算法的局限性——以及如何超越它们 。如果你这周有 30 分钟的空闲时间,我们鼓励你找到一种比阅读迈克尔·布朗斯坦的最新贡献更有启发性的方式来使用它们。它为图形神经网络探索了一个有前途的新方向——物理学启发的“连续”学习,它避开了消息传递,并开辟了一系列强大的新工具。

寻找更多阅读推荐?我们希望如此,因为我们最近发表了一些优秀的新帖子。例如:

我们希望你喜欢本周的阅读和探索!如果你有一个有趣的项目或话题要告诉我们(和我们的读者),为什么不把它发给我们呢?

直到下一个变量,

TDS 编辑

您可能错过的自动化探索性数据分析的四个 R 包

原文:https://towardsdatascience.com/four-r-packages-for-automated-exploratory-data-analysis-you-might-have-missed-c38b03d4ee16

通过 R 中的实际例子探索有用的工具来简化探索性任务

图片作者。

目录

  1. 简介
  2. 自动化探索性数据分析包
    2.1Data explorer
    2.2g gally
    2.3SmartEDA
    2.4tableone
  3. 结论
  4. 参考文献

1.介绍

探索性数据分析 (EDA) 旨在通过统计和可视化技术总结数据的特征,对数据进行初步调查,这是任何数据科学工作流程中至关重要的早期步骤。

在这篇文章中,我们将探讨四个 R 包,它们有助于完成这个初始任务,并在数据处理、可视化和报告方面提供实质性的支持。

对于这个例子,我们使用由以下特征组成的Cardiovascular Disease Dataset:

  1. id:患者唯一标识符(int)
  2. gender:患者性别(1:女,2:男)
  3. age:以天为单位的年龄(整数)
  4. height:身高厘米(整数)
  5. weight:以千克为单位的重量(浮动)
  6. ap_hi:收缩压(int)
  7. ap_lo:舒张压(int)
  8. cholesterol:胆固醇(1:正常,2:高于正常,3:远高于正常)
  9. gluc:葡萄糖(1:正常,2:高于正常,3:远高于正常)
  10. smoke:患者是否吸烟(二元)
  11. alco:酒精摄入量(二元)
  12. active:身体活动(二进制)
  13. cardio:有无心血管疾病(二元)

我们加载数据集并观察第一行:

图片作者。

Base R 提供了summary,这是一个通用函数,用于从不同的输入对象(如数据集)生成结果概览。特别是,当数据集作为输入(例如summary(df))提供时,它为数字列返回不同的度量(例如平均值、中值、最小值、最大值等),为分类列返回分布(计数)。它还返回有关丢失数据的信息(如果存在):

总结的输出。图片作者。

现在,让我们跳到提供进一步数据探索功能的包。

2.自动化探索性数据分析包

2.1 数据浏览器

DataExplorer简化并自动化 EDA 流程和报告生成。该软件包自动扫描每个变量,执行数据分析,它提供了几个有用的功能来生成离散和连续特征的不同图表。

重要的是,该包允许通过调用数据集上有用的create_report函数(例如create_report(df))来轻松生成完整的 HTML 报告。可以传递额外的参数,如响应变量y,以将各种双变量分析添加到报告中:

该代码片段在工作目录中生成一个 HTML 文件。当用浏览器打开时,它显示如下:

HTML 报告的早期部分,目录的综合。图片作者。

从目录中可以看出,该报告涵盖了 EDA 过程中执行的大部分任务,并且可以由一行代码生成。

特定任务的附加程序包功能报告如下:

  • plot_str(df)绘制数据集结构:

plot_str(df)的输出。图片作者。

  • plot_bar(df)绘制每个离散特征的条形图:

plot_bar 的输出(df)。图片作者。

  • 图表也可以通过离散变量分组,例如心血管疾病的存在,通过plot_bar(df, by="cardio"):

plot_bar 的输出(df,by="cardio ")。图片作者。

通过观察这些图,人们可以立即注意到患有心血管疾病的患者呈现较高的胆固醇值,并且可以被诱导通过统计测试来进一步研究由较高胆固醇所代表的心血管疾病的风险。

  • plot_qq(df)绘制每个连续特征的分位数:

plot_qq(df)的输出。图片作者。

例如,通过观察第一个 QQ 图(年龄),人们可能会注意到这些点倾向于在极端处弯曲,这表明年龄具有更多的极值,如果它具有正态分布的话(这种行为被称为“重尾”)。

  • plot_density(df)绘制每个连续特征的密度估计值:

plot_density(df)的输出。图片作者。

  • plot_correlation(df)要可视化关联热图:

plot_correlation(df)的输出。图片作者。

  • plot_prcomp(df)执行主成分分析(PCA ),并绘制每个主成分解释的方差百分比:

plot_prcomp(df). Image by author.的输出

软件包文档包含可用函数的完整列表。

2.2 GGally

GGally ⁴扩展了流行的ggplot2绘图包,提供自动可视化数据集和组合几何对象的功能。

特别地,可以利用数据集上的ggpairs ⁵函数(例如ggpairs(df))并获得显示每个变量与每个其他变量的相互作用的成对图:

图片作者。

ggpairs提供了一种简单的方法来生成用于描述性分析的图,证明有助于发现异常值、观察分布和直观检查组间差异。

在我们的例子中,为了简单起见,我们选择只为数据集的四个变量生成图(df %>% select("age", "cholesterol", "height", "weight")),并且我们通过ggplot2美学(aes(color = df$cardio, alpha = 0.5))基于心血管疾病的存在应用不同的颜色。

通过在下方、上方和对角线 panels⁵.中指定所需的绘图类型,可以进一步定制图表

2.3 SmartEDA

SmartEDA ⁶是一个强大的工具包,它通过提供以下功能来自动化大多数 EDA 任务:

  • 描述统计学
  • 数据可视化
  • 自定义表格
  • HTML 报告

类似于我们对DataExplorer所做的,我们可以通过调用ExpReport函数生成一个完整的 HTML 报告。我们可以指定一个响应变量Target来包含对报告的进一步双变量分析:

这个代码片段在工作目录中生成一个名为Report.html的文件。当用浏览器打开时,它看起来如下:

HTML 报告的早期部分,目录的综合。图片作者。

该报告包含一套完整的探索性分析,无需编码即可生成。

特定任务的附加程序包功能报告如下:

  • ExpData(data=df, type=1)显示数据集的概览:

ExpData 的输出(数据=df,类型=1)。图片作者。

  • ExpData(data=df, type=2)显示了数据结构:

ExpData 的输出(数据=df,类型=2)。图片作者。

  • ExpCatStat(df, Target="cardio", Pclass="1", plot=TRUE)对于基于信息值的可变重要性:

ExpCatStat (df, Target="cardio", Pclass="1", plot=TRUE)的输出。图片作者。

从该输出中,我们获得了与心血管疾病(Target="cardio")的每个协变量的关联度预测能力的信息。特别是我们可以观察到,收缩压和舒张压被归类为心血管疾病的 高度预测 ,而胆固醇和年龄则显得是 中度预测 。该函数还生成每个变量的信息值图:

ExpCatStat (df, Target="cardio", Pclass="1", plot=TRUE)的输出。图片作者。

该软件包提供了各种方便的功能来生成图和表。可用函数的完整列表以及示例都可以在 documentation⁷或 GitHub⁶.的软件包中找到

2.4 表一

在生物医学期刊论文中,表 1 提供了基线患者特征的定量细分。tableone包旨在从研究出版物中生成典型的表 1 (因此得名)。

在使用它之前,我们执行一个可选的预处理步骤,使类别和列名更具可读性:

作者图片

我们现在使用CreateTableOne函数来生成一个对象,该对象总结了所有的基线变量,包括连续变量和分类变量,可选地通过一个或多个变量进行分层,并执行统计测试。在我们的案例中,我们通过strata论证决定根据心血管疾病的存在进行分层:

打印输出(表一)。图片作者。

从表中可以看出:

  • 分类变量表示为计数和百分比
  • 连续变量显示为:
    -在正态分布的情况下:均值和标准差
    非正态分布时:中位数和四分位距

该表提供了按心血管疾病分层的基线特征的直接和直观的概述。

通过分别访问TableOne对象的CatTableContTable元素,可以只看到分类变量或连续变量。例如:

# categorical part only
df$CatTable

df$CatTable 的输出。图片作者。

3.结论

在本文中,我们使用了四个 R 包来完成不同的 EDA 任务,从汇总表到详细的 HTML 报告,并大大简化了对新数据集的探索。

r 提供了几个软件包,这些软件包具有灵活快速地汇总数字和分类数据的特性。我们举几个:skimr ⁹、Hmisc ⁰、desctablesummarytoolsdlookr

普图恩达等人⁴ (2019)分享了提供 EDA 功能的不同封装之间的深刻比较,以及 CRAN 中可用的封装:

不同 R 封装的 EDA 特性比较。来自⁴的普图恩达等人(2019 年)。

出于好奇,我们可以观察一下去年(2021 年)被引用包的下载次数如下:

图片作者。

我们还可以观察包的下载趋势:

图片作者。

4.参考

[1]kaggle.com/sulianova/cardiovascular-disease-dataset

[2]rdocumentation . org/packages/base/versions/3 . 6 . 2/topics/summary

cran.r-project.org/package=DataExplorer

cran.r-project.org/package=GGally

[5]rdocumentation . org/packages/GGally/versions/1 . 5 . 0/topics/gg pairs

github.com/daya6489/SmartEDA

cran.r-project.org/package=SmartEDA

cran.r-project.org/package=tableone

[9]cran.r-project.org/package=skimr

cran.r-project.org/package=Hmisc

cran.r-project.org/package=desctable

cran.r-project.org/package=summarytools

cran.r-project.org/package=dlookr

[14] Putatunda,Sayan 和 Ubrangala,Dayananda 和 Rama,Kiran 和 Kondapalli,Ravi,“SmartEDA:a R Package for Automated explorative Data Analysis”,开源软件杂志,2019 年第 4 卷,链接

改善数据管道的四种软件工程最佳实践

原文:https://towardsdatascience.com/four-software-engineering-best-practices-to-improve-your-data-pipelines-484c1f368e28

从敏捷到抽象,用我们思考软件的方式思考数据可以让我们避免很多痛苦。

车头拍摄

让我们从一些重要的事情开始:数据工程和软件工程之间有一些主要的区别。

同时,它们足够相似,以至于许多起源于软件工程的最佳实践对数据工程非常有帮助,只要你正确地构建它们。

在本文中,我将介绍几个软件工程最佳实践,以及它们如何帮助您创建和维护更好的数据管道。我将特别关注管道,因为这是我们在河口关注的内容,但是这些原则同样适用于您的数据堆栈。

讨论将是高层次的。我自己不是软件工程师,我也不认为你必须从这些原则中获得战略和领导价值。

软件工程与数据工程的异同

数据和软件产品不同,其利益相关者也不同。

一般来说,构建软件产品需要高技术团队之间的协作。该产品可以交付给各种各样的用户群,通常是商业性的。例如,一家银行可能为其客户创建一个移动应用程序。

相比之下,数据产品往往存在于企业的范围内。所涉及的利益相关者和参与者可以是技术含量很高的工程师,也可以是需要数据来完成工作的非技术专业人员。例如,同一家银行可能会创建关于其客户的金融和人口统计数据产品来帮助安全、销售和战略。

如果你在读这篇文章,你会在数据空间里闲逛,这意味着我可能不需要去纠缠这些区别。您已经亲眼目睹了数据如何被视为与软件完全不同,尤其是从业务角度来看。

但是数据工程和软件工程的基本实践是相同的。您正在编写、维护和部署代码来解决一个可重复的问题。正因为如此,有一些有价值的软件工程最佳实践可以转化为数据工程最佳实践。许多最新的数据趋势——如数据网格和数据操作——以一种新的方式应用软件工程实践,并取得了出色的结果。

软件工程与数据工程的历史

为了理解为什么这些最佳实践来自软件,并且只是最近才被应用于数据,我们需要看看历史。

软件工程学科在 20 世纪 60 年代首次得到认可。当时,认为创造软件的行为是一种工程形式的想法是一种挑衅性的想法。事实上,术语“软件工程”是有意选择的,目的是让人们停下来,并鼓励从业者将科学原理应用到他们的工作中。在接下来的几十年里,软件工程师测试并提炼了应用科学和机械工程的原理。

(查看这篇普林斯顿的文章,了解所有这些大胆主张的更多细节。)

然后,在 20 世纪 90 年代,该行业落后于日益增长的软件需求,导致了众所周知的“应用程序开发危机”危机鼓励软件工程师采用敏捷开发和相关实践。这意味着优先考虑一个快速的生命周期,迭代,并为软件背后的人类系统赋予价值。

另一方面,我们所知的数据工程是一个相对年轻的领域。当然,数据在人类历史的大部分时间里就已经存在,关系数据库是在 20 世纪 70 年代创建的。但直到 2000 年,数据库还只是由一小部分管理人员(通常是 IT 人员)管理。数据基础设施作为具有许多组件的企业级资源是一个相对较新的发展(更不用说它仍在快速变化中)。而职位名称“数据工程师”起源于 2010 年代。

简而言之,软件工程师已经做了大约 60 年的工作,至少大体上类似于他们今天仍然在做的事情。在那段时间里,他们解决了很多问题。数据工程界可以利用这一点。

话不多说,这里有一些你可以(也应该)应用到数据管道的软件工程最佳实践。

1 —设置(短)生命周期

产品(软件或数据)的生命周期是一个循环过程,包括规划、构建、记录、测试、部署和维护。

敏捷软件开发通过缩短开发生命周期来扭转这一局面,以便在满足需求的同时继续迭代和改进产品。

同样,您可以——也应该——为您的数据管道实现快速的生命周期。

整个组织对新数据产品的需求将会快速而频繁地增长。确保您已准备好开始您的生命周期工作流程。

  • 与利益相关者一起制定计划,以确保你的管道将交付所需的产品。
  • 构建管道——根据平台和接口的不同,你可以编写一个规范或者创建一个 DAG。
  • 文档管道——这可能包括模式、元数据或书面文档( dbt 文档是一个有趣的例子,尽管在数据堆栈的不同部分)。
  • 在部署之前测试管道——管道工具可能有内置测试,或者您可以编写自己的测试。
  • 部署管线。
  • 监控it——注意错误警报并进行更新。
  • 随着用例的变化快速迭代——继续在以前的管道上构建并回收组件。

将敏捷开发方法集成到数据中的概念是 DataOps 框架的一个重要组成部分。查看我关于这个主题的整篇文章。

2-选择正确的抽象层次

为了保持您的数据生命周期紧凑,不要迷失在技术实现细节中是很重要的。这需要抽象。

软件工程师对抽象的概念相当熟悉。抽象是将信息简化成更一般的对象或系统。也可以认为是泛化或者建模。

在软件工程中,相关的抽象层次通常存在于代码本身中。例如,函数或面向对象的编程语言是有用的工具,但是它们没有揭示它们是如何执行的细节。

在数据方面,你需要使用比代码更高的抽象层次。这有两个主要原因:

  • 数据产品和它们所服务的业务用例之间的直接联系意味着你会想用更“真实”的术语来谈论数据。搞清楚这个抽象层次意味着建立一个通用语义层——并且有助于避免在不同的 BI 工具和用户组中出现多个、冲突的语义层的常见问题。
  • 你会在数据利益相关者中发现更广泛的技术层次,这意味着谈论一些高度技术性的东西,比如代码,不是很有用。

对于数据管道,两个相关的抽象是从一个系统获取数据并将其推送到另一个系统的行为(在河口,我们使用术语捕获具体化,但语义会有所不同)。

当我们使用“捕获”和“具体化”这样的术语来谈论管道时,工程师和业务用户都能够围绕管道的语义值团结起来(它从系统 X 获得数据到系统 Y,以便我们可以做 Z)。

3 —创建声明性数据产品

好吧,你抓到我了,这真的只是抽象讨论的延续,但它会给那个讨论更多的实质内容。

让我们考虑将数据作为一种产品的想法。这是流行的数据网格框架的核心原则。

数据即产品归公司内的不同领域所有:拥有不同技能但共享数据运营用例的团队。数据即产品可以快速转化为多种形式的可交付成果,但始终由用例驱动。换句话说:它们是关于什么而不是如何。

与此平行的软件工程是声明式编程。声明式编程关注程序能做什么。这与命令式编程相反,命令式编程确切地说明了任务应该如何执行。

声明式编程是命令式编程之上的一种抽象:在运行时,当程序被编译时,它必须决定如何进行。但是声明式编程在运行时允许更多的灵活性,潜在地节省了资源。此外,它更容易在精神上保持控制,使其更加平易近人。

通过使您的管道具有声明性——首先基于它们的功能而不是它们的机制来构建——您将能够更好地支持数据即产品文化。

你将从管道要交付的产品开始;比方说,一个特定的物化视图,并在此基础上设计管道。管道化的声明性方法使人们更难迷失在技术细节中,忘记数据的商业价值。

4 —防止故障

无论是软件开发还是数据管道,失败都是不可避免的。这是我们许多人从惨痛的教训中学到的:匆忙修复灾难性崩溃的系统,因停机而丢失进度或数据,或者只是允许一个愚蠢的错误进入生产。

您可以也应该在软件和数据环境中应用非常相似的预防和备份措施。

以下是一些重要的考虑事项。这些功能中有许多可以通过数据编排工具或者通过管道供应商提供的工具来实现。

测试

这应该是管道生命周期的一部分,就像在软件中一样。

除了在部署之前进行全面的手动测试之外,您还应该编写自动化的单元测试来关注生产中的管道。

如何编写这些依赖于您的平台以及您必须如何与它交互。例如,如果你为你的管道使用气流,你将创建 Python 脚本来测试它们。或者,你可能更喜欢或要求一个更强大的监控设置来捕捉所有潜在的问题。

根据经验,数据管道应用的转换越多,需要的测试就越多。

版本控制

软件工程师使用版本控制(通常是 Git)来协作工作,并保留将软件回滚到以前版本的能力。

如果您使用供应商的产品,它可能会提供 GitOps 工作流,这意味着工程师可以在他们喜欢的开发环境中使用 Git 在管道上进行协作。然而,并不是所有人都这样。

即使您不能将 Git 用于您数据基础设施,您的供应商也会启用一些选项来备份您的管道,所以一定要充分利用这个功能。

分布式存储和回填能力

云托管和存储的出现降低了停机和数据丢失的风险,但这种风险并没有消失。

你的数据基础设施应该是分布式;也就是说,不同的组件应该分布在不同的服务器上,使其具有容错能力。您对此的控制程度取决于您选择的云提供商和供应商。

总是迭代

软件工程最佳实践的最后一课是:当某些东西不起作用时,迭代。

现状和最佳实践总是在不断变化。这适用于软件工程,当然也适用于数据工程。

最好的方法总是经过深思熟虑的,安全地引入变化,并包括所有利益相关者的认同。

从这些原则开始,用它们来适应你的数据团队的系统和文化。注意积极的效果和需要改进的地方,并从那里开始。

本文改编自 河口博客 。你可以在LinkedIn上找到我们的团队,在GitHub上找到我们的代码。

强健线性程序的四个步骤

原文:https://towardsdatascience.com/four-steps-to-robustify-your-linear-program-281477bd8190

以及它如何帮助你烘焙蛋糕

安托万·道特里在 Unsplash 上拍摄的照片

在大学的第一年,我接触了线性编程,这为我打开了一个全新的世界。教授们教我们使用 LP 在广泛的领域中对各种问题建模。这吸引了我,因为它可以给出一个最优的解决方案——如果问题足够小的话——并且相对容易构建。由于对这种方法过于热衷,我几乎在每个 uni 项目中都使用了这种方法,甚至最终我的整个论文都是以 LP 为基础的。

直到我读硕士的第二年,我才意识到我从来没有学会如何在构建 LPs 时将不确定性考虑在内。当我现在看它的时候,感觉很傻。毕竟,你有多少次听说数据是 100%准确的,或者在他们的实验中有零估计误差的研究?可能永远不会,对吧?基本 LPs 通常基于精确的输入数据,并且不考虑那些输入值可能不完全准确。这些模型缺乏对不同最坏情况的鲁棒性。例如,如果你正在烘烤一个蛋糕,但是由于浪费,你每次消耗的配料都不一样,你可能希望有一个永远有效的解决方案,不管溢出多少。如果你想实现稍微复杂一点的数学模型,包括不确定性,你可能需要一个健壮的 LP 版本。

对我来说足够幸运的是,我上了一堂数学最优化的课,在课上他们详细地讨论了这个话题。其中一点是 LP 的稳健性可以归结为 4 个步骤,我将在本文中解释。

定义

首先,健壮是什么意思?一个健壮的解决方案在某种程度上是可行的——并且不会严重恶化——以应对输入数据的变化。换句话说:它优化了你的最坏情况。就蛋糕烘焙的例子而言,你总是希望能够烘焙出一个蛋糕,不管蛋糕是否溢出。

基本 LP

假设我们只需要 4 种原料来烘焙一个蛋糕:牛奶、鸡蛋、面粉和糖,我们的面包店出售两种蛋糕。它们分别产生 12 美元和 9 美元的利润,然而,它们有不同的配方。第一个蛋糕需要一单位的牛奶和面粉以及四单位的糖。第二个需要一个单位的鸡蛋和面粉以及两个单位的糖。由于面包店相当小,每天只有 1000 单位的牛奶、1500 单位的鸡蛋、1750 单位的面粉和 4800 单位的糖。

问题是:在不超过最大配料量的情况下,我们应该烘烤多少种类型的蛋糕才能使我们的利润最大化?下面,你可以找到对应的 LP。

资料来源:A. Zocca & R. Paradiso,来自 VU 阿姆斯特丹。

由于我们面包店的蛋糕是手工制作的,有时会洒一点糖。在决定最佳烘焙策略时,我们希望将这一点考虑在内。聪明的面包师想要确定最佳的生产组合,这种组合在糖消费波动的情况下仍然可行。然而,前面提到的 LP 没有做到这一点。因此,我们需要增加一些不确定性。

不确定集

不确定性有许多不同的种类和味道,但在这篇文章中,我将坚持三个基本的不确定性集合:

  • 盒子不确定性集合(黑色)
  • 椭球(bal)不确定性集(蓝色)
  • 多面体不确定集(橙色)

不确定集是不确定参数可能取值的集合。在我们的示例中,有两个参数:每个蛋糕一个溢出参数。下面,你可以看到三个不确定性集合。

资料来源:A. Zocca & R. Paradiso,来自 VU 阿姆斯特丹。

如你所见,盒子不确定性集合(用黑色方块表示)是最保守的。毕竟它覆盖了不确定性状态空间中最大的面积。正方形的角表示两个不确定参数可以同时具有极值。球的不确定性(用蓝色圆圈表示)不会出现这种情况:不包括拐角。多面体不确定性集(用橙色菱形表示)甚至更进一步。根据你对保守主义的偏好,你可以为你自己的问题选择这些集合中的一个。为了简单起见,我们在我们的例子中采用盒不确定性集。

台阶

为了使 LP 稳健,应该采取四个步骤:

  1. 构造标称问题
  2. 构建对手问题
  3. 找出对立的对偶
  4. 插头标称问题中的对偶

名义问题

名义上的问题是简单地写下你最初的 LP,我们已经做了几段了。然而,也有必要确定不确定的参数,我们迄今为止还没有这样做。下面,你可以看到红色的是什么改变了:我们增加了不确定的参数。它们来自一个盒子不确定性集合,在最后一行用数学方法定义。

资料来源:A. Zocca & R. Paradiso,来自 VU 阿姆斯特丹。

很明显,两个不确定参数的最保守值为 0.1。然而,为了举例,我将解释你如何从数学上得到这个。

对手问题

请记住,我们希望针对最坏的情况优化我们的策略。因此,我们引入一个人造对手,它的目标是让你的优化变得可怕。下面,你可以看到红色的对手是如何工作的。它想最大化糖约束的左边,以使你的最优解不那么好。

资料来源:A. Zocca & R. Paradiso,来自 VU 阿姆斯特丹。

这是我们想要关注的红色部分。我们最大化约束的左侧,同时确保不违反不确定性集合的约束。下面可以看到由此产生的对手问题,可以看作是“对手”的数学模型。

对手问题。

双重的

找到对手问题后,是时候构建相应的对偶了。如果你对原始问题和对偶问题的概念不熟悉,看看这个视频。下面,你可以发现对抗性的双重问题。

双重问题。

即插即用

对偶问题可以代替名义问题中的红色约束。生成的稳健 LP 如下所示:

LP 的健壮版本。

这种 LP 的好处是它对输入数据的变化保持可行。如果洒了一点糖,最佳烘焙策略不需要改变。这使得数学建模更加现实,因此更适用于现实生活中的例子。

外卖

如果您的数学模型的输入数据不准确或不可靠,那么稳健地强化您的 LP 是值得的。这可以通过四个步骤完成:

  1. 构造出名义上的问题
  2. 构建对手问题
  3. 找到敌对双方的对偶
  4. 标称问题中的对偶

然而,如果你发现这个概念仍然相当模糊,不要担心。你已经听说过这件事了。老实说,烤蛋糕的时候谁还需要 LP 呢?烘焙快乐!

Monika Grabkowska 在 Unsplash 上的照片

参考

A. Zocca 博士和 R. Paradiso 博士在阿姆斯特丹自由大学的数学优化课程中,2021/2022。

Python 中的四种形参和两种实参

原文:https://towardsdatascience.com/four-types-of-parameters-and-two-types-of-arguments-in-python-357ccfdea3db

图片来自皮克斯拜

什么是强制、可选、args、kwargs 参数和位置、关键字参数

不同的编程语言有不同的语法,但函数是他们永远无法回避的概念之一。当我们提到函数或方法时,在大多数情况下,我们会将参数传递给这些函数。

作为最灵活和动态的编程语言之一,Python 对于函数参数的传递有一些特殊的规则。这是为了使开发人员能够以最大的灵活性完成他们的工作。然而,对于新手来说,掌握所有这些标准可能并不容易。

在本文中,我将使用示例来展示所有这些规则,包括强制、可选、关键字和非关键字可变长度参数、位置和关键字参数。

0.参数和自变量的定义

图片由 Gerd AltmannPixabay 拍摄

我不是一个喜欢记忆概念或定义的人。然而,为了方便起见,澄清一些我们将在本文中大量使用的关键词是很重要的。

当我们说形参和实参时,可能会有些混乱。有些人可能认为他们是一样的。然而,普遍认为它们有如下不同。

  • 参数是函数定义中的变量。换句话说,它们存在于函数签名中,并将在函数体中用作变量。
  • 参数是我们调用函数时传递给它的实际值。换句话说,参数可以是整数、字符串或任何对象。

1.强制和可选参数

来自像素库像素的图像

在 Python 中,我们可以很容易地定义一个带有强制和可选参数的函数。也就是说,当我们用默认值初始化一个参数时,它就变成可选的了。否则,该参数将是强制性的。

def my_func(man1, man2, opt1=0, opt2=''):
    print('man1:', man1)
    print('man2:', man2)
    print('opt1:', opt1)
    print('opt2:', opt2)

在上面的例子中,man1man2是强制的,因为它们没有在函数定义中初始化。因此,如果我们调用函数而不传递参数,它将抛出一个错误。

对于可选参数,我们不需要传递任何参数。如果没有,将使用默认值。

my_func(man1='a', man2='b')

1.1 所有的强制参数必须在前面

当我们想定义一个既有强制参数又有可选参数的函数时,所有的强制参数必须在所有的可选参数之前。

def my_func(opt1=0, man1, man2, opt2=''):
    print('man1:', man1)
    print('man2:', man2)
    print('opt1:', opt1)
    print('opt2:', opt2)

这是因为 Python 允许位置参数,这将在下一节介绍。如果允许像上面那样定义一个函数,当我们传递一个位置参数a时,无论是可选参数opt1还是强制参数man1都会很混乱。根据论据的数量来决定这个太不靠谱了。

2.位置参数和关键字参数

图片来自 PixabayGino Crescoli

2.1 什么是立场论点?

当我们把参数传递给函数时。我们不必给出参数名,只要我们将参数传递到它们原来的位置。这就是立场论点。

# my_func(man1, man2, opt1=0, opt2='')my_func('a', 'b')

2.1 关键字参数不区分位置。

当参数不是位置参数时,它必须是关键字参数。当我们明确地提到一个参数的参数名时,它就变成了一个关键字参数。

虽然我们需要键入更多的单词,但使用关键字参数可能会更方便,因为位置是不敏感的,所以我们不需要关心位置。

# my_func(man1, man2, opt1=0, opt2='')my_func(man2='a', man1='b')

在上面的例子中,我们在man1前面经过man2

2.2 强制参数和可选参数的位置参数

位置参数不仅仅用于强制参数。只要我们遵循这个位置,它也可以被传递给可选参数。

# my_func(man1, man2, opt1=0, opt2='')my_func('a', 'b', 10, 'c')

2.3 混合位置和关键字参数

我们也可以混合这两种类型的论点。当我们想要使用一个带有几个强制参数但有许多可选参数的函数时,这通常是有效的。

例如,我们可以将两个位置参数传递给两个相应的强制参数,然后通过对第二个可选参数使用关键字参数来跳过第一个可选参数。

# my_func(man1, man2, opt1=0, opt2='')my_func('a', 'b', opt2='c')

2.4 位置参数必须在前面

到目前为止,它非常灵活,对不对?现在,有一个重要的规则。也就是说,所有位置参数必须在所有关键字参数之前。

# my_func(man1, man2, opt1=0, opt2='')my_func(man1='a', 'b', 10, opt2='c')
my_func(man1='a', man2='b', opt1=10, 'c')

以上两个函数调用无效。

Python 不接受这一点是有道理的。假设我们有四个参数p1p2p3p4

  • 如果我们有abp4=d的论点,那么很明显p1=ap2=bp4=d
  • 如果我们有参数p2=bc,那么参数c应该传递给p1p3会非常混乱。它需要更多人为的规则来解决这个问题,开发者需要记住它。

2.5 强制关键字参数

Python 中有一个技巧,可以强制函数调用使用关键字参数而不是位置参数。我们可以在函数参数列表中添加一个星号,右边的任何参数只能通过关键字实参传递。

def my_func(man1, *, man2):
    print(man1)
    print(man2)

如果我们试图对它们都使用位置参数,就会出现错误。

my_func('a', 'b')

但是,如果我们对man2使用关键字参数,就不会有任何问题。

my_func('a', man2='b')

3.可变长度参数

图片来自 Pixabay鲁迪和彼得·斯皮特林

在 Python 中,我们可以定义两种长度可变的参数。它们可以被认为是可选参数的特殊类型。换句话说,当我们调用这个函数时,传递给参数的参数数量没有限制。

3.1 可变长度参数— *args

当我们要定义一个变长参数时,参数*arg就是这样一个语法。

def my_func(*args):
    print(args)

当我们调用这个函数时,我们可以传递任意多的参数。它们将作为元组在函数中被访问。

3.2 关键字变长参数— **kwargs

另一种类型的可变长度参数是关键字。名称kwargs代表关键字参数。

def my_func(**kwargs):
    print(kwargs)

当我们调用函数并希望将参数传递给kwargs参数时,我们需要为每个参数指定键和值。

如示例所示,参数将作为字典接收。

还有一个限制是我们不能使用任何带特殊参数的键。基本上,键必须符合 Python 变量名的语法。

3.3 组合关键字和非关键字变长参数

我们可以在函数中使用关键字和非关键字的变长参数。但是,不允许将关键字一放在非关键字一的前面。

# invaliddef my_func(**kwargs, *args):
    print(kwargs)
    print(args)

反过来也可以。

def my_func(*args, **kwargs):
    print(args)
    print(kwargs)my_func('a', 'b', 'c', name='Chris', age=33)

这与我们不能在任何位置参数前有关键字参数的规则非常相似。

3.4 可以重命名“*args”和“**kwargs”

我发现有些学习者不知道我们可以改变这两个变长参数名的名称。他们认为这些是 Python 中的保留关键字,并作为语法强制执行。

其实那也不确定。我们可以用他们的任何名字。唯一重要的是对可变长度参数使用单星号,对关键字参数使用双星号。

def my_func(*whatever, **does_not_matter):
    print(whatever)
    print(does_not_matter)my_func('a', 'b', 'c', name='Chris', age=33)

4.组合所有类型的参数

图片来自 PixabayGundula Vogel

4.1 关键字变长参数必须是最后一个

我们可以在一个函数中使用所有 4 种类型的参数。当然,也有一些强制规则。

因为关键字可变长度参数要求我们用关键字传递参数,所以它必须在参数队列的末尾。

# Invaliddef my_func(**kwargs, man):
    print(args)
    print(kwargs)def my_func(**kwargs, opt=0):
    print(args)
    print(kwargs)

4.2 使用所有 4 种类型时的陷阱

当我们在一个函数中使用所有四种类型的参数时,有一个陷阱需要特别小心。

假设我们将函数定义如下。

def my_func(man, opt='default', *args, **kwargs):
    print(man)
    print(opt)
    print(args)
    print(kwargs)

当我们调用这个函数时,可选参数和变长参数很容易混淆。考虑下面的函数调用。

my_func('mandatory value', 'a', 'b', 'c', name='Chris', age=33)

a被认为是可选参数的值。在这种情况下,可选参数不再是可选的。如果我们希望将abc都传递给args,那么下面的函数调用是有效的。

这个问题可以通过在定义函数时调整参数的位置来解决。

def my_func(man, *args, opt='default', **kwargs):
    print(man)
    print(opt)
    print(args)
    print(kwargs)

如果我们改变argsopt的位置,我们可以如下调用函数,使得可选参数仍然是可选的。

my_func('mandatory value', 'a', 'b', 'c', name='Chris', age=33)

摘要

图片来自皮克斯拜

在本文中,我介绍了 Python 中所有 4 种类型的参数和 2 种类型的实参。参数是函数中的变量,而实参是调用函数时传递给参数的值。

这些参数可以是强制的,也可以是可选的。对于可选参数,它们可以有不同的长度,可以有也可以没有关键字。变元可以是位置变元,它依赖于用值决定哪个参数的位置。或者,我们可以用关键字显式传递值,这是关键字参数。

https://medium.com/@qiuyujx/membership

如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和成千上万的其他作者!(点击上面的链接)

结合数学优化和机器学习的五种方法

原文:https://towardsdatascience.com/four-ways-to-combine-mathematical-optimization-and-machine-learning-8cb874276254

泰勒·西伯特在 Unsplash 上的照片

两种力结合的实例。

你是数据科学家吗?还有你知道怎么用数学优化和机器学习吗?你曾经在一个项目中把它们结合起来吗?写这篇文章是为了给你一些启发,并鼓励你下次遇到可以使用它们的问题时尝试一下。对于每个数据科学家来说,至少了解一点数学优化是很重要的。因为如果实际问题是一个完美的优化用例,你就不应该创建一个复杂的机器学习解决方案。

数学优化和机器学习

数学优化(或数学规划)是一个强大的决策工具。通过将目标公式化并指定约束和变量,数学优化可以帮助在给定当前现实环境的情况下做出最佳决策。它已经在许多不同的行业证明了自己的价值,比如航空业、物流业、电力业和金融业。如果你正在处理一个有明确目标和约束的问题,数学优化可能是一个很好的选择。

机器学习是人工智能的一个子领域。计算机可以识别数据中的模式,并学会预测未来,可以进行聚类,检测异常或生成新的音乐或图像。可能性是无限的。三种类型的机器学习(监督、非监督和强化学习)可以应用于所有行业,如医疗甚至艺术。机器学习模型都是关于概率的,预测会发生什么的概率。

这两种方法各有利弊。当数据变化太大时,机器学习模型变得毫无用处,模型需要重新训练或从头重建。数学优化需要清晰的说明和良好的数学描述。它不能像机器学习那样处理非结构化数据。此外,如果问题变得太大,你可能需要一个商业解决方案来解决问题,这可能是相当昂贵的。

有些问题更适合机器学习,有些情况下数学优化更适合。当你想要发现数据中的模式,找到相似的数据样本或者预测天气时,你应该使用机器学习。如果您想创建一个时间表,找到设施的最佳位置或最小化某个问题的成本,数学优化是更好的选择。当错误的方法被应用时,它是无效的,并且它可以从正确的方法开始为你节省大量的时间和挫折。

ML & MO 怎么组合?

将数学优化和机器学习结合起来会很有用。它们各有优缺点,有些问题太复杂,不能只用两者中的一个。它们可以相互补充。这里有五种方法,通过实例告诉你如何将它们结合起来。

1.使用机器学习预测作为优化模型中的约束

这是将 ML 和 MO 结合起来的最简单直接的方法。首先,你使用机器学习来预测一些事情,这些预测被用作优化问题的输入。您可以使用机器学习模型的输出来设置约束。

举例:用 ML 预测收视数字,用它们作为输入,用优化 你是一家广播公司的数据科学家,你向公司出售广告位。广告客户购买播出时间,广播公司根据有根据的猜测来判断有多少人会看到广告客户的广告。作为一名数据科学家,你希望尽可能最好地利用广告空间。你要给广告商准确的观众数量,这样你就不会浪费本可以卖给别人的播放时间。首先,你使用机器学习根据历史数据预测观看人数。然后,您创建一个优化模型,该模型使用查看数字作为输入。你通过给每个广告客户他们想要的观看数字的精确数量来优化时间表。通过这样做,你可以最大化公司的利润。

网飞没有广告!😀(atm)照片由自由股票Unsplash 拍摄

2.使用优化决策作为机器学习模型中的训练特征

与方法 1 相比,这是另一种方式:首先,优化模型做出决策,这些决策被用作机器学习模型中的特征。实际上,这种方法不太常见,因为大多数情况下,决策(MO)是在预测(ML)之后。但是这是可能的,并且可能在特定的项目中有用。

例子:在机器学习模型中使用运输决策 数学优化在物流中被广泛使用。如果你使用最优化来决定你需要从生产工厂运送多少物资到市场,它可以节省大量的时间、金钱和资源。模型的结果包含工厂和市场之间的发货数量。获得这些结果后,您可以将它们用于机器学习问题,例如预测某一天每个工厂需要多少员工。

UnsplashMarcin Jozwiak 拍摄的照片

3.使用机器学习输出来确定数学优化模型的范围

除了在优化问题中直接使用机器学习输出之外,您还可以选择更单独地组合它们。您在同一个项目中使用它们,但不是在同一个过程中。您可以使用机器学习输出来缩小数学优化问题:您可以通过机器学习来确定优化模型的范围。一个额外的好处是优化模型可以在更短的时间内解决。

示例:使用预测性维护和聚类来确定路由问题的范围 对于此示例,我们看一家修理电源箱的公司。他们可用的维修人员数量有限,并且希望以最佳方式使用他们。首先,你可以使用预测性维护(机器学习)来决定哪些电源箱的故障风险最高。然后,您使用聚类(机器学习)来对高风险电源箱进行聚类。你群集是因为你希望各组电源箱彼此靠近。您可以选择与可用维修人员数量相等的集群数量。最后,通过数学优化,您可以在每个集群的电源箱之间创建最佳路径,每个修理工一条路径。

涂鸦的电源盒。卡塔林·ApostolUnsplash 上拍摄的照片

4.用最优化解决机器学习研究问题

这是一个使用优化来帮助解决机器学习挑战的研究领域。您可以使用优化来为机器学习问题找到一组最佳参数。ML 和 MO 在这里紧密集成,因为您在 ML 问题内部使用了优化。下面的例子表明,混合整数规划(MIP)已经证明了它在解决一个经典研究问题中的价值。

举例:解决线性回归中的最佳子集选择问题 在建立回归模型时,去除不相关的特征会使模型更容易解释,更不容易过度拟合数据。很难找到特征的最优子集,称为最佳子集选择问题。在过去的几十年里,混合整数规划(数学优化)在速度上有了显著的提高,这使得在现有问题上测试它变得非常有用。在这个问题上进行了测试,结果很好

照片由艾米丽·莫特Unsplash 上拍摄

5.让一个机器学习模型从许多类似的优化问题中学习

你可以将机器学习和优化结合起来的最后一种方式如下:如果你有一个反复解决同一类型问题的优化模型,那么让机器学习帮助你是可能的。通过在每次优化模型运行时存储数据和解决方案,您可以为应用机器学习建立一个有趣的数据库。本文提出了使用机器学习的不同方式。可以预测最佳目标值,预测决策变量的值(参见示例),或者加速算法的性能。机器学习和数学优化的结合仍然是一个活跃的研究领域。

示例:预测医院网络中血液单位的最佳转运 对于医院来说,保持良好的血液库存水平非常重要。每天,血液都在邻近的医院之间运送,以保持足够高的水平。这个问题的变量是关于运送指令:在医院之间运送一定数量的某种血型。
在数学优化中,如果为模型提供决策变量的良好初始值,这个问题可以更快地解决。这叫做热启动。不能保证最终的解决方案会使用这些变量的值。一切皆有可能,这只是一个不错的第一解决方案。如果你将数据与使用数学优化找到的最优解存储在一起,一个机器学习模型可以试图找到数据和目标之间的模式。该模型将尝试预测决策变量的最佳值。如果机器学习模型找到好的值,这可以加快优化过程。在一个完美的世界里,完全跳过优化部分是可能的。这项研究的结果很有希望。预测血液运输案例的机器学习模型在决策变量之间具有高达 98%的相似性,并且约束几乎总是得到实施。

结论

希望这篇文章能启发你尝试数学优化和机器学习的有趣结合!它们适用于不同类型的问题,可以互为补充。除了显而易见的方法(使用一个的输出作为另一个的输入),还有其他方法可以组合它们。你可以将它们松散地结合起来,就像第三种方法一样,使用机器学习来确定优化问题的范围。或者你可以将它们紧密结合起来解决研究问题,如第四个例子所示。

对于任何数据科学家来说,至少了解一点数学优化都是有益的。以下是一些相关文章,供您参考:

内核大小为 1024x1024 或更大的傅立叶卷积

原文:https://towardsdatascience.com/fourier-cnns-with-kernel-sizes-of-1024x1024-and-larger-29f513fd6120

卷积神经网络中的多维傅立叶变换

照片由 Edz NortonUnsplash 上拍摄

C 最近,选择性神经网络(CNN)得到了广泛的应用。不管它们是否成功,卷积都是低效的。滑动窗口需要许多计算,并且限制了内核的大小。同时,通常在[3,3]到[7,7]之间的小核限制了感知场,并且需要许多层来捕捉输入张量的全局背景(例如 2D 图像)。图像越大,小滤镜的效果越差。这就是为什么你很难找到输入高分辨率图像的 CNN。

如果我告诉你有一种方法可以将内核大小扩展到[1024,1024]甚至更大,有一种方法可以在给定的输入分辨率下增加内核大小,而对推理时间几乎没有影响,如果有一种方法可以在不丢失几乎任何信息的情况下大幅降低特征图的空间维度,又会怎么样呢?

所有这些承诺都基于一个简单的数学属性:傅立叶变换的卷积定理(准确地说是互相关定理),我将向您展示如何正确地利用它!

注意:随着这篇文章,我在我的 GitHub 上发布了一个包含所有代码的笔记本

概述

  1. 卷积的缺陷
  2. 2D 离散傅立叶变换结果
  3. 发现 2D FFT 频谱
  4. 在 TensorFlow 中实现
  5. 结论
  6. 进一步阅读和链接

卷积的缺陷

让我们回顾一些基础知识。卷积是应用于两个函数的数学运算。让我们从一维情况开始:

1D 卷积,(上)连续 1D 卷积,(下)离散 1D 卷积

换句话说:取两个信号,一个保持原样,另一个绕坐标轴翻转。将固定信号上的翻转信号从负无穷大移动到正无穷大(或者直到信号的所有非零部分都已重叠)。对于每个步骤,计算元素乘积并对所有值求和。结果值是该特定步骤的卷积结果。卷积也可以应用于二维信号(例如图像):

2D 卷积,(上)连续 2D 卷积,(下)离散 2D 卷积

但是为什么我之前提到了互相关呢?这是因为卷积和互相关实际上是以相同的方式计算的,唯一的区别是滤波器被翻转了。这由不同的符号表示:

(上)离散卷积和(下)离散互相关之间的比较

TensorFlow 和 PyTorch 实际上是在计算输入信号和可学习滤波器的互相关,而不是卷积。有关系吗?不尽然!由于滤波器是由网络学习的,所以滤波器是否翻转并不重要。网络会自己找出什么是最好的。不翻转过滤器甚至可以节省一些计算。但是,如果过滤器是固定的,这意味着如果您加载一个训练好的模型,您应该知道它是使用互相关还是卷积来训练的,并且最终必须翻转过滤器的权重。

你应该记住两件事:

  1. 需要大量的计算来计算输出序列中的单个点。
  2. 输入信号越大(即图像的分辨率越高),滤波器必须移动得越频繁,因此需要更多的计算。这同样适用于较大的过滤器。

更多的计算意味着更多的内存和更长的等待时间,直到结果可用。CNN 的后果是显而易见的:更低的输入分辨率和更小的滤波器。问题:更少的像素意味着更少的细节,更小的过滤器导致更小的感受野。网络需要有多个连续的卷积层,以增加接收域。网络变得更深,这又给培训带来了新的挑战。

卷积神经网络感受野的增长

2D 离散傅立叶变换来拯救

从数学上讲,时间变量 t 的实函数或复函数 x(t)的傅立叶变换是实频率变量 f 的复函数 X(f ):

傅里叶变换

你也可以说我们将信号从时域投射到频域。通过这样做,我们可以受益于傅立叶变换的特殊性质,即卷积定理和相关定理。

傅立叶定理,(上)卷积定理,(下)互相关定理

这些特性非常重要,也是本文的基础:时域中的卷积/相关对应于频域中简单的逐元素乘法。但这有什么好激动的呢?如前所述,卷积需要很多计算,特别是对于大图像和过滤器。其复杂度与序列长度成二次比例,即 O(N)。根据卷积定理,我们只需要对变换后的输入和变换后的滤波器执行逐元素乘法。存在有效的算法来计算傅立叶变换,即快速傅立叶变换(FFT),其将复杂度降低到 O(N log(N))。而最好的部分,只要滤波器作为输入信号更小,计算要求是不变的。不管我们的过滤器的内核大小是[3,3]还是[1024,1024]。在 TensorFlow 中实现的部分中的细节。

傅立叶变换也存在于实数或复数离散信号 x[k]中,其分配实数变量 n 的复数离散信号 X[n]:

离散傅立叶变换,(上)1D 离散傅立叶变换,(下)2D 离散傅立叶变换

离散傅立叶变换(DFT)注定用于数字信号处理,因为计算机以离散值存储信号。

注意:离散信号是时间离散和数值离散的。时间离散型,因为它以特定的时间间隔进行采样;数值离散型,因为每个值都由特定数量的位表示,例如 INT32 为 32 位。

在使用 DFT 时,我们需要记住一些含义:

  1. 假设输入信号是周期性的,并且采样一个完整的周期
  2. 产生的光谱是周期性的

注:图像可以被解释为空间信号,而不是时间信号。在计算机上,图像是空间离散的,因为值存储在像素中,这些像素是从具有空间分布的单元的图像传感器中采样的或者被数字化的。

二维 DFT(以及 2D 连续傅立叶变换)可以分成连续的 1D DFT,其中行和列可以分别计算。

2D DFT 在连续的 1D DFT 中分离

这至少有两个优点:第一,可以重复使用 1D DFT 的算法,第二,有助于建立 2D DFT 的直觉,因为行和列可以单独解释。

但是当然,离散傅立叶变换有一个小细节:卷积定理不适用于 DFT。

两个信号的 DFT 的乘积对应于由算子⊛表示的它们的循环卷积,而不是它们的线性卷积。

DFT 的循环卷积定理

循环卷积是以信号长度 N 重复的周期性信号,而线性卷积的长度为(N+F-1),其中 F 是滤波信号的长度。因此,如果您盲目地在频域中求乘积,您会将长度为(N+M-1)的信号压缩到长度为 N 的信号中,这可以被视为时域中的混叠,从而在最终结果中产生不需要的伪像。幸运的是,循环卷积和线性卷积共享一些值,即(N-F+1)。剩余的(F-1)值被绕回并干扰信号的其他值。

这里有一个想法:如果被包装的值干扰的值为零,意味着没有干扰,那会怎么样?这意味着我们可以从循环卷积中重建线性卷积。当信号至少用(F-1)零填充时,包装值不会干扰真实值。然后,我们可以循环地将包装的值移回它的位置,并裁剪填充的值。详细信息将在实施部分显示。

我不再赘述所有的数学细节,并在文章末尾链接了更多的资源。

发现 2D DFT 频谱

现在我们已经讨论了理论,让我们发现一些 2D 傅立叶变换,并加强我们对 2D 傅立叶变换的直觉。

基本测试信号及其对细胞神经网络的影响

考虑像素强度遵循对角正弦波的图像。你期望什么样的振幅谱?如前所述,2D 傅立叶变换可以通过将 2D 傅立叶变换沿着图像的每个轴分成多个 1D 傅立叶变换来计算。如果你想象沿着横轴行走,你会遇到重复的模式。如果你想象沿着纵轴行走,情况也是如此。因此,在第四象限(右下),即频率成分为正值的象限内,很自然地会出现高频谱值。

对角正弦输入图像的 2D DFT(上图)图像图,(下图)作者的 3D 图

注意:2D 振幅谱在绘制时通常使用对数函数进行缩放,因为图像无论其内容如何都具有高偏移,因为它们通常以无符号整数表示,而无符号整数仅表示正值。

现在,让我们考虑具有不同边长的矩形的输入图像。如果你再次想象沿着每个轴走,你将在水平轴上遇到一个具有短脉冲宽度的矩形,在垂直轴上遇到一个具有较宽脉冲宽度的矩形。如果你熟悉信号理论,你会立即想到你的频谱有某种 sinc 函数,其中 sinc(x)=sin(x)/x。

不同边长矩形的 2D DFT

如果你期待一个正弦函数,你是完全正确的。频谱由沿两个轴的正弦函数组成。这里可以做一个 基本面观察 : 横轴作为纵轴有较高的频率成分,过零点在横轴更分散。这一观察有两层含义:

  1. 输入图像中的窄空间特征在振幅谱中具有高频分量,因此它们具有高带宽。高带宽滤波器容易产生噪声。
  2. 光谱与输入图像中要素的空间长度成反比。窄特征导致宽光谱,宽特征导致窄光谱。

这对于我们的 CNN 来说意味着什么呢?根据我们上面的观察,这应该意味着具有小滤波器的CNN 充当高带宽滤波器,因此容易产生输入噪声 滤波器尺寸越大,滤波器带宽越低,选择性越强。

图像的 2D DFT 和频域滤波

既然我们已经讨论了一些基本信号,让我们研究真实图像的 2D DFT。

作者的图像的 2D DFT

频谱的中心表示频率为零,也称为偏移。离中心越远,输入中的频率成分越高。记住这一点,你可以很容易地得出一个高通滤波器和一个低通滤波器。

高通滤波器抑制低频,保留高频成分。这种行为可以通过这样的滤波器来实现,该滤波器在靠近中心的位置具有 0,而在远离中心的位置具有 1。通过将滤波器与频谱相乘,然后计算傅立叶逆变换来应用滤波器。

作者的高通滤波器

如上图所示,高通滤波器可以用作边缘检测器。图像中的边缘的特征在于像素值的突变,因此它具有高梯度。梯度越高,涉及的频率越高。

另一方面,低通滤波器抑制高频分量,保留低频分量。低通滤波器可以通过在中心区域为 1 而在外部区域为 0 的掩模来实现。

作者的低通滤波器

经过低通滤波的图像看起来模糊不清,失去了清晰度。计算机视觉中使用的典型滤波器是高斯滤波器。它也是一个低通滤波器,但对于较高的频率,滤波效果逐渐增强,而不是突然截止频率。它使图像平滑。下图由高斯滤波器滤波,其方差(sigma 平方)等于之前圆形低通滤波器的截止频率。

作者的高斯滤波器(平滑)

图像模糊,但失真较少。它看起来更光滑。

机器学习应用的一个非常有趣的过滤器是矩形过滤器。卷积神经网络通常逐渐减小空间宽度并增加通道数量。汇集,如最大汇集或平均汇集经常被用来减少空间宽度。如果我们在频域中汇集呢?

作者的矩形过滤器

通过在频域中应用矩形滤波器,我们可以彻底去除频率分量,而不会对空间域中的图像质量产生大的影响。

DFT 对于实输入有一个有趣的性质:它关于原点共轭对称。对称性意味着频谱包含冗余,在计算过程中可以忽略这些冗余,以进一步加快处理速度。下图显示了这种变换及其从光谱重建的逆变换。

作者的图像的 2D 快速傅立叶变换

从本节中取走:

  1. 低频成分在 2D 频谱的中心,而高频成分远离中心
  2. 您可以利用 DFT 的对称性来减少所需的计算资源,只计算 rDFT。
  3. 小尺寸滤波器由于其高带宽而易于产生噪声。

这是我为生成这些图而创建的笔记本:

https://github.com/sascha-kirch/ML_Notebooks/blob/main/2D_FFTs.ipynb

TensorFlow 中的实现

利用离散傅里叶变换实现线性卷积,我们已经具备了所有条件。

概括地说,我们需要实施以下 6 个步骤:

  1. 填补输入图像以避免时域中的混叠
  2. 对图像大小进行填充过滤,以准备元素乘法
  3. 计算输入图像和滤波器的 2D rFFT
  4. 变换输入和变换滤波器的逐元素乘法
  5. 计算滤波输入的 2D 逆 rFFT 以获得循环卷积
  6. 从循环卷积重建线性卷积

步骤 1 —填充输入图像

为了避免时域中的混叠效应,我们需要用至少(F-1)零点来填充图像,其中 F 是滤波器的边长。此外,计算 DFT 的 FFT 算法对于信号长度为 2 的幂(例如 128,512,1024)特别有效。

至少有两种选择来填充输入图像:首先,我们手动填充图像。其次,我们将 FFT 的序列长度设置为填充信号的长度。我更喜欢后者。

下面的代码手动填充图像。

这里是我在计算 FFT 时指定更高序列长度的首选方法:

# image is of shape [b,c,h,w]
padding **=** GetImagePadding(filter_shape[0]) 
image_shape **=** (input_shape[0],
               input_shape[1],
               input_shape[2]**+**2*****padding,
               input_shape[3]**+**2*****padding)image_shape **=** FillImageShapeToPower2(image_shape)

F_image **=** tf**.**signal**.**rfft2d(image, fft_length**=**[image_shape[**-**2],image_shape[**-**1]])

步骤 2-根据图像大小填充滤镜

因为我们需要用变换后的滤波器计算变换后图像的元素乘积,所以在计算傅立叶变换之前,我们需要将滤波器填充到填充后图像的大小。过滤器用零填充。同样,我建议通过正确设置 fft 计算的 fft _ lenght 参数来填充滤波器,即

F_filter **=** tf**.**signal**.**rfft2d(filter, fft_length**=**[image_shape[**-**2],image_shape[**-**1]])

步骤 3 —计算 2D rfft

我们已经准备好输入信号,现在可以计算填充图像和填充滤波器的 FFT:

# Image shape [b,c,h,w], Filter shape [out, in , k, k]
F_image **=** tf**.**signal**.**rfft2d(image, fft_length**=**[image_shape[**-**2],image_shape[**-**1]])F_filter **=** tf**.**signal**.**rfft2d(filter, fft_length**=**[image_shape[**-**2],image_shape[**-**1]])

我们利用实输入的共轭对称性,仅使用 rfft2d 计算 2D 信号的实 FFT。具体来说,我们输入无填充信号,并将 fft_length 设置为大于输入长度的值。这将自动用零填充信号。

重要 : TensorFlow 对 rfft2d 的实现计算输入的最后两个维度上的 fft。不像在 numpy 的实现中,你不能通过参数改变维度。因此,图像的形状是[批次、通道、高度、宽度],内核的形状是[输出过滤器、输入过滤器、内核高度、内核宽度]

如果我们在频域中执行卷积,我们就完成了。由于 TensorFlow 实际上实现了互相关,因此我们需要对变换后的滤波器进行共轭,以获得一致的结果:

F_filter **=** tf**.**math**.**conj(F_filter)

步骤 4—变换图像和变换滤波器的相乘

如果我们只有单个图像和单个过滤器,则逐元素乘法将简单地为F_image*F_folter。在一个真实的场景中,我们通常以批处理的形式有多个图像,并且我们并行地应用多个过滤器。我们需要重新排列输入信号的维度,并利用阵列广播来执行此操作,而不涉及任何循环。

TensorFlow 的 einsum() 函数可用于轻松重塑维度。箭头左边的字符描述输入形状,右边的字符描述输出形状。图像和过滤器的尺寸以这样一种方式重新排列,即当计算元素乘积时,所有批次和所有输出过滤器将被广播。乘法之后,通过整形和减小输入滤波器维度来恢复初始形状。

步骤 5 —计算逆 2D rFFT

逆 FFT 简单地取自具有与 FFT 相同的 fft_length 参数的滤波信号:

out **=** tf**.**signal**.**irfft2d(filterd_image, fft_length**=**[image_shape[**-**2],image_shape[**-**1]])

步骤 6-从循环卷积重建线性卷积

记住,要从循环卷积重建线性卷积,我们需要执行两个步骤:首先,我们需要按照填充量循环移位结果。第二,我们需要截断到我们感兴趣的区域,所有批次的图像的初始形状和新的通道数。

#Circular shift
out **=** tf**.**roll(out,shift **=** [2*****padding,2*****padding],axis **=** [**-**2,**-**1]) #Truncation
out **=** tf**.**slice(out, 
               begin **=** [0, 0, padding, padding], 
               size**=**[input_shape[0], 
                     filter_shape[**-**1],  
                     input_shape[2], 
                     input_shape[3]]
               )

仅此而已!
下面的代码显示了步骤 1 到 6 的完整实现:

注意:傅立叶域中的卷积也以 TensorFlow 层的形式实现,作为我的 DeepSaki 包的一部分,可以在 GitHub 上找到或从 PyPi 下载。

确认

你可能会问自己:但是这真的有用吗?一起来看看吧!所有验证步骤都包含在本文的笔记本中。

首先,我们将查看两个函数的内核大小的执行时间(秒): tf.nn.conv2d() 和我们的实现。

2D 卷积和 2D DFT 卷积的执行时间与内核大小。图像大小:550x550x1,批次:1,过滤器:1(作者)

正如您所料,2D 卷积的执行时间会随着内核大小的增加而增加。另一方面,无论滤波器大小如何,2D DFT 卷积在执行时间上是恒定的。这是因为滤镜被填充到图像的大小。如果过滤器越大,填充的值就越少。

现在让我们来看看调查结果中的差异。为此,我们在 720x720 像素的图像上应用大小为 3x3 的具有 8 个过滤器的内核。我们通过两种算法运行它,并计算绝对差的平均值和标准偏差。

convResult **=** CalcConv(image, filter)
dftResult **=** CalcDFT2D(image, filter)

error **=** tf**.**math**.**abs(convResult**-**dftResult)
mean = tf**.**math**.**reduce_mean(error)
std = tf**.**math**.**reduce_std(error)# Mean Absolute Error: 0.001560982083901763
# Standard deviation: 0.0015975720016285777

均值和标准差都很低。这种差异来自于数字的不准确性。当观察滤波后的图像和相应的振幅谱时,我们可以看到它们是不可区分的。

过滤图像和 2D 光谱的直接比较。(上)2D 线性卷积(下)2D DFT 卷积作者

结论

我们已经了解了卷积和 DFT 背后的数学基础,通过观察不同的光谱获得了一些直觉,查看了 TensorFlow 中的代码,并最终验证了结果的正确性。

设计在频域而不是空间域工作的层带来了新的机会,特别是对于大输入图像和大滤波器尺寸。绕过频域似乎有悖直觉,但实际上加快了计算速度。

进一步阅读和链接

https://github.com/sascha-kirch/ML_Notebooks/blob/main/2D_FFTs.ipynb https://github.com/sascha-kirch/DeepSaki https://pypi.org/project/DeepSaki/

傅立叶变换,应用(5):傅立叶备忘单

原文:https://towardsdatascience.com/fourier-transform-applied-5-fourier-cheatsheet-f73a0c8eac04

Numpy 的傅里叶变换函数之旅

为了结束这个系列,让我们回顾一下最常见的数字傅里叶变换函数。

查看该系列的前几篇文章!

照片由阿尔方斯·莫拉莱斯Unsplash 拍摄

微妙的区别

到目前为止,在我们的讨论中,我已经交替使用了“傅立叶变换”和“快速傅立叶变换(FFT)”。在这一点上,值得注意的区别!FFT 是“离散”傅立叶变换(DFT)的有效算法实现。“离散”表示我们可以将变换应用于一系列点,而不是完整、连续的信号。在数据科学应用中,我们通常有一组样本,而不是连续的输入函数,所以我们通常对 DFT 感兴趣!

傅立叶变换函数

np.fft.fft

FFT 将时域或空域中的实数或复数信号作为输入,并返回离散傅立叶变换。如前所述,np.abs 允许我们恢复频率成分的幅度,np.angle 允许我们恢复相位。

我们以前见过这个函数(见本系列早期的故事)!我们会回到这个注意,但它是足够重要的说两次!傅立叶变换可以应用于复杂的输入信号。对于复数输入,傅里叶变换返回的负频率项是完全重构信号所必需的。对于实际输入——就像我们在本系列中讨论过的输入——只需要正频率项。您仍然可以对实值信号使用完整的 FFT,只需知道您可以使用重复值更少的 RFFT(参见下文)。

索引 0: 第一个值有时被称为“DC”项(来自电气工程领域的“直流”),并用作偏移项。它不振荡,相当于 0Hz(即 DC)。它只是信号的总和!

索引 1 到((N/2) -1)如果 N 是偶数,否则索引 1 到((N-1)/2): 正频率分量按递增的正顺序排列。

索引(N/2)到(N-1)如果 N 是偶数,否则((N+1)/2)到(N-1): 负频率分量按负的递减(即正的递增)顺序排列。

提示!如果您对哪些元素对应于哪些频率感到困惑,请查看 np.fft.fftfreq!它会告诉你频率的顺序,例如 np.fft.fftfreq(5)告诉我们频率仓中心为 0。、0.2、0.4、-0.4 和-0.2。

np.fft.ifft

IFFT 将傅立叶变换作为输入,并返回时域或空域中的实数或复数重构信号。

如前所述,逆 FFT 允许我们从频域转换回时间/空间域。不出所料,如果我们将 IFFT 应用于信号的 FFT,我们又回到了起点。

IFFT(FFT(x)) ≈ x,逆性质成立!

np.fft.fftshift

FFT 变换将傅立叶变换作为输入,并将值从“标准”顺序重新排序为“自然”顺序:最负到零到最正。

如果组件按自然顺序排序,那么对 FFT 结果进行可视化和推理会容易得多。标准订单可能会非常混乱!

FFTSHIFT 从最负到最正排列频率中心。

np.fft.ifftshift

FFT 变换将傅立叶变换作为输入,并将值从“自然”顺序重新排序为“标准”顺序:DC 项,然后是正频率,然后是负频率。

IFFTSHIFT 恢复“标准”顺序。

np.fft.rfft

RFFT 将时域或空域中的实信号作为输入,并返回离散傅立叶变换。

本文前面提到过,实信号的 FFT 有一个有趣的特性:正负频率分量互为镜像。形式上,这意味着实信号的傅立叶变换是“厄米变换”这其中的数学原因非常有趣,我希望在以后的文章中对此进行详细阐述。不过现在,我们只需要注意到这是真的。RFFT 允许我们跳过这些多余的术语!

RFFT 利用厄米对称性来跳过重复的负频率分量。

np.fft.irfft

IRFFT 将实值函数的傅立叶变换作为输入,并在时域或空域中返回实重构信号。

IRFFT(RFFT(x)) ≈ x,逆性质成立!

NP . FFT . FFT T2

FFT2 将时域或空域中的实数或复数 2D 信号作为输入,并返回离散傅立叶变换。如前所述,np.abs 允许我们恢复频率成分的幅度,np.angle 允许我们恢复相位。

之前的文章中,我们展示了我们可以使用相同的逻辑来分解 2D 信号中频率成分的幅度和角度(比如图像!).np.fft.fft2 允许我们这样做:计算输入的二维快速傅立叶变换。

np.fft.ifft2

IFFT 将傅立叶变换作为输入,并返回时域或空域中的实数或复数 2D 重构信号。

正如所料,我们有一个类似的二维输入的逆变换!

IFFT2(FFT2(x)) ≈ x,2D 逆性质成立!

np.fft.fftn

我们可以将 FFT 扩展到三维、四维、五维输入!事实上,对于一个 n 维输入,有一个 n- 维 FFT…

np.fft.ifftn

…以及相应的一个 n 维逆变换。

IFFTN(FFTN(x)) ≈ x,n-D 逆性质成立!

感谢您的参与,我们已经完成了这个系列!接下来我想写一些关于傅立叶变换实现的内容。如果你感兴趣,或者有其他概念需要我解释,请在下面留下评论。

你可能也会对我其他一些关于傅立叶直觉的文章感兴趣!

用于数据科学和机器学习的免费云 CPU

原文:https://towardsdatascience.com/free-cloud-cpus-for-data-science-and-machine-learning-e4a7fc13043e

含电池。附带 Jupyter 笔记本和预装的 python 包

卡斯帕·卡米尔·鲁宾在 Unsplash 上的照片

介绍

我们选择在本地机器上使用云 CPU 而不是 CPU 的原因有很多。

  1. CPU 工作负载:第一个也是最明显的优势是使用云 CPU 可以从本地机器上释放 CPU 工作负载。如果您的本地计算机中有一个旧的 CPU,这将是非常有益的。
  2. 代码共享:使用托管在云中的 Jupyter 笔记本使得代码共享更加容易。只需将其公开并共享笔记本的 URL。
  3. 存储:这些平台还为您的数据提供存储空间,这有助于释放本地机器中的存储空间。

在这篇文章中,我们将看看 3 个平台,它们为您的机器学习项目提供免费和无限制的 CPU 使用时间。

数据砖

Databricks 是数据科学家、分析师和工程师用于开发和部署 ETL 管道、机器学习模型和数据分析的数据科学、数据工程和机器学习平台。Databricks 提供免费的社区版帐户,您可以通过云托管的 Jupyter 笔记本(又名 Databricks 笔记本)获得自己的工作空间。要注册 Databricks 社区版:

  1. 转到:https://databricks.com/try-databricks
  2. 填写您的详细信息

作者图片

3.点击“社区版入门”

作者图片

4.验证您的电子邮件地址

创建集群

登录后,您将看到以下主页。我们需要一个活跃的集群来开始处理数据集。让我们创建一个。

  1. 转到左侧面板,单击“计算”

作者图片

2.点击“创建集群”

作者图片

3.为集群命名,选择 Databricks 运行时版本,然后单击“创建集群”

作者图片

Community edition 用户有权使用一个 15GB RAM 的驱动程序节点(2 个内核),而没有工作节点。Databricks ML 运行时支持 Scala、Spark (Pyspark)并预装了常用的数据科学 python 包,如 pandas、numpy、scikit-learn 等。

上传数据

  1. 让我们以虹膜数据集为例。我们可以从 UCI 资源库下载。
  2. 提取 zip 文件。数据是 CSV 格式的。
  3. 在 Databrick 的左侧面板上,单击“数据”选项卡

作者图片

4.要将 CSV 文件上传到 Databricks,请单击“上传文件”

5.浏览选择文件或简单地将其拖放到灰色框中

6.单击“在笔记本中创建表格”

作者图片

7.在笔记本单元格中,将infer_schemafirst_row_is_header更改为True,将delimiter更改为;

# File location and type
file_location = "/FileStore/tables/bank_full.csv"
file_type = "csv"# CSV options
infer_schema = "True" # change to True
first_row_is_header = "True" # change to True
delimiter = ";" # Change to ;# The applied options are for CSV files. For other file types, these will be ignored.
df = spark.read.format(file_type) \\
  .option("inferSchema", infer_schema) \\
  .option("header", first_row_is_header) \\
  .option("sep", delimiter) \\
  .load(file_location)

8.在最后一个单元格中,您可以使用变量permanent_table_name命名表格,并将数据帧df写入表格

permanent_table_name = "bank_marketing"
df.write.format("parquet").saveAsTable(permanent_table_name)

9.这将在左侧面板的data选项卡下创建一个新表

作者图片

10.现在,我们可以在新笔记本中使用该表。转到左侧选项卡中的Create并创建一个笔记本。给笔记本分配一个NameCluster

作者图片

11.将表格读入新笔记本

作者图片

分享笔记本

要共享笔记本,请点击笔记本右上角的publish按钮

作者图片

Google Colab

由 Google 支持的 Google Collaboratory 是一个 Jupyter 笔记本集成开发环境,可以使用无限的免费 CPU。它还配有有限的免费 GPU 和 TPU 。你只需要一个谷歌账户就可以开始了。Google Colab 允许您将 Google Drive 安装为项目的存储文件夹,免费版本的 Google Drive 配有 15GB 的存储空间。

如何使用 Google Colab?

  1. 转到谷歌合作实验室
  2. 创建新笔记本
  3. 要使用已经存储在 Google drive 中的数据,请单击左侧面板中的“安装驱动器”图标

作者图片

4.安装驱动器后,我们会在目录中看到一个drive文件夹。这是你的 google drive 目录。

作者图片

5.阅读 csv 文件

import pandas as pd
df = pd.read_csv('/content/drive/path/to/data.csv')

分享笔记本

要共享笔记本,请点击笔记本右上角的share按钮。

作者图片

卡格尔

Kaggle 提供 CPU 时间无限,GPU 时间有限的 Kaggle 笔记本。Kaggle 上有一个丰富的数据集库,你可以把它添加到你的 Kaggle 笔记本上开始使用。

如何使用 Kaggle 笔记本

  1. 创建一个 Kaggle 帐户
  2. 创建笔记本

作者图片

3.使用Add data按钮选择一个数据集。我们可以上传自己的数据集或使用现有的 Kaggle 数据集。

作者图片

分享笔记本

要共享笔记本,请单击右上角的share按钮,将笔记本公开。

结论

我们为云托管的 Jupyter 笔记本电脑探索了 3 种不同的选项,提供免费和无限制的 CPU 运行时间。这些平台提供 12GB-15GB 的 RAM,使其适合为中小型数据集训练经典机器学习模型。如果你正在训练深度学习模型,建议使用 GPU 而不是 CPU。查看我的另一篇关于免费云 GPU 的文章,用于训练你的深度学习模型

从招聘经理的角度来看:我在面试中对初级数据科学家的要求是什么

原文:https://towardsdatascience.com/from-a-hiring-managers-perspective-what-i-was-looking-for-in-a-junior-data-scientist-role-during-18fe3579c6bd

你的技术技能没有你想象的那么重要

89 号仓库在 Canva 上拍摄的照片

作为一名招聘经理,我总是在候选人身上寻找一些关键品质。我希望看到他们是聪明的,能够有效地沟通。此外,候选人必须对数据科学概念有很好的理解——但这并不像你想象的那么重要。最后,我也在寻找候选人有上进心和能够独立工作的迹象。以下是我在面试中寻找的一些具体的东西:

背景:我在一家大型政府机构中管理一个由数据工程师和数据科学家组成的小团队;因此,我们作为一个小单位服务于大量的观众。我对那些多面手的员工有偏见。我承认这些建议并不适合所有人。我们通常不构建工具,而是专注于构建数据驱动的能力,以识别我们监管的行业中的不良行为者。

1.对数据科学概念的理解:你是否很好地掌握了基本概念,如假设检验和机器学习?

毋庸置疑,对于一名数据科学家来说,对数据科学概念的深刻理解是非常重要的。数据科学概念将在工作面试中进行测试,以了解候选人是否具备所需的知识。一些可能被问到的常见问题包括:你如何向没有先验知识的人解释假设检验?有哪些最流行的机器学习算法,你认为这是为什么?通过能够回答这些类型的问题,这将表明候选人在数据科学概念方面有很强的基础,并将能够在他们的新角色中立即投入工作。

然而,正如我在介绍中提到的,至少对我的团队来说,我对这个问题的重视程度没有其他人高。让我解释一下。

2.沟通技巧:你能有效地解释你的思考过程和结果吗?

求职面试中最重要的沟通技巧之一就是有效地解释你的思维过程和结果。对于初级数据科学家角色来说尤其如此,在这种情况下,你的沟通技能将受到测试,以证明你可以与一线员工和团队成员一起工作。为了有效地交流你的思考过程和结果,你需要能够将复杂的概念分解成简单的术语。练习提供一个例子,向不了解数据的观众解释一个复杂的概念或结果。这里的关键词是“实践”

练习提供一个例子,向不了解数据的观众解释一个复杂的概念或结果。例如,如果您正在从事一个涉及预测客户流失的项目,您可能需要向不从事数据科学的人解释流失的概念。要做到这一点,你需要从解释什么是客户流失以及它对企业意味着什么开始。你还需要解释你的团队如何预测客户流失和你的分析结果。

3.动机:你对在我的组织中从事数据科学项目感到兴奋吗?

当在求职面试中被问及为什么想为某个特定的组织工作时,我诚实的回答总是,我对有机会从事能够产生真正影响的数据科学项目感到由衷的兴奋。在我之前的工作中,我是开发数据驱动方法以提高客户满意度得分的团队的一员。例如,你可以说,“我亲眼目睹了我们的工作如何改变了客户的生活,我渴望在我的下一份工作中继续发挥这种积极的影响。鉴于您的数据科学计划的前沿性质,我相信我可以在这个组织中继续成长为一名数据科学家,并为令人兴奋的项目做出最有影响力的贡献。”

4.独立性:你能在没有监督的情况下独立工作吗?

能够独立工作是任何数据科学家的重要技能,无论他们的经验水平如何。在工作面试中,潜在的雇主可能会问你是否能够在没有监督的情况下独立工作。这个问题旨在评估你在没有大量指导的情况下主动完成任务的能力。回答这个问题的最好方法是提供一个你能够成功独立工作的具体例子。例如,你可以提到一个你完全独立完成的项目或者一项你在正常工作职责之外承担的任务。通过给出一个具体的例子,你可以表明你具备雇主所寻求的独立解决问题的能力。

独立工作的一个重要部分是能够有效地管理自己的时间。假设你能证明你有能力在没有持续监督的情况下完成任务。在这种情况下,这将向雇主展示你拥有在数据科学岗位上取得成功所必需的自我激励和自律。

💔-critical-aspects-of-effective-stakeholder-management-for-data-science-projects-b2bf3a472da3>

5.态度:你有正确的态度吗?

众所周知,态度在职场中很重要。你的态度可能是事业成功和一系列没有前途的工作的区别。所以,毫不奇怪,雇主在工作面试中非常重视态度。如果你在面试一个初级数据科学家的职位,这里有一些小技巧可以确保你有正确的态度。

第一,要谦虚。对自己的能力有信心很重要,但你不想给人留下傲慢的印象。记住,你申请的是初级角色。面试官希望看到你愿意学习,并且足够谦虚来接受别人的指导。

第二,积极向上。积极的态度是有感染力的,可以在面试中给人留下好印象。一定要微笑,流露出自信,即使你内心感到紧张。

最后,准备好回答关于你态度的问题。许多雇主会特别询问你的态度,以及这与你申请的工作有什么关系。准备好你积极的态度对工作产生影响的例子。

虽然技术技能很重要,但软技能对数据科学家同样重要。为了在数据科学市场上取得成功,您必须通过展示您能够与其他团队成员有效沟通、独立工作以及有学习新事物的动力来脱颖而出。如果你能在面试中展示这些品质,你将有更好的机会获得这份工作。最后,要想在下一次面试中胜出,记得练习你的回答,因为良好的沟通技巧对任何职位来说都是必须的。

https://ilro.medium.com/membership

从厌恶到激情:如何用 Python 写文档

原文:https://towardsdatascience.com/from-aversion-to-passion-how-to-write-documentation-in-python-fdfefde34ca9

代码更多的是被阅读而不是被编写

帕特里克·托马索在 Unsplash 上的照片

到无论是谁在读这篇文章,你一定是世界上最关心文档的顶尖工程师之一:)。文档技术从来没有得到开发人员社区的足够重视。

为什么会有这种反感?嗯,写不难,写清楚很难。这适用于所有人,包括开发者。它需要我们组织自己的思想,并清晰地表达出来。通常,一个问题没有单一的解决方案,所以除了写下已经做了什么,我们还需要一个背后的基本原理。而如果你写得不好,只会适得其反。

但是对我来说,这种现象的主要原因是——没有文档不会阻止软件的功能。没有文档的蹩脚代码,只要还能用,还是会出货的。这就像是一笔科技债务,等待着一位先驱将其带到桌面上。对于快速增长的公司来说,它大多被搁置在积压订单的底部。

在这篇文章中,我不想说服你从今天开始处理那些过时的票。然而,我想告诉你一些小技巧,从项目一开始就让这个记录过程变得更容易、更愉快。你可以在你的下一个项目中运用它们,带着一个全新的思维和积极的记录态度。

作为这篇文章的范围,我将讨论记录你的源代码的 4 个步骤。自述文件或图表等一般项目文档不在此范围内。

创建 Python 文档的 4 个步骤

最简单的方法——不要记录任何东西

是的,你没看错。文档的一个大问题是,随着软件的发展,它们会过时。作为一个读者,如果你不断发现文档和代码之间的差异,你会在某个时候放弃阅读它。

这就是为什么自文档化代码的想法脱颖而出。Python 就是一个很好的例子。我想分享两个创建自文档化代码的技巧。

第一个技巧是尽可能多地跟随 Python 习语。Python 被设计成一种高度可读的语言。它更频繁地使用英语关键词,而其他语言使用标点符号。Python 确实带来了很多语法上的好处,所以代码 like 不需要注释。

如果你想更新你的 Python 习语的知识,看看这个 Youtube 视频和我的以前关于写 Python 代码的文章

使用正确的 Python 习语有助于读者理解代码的最底层。但是为了理解代码在做什么,有必要阅读每一行吗?

这个问题把我们带到了第二个技巧——创建概念模型,从上到下设计代码结构。我们先来看这个例子。

该功能根据乘客的疫苗接种记录和聚合酶链式反应测试检查其是否被允许旅行。尽管代码遵循了 Python 习语,但它却因为放入了太多的信息而让用户陷入了困境。代码可以重构为更清晰的格式。我们来读另一个版本。

很明显,这个版本的台词更多。但是用户关心的仅限于函数is_allowed_to_travel(),它本身读起来就像一个文档。用户可以决定是否要深入了解细节(例如,检查有效聚合酶链式反应测试的定义)。

如果我们将该功能分成几个逻辑单元,那将是—检索用户疫苗接种数据->验证用户疫苗接种记录->检索用户聚合酶链式反应测试数据->验证用户聚合酶链式反应测试。视觉表现是这样的:

逻辑表示(由创建)

每个蓝框可能包含低级逻辑单元。经验法则是把你的顶层函数当作一个‘粘合函数’来缝合你的底层逻辑单元。每个逻辑单元都是一个动作。

这给我们带来两个好处。第一,代码可读性更好,并且可以自我解释。第二,通过分离独立的逻辑单元,代码库更易于维护、重用和测试。每当引入新的疫苗接种时,我们只需要改变VaccinationRecord类,而不需要更新任何其他函数。这也被称为 关注点分离 一种将程序分成不同部分的设计模式。

除了分离逻辑,这段代码还创建了几个概念模型,如VaccinationRecordPCRRecordDBConnection。每个模型管理其对象周围的属性和行为。我们正在逐渐进入面向对象编程的世界,在本文中我不会叙述太多。但总的来说,OOP 会把一个大问题分解成可解决的小块,这有助于开发者和用户解决或理解问题。

总之,遵循 Python 习语、分离逻辑单元和应用面向对象的设计提高了整体的可读性,从而将您从维护过时的文档中解放出来。

将文档链接到代码

编写自文档代码是一个很大的进步,但它不能解决所有的问题。这有点可悲,因为不是每个人都理解代码,因此开发人员必须用自然语言编写文档并保持更新。

问题是如何绑定文档和代码,使得一方的改变会影响另一方。我们寻求的解决方案是以下两种之一:

  • 一个可以基于代码自动生成文档的框架。
  • 一个可以基于文档自动生成代码的框架。

对于第一个场景,一个很好的例子是自动生成 Swagger ,这是一组围绕 OpenAPI 规范构建的工具,可以帮助您设计、构建和记录 REST API。许多现有的 Python API 框架如 FastAPIconnexion 都支持自动生成 Swagger UI,它描述了你的整个 API,包括端点、参数、认证等。它完全依赖于实际的实现,所以不需要注释或 docstring。

来源:https://swagger.io/tools/swagger-ui/

每次更改 API 设计时,Swagger UI 都会更新。所以开发者不需要担心它的新鲜度。

另一方面,第二个场景引入了一种不同的工作方式,在这种方式中,文档是预先创建的。在敏捷软件开发中,这个过程被称为行为驱动设计(BDD)——一个鼓励软件项目中开发者和企业之间协作的过程。与传统的软件开发不同,BDD 要求开发人员在编码前用结构化的自然语言列出需求。

这种语言被称为小黄瓜,它允许用消费者能够理解的逻辑语言来描述预期的软件行为。在 Python 中, pytest-bdd 实现了 Gehkerin 语言,以支持自动化项目需求测试并促进 bdd。

让我们在 pytest-bdd 中实现上面的例子。开发者首先用 Gherkin 语言描述预期的行为,给出关键字, When,Then。

特征文件中的示例

  • 给定描述了测试前的前提条件和初始状态——乘客的情况。
  • 描述动作时——检查乘客。
  • 然后描述动作的结果——乘客被允许/不被允许。

第二步是为每个语句创建一个测试函数。这是整合发生的地方。该测试将文档与代码连接起来,并确保一方的逻辑变化会影响另一方。

测试代码示例

对于特征文件中的每个语句,必须创建一个测试函数。Python 文件中的文本必须逐字符匹配特征文件。它的主要优点是确保文档的新鲜性和正确性。

我使用 pytest-bdd 大约有 2.5 年了。我发现它在协作主要发生在开发人员和非开发人员之间的环境中非常有用。协作的价值在于最初的头脑风暴会议,而不在于开发人员不断向不同的利益相关者解释事情应该如何运作的后续会议。利益相关者可能会感谢工程师的支持,但在我看来,这是一种浪费的合作,因为时间不是花在创造新东西上,而是花在解释现有东西应该如何工作上。

BDD 的方法鼓励多个涉众在最初的集思广益阶段进行高质量的合作。结果(也称为特征文件)成为程序的输入和商业用户的阅读材料。这将减少不必要的会议。

在本节中,我们看到了集成文档和代码的两种不同方式。目标是确保它的新鲜性和正确性,从而提高开发者和用户之间的协作效率。

Docstring v.s .注释

尽管 BDD 在开发过程中很有帮助,但它的主要目标受众是非技术用户。对于为开发人员设计的程序,编写特征文件可能是一项开销。开发人员通常更喜欢在他们的代码上记录和注释。

在我们深入之前,我们需要区分文档和注释。一般来说,文档向用户描述功能,而注释向维护人员和开发人员描述代码。

根据维基百科的说法,评论有多种用途:

  • 规划和评审:在编写实际代码之前,编写伪代码来概述意图。应该在实现完成后删除它。
  • 代码描述:总结代码,说明程序员的意图。注意,这可能是代码应该被重构和自我解释的标志。
  • 基本原理:解释选择一种技术或算法的原因。
  • 标签:添加代码标签,如 TODO、BUG、FIXME。它们也许可以被 IDE 工具搜索到,比如 vscode 中的 Todo Tree。

除了 PEP8 中定义的 72 个字符的最大长度外,评论的格式没有太多限制。另一方面,docstring 有自己的约定,在 PEP 257 中有描述。Docstring 可以进一步分为三类:

  • 类文档字符串
  • 包和模块文档字符串
  • 脚本文档字符串

许多 Python 包中使用了一些众所周知的 docstring 格式。选择哪种格式并不重要,但是在整个项目中应该坚持使用相同的格式。

  • reStructuredText :官方 Python 文档标准。这是一种非常丰富的格式,不仅适用于 docstring,还广泛用作文档化的 Markdown 格式。

重构文本的一个例子

  • Google docstrings : Google 推荐的文档格式。这也是我最喜欢的格式。

Google docstring 的一个例子

  • Numpy docstring: NumPy 的 reStructuredText 和 Google Docstrings 的组合。

Numpy docstring 的示例

可以使用类似于 Pyment 的工具来自动生成 docstring 或将 docstring 从一种格式转换成另一种格式。像 autoDocstring 这样的 Vscode 扩展也能够使用简单的快捷方式创建一个 Docstring 结构。

99%的软件和软件包都有 docstring 和注释,而不管其他格式的文档。一般来说,docstring 和 comments 应该保持简洁,以便于维护,但仍然要足够详细,以便新用户理解如何使用该对象。

Python 文档服务器

最后一步是在 web 服务器上发布 docstring,并使用户可以访问它。因此,用户能够在实际下载软件包之前理解如何使用它。有一些现有的工具可以基于文档字符串自动生成 HTML 文档。

  • sphinx :迄今为止最全面的文档生成器。它最初是为 Python 创建的,现在已经将其功能扩展到了多种语言。它读取 reStructuredText 中的 docstring 并生成 HTML 输出。大多数著名的 Python 包都使用它来生成文档,如 FlaskPytestJinja 等。但是老实说,假设你在一个新的项目上工作,设置需要一些努力。
  • pdoc :一个轻量级的文档生成器。它比 sphinx 更简单,因为它不需要任何配置。适合小型项目。但是与 sphinx 相比,它的定制选项较少,因此不适合大型项目。
  • Doxygen :另一个支持多种编程语言的文档工具。但是它在 Python 社区中不如 Sphinx 受欢迎,因为它主要不是为 Python 设计的,而且外观不如 Sphinx 好看。

他们产生的输出是 HTML 页面。您可以使用额外的工具,如Read Docs来托管这些静态文件,并将其公之于众。

结论

我们已经讨论了一些技术来使文档化过程变得更加容易和愉快。

编写自我文档化的代码是每个开发人员都应该做的事情。它保持了代码库的整洁,更容易维护和共鸣。Python 开发者应该利用 Python 的语法优势。

使用像 FastAPI 或 pytest-bdd 这样的框架来集成文档和代码。一方的逻辑变化会自动影响另一方,这样我们就可以避免过时的文档。

但是不可避免的是,随着程序变大或公开,开发人员将花费时间为用户和共同维护者编写 docstring 和注释。其目的是告知他人如何使用该物品或故意选择背后的理由。对于新用户来说,重要的是要保持文字简洁,但也要有足够的信息量。

最后但同样重要的是,您希望发布 docstring 以获得更多的读者。Sphinx 和 pydoc 等工具生成静态 HTML 页面,这些页面可以在 Nginx 等 web 服务器上托管并读取文档。

文档不仅仅是为了它而创建的。开发人员必须确保文档是在创造价值,而不是迷惑用户。我希望这篇文章能在一定程度上消除你对文档的厌恶。如果你有更多的建议,请在下面评论并告诉我们。

参考

https://kislayverma.com/programming/how-to-write-self-documenting-code/

从麦乐鸡到数据科学,使用 Python

原文:https://towardsdatascience.com/from-chicken-mcnuggets-to-data-science-using-python-78bd525338e2

梅根·赫斯勒在 Unsplash 上拍摄的照片

你的麦当劳订单将永远不会相同

"你在麦当劳点过 43 块麦乐鸡吗?"

当我第一次听到这个故事时,我完全惊呆了,但事实上,麦乐鸡有一个数学故事,而且非常有趣。

原来你只有几盒 6920 麦乐鸡。当数学家亨利·皮乔托和他的儿子一起吃饭时,他开始思考他可以用这三个值的组合订购的实际数字。这个数字被称为https://mathworld.wolfram.com/McNuggetNumber.html

我们将从给出我们正在考虑的离散域的深度细节开始,并最终解决关于它的一个优化问题。** 我们开始吧!**

1.数学问题

我们试图解决的数学问题被称为硬币问题 。这个问题最一般的表述如下:

给定 a1,a2,一个正整数,使得 gcd ( a 1, a 2,…,a)= 1,我们要找出不能表示为这个数的圆锥组合的最大数,也就是说:

我用 LaTeX2png.com 制作的图像

其中 k1、k2 和 kn 为非负整数。那我们的数字是多少?嗯……其实是 43

有几种方法可以找到这个结果,但我觉得下面的方法更直观。

我们有以下结果:

我用 LaTeX2png.com 制作的图像

对于所有大于 49 的值,我们可以简单地从这 5 个数字开始,加上 6 的倍数。让我们证明这一点:

给定一个数(大于 50),这个函数给出你需要给 44 到 49 中的一个数加多少个 6。例如,82 是 46+6x6= 6+2x20+6x6=7x6+2x20

很好,但是所有的数字都是真的吗?是的,的确是。这是一个从 50 到 1000 的例子:

好,我们知道不会有大于 43 的麦乐鸡数量。但是 43 是不是我们不能通过组合 6、9 和 20 个麦乐鸡创造出的最大数字呢?

我们来证明一下。

****注意!我确信有比我即将提出的方法计算量更低的方法。尽管如此,由于我们只考虑了最多 43 个麦乐鸡,代码运行速度非常快,不需要任何优化。

我们基本上想做一个超级简单的网格搜索,搜索所有我们可以选择的可能数量的盒子。

给定一个特定的数量,我们能做的第一件事就是找出只使用一个盒子可以订购的麦乐鸡的最大数量,而不超过这个数量。我用一个例子来解释最后一句话:****

假设我们想点 23 份麦乐鸡。我们知道,考虑 20x2、9x3 或 6x4 的麦乐鸡块进行网格搜索是没有意义的,因为如果我们考虑 4 盒 6 个麦乐鸡块,我们会得到 24 个,这大于 23,同样的推理适用于 20 和 9。

因此,根据您想要订购的麦乐鸡数量,我们希望找到 6 盒、9 盒和 20 盒的合理范围。然后我们将进行网格搜索,看看我们想要查找的值是否在网格中。要做到这一切,只需运行以下代码:

这里有一个例子:

而且,正如你可能看到的,43 是你不能点的麦乐鸡数量:(

让我们来看看你不能点的麦乐鸡的清单:

而且可以看到, 43 是最大的一个。

2.让我们来谈谈数据科学

****我觉得这个问题超级酷。这个整数域的特殊性质是,它不遵循我们在处理连续数时习惯遵循的直觉推理。

如果你有三个整数,前面的系数可以是连续的,你实际上是在玩实定义域。当你强迫系数为整数时,你会得到一个充满“漏洞”的定义域。

但是如果我们想解决这个问题,在这个领域:

“我想尽可能多地吃麦乐鸡,用有限的预算,只用 6 块、9 块或 20 块麦乐鸡做成的盒子”。

最后,我们考虑一个离散线性优化问题。****

我们使用术语“优化”来表示我们:

  • 想要最大化某事:那是我们的麦乐鸡数量,
  • 我们的预算有一些限制
  • 我们模型中的一切都是线性的
  • 你不能订购 3.14 盒麦乐鸡,所以这实际上是一个离散的问题

我们希望最大化的功能如下:

x1,x2 和 x3 是你订购的 6,9 和 20 个盒子的数量,图片是我用 LaTeX2png.com 制作的

现在我们知道下面的麦乐鸡是这样的:

作者图片

因此约束变成:

我用 LaTeX2png.com 制作的图像

为了解决这个问题,我们使用了一个名为https://coin-or.github.io/pulp/****的库。 最后你会知道,给定你的投入预算:****

A.你要吃多少块麦乐鸡(例如 55 块麦乐鸡)
B. 你要吃多少块麦乐鸡(例如一个 6 盒、两个 20 盒和一个 9 盒)

重要的是要说这是最优解。这意味着给定一定的预算,你不能吃得比这个多。很酷吧?😃

下面是实现这一点的代码:

  1. 安装库:

2.定义问题:

3.您的最佳订单已经准备好了!

例如,如果你有 30 欧元,你可以订购 52 块麦乐鸡,订购 2 盒 20 块麦乐鸡和 2 盒 6 块麦乐鸡。这是你用 30 块钱最多能吃多少麦乐鸡:)

这是沙克的普通午餐:

注意!优化是一个巨大的领域,尽管我非常了解基本概念,但我远不是这个领域的专家。更多请阅读这里,这里这里或者这里

3.结论

线性(离散)优化是如此有趣。我们倾向于认为线性模型是一个特殊且非常具体的领域,大部分时间都远离现实世界的问题。我希望我用这个有趣的例子向你们展示了,大多数时候,情况恰恰相反:)

如果你喜欢这篇文章,你想知道更多关于机器学习的知识,或者你只是想问我一些你可以问的问题:

A.在 Linkedin 上关注我,在那里我发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大数量的故事”,你可以阅读我(以及成千上万其他机器学习和数据科学顶级作家)写的任何关于最新可用技术的文章。

从数据驱动到数据科学驱动

原文:https://towardsdatascience.com/from-data-driven-to-data-science-driven-f2db93f83d51

数据科学驱动是新数据驱动的三个原因

作者用 Pixabay 的图片创作的图片(左:Momentmal,右:sick-street-photography)

2010 年,当我还在研究生院攻读学位时,我创办了一家小型咨询公司,出售我的数据分析技能。我网站的口号是“数据驱动”就这样。我知道这是原创,但我试图捕捉 90 年代的潮流。

早在 20 世纪 90 年代和 21 世纪初,企业应该更加“数据驱动”的想法就像野火一样蔓延开来。越来越多的证据表明,创造一种数据驱动的决策文化有助于企业在缺乏数据知识的竞争中脱颖而出,越来越多的企业开始要求他们的领导者具备数据素养。

Gartner 和 Forrester 开始开发评估公司“数据驱动”程度的指标。公司开始收集更多的数据,供应商也在兜售能够自动化数据洞察工作流程的产品,这是领导者真正成为数据驱动型决策者所必需的。

然而,今天,数据驱动开始有点过时了。这个概念让人联想到结构化数据集和预制仪表板的图像。在当今的数字世界中,数据产生的速度如此之快,以至于它们很少能够很好地处理结构或洞察力,而是以杂乱的物联网消息流、网站中的嵌入式信息或文件共享中的大量扫描图像的形式出现。

数据驱动并不是不好,只是在当今的商业环境中不够用,需要改进。为了让大大小小的企业能够更好地应对这些新数据源,从而保持相关性和竞争力,学生、领导者和企业家需要开始培养数据科学驱动的思维模式。

在本文的剩余部分,我提供了 3 个理由,说明为什么我认为数据科学驱动的思维模式是我们用数据思考的下一步发展。

超级数字

早在 21 世纪初,数据驱动意味着你擅长使用数据来为决策提供信息。作为领导者,我们面临的最大挑战是利用数据获取洞察力的速度,这将有助于及时做出业务决策。为了实现这一目标,公司花费了大量资金来收集、组织、清理和自动化数据分析,以便在做出决策之前将信息掌握在正确的人手中。

从那时起,世界变得更加数字化,疫情只是加速了技术专家对全球经济数字化的预测。公司看到近 70%的员工在疫情的高峰期远程工作。

再加上脸书宣布建造元宇宙,一个大规模多模式的虚拟体验,我们剩下的是工作世界有史以来最大的数据足迹。

不幸的是,对于旧的数据驱动的思维模式来说,这些新的数字信息来源并没有变得更容易解释和分析。事实上,像物联网消息、互联网和保存的虚拟聊天视频这样的数据源在这个巨大的数字信息海洋中更具代表性,因为它们是非结构化的,非常嘈杂。

但亲爱的读者,不要害怕,因为有一个解决方案可以从这些新的数字流中学习,以解开它们隐藏的秘密,并利用它们不断发展的普遍性,这个解决方案就是数据科学。在当今世界,数据科学驱动的思维方式比单纯的数据驱动思维方式更有价值,这至少有 3 个主要原因。

原因 1:数据科学随处可见数据

过去,数据驱动意味着您能够快速使用数据以及时的方式为决策提供信息。这也意味着,当数据进入您的组织时,您可以应用于数据的结构越多,就越容易获得洞察力并做出明智的决策。

不幸的是,当今世界已经高度数字化,产生的许多信息都是嘈杂的、非结构化的信息,这些信息旨在为用户提供体验,而不是为决策者提供决策数据。因此,尽管在当今的商业环境中,许多领导者都懂数据,但许多人都忽略了这些新的数字信息流中包含的潜在价值。

拥有数据科学驱动的思维模式有助于我们摆脱这种限制,因为数据科学的工具和技术允许我们开始假设我们可能如何从这些非结构化的来源中获得结构。自然语言处理的数据科学领域使我们能够使用聚类分析或潜在狄利克雷分配等统计方法来将非结构化源组织成有意义的。一旦结构化,就可以利用更传统的分析来支持数据驱动的下游决策,或者支持其他面向数据科学的任务。我们可以从网站到结构化数据集,以支持数据驱动的格式访问信息(见图)。

作者图片

理由 2:数据科学使我们能够预测、分类和识别数据中的可靠信号

仅仅使用数据科学来从非结构化来源中构建结构只是数据科学驱动的思维方式能够改善数据驱动的思维方式趋势的一种方式。更传统地说,数据科学还允许我们超越使用数据作为描述过去发生的事情的方式。数据科学使我们能够估计未来可能发生的事情,组织新数据,并为我们提供一种了解信号是否是真正可靠的信号的方法。

因此,那些发展数据科学驱动思维的人也将有更多的选择来了解数据如何在业务中操作。

形成这种思维模式的人可能会看到,分析和构建与特定公司和/或行业相关的新闻警报源可能会预测未来的股票价格。

培养数据科学驱动思维的人可能会理解,如何根据客户通过电子邮件发送的文档过去的发送历史,将这些文档自动发送给相应的响应团队。

那些发展数据科学驱动思维模式的人将会明白,相对于数字空间中的噪音,他们品牌周围的人口统计情绪差异何时是真正有意义的。

原因 3:数据科学支持更复杂的业务决策自动化

第三个也是最后一个原因是,数据科学驱动思维是对数据驱动理念的改进,是前两个原因的延伸。也就是说,了解数据科学的前景和局限性,可以确保领导者意识到业务决策流程可能会受益于更复杂的自动化。自动化允许我们在决策过程中考虑一些不确定性。

数据驱动在很大程度上与基于规则的思维相关联。基于规则的思考看起来有点像这样:如果这个信号是真的,那么就执行这个业务流程,否则就执行另一个流程。

数据科学驱动使我们在决策时更加细致入微。相反,我们认识到,如果对某件事情的真实性有 90%的信心就足以自动做出决定,那么数据科学就给了我们承担这种风险的方法。

数据科学如何做到这一切?统计数据

数据科学及其相关工具和技术能够做到这一切的主要原因与该领域的统计基础有关。统计学允许我们通过在那些信号周围建立噪声模型来在噪声数据中找到信号。然而,大多数领导者并不知道这种努力的价值,因为大多数统计学教授的重点是数学,而不是统计思维能让 https://iase-web.org/documents/papers/icots6/5b1_john.pdf?实现的思维方式 1402524962 。也就是说,传统上统计学没有得到很好的应用。另一方面,数据科学已经取得了如此大的成功,因为它的基础是从应用中诞生的。因此,begin data science driven 进一步鼓励和支持对统计思维的应用理解。

结论

所以,最后,我想引用 Samuel S. Wilks 的一句话,他在 1951 年向美国统计协会发表演讲时解释了赫伯特·乔治·威尔斯的话:

"总有一天,统计思维会像读写能力一样成为高效公民的必要条件!"

虽然我们在对待社会世界的方式上可能没有实现这样的社会进化,但商业世界为我们提供了自己的压力来推动思想的信封。数据驱动的思维减弱;数据科学驱动思维万岁!

比如参与学习数据科学、职业发展或糟糕的商业决策?加入我

从数据科学到碳足迹合规:探索 TinyML 的世界

原文:https://towardsdatascience.com/from-data-science-to-carbon-footprint-compliance-discover-the-world-of-tinyml-ae06418e6672

使用 TensorFlow Lite Micro

图由 itemis AG,https://info . item is . com/IOT-systems/download-tinyml-rock-paper-scissors-dataset/(经许可)

合著 尼古拉·里德 拉斐尔·塔佩大师

摘要

你想扩展你在人工智能方面的专业领域,并想探索一种更节能的机器学习(ML)方法。本文将向您介绍微控制器上的 ML(MCU),也称为微型机器学习(TinyML)。准备好在剪刀石头布上输给 MCU 吧。您将涉及数据收集和预处理、模型设计以及如何让您的模型在 MCU 上运行。我们的例子为您提供了自始至终完成您自己的 TinyML 项目所需的一切。

什么是 MCU

MCU 代表微控制器单元。MCU 具有连接到各种输入和输出引脚的弱处理器。与你的电脑不同,MCU 没有完整的操作系统,一次只能处理几个进程。MCU 还具有少量内存和 RAM。

对于我们的项目,来自 Espressif 的 ESP-EYE 板是理想的。该板将 ESP32 微控制器与已经连接的摄像头捆绑在一起,为我们节省了一些工作。

我为什么要在乎 TinyML?

像 DeepMind 和 OpenAI 这样的科技公司凭借专家和 GPU 的力量主导了 ML 领域。特别是自然语言处理(NLP)中的模型,如 GPT-3,需要几天的时间来并行使用几十个高性能 GPU 进行完整的训练周期。这伴随着高能耗,并且随着 NLP 模型每年进一步增长,它们对能量的需求也在增长。开发成本是单个训练周期的数倍,并且需要额外的计算时间来优化超参数。TinyML 通过变小来扭转局面。由于内存限制,大型人工智能模型不适合微控制器。下图显示了硬件要求之间的差异。

图由 itemis AG,https://app.hubspot.com/documents/761475/view/277264732?accessId=dc8033

与在云中使用人工智能服务相比,MCU 上的 ML 提供了哪些优势?我们发现您可能希望使用 TinyML 的七个主要原因。

成本
购买和运行微控制器都很便宜。

环保 在微控制器上运行 AI 几乎不消耗能源。

集成 微控制器可以轻松集成到现有环境中,例如生产线。

隐私和安全 数据可以在本地设备上处理。数据不一定要通过互联网发送。

快速原型 TinyML 使您能够在短时间内开发出概念验证解决方案。

自主可靠 微型设备可以在任何地方使用,甚至在没有基础设施的时候。

实时 数据在微控制器上无延迟处理。唯一的限制是 MCU 的处理速度。

你可能想知道这些点有多大,尤其是在能源消耗方面。本地加工真的这么重要吗?甚至一个谷歌搜索消耗的能量相当于给一个 60 W 的灯泡供电 17 秒。TinyML 的有效性被 Siri、Google Assistant 和 Alexa 等语音助手所利用,其中唤醒词识别在本地进行。最近,iOS 上的听写功能也可以在本地将语音转录为文本。那是 TinyML 在行动。从更大的角度来看,随着更多智能设备的上线,TinyML 是不可避免的。产生、共享、消费或存储的数据量呈指数级增长。2010 年,这一数字达到了 2 千兆字节(ZB),预计到 2025 年将增长到 181 千兆字节。TinyML 正好符合全球可持续发展的努力。

石头剪刀布

作者提供的数据

你曾经在石头、布、剪刀和人工智能的比赛中输过吗?还是想通过打败一个 AI 来打动朋友?您将使用 TinyML 与 ESP-EYE 板进行游戏。这个用例很好地概述了 TinyML 的功能。开发的模型使用卷积层,因此您将了解在 TensorFlow Lite Micro 中使用卷积层的复杂性。您也可以调整我们的项目来识别您的猫!这个项目也将展示 TinyML 在准确性方面的局限性。你需要采取五个步骤来实现你的项目。以下部分提供了必要步骤的高级概述。如果您想更深入地了解一下,请查看我们项目库中的文档。它解释了漂亮的细节。

收集数据

收集好的数据是 ML 的关键部分。为了让事情运转起来,你需要拍摄你的手形成石头、布、剪刀手势的图像。越独特的图像越好。人工智能将学习你的手可以在不同的角度,位置或灯光变化。数据集包含记录的图像和每个图像的标签。

最好使用同样的传感器和环境来运行你的人工智能,也用来训练它。这将确保模型熟悉传入的数据。例如,考虑温度传感器,由于制造差异,对于相同的温度具有不同的电压输出。就我们的目的而言,这意味着用 ESP-EYE 摄像机在统一的背景上记录图像是理想的。在部署期间,人工智能将在类似的背景下工作得最好。你也可以用网络摄像头记录图像,但可能会损失一些准确性。由于单片机的能力有限,我们记录和处理 96×96 像素的灰度图像。收集数据后,我们将数据分为训练集和测试集。

作者照片

按照上面的步骤,你收集的图像应该看起来像这样。如果你现在不想收集数据,你可以在这里下载我们现成的数据集

预处理数据

在我们的数据集中,我们使用 ESP-EYE 和网络摄像头记录图像。由于 ESP-EYE 可以捕获分辨率为 96×96 的灰度图像,所以我们不需要在这里做进一步的处理。然而,我们需要将网络摄像头图像缩小并裁剪为 96×96 像素,并将它们从 RGB 转换为灰度格式。最后,我们归一化所有的图像。下面,你看到我们处理的中间步骤。

作者照片

设计一个模型

由于我们正在处理图像处理,卷积层对我们的模型至关重要。因为 MCU 上的内存非常有限,所以我们不能使用深度架构。即使是为边缘设备设计的型号,如 MobileNet 及其后继产品也太大了。因此,我们的模型依赖于三个卷积层模块,包括卷积、批量归一化和最大池。用 L2 和 L1 正则化来正则化卷积层,以减少过拟合。通常,我们使用 ReLU 激活。

在第三卷积块之后使用 9 个神经元的密集层。最后,是一个密集的 softmax 层。

超参数

我们使用随机搜索来调整超参数。与其他超参数优化方法相比,随机搜索易于实现、理解、并行化,并且搜索可以随时停止和恢复。使用随机搜索,我们为 100 个模型训练了 20 个时期,每个时期的挂钟持续时间为 10 小时。一个有趣的结果是 SGD 优化优于 Adam。我们知道,亵渎。搜索还应用于正则化参数、卷积滤波器的数量、内核大小和步幅以及学习速率。

转换模型

在训练我们的模型之后,我们获得了张量流格式的 AI 模型。因为 ESP-EYE 不能解释这种格式,我们将模型转换成微处理器可读的格式。我们从转换成 TfLite 模型开始。TfLite 是一种更紧凑的 TensorFlow 格式,它使用量化来减小模型的大小。TfLite 通常用于世界各地的边缘设备,如智能手机或平板电脑。最后一步是将 TfLite 模型转换为 C 数组,因为微控制器不能直接解释 TfLite。

嵌入式环境

在本节中,我们将讨论在 MCU 上运行的代码。你可能知道微控制器通常是用 C/C++编程的。嗯,有 MicroPython ,但那是另一个故事了。在继续之前,你至少应该对 C/C++有一个基本的了解。有大量有用的教程和初学者指南,可以学习一些人称之为所有编程语言之母的东西。

让我们看看在 MCU 上会发生什么。在下图中,您可以看到我们代码的结构。我们从读取相机传感器的原始数据开始,稍后我们可以将这些数据提供给模型。然后,我们将用于训练数据的相同预处理应用于输入的原始数据。之后,我们将预处理后的数据传输到主函数,在这个函数中,预测由 TensorFlow Lite Micro 库完成。由于我们使用 softmax 层,最大概率是给定图像的预测类。为了提高准确性,我们在短时间内拍摄 5 幅图像,以进行整体预测。最后一步是模型自己采取行动。

作者提供的数据

为了充分理解 C/C++方面发生了什么,我们建议您看一下代码。为此,我们想给你指出正确的方向。由于 C++程序是从 main.cpp 文件开始的,所以这可能是您希望所有内容都集中在一起的地方。但是,您应该查看 main_functions.cpp 及其循环函数。该功能在无限循环中执行,也称为超级循环,不断重复上图中的步骤。

部署模型

现在我们可以将我们的模型部署到微处理器上。在我们构建(编译)和刷新 C++程序之前,我们需要放置新的 C 数组,它将我们的模型编码到目标文件 micro_model.cpp 中。替换 C 数组的内容,不要忘记替换数组长度变量 micro_model_len。我们提供了脚本 model_to_mcu.py 来简化这个过程。就是这样!

结论

通过这篇文章,我们希望为您带来机器学习的新视角。大数据、云部署和昂贵的培训并不是数据科学的唯一发展方向。TinyML 减少了 AI 模型的碳足迹,并搭建了一座通往嵌入式系统和物联网世界的桥梁。边缘计算启发我们提出比云或桌面 PC 更自然的人工智能部署。TinyML 并非没有挑战,例如 C 和 C++的流行以及 TensorFlow Lite Micro 与经典 TensorFlow 相比的功能不完整性。

展开示例

挑战一下怎么样?人生的新目标?想打动老朋友还是找新朋友?通过添加蜥蜴和斯波克,将石头、剪子、布带到下一个级别。你的 AI 朋友将会是一个更接近统治世界的技能。嗯,首先你应该看看我们的石头、布、剪刀并且能够复制上面的步骤。自述文件将帮助您了解详细信息。下图解释了扩展游戏的规则。你需要添加两个额外的手势和一些新的输赢条件。

照片由 itemis AG,https://info . item is . com/IOT-systems/download-tinyml-rock-paper-scissors-dataset/

开始你自己的项目

如果你喜欢这篇文章,并想开始自己的项目,我们为你提供了一个项目模板,使用相同的简单管道作为我们的石头,布,剪刀项目。你可以在这里找到模板。不要犹豫,通过社交媒体向我们展示你的项目。我们很好奇你能创造什么!

你可以在这里和那里找到更多关于 TinyML 的信息。皮特·沃顿的书是一个很好的资源。

参考

从开发到部署:包含 MLflow、SageMaker 和 Streamlit 的端到端情感分类器应用

原文:https://towardsdatascience.com/from-dev-to-deployment-an-end-to-end-sentiment-classifier-app-with-mlflow-sagemaker-and-119043ea4203

在本教程中,我们将从 DagsHub-MLflow 开始构建一个 NLP 应用程序,然后在 SageMaker 和 EC2 中进行部署,前端在 Streamlit 中。

图片由 Unsplash 上的 Yoann Siloine 拍摄

目录

— 1。设置 DagsHub repo 和需求
创建您的 DagsHub repo
设置虚拟环境
用 DVC
2a 下载数据。在 MLflow 中启动您的第一个实验
朴素贝叶斯模型作为情感分类的骨干
建立 DagsHub MLflow 并运行实验
2b。做一个更通用的代码来核算不同的型号
3。在 MLflow 中注册您的模型并部署到 SageMaker 端点
将模型添加到 MLflow 注册表
设置 AWS IAM 角色以从 MLflow
部署— 将您注册的模型部署到 AWS SageMaker
4。在 EC2 实例上部署一个 streamlit 应用程序,并通过您的仪表板运行预测
创建一个简单的 Streamlit 仪表板应用程序
将您的仪表板部署到 EC2 实例
优点、缺点、局限性、未来要做的工作
结论
支持我的写作

欢迎来到这个关于机器学习的新教程。今天我们将使用以下工具开发一个完整的端到端应用程序,从模型开发到模型部署:DagsHub、MLflow、AWS SageMaker、AWS EC2 和 Streamlit。特别是,我们将使用 DagsHub 作为我们的 GitHub repo,因为它提供了 MLflow 和数据版本控制dvc的集成版本。通过这种方式,我们不必在云上设置新的 MLflow 服务器和存储桶,因为一切都已准备好用于我们的新模型实验。

我们将实施情感分类模型,这是我们必须处理的工作的大纲:

  1. 我们将探讨如何建立 DagsHub 回购以及我们需要什么要求。
  2. 我们将致力于一个 tweet 情感数据集,分析不同的sklearn模型,并将在 MLflow 中跟踪它们。
  3. 一旦我们学会了如何比较模型,我们将跳到 AWS SageMaker 上的 MLflow 部署端。我们将回顾 IAM 用户和角色以及端点创建
  4. 最后,我们将把我们的分类模型包装在一个 Streamlit 应用程序中,托管在一个 EC2 实例上。

1.设置 DagsHub 回购和要求

在这里你可以找到我今天要处理的所有相关代码。

创建您的 DagsHub repo

图 1:一键创建一个新的 DagsHub repo。

图 1 显示了如何在 DagsHub 中创建一个 repo。只需点击你的个人资料图片旁边的“创建”按钮,然后给回购提供一个名称和描述,你就可以开始了。一旦创建了回购,与 Github 不同的是,您会看到更多的特性。如果你点击“远程”,窗口会显示 DagsHub 提供的 3 种服务:Git、DVC 和 MLflow。

设置虚拟环境

对于这个项目,我强烈建议你使用虚拟环境。因此,用以下代码克隆您的 DagsHub 存储库

git clone [https://dagshub.com/stefanobosisio1/sentiment_mlflow.git](https://dagshub.com/stefanobosisio1/sentiment_mlflow.git)

并创建一个 Python venv在你的终端python -m venv venv中键入这个命令将创建一个venv/文件夹。使用venv/bin/pip install --upgrade pippip升级至最新版本

和 DVC 一起下载数据

正如我之前提到的,DagsHub 提供了一个基于dvc的数据版本系统——你可以在这里找到更多信息。简而言之,DVC 是一个出色的 Python 工具,它允许您保持对数据的控制和版本。

我们将使用下面的数据。这些是来自 Twitter 的无版权数据,CC0 1.0 许可证,可从这里公开获得。在 Python: venv/bin/pip install dvc中安装dvc,并使用:venv/bin/dvc init在终端dvc中的存储库文件夹中初始化

现在你已经准备好克隆split-data文件夹了:

venv/bin/dvc get https://dagshub.com/nirbarazida/tweet-sentiment-analysis-data split-data/

我们已经准备好第一次提交 DagsHub——注意我们是如何使用dvc提交的:

venv/bin/dvc add split-data
git add .gitignore split-data.dvc 
git commit -m "A NICE MESSAGE FOR COMMIT"
git push origin 

2a。在 MLflow 中启动您的第一个实验

我们已经准备好处理 MLflow 了。首先,让我们用一个非常简单的朴素贝叶斯分类器为我们的代码创建一个主干,来学习如何构建模型和 MLflow 设置

作为情感分类骨干的朴素贝叶斯模型

在这里你可以找到代码。我们将尝试获取整个工作流的精髓,而不是深入数据科学的本质,因此我们将只使用这些文件:split-data/X_trainsplit-data/y_train。首先,我们需要预处理我们的数据,并清除它们:

  • 停用词,如and, but, a, an...
  • 标点
  • 特殊字符,如标签、标记、换行符或撇号

在 99%的情况下,这是每个 NLP 项目的第一步。因此,我们只需要三个简单的函数:remove_stopwordsremove_punctuationremove_specific_chars:

图 2:文本清理功能:用 NLTK 删除停用词,用 string 模块删除标点符号,而特定字符删除标签、新行以及撇号。

每个函数接收给定的文本作为输入,并创建一个新的outline字符串。此外,在预处理之前,我们要将输入数据帧中的所有字符串小写,如图 3 所示:

图 3:输入文本的预处理和清理。

在设置 MLflow 之前,让我们完成第一个朴素贝叶斯分类器下的工作流(图 4)。在预处理位之后,数据可以分成训练集和验证集。要将字符串转换成数字,需要调用sklearn矢量器(如CountVectorizerTfidfVectorizer)。矢量化之后,输入数据可以被分类器读取,MultinominalNB,我们可以继续进行训练和度量。

图 4:全模型循环。数据被清理,然后分成训练集和验证集。输入字符串用 CountVectorizer 进行矢量化,然后传递给朴素贝叶斯模型进行分类。

设置 DagsHub MLflow 并运行实验

要设置 DagsHub MLflow 服务器,我们需要来自 DagsHub 的跟踪 uri、跟踪用户名和密码。为什么?

  • 跟踪 uri 是 MLflow 服务器的 url,工件将被报告到该服务器。我们需要这些信息来建立连接
  • DagsHub 需要跟踪用户和密码来验证我们的实验并访问 MLflow 服务器

在您的 repo 中,单击远程按钮,您将找到所有需要的信息,如图 5 所示。

图 5:跟踪 uri、用户名和密码(隐藏)以设置 DagsHub MLflow 跟踪。

您可以将这些信息复制到一个setup_mlflow.txt文件中,并在我们的主代码中解析它。然后,图 6 显示了如何在您的代码中设置 MLflow 跟踪,以及 MLflow 如何集成到您的主干代码中——记住在这里您可以找到完整的代码

图 6:模型代码中 MLflow 的设置。

简而言之,这些是 MLflow 用于跟踪实验的关键要素:

  • 通过mlflow.set_tracking_uri(mlflow_tracking_uri)建立与 MLflow 服务器的连接
  • 设置一个实验,如果已经存在mlflow.create_experimentmlflow_client.get_experiment_by_name,则检索其id
  • 用上下文管理器with mlflow.start_run(...)开始实验
  • 尽可能多地利用ml flow 自动日志功能,比如mlflow.sklearn.autolog(...)。自动记录功能使用 Python 模块inspect跟踪实验以及为不同“风格”的模型(如sklearntorch等)生成的人工制品和物体。)
  • mlflow.end_run()结束你的实验

实验运行后,您可以在 MLflow UI 中可视化指标和工件,可以通过两种方式访问:

图 7 显示了 MLflow UI 应该是什么样子。通过点击“开始时间”,您将访问关于您的模型的更多信息,比如模型的参数、度量、标签和工件。在 artefacts 框中,您会注意到一个model文件夹,它存储由 MLflow 自动生成的文件,以便在预测时调用您的模型,例如 condo 环境、python 需求和二进制文件。所有这些都在两行简单的代码中!

图 7:使用朴素贝叶斯运行第一个测试后的 MLflow UI 示例。

2b。编写一个更通用的代码来考虑不同的模型

通常最好的做法是有一个通用的代码,以考虑不同的场景和不同的模型。出于这个原因,我们将把我们的朴素贝叶斯代码发展成一个更通用的代码,在这里我们可以选择不同的单词矢量器,以及不同的模型。在这里你可以找到参考代码

第一件事是将我们所有的预处理函数和模型转换成一个sklearn管道:

图 8:使用清理函数和情感分类器返回 sklearn 管道的函数。

在选择了我们想要使用的模型和矢量器之后,pipeline就创建好了。[PreprocessTweets](https://dagshub.com/stefanobosisio1/sentiment_mlflow/src/main/multiple_models.py#L44)现在是一个类,它包含了我们在上面中创建的所有清理函数。为了设置它,我们需要记住导入sklearn.base.BaseEstimatorsklearn.base.TransformerMixing,并在类定义中继承这些方法。核心功能在fit_transform函数中,其中输入数据帧列text被清理并作为numpy数组返回,因此它可以被矢量器步骤接收。

第二步是拥有一个解析器功能,例如用argparse

图 9: Argparse 处理不同的输入,有一个更通用的接口。

通过这种方式,可以处理不同的模型,扩展代码并并行运行。

图 10:设置 MLflow 参数,直接调用跟踪器,安装管道并保存所有内容。

图 10 显示了概括的最后步骤。给定输入的解析参数,我们现在可以为不同的实验设置 MLflow(例如,一个实验将运行朴素贝叶斯,另一个是逻辑回归,另一个是随机森林)。我们可以用mlflow.start_run直接开始跟踪,而不是把所有东西都包在mlflow上下文管理器中。请记住,可以同时调用多个自动记录功能,MLflow 将能够记录不同型号口味的产品。最后,运行预测,保存模型并将其报告给 MLflow 服务器。

通过这种方式,您将能够并行地或者用一个简单的 bash 脚本运行多个实验(图 10)。对于本教程,我已经运行了一个朴素贝叶斯模型,一个逻辑回归模型,以及带有CountVectorizerTfIdfVectorizer矢量器的随机森林。

图 11:提交多个实验和模型的 bash 脚本示例。

在这个阶段,在 MLflow tracking 中,所有这些模型都在同一个实验系列下,因此您可以对它们进行比较,并决定哪一个是最好的模型——图 12

图 12:在 MLflow 中,您可以立即比较您运行的所有实验,并检查它们的指标和图表。

3.在 MLflow 中注册您的模型并部署到 SageMaker 端点

向 MLflow 注册表添加模型

每次在 MLflow 中保存模型时,您都会在模型的工件框中看到“注册模型”选项。这个小按钮允许您将当前模型添加到 MLflow 数据库中,该数据库确定哪些模型已注册并准备好用于“试运行”和“生产”环境,以及在模型需要淘汰时用于“归档”。

图 13:带有“注册模型”选项的 MLflow 中的人工制品框。

只需选择性能最佳的型号,并注册到 MLflow 注册表中。如图 14 所示,这种手动操作可以在 Python 中轻松完成。这种方法对于 CI/CD 非常有用。

图 14: Mlflow Python API 允许通过运行 id 在注册表中添加模型。

选择您的模型应该处于的环境,即“暂存”或“生产”。您的模型将有一个来自模型注册中心的类似 uri:models:/NAME_OF_YOUR_MODEL_IN_THE_REGISTRY/environment

设置要从 MLflow 部署的 AWS IAM 角色

如果你已经准备好了所有的 AWS 角色,你可以跳过这一段,否则请跟我来。

假设你在 AWS 中有一个注册账户,让我们进入 IAM(身份和访问管理)下的 AWS 控制台——同样的操作可以在 Terraform 中完成。在这里,我们可以转到Users下,点击add Users。选择一个用户名,例如stefano,点击access key。这将允许您拥有对正确设置至关重要的aws_access_key_idaws_secret_access_key

图 15:创建一个新用户并选择访问密钥凭证类型。

在第二步中,将用户添加到组中。如果你没有 sagemaker 群,创建一个新的群。像sagemakergroup一样设置组名,添加两个策略:AmazonSageMakerFullAccessAmazonEC2ContainerRegistryFullAccess。这些是授予用户处理 SageMaker 和图像容器的所有权限的关键角色。继续剩余的步骤,最后下载您的凭证 csv 文件——或者记下访问密钥。

接下来,转到“角色”下,单击“创建角色”。在Trusted Entity Type中选择AWS service,在Use Cases下寻找SageMaker,选择Sagemaker — Execution。继续,最后给角色起个名字(例如awssagemakerdeployment)。一旦角色被创建,点击它并在某处复制arn——它将类似于arn:aws:iam::YOUR_AWS_NUMBER:role/awssagemakerdeployment。我们以后会需要这些信息。

最后,您需要设置 AWS CLI 界面。在您的终端类型aws configure中,系统会提示您输入用户访问密钥和秘密访问密钥(包含在下载的 csv 文件中),并添加最适合您的项目的区域(例如,对我来说是eu-west-1)和输出格式(例如json)。这将为 MLflow 使用正确的用户和权限部署到 SageMaker 准备所有必需的凭证设置。

将您注册的模型部署到 AWS SageMaker

我们需要做的第一件事是在 AWS ECR 中创建 MLflow 模型启动器的图像。MLflow model launcher 是您在每个模型的 artefact box 中看到的东西,其中我们有一个模板化的代码,可以检索保存的模型并作为前端接口运行它,以进行预测。要创建这个容器,只需使用mlflow并在您的终端中键入:

venv/bin/mlflow sagemaker build-and-push-container

图 16: MLflow 已在 ECR 中推送 mlflow-pyfunc 映像。

根据您的互联网连接,此命令将在 AWS ECR 上构建和推送基本 MLflow 映像。检索图像 uri,类似于YOUR_AWS_NUMBER.dkr.ecr.YOUR_REGION.amazonaws.com/mlflow-pyfunc:VERSION_NUMBER

从这里开始,您需要一小段代码,这里是链接,以将您的模型部署到 SageMaker——图 17。

图 17:将注册的 MLflow 模型部署到 SageMaker 端点。

在图 17 的 Python 代码中,我们使用了 create ECR image container uri、model uri——在本例中用于“staging”环境——和awssagemakerdeploymentarn。部署将需要几分钟时间。前往 SageMaker 推理页面,您将看到新的闪亮端点。请记住:端点是有成本的!所以实验完记得删。在这种情况下,我们使用一个ml.t2.medium实例,它是最小和最便宜的。

这样一个端点可以很容易地从这个脚本中进行本地查询:

图 18:给定一条输入 tweet,从部署的 SageMaker 端点运行预测的脚本。

图 19:一旦模型被部署,模型在 Sagemaker 推断端点中是可见的。

4.在 EC2 实例上部署 Streamlit 应用程序,并通过您的仪表板运行预测

我们遗漏了什么?一个简单的前端,一个实用的用户界面,通过简单地输入我们的输入 tweet 来查询模型。为此,Streamlit 派上了用场。Streamlit 已经做好了一切准备,可以使用它简单而伟大的 Python 模块来创建一个伟大的界面,正如你从 Streamlit 图库中看到的那样。

我必须承认,我不是 Streamlit 中疯狂花哨设计的超级专家,但是,我知道如何用最少的努力创建一个简单的仪表板,以接收输入 tweet 并返回预测。

创建简单的 Streamlit 仪表板应用程序

仪表板的参考代码可在此处找到。

首先,安装streamlit版本 1.11.0,因为您可能会遇到新版本和 Python > 3.8 的错误:

venv/bin/pip install streamlit==1.11.0

然后,我们需要一些元素来创建一个紧凑的前端。从图 18 中,我们可以复制check_statusquery_endpoint函数,它们将被用来调用端点。如图 20 所示,这些调用然后围绕 Streamlit 进行包装

图 20:在我们的 Streamlit 应用程序中接收输入 tweet 并返回情感的简单代码。

您可以通过调用streamlit在本地测试这段代码,如下所示:

venv/bin/streamlit run dashboard.py

这将直接在您的浏览器中运行。如果一切正常,您就可以将这个应用程序部署到 EC2 实例了

将您的仪表板部署到 EC2 实例

在 AWS 控制台上,转到 EC2 并点击Launch Instance:

  • 为您的机器选择一个名称。
  • 在“应用程序和操作系统映像(亚马逊机器映像)”下,选择 Ubuntu——我用的是 22.04
  • “实例类型”可以是一个t1.micro——我们不需要这个应用程序的超级能力
  • 在“密钥对”上,单击“创建密钥对”。选择RSA.pem格式,并给密钥对命名,例如StreamlitDeployment。一个.pem文件将被下载到您的本地。
  • “网络设置”:点击“编辑”并向下滚动,直到您可以添加一个“自定义安全组”,如图 21 所示。这里添加一个“自定义 TCP ”,可以访问 Streamlit 使用的端口“8501”。将来源保留为“任何地方”。请注意,对于公司的应用程序,这必须在您的 VPC 内得到保护。

图 21:选择 Add security group rule 为 Streamlit 使用的端口 8501 建立新的 TCP 连接。

保持所有其他设置不变,并创建虚拟机。然后,记下机器的“公共 IPv4 DNS”,其格式类似于ec2-MACHINE_IP.compute-1.amazonaws.com。最后,检查机器的“用户名”,点击机器的“实例 ID ”,然后点击“连接”。在这种情况下,机器用户名应该是ubuntu

现在我们可以从本地命令行连接到机器。首先,我们需要对下载的pem密钥对文件授予读取权限,然后我们可以使用以下命令对虚拟机进行ssh:

chmod 400 StreamlitDeployment.pem 
ssh -I "StreamlitDeployment" ubuntu@PUBLIC_IPv4_DNS

在前面的段落中检索到了PUBLIC_IPv4_DNS

在虚拟机中,我们需要安装所有缺失的依赖项:

sudo apt-get updatewget [https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh](https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh) -O ~/miniconda.shbash ~/miniconda.sh -b -p ~/minicondaecho "PATH=$PATH:$HOME/miniconda/bin" >> ~/.bashrcsource ~/.bashrcpip install streamlit
pip install pandas
pip install boto3

最后,我们可以将我们创建的dashboard.py文件复制到虚拟机:

scp -i "StreamlitDeployment.pem" dashboard.py ubuntu@PUBLIC_IPv4_DNS:~/. 

以及 AWS 存储在我们笔记本电脑上的~/.aws下的credentials文件

scp -I "StreamlitDeployment.pem" ~/.aws/credentials ubuntu@PUBLIC_IPv4_DNS:~/.aws/.

直接从终端,我们可以立即测试 Streamlit 应用程序:

streamlit run dashboard.py**You can now view your Streamlit app in your browser.**Network URL: **http://172.31.22.205:8501**External URL: **http://54.146.192.225:8501**

如果您连接到[http://54.146.192.225:8501](http://54.146.192.225:8501,) External URL,您将能够与仪表板应用程序进行交互:

图 22:我们创建并从 EC2 实例运行的 Streamlit 应用程序示例。

一切都很好,但我们可以做得更好,利用TMUX启动一个流会话,这样我们就可以从虚拟机注销,并在不保持连接的情况下与应用程序交互。让我们安装 TMUX:

sudo apt-get tmux

并开始流式会话:

tmux new -s StreamlitSession

现在我们可以自由运行 Streamlit,应用程序将在 tmux 会话中运行:

图 23:仪表板中给出的输入和来自情感分类器的输出结果的例子。

您可以通过按下Ctrl+B离开 SSH 外壳,并在旁边按下D键。这个组合将使您脱离tmux会话,您将能够从 SSH 连接中注销。如果您想停止tmux会话,只需重新连接到机器并寻找正在运行的作业的PID

ps aux | grep streamlit

并终止该进程

kill PID NUMBER

优点、缺点、局限性和未来的工作

在这个阶段,我们可以稍微回顾一下整个过程。我们可以立即指出一些优点:

  • Git-DagsHub 接口通过提供一个专用的 MLflow 服务器和存储来减轻我们的负担。这节省了大量时间和麻烦,因为我们不必设置虚拟机或特定的 VPC 权限或基础架构(例如对等),就可以在我们的所有云工具之间共享 MLflow 系统
  • 如果我们是基于 AWS 的 MLflow 提供了一个超级简单的接口来处理我们注册的模型。我们不需要担心创建 docker 文件或特定的 SageMaker 代码

还有一些缺点:

  • 我们可能不基于 AWS。在这种情况下,MLflow 为 Azure 部署提供了一个很好的解决方案,但不适用于 Google 云平台。不久前我曾试图填补这一空白,但还需要进一步的工作,以在 MLflow 模型注册中心和 GCP 之间建立更无缝的接口
  • 如果您想将这个模型升级到 Heroku,DagsHub 目前没有提供任何与这个部署提供者的 CI/CD 接口。因此,您需要创建更多的图像(docker-compose ),让注册的模型和端点接口被 Heroku 包装起来,并作为定制图像托管
  • SageMaker 端点成本!在部署您的超级 ML 模型之前,考虑部署 SageMaker 批量转换作业而不是端点的可能性。这里是 GitHub 问题请求和到适应该批量转换请求的 MLflow PRs 的链接。

因此,我们需要从这个出发点考虑今后的工作。首先,我们可以找到一种合适的方式来填补 MLflow 和其他云提供商(不是 Azure,不是 AWS)之间的差距。额外的帮助可能来自谢顿。Seldon 提供了一个 MLflow 接口来托管和旋转模型——不管 Kubernetes 可能会带来哪些复杂问题。值得一提的是最近的 MLflow 实现,这是一个新的 mlflow 部署控制器,可以在多个云平台上铺平所有的部署道路。请继续关注,因为我将尝试与您分享类似的内容:)

结论

这是一个很长的教程,但我想你对最后的结果很满意。我们学到了很多东西,所以让我们来回顾一下:

  • 我们学习了如何创建一个简单的模型并将其记录到 MLflow 中,如何向 MLflow 认证,以及跟踪我们的实验。关键要点:实现 MLflow Python bits、身份验证、跟踪概念。
  • 我们看到了如何使我们的代码更通用,利用了sklearn管道的功能,创建了我们的定制转换器来处理数据,并使它们成为最终训练管道的一部分。
  • 我们学习了如何将我们最好的模型部署到 AWS。首先,我们将模型注册到 MLflow registry,然后使用 MLflow sagemaker 部署方案并设置 AWS IAM 角色。
  • 最后,我们用 Streamlit 为我们的模型创建了一个前端,并学习了如何设置 EC2 实例来适应这个应用程序。

我希望你喜欢这个教程,并感谢阅读它。

支持我的写作

通过我的推荐链接加入 Medium 来支持我的写作和项目:

https://stefanobosisio1.medium.com/membership

如果有任何问题或意见,请随时给我发电子邮件,地址是:stefanobosisio1@gmail.com,或者直接在 Medium 这里。

从图 ML 到深度关系学习

原文:https://towardsdatascience.com/from-graph-ml-to-deep-relational-learning-f07a0dddda89

走向深度关系学习

整合神经网络 关系学习 以达到超越当前状态的结构化深度学习模型,如图神经网络

深度关系学习旨在使神经网络能够进行 关系学习 ,即捕捉像关系逻辑(程序)的语言一样有表现力的学习表示。图片由作者提供。

图形结构化数据无处不在。随着最近深度学习的出现,研究人员开始用神经网络探索这种数据表示似乎是很自然的。目前,我们经历了图形神经网络**【GNN】类的爆炸,无数的模型以各种(吸引人的)名字被提出。然而,这些模型中的大多数都是基于相同的简单图形传播原理。

为了从更广阔的视角来看待这个问题,我们将在这里从 关系机器学习 的一般视角来揭示潜在的 GNN 原则,这一点我们在上一篇文章中讨论过。

  • 与(主要)经验驱动的深度学习相反,关系机器学习在很大程度上建立在关系逻辑的形式语言上,而反过来又提供了一些用于表示和操作结构化数据的坚实原则,如各种集合、图表和关系数据库

在这篇文章中,我们将探索一种优雅的方式来连接关系学习神经网络 。这将允许我们以一种非常透明的方式优雅地捕捉 GNN 模型,允许随后将推广到超越其当前状态深度关系学习

神经网络关系学习简史

虽然对于我们许多人来说,最近 GNNs 的普及可能是我们第一次遇到从结构化数据中学习的,但实际上已经有很长一段时间的研究旨在将机器学习模型外推至这些数据表示。

关系学习 的保护伞下,这个问题已经被根植于关系逻辑及其概率扩展的方法统治了几十年,这些方法被称为 统计关系学习 (SRL)。

然而,神经网络提供了高效的潜在表示学习,这超出了逻辑系统的能力。这里,不是将符号视为独立的原子对象,而是引入了学习嵌入的思想,即(离散)符号对象的分布式固定大小数值(向量)表示。

  • 它可以追溯到线性关系嵌入[2],随后的论文提出了不同的模式来改善学习并将符号的嵌入扩展到它们的组合中。这个工作流还包括现在广泛流行的“word 2 vec”[3]。

学习结构的嵌入,如,然后追溯到递归自联想记忆【4】。在这些作品中,(逻辑)关系的表示通常作为对象对(嵌入)之间的相似性度量来处理,例如通过张量来参数化。这种方法后来成为流行的递归神经网络【6】。

与此同时,关系学习社区也提出了各种修改,以使经典神经网络适应关系(逻辑)表示。在很大程度上本着 SRL 的精神,这里宣布的模型目标是正确地组合选择聚集偏差,即学习如何选择关系模式以及如何同时从它们中聚集信息。

  • 仅考虑这里的聚合设置对应于“多实例学习”,即从(独立的)样本集学习,这也在关系学习社区中提出[8]。最近,类似的模型也随着“深集”[7]和类似的架构而复活。

这种 SRL 观点激发了“关系神经网络”[9]——一种基于特定关系数据库模式的结构模型,样本将从该模式中抽取。然后引入了一种类似的方法,分别作为(原始) 图形神经网络 模型(GNN)【10】的图形数据。当时,在[11]和[12]中进一步回顾和比较了这两种“竞争”方法。

这里的重要见解是,这些方法遵循了动态构建 神经模型的 概念。这种计算模式的转变使得能够应用从原始关系数据中进行端到端学习的核心思想。与基于将结构预处理成固定大小的向量(张量)的(命题化)方法相比,这允许模型直接利用输入示例呈现的关系偏差。

图形神经网络的先验知识

现代的 GNN 变体可以被视为这一原始 GNN 概念的延续(尽管它们通常更简单[13])。这里一个常见的解释建立在“消息传递”的概念上,每个节点都被视为向输入图 Xi 中的邻居发送“消息”。虽然这一解释与魏斯费勒-雷曼启发法有一些重要的联系,但⁴直接通过其底层计算Gi 的结构来查看 gnn 可能更具启发性,类似于查看经典的深度学习模型。

从这个角度来看,⁵ GNNs 可以被视为标准的 CNN 技术的扩展,即卷积聚集不规则图结构 Xi ,在节点( N )之间具有任意边( E )。为了促进这一点,他们从每个输入图形 Xi 中动态展开每个计算图形* Gi ,而不是固定像素网格上的静态架构。*

这样,GNN 就是一个简单的“标准”多层前馈 CNN 架构,唯一需要注意的是,计算图* Gi 中每层 k 的结构准确反映了输入图 Xi 的结构。*

一个典型 GNN(“g-Sage”)的计算图,在每一层内具有权重共享(“卷积”),在一些不规则输入图 X (紫色,左侧)上展开。单个卷积运算节点( C橙色)之后是聚合运算( Agg 、蓝色),其形成输入到输入图的下一层表示( X )(浅紫色)。图片由作者提供(来自[32])。

P 具体来说,每个输入Xi 中的每个节点 N 都可以关联一个特征向量(或嵌入),形成计算Gi 中的输入层表示。
对于下一层 k 表示的计算,每个节点 N 通过聚集 A 【池化】 M : (N,M)∈ E
输入Xi 中相邻的相邻节点的值来计算自己的隐藏表示 h(N) 。这些可以通过某个参数函数 C (卷积)进一步转换,该参数函数在各层 k 内与相同的参数化 W₁ 重复使用:**

  • 在一些 GNN 模型中,该 h 表示通过另一个 Cw₂ 与来自前一层k1的“中心”节点的 N 表示进一步组合,以获得层 k 的最终更新值,如下所示:

然后,他的通用“聚合和组合”[17]计算方案涵盖了各种流行的 GNN 模型,然后简化为特定聚合 A 和激活/卷积 Cw 的选择。例如在 GraphSAGE [18]中,操作是

**

而在流行的图卷积网络[19]中,这些甚至可以合并成一个单一的步骤

同样的一般原则也适用于许多其他 GNN 作品[17]。⁰

批判的视角。最近,已经提出了这个计算公式的大量不同变体。本质上,每一个这样引入的 GNN 变体都提出了(共同的)激活和聚集功能的某种组合,并且/或者提出了用从其他神经架构借用的层来扩展该模型。最终,在新的 GNN 名字下引入这些普通 DL 模块的详尽组合导致了我们今天看到的 GNN动物园模型,具有很大程度上模糊的性能【21】。

要真正看透模型的原理,现在看来,在这个相当常见的“低挂果实”收集的早期研究阶段之后,GNN 领域可能会受益于一些更深入的见解…

对称的观点

M. Bronstein 等人对 GNN 模型类最近的(r)发展提出了一个非常有见地的观点,通过对每个领域中的 对称性 的基本假设来观察 DL 架构。从几何领域中常见的对称概念开始,这种方法后来被创造为“ 几何深度学习 ”(请在 Medium 上查看迈克尔·布朗斯坦的优秀文章系列)。

自然地,所有的机器学习模型本质上都试图探索输入数据分布中的对称性,即某种形式的重复规律。然而,在一些结构化模型类中,这种规律性也存在于模型参数空间中。

在深度学习中,这可能是最有名的 CNN,旨在反映像素网格中的不变性* w.r.t .平移(shift),这是这种几何对称最流行的例子。*

  • 这意味着无论你将输入模式转移到哪里(在“感受野”的范围内),模型输出都将保持不变。

CNN 卷积层中的对称(规则)加权共享模式。图片由作者提供。

以类似的方式,递归和递归神经网络被设计成在线性和树形结构中反映递归对称性

递归(左)和递归(右)神经网络的公共权重分配方案和对称性。图片由作者提供(来自[32])。

最后,gnn 试图用一般的图不变量做同样的事情,也就是关于图同构(置换)对称的函数。

应用于高度对称分子(甲烷)的图形神经网络中的规则权重分配模式。图片作者(来源)。

集合上反映完全置换对称性的模型,例如“深集合”,可以被视为一种“基础情况”,其中所有可能的输入置换都被认为是等价的。**

  • 而在前面的模型中,只有可能的输入排列的子集被不变地处理(想象一下,例如,移动一只猫的图像与随机移动它的所有像素)。

自然地,在每个学习领域中结合关于相应对称形式的正确先验最终导致更好的样本效率和泛化,并且历史证明允许利用这种先验的模型非常有用。

超越几何对称

传统上,大多数深度学习进展都是通过应用程序根据经验驱动的,而不是一些先前自上而下的理论分析。这也许也激发了对计算机视觉中出现的几何对称性的主要方向的探索,这是最普遍的应用领域。然而,神经网络中的对称性不需要局限于几何。

举例。为了简单的演示,让我们回顾一下上一篇关于神经符号整合的文章中的简单逻辑 XOR 例子。

  • 在那里,我们解释了编码简单的逻辑函数,如 AND 和 OR,如何在神经网络的早期进化中发挥重要作用,早期“神经网络”无法学习 XOR 问题导致了重大挫折。

尽管几十年来 XOR 问题的解决方案已经广为人知,但有趣的是,即使使用现代的深度学习,正确地学习仍然是非常重要的!特别地,虽然最佳解决方案可以由少至 2 个(或者甚至 1 个)隐藏神经元组成,但是实际上很难正确地训练如此小的网络的参数,因为优化将大部分被次最佳解决方案卡住。(自己轻松试试吧!)

只有当我们采用普通的(“蛮力”)方法将网络参数化增加到比必要的维数高得多的维数时(app。10+隐藏神经元进行二进制异或),训练最终变得可靠。

虽然蛮力通常是深度学习中的一个答案,例如当诉诸数据集扩充时,我们可以通过将正确的先验纳入模型来做得更好。特别地,这里我们知道异或函数是对称的因为 XOR(x₁,x₂) = XOR(x₂,x₁).因此,我们不需要单独处理所有的排列,函数的神经参数化也应该有一些内部对称性。

因此,代替(常见的)过度参数化方法,我们实际上可以保持小的网络规模,并且通过绑定权重来进一步减少参数的数量。

二进制 XOR 函数学习的(自动诱导的)对称权重共享先验的例子。图片来自作者的学生马丁·克鲁茨基。

然后,这种权重共享显著地改进了逻辑函数的学习,类似于在计算机视觉(和其他应用)中通过卷积使用几何对称性的 DL 模型的⁵。

用逻辑捕捉对称

如果我们可以从将正确的先验知识编码到模型中开始,而不是在顶级通用模型上修补过度参数化和其他经验(蛮力)技巧,这不是很好吗?

几何深度学习运动现在用基于几何对称性的神经网络实践观点提出了这样一个有趣的观点。然而,这个原理长期以来也在 关系学习 领域中被研究,作为逻辑上的 背景知识并入。**

这两种方法之间有什么联系吗?关系逻辑与(几何)对称性有什么关系吗?

是的。尽管被 DL 支持者普遍忽视(或轻视),关系逻辑实际上是捕捉各种对称和离散领域规则的完美形式。⁶

到与“几何”解释一致,让我们从集合中置换对称的最简单概念开始。有了逻辑,就不需要为此重新发明轮子了,因为我们已经在逻辑中将集合定义为满足给定属性的对象 X 的枚举,即{ X | X 是一个节点}或简单的 N(X) 对于一些一元(属性)关系 N。请注意,在集合的这种表示中,没有关于元素 X 的任何排序的概念,因此相应的排列对称性在设计中得到考虑!

然后,这可以直接推广到图形,这只是一个二元逻辑关系的情况。因此,为了表示一个图,我们可以写 Edge(X,Y) 来表示它的所有边的集合。自然,我们在这里也可以将相应节点的集合指定为节点(X)节点(Y) 。同样,在这种表示中,没有关于节点或边的任何排序的概念。

  • 此外,关系逻辑并不仅限于图,同样的原理直接应用于更高级的结构 Relation(X,Y,Z,…) 以及它们通过逻辑连接词的组合(例如,形成关系数据库和 SQL 的形式模型)。

虽然每个(以前的)计算机科学学生肯定都熟悉这种形式(数据库)表示和相应的关系代数/逻辑运算,但这种形式概念能否用于机器学习实践可能并不明显。
然而,这已经被确切地的观点 解除了建模 的范式【27】(在我们的以前的文章中描述了的关系学习),这里的的关系逻辑的表达形式主义已经被直接用于捕捉机器学习中的对称性问题。

重要的是,这里的对称不仅仅是描述性的,关系逻辑已经被直接用于编码图形模型,比如在流行的马尔可夫逻辑网络【16】。

分别来自深度学习和统计关系学习 (SRL)领域的 CNN(左)中卷积滤波器展开的对称权重共享模式和 MLN(右)中提升的逻辑模板之间的类比。作者图片。

然而,尽管用关系逻辑将领域对称性的先验编码到图形模型中已经研究了几十年,但在深度学习方面似乎令人惊讶地缺乏。

  • 当然,深度学习社区对逻辑有一定的厌恶。尽管如此,考虑到神经网络和命题逻辑之间的历史相互作用,神经网络中基于逻辑的对称性缺乏基础似乎有些令人惊讶。

然而,正是标准神经网络的“命题固定”限制了它们在学习问题中捕捉有趣的(提升的)对称,并使深度学习与关系逻辑的集成变得复杂,正如上一篇文章中所概述的

深度关系学习

但是等一下,现在我们有强大的 GNNs!他们不再受命题固定的困扰,因为他们可以捕捉图形数据。难道我们现在就不能把一切都变成图,用 GNNs 来解决我们所有的问题吗?

当然,图形是无处不在的,如果你尝试,你可以将手头的任何问题以某种方式转化为图形表示。因此,这是非常方便的只是应用一个过剩现成的 GNN 模型,看看你是否运气好一些调整。⁸

然而,请注意,这是另一种“如果你只有一把锤子……”的认知偏见,类似于之前讨论的“将一切都变成向量”的命题化方法论。

最后,gnn 只是一种特殊的图传播启发式算法,具有源于底层 WL 的固有限制。重要的是,即使 GNN 可以正确地捕捉图形结构(超越 WL),这仍然不能保证其实际的学习(泛化)能力,类似于它显然不足以坚持基于通用近似定理的简单 2 层神经网络【29】。

这就是为什么我们设计越来越复杂的模型和学习表示,直接反映我们问题的结构。因此,将复杂的关系学习表示和推理算法从图和图传播转回到 GNNs 可能不是最好的主意。

因此,如果我们不是试图将更复杂的关系问题表示嵌入到图和 gnn 中,而是可以将关系逻辑的表达用于一些成熟的“深度关系学习”,捕捉当前神经模型中的对称性作为特例,类似于提升的图形模型如何在 SRL 中概括标准图形模型,这不是很好吗?

正如你现在可能已经猜到的那样,有一个学习框架可以完全做到这一点。具体来说,它采用 提升建模 策略,并将其外推到深度学习设置中。然后,这种范式能够将神经网络和命题逻辑之间熟悉的对应直接带到关系层面。

因此,在命题逻辑被提升到关系逻辑的更高表达能力的同样意义上,该框架将经典神经网络提升到这些模型的更具表达能力的版本,称为“提升的关系神经网络”[34]。⁵

提升关系神经网络

与提升的图形化模型类似,“提升的关系神经网络”框架的核心是一种基于关系逻辑的语言,用于定义神经模型,进一步简称为“https://github.com/GustikS/NeuraLogic”。该语言直接源自 Datalog(或 Prolog),它通常用于高级数据库查询和逻辑编程,具有一个额外的特性——它是可微分的

  • 虽然现在有许多可区分编程的框架,但 NeuraLogic 的显著不同在于它是声明性的。这允许以非常简洁和优雅的方式表达一些复杂的算法和原理,这特别适用于表现出各种规律性(对称性)的关系问题。

举例。**让我们跟进上面介绍的关系逻辑如何捕捉集合和图形中的对称性的形式原则,并在构建 GNN 时使其可直接操作。特别地,我们现在知道图是节点(节点(X)节点(Y) )之间的一组边(边(X,Y) )。然后,我们知道通用 GNN 计算规则是将节点的表示传播/聚集到它们的邻居,这可以放入逻辑规则中,如下所示:**

**node2(X) <= W node1(Y), edge(X, Y).**

…就是这样,真的!这是神经语言中 gnn(gcn[19],特别是⁰)的可运行代码。内容如下:

"为了计算任何对象 X 的表示' node2 ',聚合所有对象 Y、的 a 'W【T21 '-八分表示' node1 ',其中' edge '位于两者之间。"

  • 那些倾向于关系逻辑的数据库解释的人可能会这样理解:“在 Y 列上连接表‘node 1’和‘edge’,按 X 分组,并聚合成一个新表‘node 2’”。

在神经对话框架中运行这个逻辑规则与在传统框架中运行 GCN 完全一致,也就是说,它将执行

前面讨论的公式。当然,激活()和聚集( Agg )函数(以及其他超参数)的规范也可以添加到规则中。然而,您也可以像这样让它们保持默认值( tanh+avg )来享受 GCNs 的基本原理的清晰性,即节点和相应边的局部排列的不变性。

因此,在 NeuraLogic 中,模型对称性先验的定义成为模型本身的代码!

重要的是,这个规则是完全通用的,也就是说,在专门为图形或 gnn 设计的整个神经对话框架中没有特定的功能。

  • 然后,类似的简单提升规则对深度学习模型中的各种递归/递归和其他对称权重共享(卷积)方案进行编码,本文通篇以图片中的颜色为例。

这意味着你可以轻松地编写任意的关系规则,编码新颖的神经建模构造具有高级对称先验的,捕捉复杂的关系学习原则 超越 GNNs【36】。

先睹为快“超越图神经网络与提升的关系神经网络”[32]

虽然我们在这里用提升的关系神经网络对深度关系学习进行了先睹为快的总结,但我们将在下一篇文章中详细探索神经对话框架,在那里我们将展示它的实际用途、表达能力和计算效率。

1.最直接,也是历史上占主导地位的方法是将关系数据转化为固定的张量表示,作为预处理(命题化)步骤,然而,正如前面所讨论的,这隐藏了许多缺点。

[2] A .帕卡纳罗和杰弗里 e .辛顿。"使用线性关系嵌入学习概念的分布式表示."摘自:IEEE 知识与数据工程汇刊 13.2 (2001),第 232–244 页。刊号:10414347

[3]托马斯·米科洛夫、伊利亚·苏茨基弗、程凯、格雷戈·S·科拉多和杰夫·迪恩。"单词和短语的分布式表示及其组合性."载于:神经信息处理系统进展 26 (2013),第 3111–3119 页。

[4]乔丹·波拉克。"递归分布式表示."摘自:人工智能 46.1–2(1990),第 77–105 页。issn: 00043702。

5 杰弗里·e·辛顿。“将部分-整体层次映射到连接主义网络中。”摘自:人工智能 46.1–2(1990),第 47–75 页。issn: 00043702。

[6]理查德·索赫尔、齐丹·陈、克里斯托弗·曼宁和吴恩达。"用神经张量网络进行推理以完成知识库."神经信息处理系统进展。Citeseer。2013 年,第 926–934 页。

[7] Zaheer,Manzil 等,《深层集合》 arXiv 预印本 arXiv:1703.06114 (2017)。

[8] Ramon,j .,& DeRaedt,L. (2000 年)。多示例神经网络。在ICML-2000 关于属性值和关系学习研讨会的会议录

[9]亨德里克·布洛克尔和沃纳·乌温特斯。"使用神经网络进行关系学习."摘自:ICML 2004 年统计关系学习及其与其他领域的联系研讨会。2004 年,第 23-28 页。

[10] Franco Scarselli、Marco Gori、Ah Chung Tsoi、Markus Hagenbuchner 和 Gabriele Monfardini。"图形神经网络模型."摘自:IEEE 神经网络汇刊 20.1 (2008),第 61-80 页

[11] Werner Uwents,Gabriele Monfardini,Hendrik Blockeel,Franco Scarselli 和 Marco Gori。"图形处理的两种联结主义模型:关系数据的实验比较."载于:MLG 2006 年,《利用图表进行挖掘和学习国际研讨会论文集》。2006 年,第 211-220 页

[12] Werner Uwents、Gabriele Monfardini、Hendrik Blockeel、Marco Gori 和 Franco Scarselli。"关系学习的神经网络:实验比较."载于:机器学习 82.3(2011 年 7 月),第 315–349 页。issn: 08856125。

[13] Lamb,Luis C .等人,“图形神经网络与神经符号计算的相遇:综述与展望”arXiv 预印本 arXiv:2003.00330 (2020)。

14.GNN 模型可以被视为用于图同构驳斥检查的著名的魏斯费勒-雷曼(WL)标签传播算法的连续、可微分版本。然而,在 GNNs 中,不是离散的标签,而是连续的节点表示(嵌入)被连续地传播到节点的邻域中,反之亦然,用于相应的梯度更新。

15.接受这种动态计算图的观点,这与之前介绍的递归网络非常相似,然后使模型无状态,这消除了源于输入图内的消息传递解释的可能的歧义。

理查森、马修和佩德罗·多明戈斯。“马尔可夫逻辑网络。”机器学习 62.1–2(2006):107–136。

[17]徐克玉路、胡、朱尔·莱斯科维奇和杰格尔卡。“图形神经网络有多强大?”载于:arXiv 预印本 arXiv:1810.00826 (2018)。

[18]威尔·汉密尔顿、之桃·英和朱尔·莱斯科维奇。"大型图上的归纳表示学习."神经信息处理系统进展。2017,第 1024–1034 页。

[19]托马斯·n·基普夫和马克斯·韦林。"图卷积网络的半监督分类."参加:2017 年 4 月 24 日至 26 日在法国 ICLR 土伦举行的第五届学习代表国际会议,会议记录。2017 年,OpenReview.net

20.我们注意到我们只讨论了“空间”表示的图形和运算。然而,一些 GNN 方法在频谱、傅立叶域中表示图形和卷积运算。然而,我们注意到,这些又大多遵循相同的“聚合和组合”原则,并可以相应地重写[17]。

[21] Dwivedi,Vijay Prakash 等,“基准图神经网络”arXiv 预印本 arXiv:2003.00982 (2020)。

22.更准确地说,由卷积滤波器的应用引起的共享权重引入了滤波器的相应变换,同时通过池化将聚合函数(例如,最大值平均值)结合在顶部,将其进一步扩展到变换 i 方差

23.尽管理论上在这里误差函数(例如交叉熵)中没有局部最小值,但是存在朝向参数空间和大鞍点的边界的发散区域,使得基于梯度的优化非常困难。

24.类似地,对于更简单的 AND 和 or 函数来说也是如此,然而,这些函数过于琐碎,不足以证明 NNs 的这种过度参数化问题的重要性。
有趣的是,虽然逻辑单元和神经元之间的功能并行性已经得到很好的认可,但就我们所知,以前没有工作利用相应神经权重中逻辑功能的平凡对称性来改善学习。

[25]马丁,克鲁茨基。探索深度学习中的对称性。理学学士论文。布拉格的捷克技术大学,2021。

26.自然地,逻辑更适合描述离散领域中的规律性,而几何解释在连续领域中更自然,如计算机视觉和机器人学。

[27] Kimmig,Angelika,Lilyana Mihalkova 和 Lise Getoor。"提升图形模型:一项调查."机器学习99.1(2015):1–45。

28.考虑到最近围绕 GNN 概念的大肆宣传,以及足够的超参数调整,人们确实已经成功地在广泛的问题上利用了 GNN,这现在变得更加诱人了。
果不其然,甚至逻辑表达式也可以转化为图形表示,如【13】所述。然而,这并不意味着 GNNs 是解决逻辑推理底层问题的合适算法!

29 乔治·西本科。"通过叠加一个 s 形函数的近似."控制、信号和系统的数学2.4(1989):303–314。

30.注意与 GCN 规则的经典矩阵视图 H = σ (W * H * A) 的对齐,其中 H 是节点表示矩阵,A 是邻接矩阵(类似于这里的‘边’谓词)。

31.一些重要的评论文章显示,与所有论文中传统上自称的最先进的结果相比,许多这些 GNN 修改的实际性能增益往往相当微不足道,有时甚至无法击败简单的(非结构化)基线模型[21],使得对大量 GNN 变体的需求有点可疑。

32.类似的工作也来自递归转换简化描述的旧思想[5],自动编码器和许多其他工作都建立在此基础上。

33.注意,原则上,递归神经网络可以被认为是递归神经网络的一般形式,它在序列上递归展开,而递归网络在(常规)树上展开。

[34] Sourek,Gustav,等人,〈提升关系神经网络〉。人工智能研究杂志 (2018)。

35.有趣的是,这种具有各种建模结构的方法,包括一些 GNN 和子图 GNN 变体,已经在 2015 年发表,后来(2017 年)也扩展到这种结构的自动学习。然而,对框架的解释根植于关系逻辑而不是(仅仅)图表,这对于 ML 观众来说可能看起来很奇怪。

[36]古斯塔夫·索雷克、菲利普·切列兹和 ondřej·库泽尔卡。"超越图神经网络与提升关系神经网络."机器学习110.7(2021):1695–1738。

作者深深地感谢 翁德雷 对于 底层概念中的无数想法解除了关系神经网络

从 Jupyter 笔记本到现实生活:MLOps

原文:https://towardsdatascience.com/from-jupyter-notebooks-to-real-life-mlops-9f590a7b5faa

为什么是必备?

Unsplash 上拍摄的 ThisisEngineering RAEng

我的第一个数据科学项目是预测二手车价格的机器学习模型。该项目的主要步骤是:

  • 在网站上刮二手车广告
  • 清理和预处理刮出的数据
  • 探索性数据分析
  • 模型创建
  • 模型评估

最终结果相当令人满意,R 平方得分为 0.9。我的意思是,考虑到这是我的第一个项目,它超出了我的预期。如果你想了解更多,我写了一篇关于这个项目的文章

注:这个故事最初发表于datasciencehowto.com

这样的项目对于学习和实践来说是非常好的。在那个项目中,我花了很多时间使用 Pandas、Scikit-Learn、Beautiful Soup 和 Seaborn,它们是数据科学领域非常流行的 Python 库。

这个项目对我来说是一次很好的学习经历,它让我觉得我做出了开始学习数据科学的正确决定。

然而,这也是一个未完成的项目。我在 Jupyter 笔记本上做了上面提到的所有步骤,但这个项目从未投入生产。它没有被任何人用作价格预测服务。

我认为这是机器学习在现实生活中最具挑战性的部分。创建一个模型并利用它进行预测并不十分困难。将它部署到生产中并作为一项持续服务来运营是另一个世界。

构建这样一个 ML 系统需要许多不同类型的操作,这些操作需要同步并协同工作。这个过程指的是机器学习操作,又名 MLOps。

您可能听说过 DevOps,这是构建大型软件系统的常见做法。MLOps 可以被认为是机器学习系统的 DevOps。

MLOps 的动机

虽然在 Jupyter 笔记本上构建 ML 模型对学习有好处,但离创造任何商业价值还很远。另一方面,建立一个在生产中持续运行的 ML 系统才是机器学习真正有价值的地方。

让我们花点时间思考一下我的预测二手车价格的项目如何提供商业价值。如果你有买卖二手车的业务,你可以用它来查找低于市场价值出售的汽车。然后,你将使用机器学习来改善你的业务,增加你的利润。

使用这种模式的另一个有价值的资产可能是一个网站。人们会通过你的网站根据当前的市场情况来了解他们汽车的价格。

这两者都无法通过在 Jupyter 笔记本上创建和训练一次的机器学习模型来实现。

MLOps 面临的挑战

我想我们都同意,MLOps 是使机器学习成为有益的工具的一个基本要求。毫不奇怪,这不是一件容易的事。

MLOps 面临的主要挑战是可伸缩性、版本控制和模型衰退。为了克服这些挑战,ML 系统被视为软件系统,并按照 DevOps 原则运行。需要注意的是,机器学习代码是整个 ML 系统中非常小的一部分。

DevOps 的两个主要原则是持续集成(CI)和持续交付(CD ),它们也适用于 MLOps。除此之外,第三个玩家也加入了游戏,这就是连续训练(CT)。

CI 要求您测试和验证代码和组件。在机器学习系统中,你还需要对数据执行这些操作。数据验证可能基于模式。例如,如果输入数据中有新的或缺失的列,整个系统可能会崩溃。数据验证脚本应该处理这样的问题。

您还需要检查这些值,如果这些值与预期值相差很大,则需要采取必要的措施。在这种情况下,最好丢弃新数据并跳过模型训练。

持续培训对所有 ML 系统都至关重要。在我们预测二手车价格的例子中,有几个因素对汽车价格有影响,如市场状况、通货膨胀率、全球趋势等等。因此,您不能使用很久以前训练的模型。为了准确地反映当前条件,您需要不断地重新训练模型。

在典型的 ML 系统中,使用许多不同的软件工具和库。因此,您的工作流程中需要采用版本控制系统。

您还需要监控和评估模型输出。机器学习本质上是实验性的。不能保证您的模型总能产生准确可靠的结果。

最后但并非最不重要的一点是,所有这些都需要具有可伸缩性并且高效工作。你可能有最精确的模型,但如果它不可扩展,你的产品或服务很可能会失败。

MLOps 是将您的模型转换为 ML 系统的关键。我们在这篇文章中提到的只是这个领域中的亮点。MLOps 中使用了广泛的工具和软件包。我将会写关于这些工具以及如何在实践中使用它们。敬请关注更多 MLOps。

你可以成为 媒介会员 解锁我的全部写作权限,外加其余媒介。如果你已经是了,别忘了订阅https://sonery.medium.com/subscribe如果你想在我发表新文章时收到电子邮件。

*https://sonery.medium.com/membership

感谢您的阅读。如果您有任何反馈,请告诉我。*

从 Jupyter 到 Kubernetes:使用开源工具重构和部署笔记本

原文:https://towardsdatascience.com/from-jupyter-to-kubernetes-refactoring-and-deploying-notebooks-using-open-source-tools-19f99585e923

数据科学软件工程

从凌乱的笔记本到在 Kubernetes 上运行的流水线的一步一步的指南

照片由 Myriam JessierUnsplash 上拍摄

笔记本电脑非常适合快速迭代和原型制作,但很快就会变得凌乱不堪。在笔记本上工作之后,我的代码变得难以管理并且不适合部署。在生产中,代码组织对于可维护性至关重要(改进和调试有组织的代码比一个又长又乱的笔记本要容易得多)。

在这篇文章中,我将描述如何使用我们的开源工具来覆盖数据科学项目的整个生命周期:从一个凌乱的笔记本开始,直到代码在生产中运行。我们开始吧!

第一步,用自动化工具清理我们的笔记本;然后,我们将使用soorgeon自动将我们的单片笔记本重构为模块化管道;之后,我们将测试我们的管道是否运行;最后,我们将把管道部署到 Kubernetes。这个工作流的主要好处是所有步骤都是完全自动化的,所以我们可以返回 Jupyter,迭代(或修复 bug),并毫不费力地再次部署。

清理笔记本

图片作者。

笔记本的交互性使得尝试新想法变得简单,但也产生了杂乱的代码。在探索数据的时候,我们经常在不考虑可读性的情况下匆忙编写代码。幸运的是,有像 isortblack 这样的工具可以让我们轻松地重新格式化代码以提高可读性。不幸的是,这些工具只能处理.py文件;然而,soorgeon使我们能够在笔记本文件上运行它们(.ipynb):

注意:如果你需要一个示例笔记本来尝试这些命令,这里有一个:

查看本节开头的图片:我在左边的笔记本上引入了一些额外的空白。然而,在应用了soorgeon clean(右图)之后,我们看到多余的空白消失了。所以现在我们可以专注于编写代码并应用soorgeon clean来轻松使用自动格式化!

重构笔记本

在单个笔记本上创建分析是很方便的:我们可以在各个部分之间移动,并且很容易地编辑它们;然而,这有很多缺点:很难协作和测试。在多个文件中组织我们的分析将允许我们定义清晰的边界,因此多个管道可以在项目中工作,而不会相互妨碍。

从单个笔记本到模块化流水线的过程既耗时又容易出错;幸运的是,soorgeon可以为我们做这些繁重的工作:

重构后,我们会看到一堆新文件:

图片作者。

Ploomber 自动将我们的笔记本变成一个模块化的项目!它生成一个带有基本指令的README.md和一个requirements.txt(从import语句中提取包名)。此外,它创建了一个包含几个.ipynb文件的tasks/目录;这些文件来自原始的笔记本部分,由降价标题分开。计算出哪些部分依赖于哪些部分。

如果您喜欢导出.py文件;您可以通过--file-format选项:

soorgeon refactor nb.ipynb --file-format py

这次tasks/目录会有.py文件:

 ├── README.md 
  ├── nb.ipynb
  ├── pipeline.yaml
  ├── requirements.txt
  └── tasks
      ├── clean.py
      ├── linear-regression.py
      ├── load.py
      ├── random-forest-regressor.py
      └── train-test-split.py

soorgeon使用降价标题确定要生成多少输出任务。在我们的例子中,有五个人。然后,soorgeon分析代码以解析各部分之间的依赖关系,并添加必要的代码以将输出传递给每个任务。

例如,我们的“训练测试分割”部分创建了一个变量XyX_trainX_testy_trainy_test;最后四个变量由“线性回归”部分使用:

图片作者。

通过确定输入和输出变量,soorgeon确定“线性回归”部分依赖于“训练测试分割”部分。此外,“随机森林回归”部分也依赖于“训练测试分割”,因为它也使用“训练测试分割”部分生成的变量。根据这些信息,soorgeon构建了依赖图。

测试我们的管道

现在是时候确保我们的模块化管道正确运行了。为此,我们将使用工具箱中的第二个包:ploomber。Ploomber 允许我们在本地开发和执行我们的管道。

这是输出:

ploomber提供大量工具来管理我们的渠道;例如,我们可以生成一个图:

图片作者。

我们可以看到依赖图;有三个串行任务:loadcleantrain-test-split。在它们之后,我们看到两个独立的任务:linear-regressionrandom-forest-regressor。将我们的工作模块化的好处是我们团队的成员可以独立工作,我们可以隔离测试任务,并行运行独立任务。有了ploomber,我们可以继续用 Jupyter 开发管道,直到我们准备好部署!

部署

为了简单起见,您可以使用 cron 部署 Ploomber 管道,并按计划运行ploomber build。但是,在某些情况下,您可能希望利用现有的基础设施。我们掩护你!使用soopervisor,您可以将您的管道导出到气流AWS 批次KubernetesSLURMKubeflow

soopervisor add向我们的项目添加一些文件,比如一个预配置的Dockerfile(如果我们愿意,我们可以修改它)。另一方面,soopervisor export将我们现有的管道导出到 Argo 工作流,这样我们就可以在 Kubernetes 上运行它。

通过更改soopervisor add命令中的--backend参数,您可以切换到其他支持的平台。或者,你可以注册免费云服务,它允许你用一个命令在云中运行你的笔记本。

结束语

笔记本清理和重构既耗时又容易出错,我们正在开发工具来使这一过程变得轻而易举。在这篇博文中,我们从拥有一个单片笔记本到拥有一个在生产中运行的模块化流水线——所有这些都是使用开源工具以自动化的方式实现的。所以请让我们知道你想看什么功能。加入我们的社区,分享你的想法!

嗨!我叫爱德华多,我喜欢写关于数据科学的所有东西。如果您想了解我的最新内容。在 MediumTwitter 上关注我。感谢阅读!

最初发布于ploomber . io

从核密度估计到 Python 中的空间分析

原文:https://towardsdatascience.com/from-kernel-density-estimation-to-spatial-analysis-in-python-64ddcdb6bc9b

如何将您的 KDE 转化为地理空间数据

图片作者。核密度估计的例子

核密度估计(KDE)是一个有用的分析和可视化工具,通常是可视化或分析工作流的最终产品。核密度估计是很好的可视化,但是它们的使用还可以更进一步。

在这篇文章中,我将展示一种使用 Python 获取内核密度估计图并将它们转换成地理空间数据以便进一步分析的方法。

核密度估计

Seaborn 的帮助下,使用 Python 创建内核密度估计图相当简单。我们从读入 GeoPandas 中的点数据集开始。我正在使用芬兰赫尔辛基的自行车共享站的数据集,从 OpenStreetMap 检索,但只要你的数据包含坐标或形状点,你应该没问题。

读入数据

自行车共享站数据框架

有了读入的数据,我们可以开始创建我们的核密度估计。我不会深入讨论 KDE 本身的细节,因为还有很多其他来源,比如 scikit-learn 文档,但是下一个代码块展示了 Seaborn 实现是如何工作的:

创建核密度估计。

这产生了 KDE 图,我们将把它转换成可用于空间操作的有形状的对象:

图片作者。KDE 情节。

为每个强度级别创建多重多边形

KDE 图的不同轮廓可以通过我们的 KDE 的collections对象来访问。通过对该对象进行迭代,我们可以访问我们之前定义的每个亮度级别的每个轮廓的每个路径。

可能会出现轮廓不连续的情况,因此我们为相同强度级别的每条路径创建一个多边形,然后将它们组合成一个多多边形,为每个强度级别提供一个多多边形。

这里的方法受到了 StackExchange 上的这个线程的启发。

将我们的轮廓转换成多重多边形

转换为地理数据

现在我们已经有了一个包含我们的多多边形和相应强度级别的元组列表,我们可以创建一个 Pandas 数据框架,然后我们可以将其转换为 GeoDataFrame。

通过地理数据框架,我们可以访问轮廓的 Shapely 对象,并利用 Shapely 的所有内置操作,例如轻松计算每个轮廓的面积估计值。

转换到地理数据框架并导出数据。

生成的地理数据框架

上面的地理数据框架显示了我们生成的多重多边形以及每个估计等高线的面积计算。由于我们现在有了 Shapely 对象,我们可以使用 Shapely 的任何内置方法来扩展空间分析,或者由于我们已经将地理数据框架导出到地理包,我们可以在 QGIS 等中打开它。

图片作者。在 QGIS 中打开 Geopackage。

结论

在这篇文章中,我简单介绍了如何将你的 KDE 图转换成形状优美的物体和地理空间文件,以便你进一步分析。

从 ML 模型到 ML 管道

原文:https://towardsdatascience.com/from-ml-model-to-ml-pipeline-9f95c32c6512

记者王Unsplash 上拍照

数据科学基础

使用 Scikit-学习 Python

建立机器学习模型不仅仅是选择正确的算法和调整其超参数。在模型实验开始之前,大量的时间花费在争论数据和特征工程上。这些预处理步骤很容易淹没你的工作流,变得难以跟踪。将注意力从 ML 模型转移到 ML 管道,并将预处理步骤视为构建模型不可或缺的一部分,有助于使您的工作流程更有条理。在这篇文章中,我们将首先看看预处理模型数据的错误方法,然后学习一种正确的方法和两种构建 ML 管道的方法。

照片由 DaYsOUnsplash 拍摄

ML 管道根据上下文有多种定义。在本文中,ML 管道被定义为预处理步骤和模型的集合。这意味着当原始数据被传递到 ML 管道时,它将数据预处理为正确的格式,使用模型对数据进行评分,并弹出预测得分。

📦 0.设置

让我们导入库和一个样本数据:titanic 数据集的子集。

我们现在将定义常用变量,以便稍后轻松引用:

是时候看看第一种方法了。

❌ 1.错误的方法

在预处理时,像这样使用 pandas 方法并不少见:

仅部分输出

我们估算缺失值,在 0 到 1 之间缩放数值变量和一个热编码分类变量。预处理后,数据被分区并拟合模型:

好的,让我们分析一下这种方法有什么问题:
◼️ 插补:数值变量应该用训练数据而不是整个数据的平均值进行插补。
◼️ 缩放:最小值和最大值应根据训练数据计算得出。
◼️ 编码:类别应该从训练数据中推断出来。此外,即使在预处理之前对数据进行了分区,使用pd.get_dummies(X_train)pd.get_dummies(X_test)进行一次热编码也会导致不一致的训练和测试数据(即,列可能会根据两个数据集中的类别而变化)。因此,在为模型准备数据时,pd.get_dummies()不应用于一次性编码。

💡测试数据应在预处理前搁置。用于预处理的任何统计数据,例如平均值、最小值和最大值,都应该从训练数据中导出。否则会出现数据泄露问题。

现在,让我们评估模型。我们将使用 ROC-AUC 来评估模型。我们将创建一个计算 ROC-AUC 的函数,因为它将有助于评估后续方法:

❔ 2.正确的方法,但是…

我们将首先对数据进行分区,并使用 Scikit-learn 的转换器对数据进行预处理,以通过正确的预处理来防止数据泄漏:

仅部分输出

很好,我们现在可以拟合模型了:

在评估之前,我们需要以同样的方式预处理测试数据集:

太棒了,这次方法是正确的。但是写好代码并不仅仅停留在正确上。对于每个预处理步骤,我们存储了训练和测试数据集的中间输出。当预处理步骤的数量增加时,这将很快变得非常乏味,因此很容易出现错误,比如在预处理测试数据时遗漏了一个步骤。这些代码可以变得更有条理、更简洁、更易读。这就是我们将在下一节中做的事情。

✔️ 3.优雅的方法#1

让我们使用 Scikit-learn 的PipelineColumnTransformer来简化前面的代码。如果你不熟悉它们,这篇文章简明地解释了它们。

管道:
◼️将输入数据分成数字组和分类组
◼️并行预处理这两组数据
◼️连接来自两组的预处理数据
◼️将预处理数据传递给模型

当原始数据被传递到经过训练的管道时,它将进行预处理并做出预测。这意味着我们不再需要存储训练和测试数据集的中间结果。给看不见的数据打分就像pipe.predict()一样简单。那很优雅,不是吗?现在,让我们评估模型的性能:

很高兴看到它与以前的方法的性能相匹配,因为转换完全相同,只是以更优雅的方式编写。对于我们的小例子来说,这是本文展示的四种方法中最好的方法。

Scikit-learn 的开箱即用的变压器如OneHotEncoderSimpleImputer快速高效。然而,这些预构建的转换器可能并不总是满足我们独特的预处理需求。在这种情况下,熟悉下一种方法可以让我们更好地控制定制的预处理方式。

✔️ 4.优雅的方法#2

在这种方法中,我们将使用 Scikit-learn 创建定制的变压器。看到我们熟悉的相同预处理步骤如何转化为定制的转换器,有望帮助您掌握构建它们的主要思想。如果你对定制变形金刚的用例感兴趣,请查看GitHub 库

与以前不同,这些步骤是按顺序一个接一个完成的,每个步骤都将其输出作为输入传递给下一个步骤。是时候对模型进行评估了:

耶,我们刚刚学会了另一种优雅的方式来达到和以前一样的效果。虽然我们在第三种方法中只使用了预构建的转换器,在第四种方法中只使用了定制的转换器,但是它们可以一起使用,只要定制的转换器被定义为与开箱即用的转换器一致地工作。

这就是这篇文章的全部内容!当使用后两种方法时,一个好处是超参数调整可以在整个管道上进行,而不仅仅是在模型上。我希望你已经学会了开始使用 ML 管道的实用方法。✨

迈克尔·泽兹奇在 Unsplash 上的照片

您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果您使用 我的推荐链接成为会员,您的一部分会费将直接用于支持我。

谢谢你看我的帖子。如果你感兴趣,这里有我的一些帖子的链接:
◼️️ 管道,ColumnTransformer 和 FeatureUnion 解释
◼️️ FeatureUnion,ColumnTransformer &管道用于预处理文本数据
◼️️
的两种方法创建自定义变压器◼️ 用这些技巧丰富你的 Jupyter 笔记本
◼️ 用这些技巧组织你的 Jupyter 笔记本
◼️ 解释 scikit-1

再见🏃💨

从 NLP 原型到生产

原文:https://towardsdatascience.com/from-nlp-prototype-to-production-c2b555488dc5

远远不同于粉笔和奶酪和粉碎技术债务

爱丽丝·帕斯夸尔在 Unsplash 上的照片

在英语中,我们有这样的说法:‘T4’粉笔和奶酪或者苹果和橘子。原型就像你从架子上摘下的苹果,而在生产环境中部署一个想法更像一个橙子。对我来说,将机器学习解决方案部署到生产中不太像橙子;这更像是种一棵橘子树,然后等待它结果。加上种树、修枝、浇水以及其他任何事情,相当于一个碾压级的技术债

如果你在谷歌上搜索“将机器学习模型部署到生产中”,你会找到大量的材料和方法。事实上,在 Medium 上,也有大量的文章。例如, Isaac Godfried 提供了一篇出色的文章和一个有用的图表。此外,李宗德·韦恩Flask 和 HTML 用户界面提供了一个使用深度学习模型的好例子。

我的任务就是为求职者揭露 NLP 程序,帮助他们改进简历。你可以在的前一篇文章中看到迄今为止所做努力的总结。上次我们谈到了保护我们的原型:锁定我们的数据科学原型和隐私概念证明。这篇文章将总结我从原型到部署的服务以及这两者之间的一切的工作。如果您愿意,您可以观看应用程序部署的视频,这有助于很好地展示这个概念。

作者 YouTube 帐户-作者的视频。

主办;主持

如果你正在使用一个模型,并且需要取消一个预测,那么有大量的选择向你开放。其中绝大多数将涉及到模型的阶段化和围绕它包装一个 API。使用该模型需要一种机制来消费 API。例如,您可以建立一个推理服务器,全力以赴采用微服务方法。公开自然语言处理技术有点不同。

我使用了 EC2 实例,但是使用了由 AWS 提供的免费层津贴支持的 t2.micro 服务器。一般来说,我会认为 Heroku 或虚拟服务器,在这种情况下,我的直觉是 EC2。但是,同样,这取决于你能接受和支持什么。

堆栈

拥有一个全新的虚拟服务器是非常好的,但它只配备了一个操作系统、一个网络连接和一个防火墙。想象一下 10 年前,启动服务器是多么的不同。然后,它涉及到购买服务器,插入它,连接东西。Omg!

尽管如此,我还是需要在 Ubuntu 服务器上分层!

照片由莎拉·塞尔韦拉Unsplash 拍摄

你可以把虚拟机想象成美味三明治中的第一片面包!我的三明治看起来像这样

  • 面包 1 : AWS EC2 虚拟机。拥有一台能够按需改变电源的完整服务器让人感到坚定和自信。然而,安全问题是真实存在的,可能是一场噩梦。
  • 黄油: apt 和 Ubuntu。所有的计算机都需要操作系统。我熟悉 Ubuntu,但你可以选择 Windows 或 Linux,或者在不同风格的 Linux 之间选择。他们都有不同的口味,有些比其他人更敏锐。黄油会变质,所以要给你的操作系统打补丁。
  • 用 Python 填充 1: FastAPI 。提供 NLP 服务需要一个后端进程准备好服务这些传入的请求。我的 NLP 类包括常见的嫌疑人!
  • 填充 2: Gunicorn 。这可能看起来有点奇怪!难道 Gunicorn 不是一种 WSGI 方法,而 FastAPI 是一种 ASGI 技术吗?是的,但是 Gunicorn 有一个伟大的工人和过程控制者。
  • 灌装 3: 紫玉米。ASGI 服务器与 FastAPI 应用程序进行通信。部署 Uvicorn 很有趣,自然地,我使用了 Gunicorn worker 类。" guni corn-k uvicon . workers . uvicon worker "
  • 蛋黄酱:主管。" Supervisor 是一个客户机/服务器系统,它允许用户在类似 UNIX 的操作系统上监视和控制许多进程。"。好吧,如果东西倒了,被打翻了,被消费者的需求或坏演员压垮了,难道你不想要一个主管来维持运转吗?
  • Topping:使用 Vue.js 的用户界面。前端保持整洁;没有人想要一个混乱的三明治体验!
  • 面包 2 : Nginx 。三明治的顶部需要结实,传达自信。如果我拿起这个三明治,它还会在一起吗?没人想全身都是三明治!添加 Nginx 可以提供负载平衡、静态文件服务和反向代理服务。

现在,这可能是世界上最神奇的三明治,为预期用户提供了很好的体验。多个 Gunicorn 工人和 Uvicorn 为由 Nginx 监督和代理 FastAPI 端点安排服务应该是一种很好的体验。如果你把你的三明治包在某个包裹里,那是最好的。当我想要一个赛百味三明治时,我知道去哪里,因此如果你想让客户访问你的应用程序,你需要发布你的地址。

马修·巴德梅克在 Unsplash 上拍摄的照片

包装产品

打包应用程序需要一些移动部件和大量的技术债务偿还!

  • 一个。首先,我需要一个用户友好的网址。https://www.justresumes.net 是我选择的,我是从 AWS 的 53 号公路上得到的。有很多可用的指南,AWS 会全程帮助你。
  • 将服务器连接到一个地址需要一个固定的 IP 地址。你不会希望你的企业名称和地址是可变的!那会让所有人困惑。我使用了来自 AWS 的一个弹性 IP 地址,并将其连接到我的服务器实例(Bread 1)。
  • 一个电子邮件地址,因为人们可能会有问题或反馈。你需要一个域的电子邮件地址来使用 SSL 证书并有一个联系页面。我使用了一个 Google workspace 账户,并按照指南将我的网址与我的收件箱账户连接起来。谷歌为你做这一切!" admin@justresumes.net "
  • 一个 SSL 证书通过验证,安装了和相应的私钥。火狐、Chrome 和 Safari 对网站的安全性有自己的看法。因此,任何没有 SSL 证书的网页都显得“不安全”。我用了一个免费的教程使用 SSL 来做这一块。90 天的证书只是另一笔等待偿还的技术性债务。
  • 一个博客——很自然地,我为此在媒体上建立了一个出版物!

下面是一些漂亮的截图因为这些东西伤透了我的心!

连接安全

作者显示登录页面安全信息的图片。

为电子邮件配置域

图片来自 53 号公路、托管区域、自定义域电子邮件服务的 DNS 设置—图片由作者提供。

证明

没有让我心碎的东西感谢 Auth0

那个看起来很酷的登录按钮——由作者截图

登录后,“注销”按钮会以冷红色显示。漂亮!

那个看起来很酷的注销按钮——由作者提供的 Vue.js 的 Auth0 starter 截图

你可以在我准备的的视频里看我吃那个三明治。

做三明治

编排整个应用程序堆栈有许多部分。事实上,给定开发应用程序所需的材料、步骤和解释,我可以写一本书。

突出

使用 FastAPI :最初,我想使用 FastAPI 应用程序提供静态和 JSON 内容(API 端点响应)。然而,那对我不起作用。所以,我决定使用 Nginx 提供静态内容,这意味着 index.html 和所有 vue.js 块都由 Nginx 直接提供。流量通过到套接字的 Nginx 反向代理流向 FastAPI 应用程序。在 Gunicorn 进程的控制下,Uvicorn 工人监控套接字,并对来自客户端的 API 调用做出必要的响应。应用程序由管理员监控和运行,这保证了服务器上应用程序的弹性。我用了一个教程帮我,很高明!

***https://blog.askjarvis.io/fastapi-with-nginx-gunicorn-and-supervisor-on-ec2-2e0258441d7c

Nginx 配置:设计有几个步骤。我用了教程来帮助我,这些都很有帮助。当我们谈论个人或敏感的个人数据时,安全性至关重要。

https://ubiq.co/tech-blog/nginx-ssl-configuration-step-step-details/ https://www.tecmint.com/nginx-web-server-security-hardening-and-performance-tips/ https://www.cloudsavvyit.com/3782/how-to-configure-cache-control-headers-in-nginx/

CORS: 我犯了 CORS 错误!所以我不得不使用 CORSMiddleware 来解决这个问题,我不认为这是一个好主意!

origins = ["*"]
# Creates app instance
app = FastAPI(openapi_url=None)
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

生产中的 Swagger UI:考虑到数据和安全问题,我不喜欢让 Swagger UI (FastAPI 文档)可用。关掉它很容易。

app = FastAPI(openapi_url=None)

发送电子邮件:两难的选择是在后台还是在前台发送。我选择从客户端在 vue.js 中做。到目前为止,我不确定这个决定是否明智。

https://www.freecodecamp.org/news/send-emails-from-your-vue-application/

认证:我使用了 Auth0

API 端点安全性是通过 FastAPI 的依赖注入实现的

@app.get("/api/private")
async def private(response: Response, token: str = Depends(token_auth_scheme)): result = VerifyToken(token.credentials).verify()
if result.get("status"):
 response.status_code = status.HTTP_400_BAD_REQUEST
 return resultreturn result

Auth0 再次提供了前端视图安全性,这里有一个 vue.js 组件作为例子。

<template>
<div>
<Highlighter v-if="$auth.isAuthenticated"/>
<Hero v-else  />
</div>
</template>

如果用户执行了身份验证,屏幕上会显示一个“荧光笔”组件;否则就是登陆页的‘英雄板块’。

除此之外,做这个三明治并不容易!

关闭

从原型到生产应用远比看起来复杂。与粉笔或奶酪的类比大不相同,令人崩溃的技术债务水平让我夜不能寐。此外,安全是根本,互联网上的一切都在不断受到攻击。如果你不相信我,可以查看 Nginx 访问日志。暴力攻击每天都有!

我写这篇文章是为了与数据科学家、分析师和对在生产环境中部署自然语言服务感兴趣的人分享这一经验。我们都同意这不容易!然而,这些讨论是初创企业每天都要面对的,当你读到你的社交媒体账户出现重大中断时,也许你现在有了更好的见解。一个小小的改变可以打破一切!

Erik Mclean 在 Unsplash 上拍摄的照片

https://cognitivedave.medium.com/membership ***

从数字到范畴

原文:https://towardsdatascience.com/from-numerical-to-categorical-3252cf805ea2

存储数字要素的三种方法

弗兰克·麦凯纳在 Unsplash 上拍摄的照片

根据原始值所在的区间将宁滨数字要素分组可以提高模型性能。发生这种情况有几个原因。首先,可以基于领域知识来定义这些箱,以帮助模型更好地识别它正在寻找的模式。第二,数据总是有测量误差,宁滨可以减少这些误差的影响。

在基于领域知识的宁滨之外,有三种常见的方法:等宽度、等频率和 k 均值方法。(k-means 方法超出了本文的范围。)等宽取数值变量的范围,并将其分成大小相等的区间。这意味着组的大小是相同的,但是每个箱中的观测值的计数可以有很大的不同。这方面的一个例子是将个人的年龄分为五年或十年。等频箱该功能在每个箱中创建大致相等的计数。在年龄的情况下,如果大多数人都是二三十岁,宁滨十年甚至五年就能创造出无用的垃圾箱。宁滨通过频率,这些共同的年龄将更好地分开,更有利于模型。

熊猫中的宁滨

使用开源软件包 RasgoQL 从数据库中提取的天气数据,

dataset = rql.dataset('Table Name')
df = dataset.to_df()

使用 pandas 的cut功能可以很容易地创建等宽箱。在这种情况下,创建了 4 个偶数大小的箱。

df['HIGH_TEMP_EQ_BINS'] = pd.cut(df.DAILY_HIGH_TEMP, bins=4,
                                 labels=False, include_lowest=True)

类似地,qcut可用于创建每个仓中计数大致相等的仓。

df['HIGH_TEMP_FQ_BINS'] = pd.qcut(df.DAILY_HIGH_TEMP, q=4, 
                                  precision=1, labels=False)

宁滨与 sci kit-学习

使用 scikit-learn 的预处理功能KBinsDiscretizer也可以创建相同的库。为了创建与箱相等,我们将策略设置为“统一”。

est = KBinsDiscretizer(n_bins=4, encode='ordinal', 
                       strategy='uniform')
df['HIGH_TEMP_SK_EQ_BINS'] = est.fit_transform(
                                         df[['DAILY_HIGH_TEMP']])

类似地,将策略设置为“分位数”将创建大致相等的频率区间

est = KBinsDiscretizer(n_bins=4, encode='ordinal', 
                       strategy='quantile')
df['HIGH_TEMP_SK_FQ_BINS'] = est.fit_transform(
                                         df[['DAILY_HIGH_TEMP']])

无论哪种情况,要将模型投入生产,都需要为生产环境重写这些转换。在熊猫版本的情况下,需要从cutqcut中提取截止值,并将其硬编码到生产代码中。在 scikit-learn 的情况下,要么需要删除截止,并重写整个转换以使用这些截止,要么需要保存预处理对象并将其重新加载到生产环境中。

除了将建模代码重构为生产代码之外,当处理大量数据时,这种方法在最好的情况下会很慢,在最坏的情况下几乎不可能。对于大量的数据,仅仅是等待数据传输到建模环境就浪费了大量的时间。如果数据足够大,它可能不适合 pandas 的内存,并且不是所有的数据都可以用于这些转换。

现代数据堆栈中的宁滨

通过利用开源 Python 包 RasgoQL ,这两个问题都可以避免。首先,因为 RasgoQL 直接在数据库中创建 bin,所以它可以处理任何大小的数据。其次,在创建这些 bin 并在 Python 中检查它们的过程中,底层 SQL 代码被保存在数据库中。这意味着,当新数据到达数据库时,它将随着应用的箱而自动可用。

为了创建等宽条,可以使用“等宽”类型调用 RasgoQL 函数bin

eq_bin = dataset.bin(type='equalwidth',
                     bin_count=4,
                     column='DAILY_HIGH_TEMP')
eq_bin.save(table_name="HIGH_TEMP_EWB")

或者,将类型设置为“ntile”将创建相等数量的箱。

fq_bin = dataset.bin(type='ntile',
                     bin_count=4,
                     column='DAILY_HIGH_TEMP')
fq_bin.save(table_name="HIGH_TEMP_NTB")

此时,这些数据集被发布回数据库,并可在建模和生产环境中使用。

虽然直接在 pandas 中宁滨数据或使用 scikit-learn 的宁滨函数很容易在传统的 Python 机器学习工作流中实现,但当处理存储在数据库中的大数据时,转换为 SQL 方法在速度上具有优势,并且易于推广到生产中。基于 SQL 的特征工程允许更快的处理、更容易的生产路径以及跨多个项目的特征重用。然而,大多数数据科学家更喜欢 Python(或 R)而不是 SQL,而且,对于许多计算来说,SQL 是复杂的。开源包 RasgoQL 允许数据科学家继续在 Python 中工作,但在数据库中执行计算。

如果你想查看 RasgoQL,文档可以在这里找到,在这里找到库

从律师助理到数据科学家——我是如何在没有定量学位的情况下开始我的数据职业生涯的

原文:https://towardsdatascience.com/from-paralegal-to-data-scientist-how-i-started-my-data-career-without-a-quantitative-degree-8e9f741873c4

分享我获得第一个数据角色的技巧和经验

Unsplash 上拍摄的香椿果

为什么我要写这篇文章

我毕业时获得了国际关系学士学位,而且只修完了少数几门统计学和经济学课程。有了 5 年的工作经验后,我开始从事分析方面的职业。我在完成兼职数据科学训练营的一年内获得了我的第一个正式数据职位,同时全职工作。我希望我的经验和技巧能够对其他渴望进入数据科学领域的人有所帮助和鼓励。

我的策略是去最有职业发展潜力的公司工作。

我之前分享了我是如何从一家律师事务所工作到一家初创企业的第一次职业转型。我收到了多个不同角色的邀请,从客户成功到法律。最终,我接受了一份内部律师助理的工作。当我试图离开法律领域的时候,为什么我选择律师助理这个角色,这看起来似乎是违反直觉的。

因为我不确定我下一步想做什么,所以我更看重我在公司的职业机会,而不是头衔或我将从事的工作类型。在与我的经理进行持续的职业对话后,我表示有兴趣在公司尝试不同的角色。在她的支持下,我对客户体验(CX)团队进行了角色转换。

在新的 CX 职位上,我被数据分析工作所吸引,并确定了所有可能的资源来加快我的学习。

三种截然不同的经历脱颖而出,让我有了适用的工作经验。更重要的是,我在这些经历中都得到了经验丰富的专业人士的指导:

  1. 我参加了一个兼职数据科学课程,学习基础统计学和编程。这门数据科学课程与众不同,因为我的搭档是一位精通编程和 ML 技术的数据科学专家。
  2. 我申请并被德尔塔分析公司接受了为期六个月的奖学金,该公司为非营利组织提供无偿数据咨询。我有机会应用我在新兵训练营学到的东西,并与其他数据专业人员建立联系。
  3. 我与我在 CX 团队的经理和数据分析主管合作,制定了一个定制的轮换计划,在这个计划中,我将与数据分析团队密切合作。在轮换期间,我创建了一个对 CX 团队和整个公司都有用的数据产品。我还得到了经理和团队成员的指导,并对在集中式数据团队中工作有了很好的了解。

完成轮岗后,我开始求职,过渡到正式的数据角色。

在我找工作的过程中,我经历了许多拒绝,因为我无法提供结构化的回答来通过案例研究面试。

最初,我申请了不同领域和部门的许多类型的数据角色。一些人在业务运营团队中专注于财务指标,其他人是分析用户行为和漏斗指标的产品角色。

面对如此多样的角色和领域,准备案例研究和完成带回家的挑战是势不可挡的。我会花几个小时去学习我不熟悉的不同指标和产品领域。

尽管我尽了最大努力,但我不会比第一轮更进一步,因为我会在案例研究面试中惨败。比失败更糟糕的是,我很难知道如何准备未来的面试,因为我对自己不知道的东西感到不知所措。我花时间思考如何前进,并意识到我可以申请突出我的优势而不是强调我的弱点的角色。

为了提高我的面试成功率,我把重点放在了我的专业知识能够为案例研究和带回家的挑战提供结构化方法的职位上。

在无数次失败的面试后,我将我申请的数据角色缩小到客户体验和运营领域。通过这样做,我可以集中精力准备面试,并从不同公司的每一轮面试中学习——不管是否顺利。我在两个月内找到了一份新工作,我的专业知识让我很快进入状态。我知道该向商业伙伴提什么问题,并在几周内赢得了第一笔交易,尽管这是我的第一个正式数据职位。

给那些目前受雇的,但不是官方数据角色的提示汇总:

  1. 与您的经理合作,寻找工作中的数据机会,或设计/申请轮岗计划。就我个人而言,这是启动我的分析职业生涯中最关键、最有影响力的经历。如果你已经在一家科技公司工作,问问你的经理你是否有专门的时间从事数据项目,并接受数据专家的指导。这对我来说是可能的,当时我在一个大约 500 名员工的 D 轮创业公司。这又回到了我之前关于决定在一家高增长的初创公司工作的观点——尽管没有正式的轮换计划,但我和我的经理能够迅速与数据分析主管合作,并在短短几周内确定轮换的要求和时间表。如果你在一家较大的公司,你可以向你的经理提出轮岗计划,或者直接申请轮岗(如果有提供的话)。如果你不确定,这也是一个在完全投入工作之前先了解一下工作的好方法。我遇到过一些年轻的专业人士,他们在自己的岗位上做一些数据工作,最终决定留在他们的运营岗位上。
  2. 完成数据科学课程,学习技术技能和基本概念,建立信誉。这可能是老生常谈,但这是学习技能和增长知识的最快方式,而不是等待工作中的合适项目或改变工作职能的绝佳机会。等待这样的机会将比投入专门的时间要长得多。通过完成一个新兵训练营,你不仅有一个真实的项目组合来展示你的技能,而且还可以在你的职业转变中获得你的经理的支持。这可以让他们更有信心去寻找数据项目并将其分配给你,因为你已经展示了你的能力。

求职者招聘第一个数据角色的附加提示:

  1. 找工作时,对职位持开放态度。即使你完成了数据科学课程,产品或数据分析师职位也能提供类似的职业机会。你应该能够在面试过程中感受到这个职位的技术性,以及它是否适合你的技能。我遇到过一些成功的转行者,他们在接受不同职位的训练营后相对较快地找到了工作。分开来说,你永远不知道一个角色加入公司后会发生什么。许多公司已经重组了他们的数据团队,从分散模式到集中模式,反之亦然,并改变了职称,作为品牌重塑的一部分。
  2. 利用你过去的经验。你可能拥有其他技术人员可能没有的宝贵领域知识。如果你在银行工作,你可能对客户如何使用他们的账户或给企业带来风险有深刻的理解——利用这一点。业务运营或财务团队中的数据角色可能非常适合。如果您的背景是客户支持,您可以寻找与客户服务和代理绩效指标相关的数据角色。
  3. 寻找志愿者机会,向其他数据专业人士学习,并解决真实的数据问题,如 Delta Analytics 。我发现这比独立完成 Kaggle 项目更有帮助,因为我需要责任感。

结论

我希望这篇文章能给你一些鼓励和启发,让你从非传统背景的职业转变为技术角色。请记住——找到工作只是您数据职业旅程的开始,但随着您知识和经验的加深,您在准备旅程时的成长和学习心态将始终适用!

卷积神经网络:导论

原文:https://towardsdatascience.com/from-perceptron-to-densenet-an-introduction-to-convolutional-neural-networks-ab37e3b7872e

从感知器到 DenseNet 的短暂旅程

杰姆·萨哈冈在 Unsplash 上的照片。

人们可能有理由怀疑,当网上有许多关于同一主题的介绍时,为什么有必要再介绍一次卷积神经网络。然而,本文将读者从最简单的神经网络感知机带到深度学习网络 ResNet 和 DenseNet,(希望)以一种可理解的方式,但肯定是以一种简洁的方式,在几个步骤中涵盖了深度学习的许多基础知识。所以我们开始吧——如果你想的话。

介绍

机器学习是人工智能的重要组成部分。它用于许多应用领域,如图像识别、语音识别、风力预测和药物设计。数据科学是一个与人工智能密切相关的新兴领域,专注于学习,预测专注于收集,可视化,推理和学习模型的预处理。特征是描述观察的变量。一个模式x∈ℝᵈ是一组特征。标签 y ∈ ℝ 是我们感兴趣的观察结果。模式-标签对形成了基本事实。

监督学习通过调整分类器的参数来适应训练模式,从而在训练阶段训练分类器。从形成 d 维训练集的一组模式-标签对(xᵢ,yᵢ)* 与 i = 1 …,nxᵢ∈ℝᵈ中,训练一个机器学习模型 f ,用于预测适当的标签信息在分类中,标签是离散的,例如 {0,1} 并被称为类或类别,而在回归中,标签是连续的。*

感知器

感知器[1]是一个简单的神经单元(f:ℝᵈℝ),汇总加权输入并将其输入激活功能

这里,x∈ℝᵈ是感知器的输入,w∈ℝᵈ是权重, b ∈ ℝ 是偏差, w x 是所有x的分量乘积之和****

激活函数 ReLU 图

功能σ:ℝℝ是一种激活功能,类似于整流线性单元,简称 ReLU,见图 1:

ReLU 可以计算得非常快,这解释了它在深度学习方面的成功。在众多进一步激活的功能中,乙状结肠功能是其中之一:

将输入映射到 0 和 1 之间的值和双曲正切值:

映射到区间 [-1,1]

将多个感知器分层允许分离更复杂的集合,也包括像 XOR 问题这样的非线性集合。密集层将众多的感知机放入一层。连续层的所有神经元相互连接,即密集连接。密集层也称为全连接(FC)层。输入层的神经元数量对应于模式的维度。一个多层感知器(MLP)由一个输入层和一个输出层以及一个或多个隐藏层组成,见图 2。

图 2:左:有一个隐藏层的 MLP 的插图。右图:多个图层允许非线性分类边界。

这个以神经元为节点的 MLP 图中的每条边都配备有自己的权重 w ᵢ.如果 x ∈ ℝᵈ 是一个层的输入并且w∈ℝ^{k×d }是一个层的权重矩阵,那么

是有偏差的加权和b∈ℝᵏ。输出是一个 k 维向量,其元素被逐元素地馈送给激活函数σ,从而产生相应层的激活向量。如果需要处理通道信息(图像的 RGB 值),张量的扩展是合理的。信息从输入层传递到输出层。因此,与具有反向连接的循环网络相比,该网络体系结构被称为前馈网络。

网络权重和偏差通常统一初始化为小值,如-0.01 到 0.01。或者,Glorot 初始化旨在使一个层的输出方差等于其输入方差。Glorot 从以 0 为中心的正态分布中抽取样本,标准偏差基于输入和输出的数量。

损失函数

在分类中使用一个热编码,这意味着一个输出神经元对应于一个返回 1 的输出类,而所有其他神经元返回 0。例如,在数字数据集的 K=10 类的情况下,使用一个热编码的 MLP 采用 10 个输出神经元,每个表示一个类,例如y=(0,0,1,0,0,0,0,0,0,0)^t 用于数字 2* )。*

为了实现对 K 类的一个热编码,最后一层的 k = K 神经元的最终激活 y’被馈送到 softmax:

它将所有输出调整为 0 到 1 之间的值,而所有输出的总和为 1。这些值将被解释为类别概率。最后,提供 softmax 函数的最高输出的输出神经元确定 MLP 的分类决策,即:

利用交叉熵(也称为对数损失)来计算整个训练集的误差,该交叉熵面向香农熵原理。在所有数据训练样本上,我们得到总体平均交叉熵损失如下:

其中 n 是训练集大小或批量大小, yᵢⱼ ∈ {0,1} 是来自训练集的标签的地面真实概率, y'ᵢⱼ ∈ [0,1] 是来自 softmax 的第 I 个示例中第 j 个类的预测概率。由于一个热编码,每个图案和每个维度都有一个 yⱼ 。对于接近 1 的大差异,罚分产生大的分数,对于接近 0 的小差异,罚分产生小的分数。

在回归中,MSE 最常用作损失函数:

对于标签 y₁、…、yₙ ∈ ℝ 和输出 y'₁、…、y'ₙ ∈ ℝ 。引入的损失函数用于调整神经网络的权重。

损失函数用于训练模型,见下一节。但是为了评估分类的准确性,像召回率和精确度这样的测量是有用的。在分类中,一个模式可以被正确分类(真)或不被正确分类(假)。在两类分类场景中,我们可以区分正确分类为正的真正(TP)模式和真正分类为负的真负(TN)模式。假阳性(FP)模式被分类为阳性,但应该是阴性,假阴性(FN)模式被错误地分类为阴性。对于两个以上的标签(阳性和阴性),混淆矩阵是一个一般化。在矩阵的每个位置,它计算属于属于该行的类的模式的数量,并且被分类到属于该列的类。模型的精度定义为 TPs / (TPs + FPs ),表示正面预测的准确度。同时,被定义为 TPs/(TPs + FNs)的回忆表明实际阳性被发现的程度。

培养

学习就是体重适应。训练阶段的典型设置是调整网络的所有权重 w 和偏差 bⱼ ,使得模式被 MLP 映射以校正训练集的标签。
神经学习中主要的权重自适应算法是反向传播。反向传播对权重执行梯度下降,以最小化损失函数 L ,该损失函数例如可以是交叉熵或 MSE。梯度下降是一种优化方法,它将搜索移动到梯度的相反方向。

点**处的梯度δl(w)w∈ℝᵏ是一个k-偏导数的维向量∂l(w)/∂ww . r . t .各参数 wᵢ、I**

梯度下降通常比无向随机搜索更快。对于回归问题,损失定义为残差平方和,对于分类,常见的损失函数是交叉熵损失,参见上面的等式。

如果损失函数 L 是可微的,可以计算偏导数 ∂L/∂w w. r. t .权重,从而得到梯度δl(w)。梯度下降通过以学习率 η 进入梯度的相反方向来执行最小化:

此更新也称为普通更新。

对于简单的感知器 f(x)f(x) = σ (wx + b) ,具有输入模式 x ,目标标签 y ,sigmoid 激活函数,以及损耗 L = 1/ 2 (y — σ (z),我们示例性地导出反向传播:

用链式法则。常数消失,又是链式法则,利用 sigmoid 的导数,我们得到:

在一个时期中,完整训练集的所有模式被呈现给网络。权重更新的一个成功变体被称为随机梯度下降(SGD)。SGD 在呈现一个模式后更新权重并计算梯度。因此,它试图通过一次只考虑一个训练样本来逼近真实梯度。如果训练集被打乱并分成不连续或重叠的批次,则训练可能是有效的。在小批量模式中,用训练样本子集的梯度来训练神经网络。SGD 不如 mini batch 健壮,但它允许更快的步骤。此外,它可能更少陷入局部最优。局部最优采用了比其邻域更好的适应度,但可能不是全局最优,见图 3。

图 3:局部最优的图示。

如果与整个训练集的真正梯度下降相比,小批量梯度下降是正常梯度下降和 SGD 之间的折衷。

动力

学习率 η 在训练网络中起着重要的作用。动量有助于避免权重更新的振荡,并允许更快的收敛。以下是对权重为 k 的多元变量的扩展,即δw,δl(w)∈ℝᵏ。在经典动量中,旧的权重变化δw再次应用递减因子β∈【0,1】:

termβδw在极端梯度的情况下减缓自适应过程,防止优化器超调。开始时,δw被设置为零矢量。左边的图 4 说明了动量原理。**

图 4:动量图(左)和内斯特罗夫动量图(右)。

内斯特罗夫动量是动量更新的延伸。它为凸函数提供了更强的理论收敛保证。实际上,它比标准动量理论更有效。它将动量视为一种前瞻,即首先进入动量方向βδw,并从那里计算现在略有不同的梯度∇l(w+βδw)在新的前瞻位置:****

图 4(右)展示了内斯特罗夫的势头。过去已经提出了不同的权重更新变体,其中一个突出的变体是 Adam。

过度拟合

专注于适应训练模式的模型可能过度适应训练数据,并且可能学习复杂的适应,尽管期望的模型结构可能不太复杂。这导致模型的泛化能力不足。过度拟合可以通过正则化、交叉验证和退出来避免。

正则化基于权重的大小,例如所有权重的平方和,向误差分类或回归误差L(w)添加惩罚:**

带参数 α 的情况下 L 正则化,其中

是权重向量 wL 范数。大的重量与过度配合有关,而小的重量被认为可以防止过度配合。对权重的惩罚强制小权重,从而防止过度拟合。

交叉验证(CV)使用一组训练样本进行训练,并在独立的验证集上评估模型质量。推广到不止一个这样的交叉验证过程,N 重交叉验证将随机混洗的数据集分成 N 个分离的子集,参见图 5。每个子集都被排除一次作为验证集。对所有的 N 个折叠重复该过程,并对误差进行平均。对独立测试集的最终评估可用于说明模型质量。

图 5: CV 重复地(这里是 3 次 3 折 CV)留下一个(灰色)验证集,并在剩余的(蓝色)折叠上训练模型。

一个极端的例子是 N=n 的留一交叉验证(LOO-CV),即每个模式都是一个折叠。从统计学的角度来看,它是有用的,但是由于大量的训练过程,它是非常低效的,因此主要适用于小数据集。

控制遗漏验证集上的误差允许早期停止,即,在验证误差增大而训练误差进一步减小的情况下退出训练过程。

在训练阶段,Dropout 关闭每个神经元的概率为 p ∈ [0,1] ,称为 dropout rate。对于每个隐藏层、每个训练样本和每次迭代,随机分数 p 激活被忽略,也称为清零。在测试阶段,所有激活都被使用,但按系数 p 减少。这说明了在训练阶段缺少激活的原因。Dropout 也用于卷积层,参见 dropout 部分,它也随机地将激活置零。

图 6 显示了辍学情况。灰色神经元不参与训练过程。它们的权重不会更新。

图 6:在退出期间,神经元以概率 p 被去激活,这里示出了训练期间的灰色神经元。

Dropout 迫使网络学习类的多个独立表示,这防止了过度拟合。也可以理解为多个子网的集成学习,将它们的决策结合起来。

卷积层

卷积网络很早就已经推出[2],但 Alex Krizhevsky 在 2012 年的 ImageNet 大规模视觉识别挑战赛中使用 AlexNet 实现了图像识别的突破。图 7 示出了示例性的网络架构。卷积层充当平移不变特征学习器。

图 7:带滤波器和信道的卷积网络架构。

XH × W × C- 维度输入。一个卷积层由C’乘以 C m × m 个滤波器核矩阵组成。对每个输入通道执行卷积运算,并对结果求和。该操作是针对每个过滤器内核执行的。卷积运算将一个 m × m- 维内核矩阵 w 从左上至右下移动到输入体积,参见图 8,计算:

滤波器内核 w 通过反向传播进行调整,并学习有用的特征检测器。输入 xw 之间的相似性产生高激活 a 。每个输入通道的激活被累加,因此由C’被加数组成。该过程被重复C’次,导致C’输出通道用于输出 A

图 8:2D 卷积过程的例子。

例如,具有 32 × 32 彩色图像的 CIFAR-10 网络的第一卷积层可以采用 64×3 滤波器核 3 × 3 矩阵。该层将输出 64 个通道。

滤波器核在输入体矩阵上移动的步长称为步幅。它采用垂直轴和水平轴。两个轴的步幅都是一个常见的选择,见图 9。较高的值降低了计算和存储的复杂性。为了避免维度收缩,输入体积的边界可以用零填充,例如,通过添加W-m零。这个过程称为零填充。

图 9:1×1 步的图示。

偏差 b 可被添加到输出体积,即所有激活。卷积层的输出应用激活函数A’=σ(A**),例如 ReLU。汇集层减少了集中于最大或平均激活的维度。滤波器核的数量通常随着网络的深度而增加。**

联营

卷积层导致激活显著增加。为了减少数量,使用了池层,参见图 10。池化是一种基于渠道的操作。

图 10: Max pooling 返回输入体积内的最大值,该最大值通常以对应于体积维度的步幅移动(这里是 2x2)

A 为激活的特征图,最大池在每个通道上移动一个 m × m 方块,选择最大值:

平均汇集是使用每个方块的平均值的相应过程。汇集层也可用于替换密集层头(激活的最后一层)。

VGG-19 是早期卷积网络的一个例子。它由 19 个权重层组成,对于 224 × 224 RGB 图像的输入具有以下配置:

****

FC 表示全连接层。这三个 FC 也被称为 MLP 头。VGG-19 使用了 1.44 亿个参数,是深度架构的一个例子。

雷斯内特

一个网络使用的参数和权重越多,它就能代表越多。数百层是可能的,但是训练起来计算量很大。但是非常深的网络容易过度拟合。随着误差以倍增的方式通过网络向后传播,每层中的梯度更新变得更小。这种效应随着层数的增加而增加,称为消失梯度问题。ResNet [3]通过以恒等式 X 的形式提供快捷连接来解决这个问题,并将它们添加到一个模块的输出 R 中,这就变成了一个来自输入的残差。残差比恒等式更容易学习。ResNet 模块提供身份,并学习与他们的偏差。层和身份的总和成为一个 ResNet 模块 R :

而剩余的 R 就要学会了。为了匹配 Rt18】xt20】的尺寸,可以添加投影矩阵 W ,从而得到:****

图 11 示出了基于两个卷积层的示例性 ResNet 模块。

图 11:ResNet 模块的标识快捷方式连接。

ResNet 原理可以应用于由全连接或卷积层组成的所有类型的模块。甚至快捷连接也可以由覆盖卷积层模块的一个卷积层组成。

对最优模型 f 的搜索可以被视为在函数类 F 中的搜索。在这个空间中,神经网络是由架构、超参数和权重定义的函数 f ∈ F 。改变函数类不一定会使其更接近 f 。一个 ResNet 模块实现了添加剩余部分的恒等式,从而表示一个嵌套函数,即它持有f’f,这强制向最优 f* 移动,参见图 12。**

*图 12:用 ResNet 模块实现的嵌套函数类(右)更接近最佳函数 f ,非嵌套函数类(左)则不一定

原始论文中的 ResNet 架构采用 152 层,一个 7 × 7、64 步距 2 卷积和一个 3 × 3 最大池,步距 2 后接以下卷积 ResNet 模块:

由每三个回旋组成。在网络的末端,使用 1000 维全连接层和 softmax 进行平均池化。

DenseNet

在 DenseNet [4]中,各层以前馈方式与后续各层相连,参见图 13 或 DenseNet 模块。像泰勒级数展开一样,将函数分解成越来越高的项,DenseNet 用越来越深的层来表示函数。

图 13:在密集块中,卷积层的每个输出都被馈送到后续层的输入。密集块的最后一层是过渡层。

l 层的密集块中 l(l + 1) / 2 连接被引入。每个卷积层接收所有先前层的输出作为输入,并向每个后续层产生自己的输出。先前层的输出被连接(不像在 ResNet 中那样被求和):

每一个密集块增加通道的数量。过渡层限制了模型的复杂性,瓶颈 1 × 1 卷积层减少了信道的数量,平均池层,例如,步幅 2 将高度和宽度减半。这样,过渡层减少了输入维度。密集架构允许具有相同性能的不太深(即,更浅)的网络,并且还解决了消失梯度问题。

结论

卷积神经网络不仅是人工智能中最重要的方法之一,其基本方法和原理也用于许多其他深度学习算法。例如,反向传播是几乎所有深度学习方法的优化主干,卷积层是众多神经架构的一部分。它们的应用不仅限于图像识别,而且在许多其他领域也得到了证明。如果您想更深入地了解上述主题和实现,请参阅进一步阅读部分的一些参考资料。Python 是深度学习的最佳编程语言,Keras 以及 PyTorch 可以让你轻松访问卷积神经网络。

除特别注明外,所有图片均为作者所有。

延伸阅读

一些(可能的)参考文献

[1] F. Rosenblatt,感知器——一种感知和识别的自动机,康奈尔航空实验室,报告№85-460-1(1957)

[2] Y. LeCun,B. Boser,J. S. Denker,D. Henderson,R. E. Howard,W. Hubbard,L. D. Jackel,反向传播应用于手写邮政编码识别,神经计算,1(4):541-551(1989)

[3]何克明,张晓明,任树生,孙杰,图像识别的深度残差学习,,第 770-778 页(2016)

[4]黄,刘,范德马滕,温伯格,稠密连接卷积网络.CVPR,2261-2269(2017)

从图表到仪表板:可视化技巧和窍门

原文:https://towardsdatascience.com/from-plots-to-dashboards-visualization-tips-and-tricks-f7e46bff5197

我们已经进入了秋季馅饼季节(这是我们刚刚发明的一个概念),这意味着圆形、切片、比例和尺寸是我们许多人的首选。现在是用一些新想法来充实我们复杂的 dataviz 技能组合的最佳时机吗?

撇开隐喻不谈,数据可视化的目标在很长一段时间内都是一致的:将复杂的关系和错综复杂的事实转化为清晰、易于解读的叙述。然而,我们用来创造有效视觉效果的工具和方法从未停止进化。本周,我们分享一些最近的文章,帮助你增加可视化工具包的深度和广度。让我们开始吃吧。

  • 谁不喜欢互动图表? 比清晰的视觉效果更好的一件事是你的观众可以玩弄和操纵的图表。Erdogan Taskesen 最近推出了开源 Python 库 D3Blocks,它使用户能够创建 10 种不同类型的流畅的交互式可视化,从热图到桑基图。
  • 【居 R 之术】 。也许,表格没有图表和绘图那么华丽,但它仍然是展示相互关联的数据的重要媒介。 Devashree Madhugiri 收集了 R 中的七个基本包来生成强大、有效的表。

凯尔西先驱报Unsplash 上拍摄的照片

  • 为您的仪表板找到正确的方法 。放大开源面板库, Sophia Yang 和 Marc Skov Madsen 展示了即使使用单一工具,“也有多种方式来构建仪表板”,这取决于项目的范围和目标。它们提供了三种不同的方法,每种方法都针对一个特定的用例。
  • 如何清晰地呈现比例和百分比 即使我们庆祝秋季馅饼季节,我们也必须承认糕点的同名图表已经不受许多数据从业者的青睐。如果你正在寻找更现代的饼状图的替代品,Boriharn K 提供了不少于 9 个想法供你考虑(互动甜甜圈图,有人吗?).

如果你的阅读欲望还没有满足,这里有一些其他的近期亮点供你探索:

谢谢你一如既往的支持。如果你想产生最大的影响,可以考虑成为中级成员

直到下一个变量,

TDS 编辑

从明信片到雪花

原文:https://towardsdatascience.com/from-postgres-to-snowflake-f4b403548066

当我把 DWH 从波斯格里斯迁移到雪花时遇到的有趣的点

图片由 Paul OwensUnsplash 上拍摄

我最近遇到了一个任务,似乎是许多使用 PostgreSQL 作为主数据库的公司在某个时候需要做的任务。

这是 ETL 脚本从 PostgreSQL 到雪花的迁移。

整个过程包括基于辛格和 DBT 技术从零开始构建 ETL 管道,我稍后可能会详细介绍,但现在我想集中讨论 PostgreSQL 和雪花 SQL 语法之间的差异。我希望它对那些发现自己处于类似情况并需要重写大量 SQL 的人有用。

数字

PostgreSQL 是一个关系数据库,具有所有优点和缺点,有时需要微调,包括转换到最适合的类型。但雪花的情况并非如此。

首先需要将int2int4int8等类型转换为int。第二个也是更容易出错的区别是,尽管两个数据库中都有一个类型numeric,但雪花将这种类型的数据视为整数,而在 Postgres 中,它也可以包含小数。换句话说,雪花不会给出任何误差,但一切都会被四舍五入。

不同于

我相信对于大多数分析数据库来说,只需要获得最后一行或第一行是很常见的情况。Postgres 有一个针对它的distinct on构造,但是 Snowflake 不支持它,所以必须使用row_number窗口函数或qualify构造来解决这个问题(https://docs . snow flake . com/en/SQL-reference/constructs/qualify . html)。例如,这就是我们如何获得第一个用户会话开始的时间戳。

JSON 空值

Snowflake 使用 JSON 很棒,但是有时那里的字段有null值,结果 SQL is null不起作用。有两种选择:要么使用特殊函数is_null_value (https://docs . snow flake . com/en/SQL-reference/functions/is _ null _ value . html,要么通过strip_null_value (https://docs . snow flake . com/en/SQL-reference/functions/strip _ null _ value . html函数去除空值,然后使用普通的is null SQL 检查。我个人认为后一种解决方案更有吸引力。

过滤

在分析中向度量(聚合函数)添加过滤器是一种常见的做法,在 SQL:2003 的 Postgres 中有一个很好的语法。不幸的是,Snowflake 不支持它,所以解决方法是通过case构造来走老路。例如,在这里,我们计算不同设备类型的会话数。

横向连接

雪花支持横向连接,此外,当需要解析 JSON 数组时,它被大量使用,但是有一些限制。其中一个限制是我们不能在横向连接中使用limit。一种常见的情况是,我们只需要获得第一行就行不通了。解决方法是将其转换为 CTE。实际上,CTE 将在 Postgres 和雪花中工作,所以横向连接的解决方案只是对 Postgres 的优化,但我们在大多数情况下不需要对雪花进行这样的优化,因为引擎的工作方式不同。在下面的代码片段中,我们得到了每个会话的第一个事件。

递归

好消息是,雪花完全支持递归,它会工作。问题在于局限性。在 Snowflake 中,默认的递归深度是 100,它可以增加,但仍然有一些限制(我们在这里将 Snowflake 视为托管服务)。不幸的是,由于这些限制,雪花递归根本不能解决一些问题,例如库存操作列表的加权平均成本/价格。(在 Postgres 中,我们可以使用这个解决方案:https://stack overflow . com/questions/22426878/calculating-the-weighted-average-cost-of-products-stock)

我找到的唯一解决方案是要么在雪花之外进行计算,要么创建一个存储过程。

其他…

当然,在函数,过程,写 UDF 的方式,和存储过程方面还有很多不同,但是这篇文章的目的是分享我的经验,所以我希望它会有用。

从原始数据到干净的数据库:对多功能数据工具包的深入研究

原文:https://towardsdatascience.com/from-raw-data-to-a-cleaned-database-a-deep-dive-into-versatile-data-kit-ab5fd992a02e

数据库ˌ资料库

使用通用数据工具包(VMware 最近发布的一个框架)和 Trino DB 的完整示例

马库斯·温克勒在 Unsplash 上的照片

最近,VMware 发布了一款新的开源工具,名为通用数据工具包(简称 VDK),可以非常快速地管理数据。该工具允许用几行代码将不同格式的数据接收到一个数据库中。

在我之前的文章中,我描述了一个基本的例子,它使用了 VDK,并且描述了如何安装和运行它。总而言之,您应该:

  • 有一个运行的数据库(VDK 以外)
  • 在 VDK 中配置数据库接口,包括用户名、口令、主机和端口
  • 在 VDK 定义如何使用 SQL 查询或用 Python 实现的更复杂的过程接收数据库中的数据。

一旦您在数据库中获取了数据,您就可以根据需要使用它们,例如,构建有趣的仪表板或可视化。

在本文中,我主要关注一个完整的例子,从数据接收开始,一直到数据处理。该示例使用 Trino 数据库和 MySQL 服务器来存储数据,使用 VDK 来接收数据库中的数据。

下图显示了一个可能的架构,包括 VDK、Trino 服务器和 MySQL 服务器:

作者图片

具体来说,文章组织如下:

  • 场景的定义
  • 数据库的设置
  • VDK 的数据接收
  • VDK 的数据处理

1 情景的定义

该方案的目标是分析美国不同地区的预期寿命,并将它们与该地区的国内生产总值(GDP)进行比较。为了实现这一目标,您应该下载与美国预期寿命相关的数据,并将其与相关的 GDP 合并。

这个例子展示了如何通过 VDK 提取和集成这个场景所需的数据。

我们可以使用以下两个数据集,它们都以 CSV 文件的形式提供:

之前的数据集是在美国政府工程许可下发布的。

除了前面的数据集,我们还可以使用以下附加数据集:

前两个数据集分别由美国经济分析局和美国人口普查局发布,两者都在公共许可下提供开放数据。

数据集2010-2015 年美国各州和人口普查区出生时预期寿命包含 73,121 条与 2010-2015 年期间美国人预期寿命相关的记录,按州和县划分。

下图显示了数据集的摘录:

作者图片

数据集“2018 年美国各州按性别划分的预期寿命”包含 156 条与 2018 年美国人预期寿命相关的记录,按州和性别(男性、女性、总数)划分。下图显示了数据集的摘录:

作者图片

数据集“美国各县国内生产总值”包含 3,163 条与美国各县实际国内生产总值相关的记录,涉及 2017 年至 2020 年期间。数据集包含许多列。下图显示了数据集的摘录:

作者图片

数据集“美国各县国内生产总值”包含 3,163 条与美国各县实际国内生产总值相关的记录,涉及 2017 年至 2020 年期间。数据集包含许多列。下图显示了数据集的摘录:

作者图片

2 数据库的设置

我们将所有数据存储在 Trino 数据库中,作为 MySQL 服务器的接口。 Trino 是一款用于大数据分析的分布式开源 SQL 查询引擎。它运行分布式和并行查询,速度非常快。对于 Trino 数据库的设置,你可以遵循我之前的教程

在本例中,Trino 在本地运行,具有以下最小的config.properties配置文件:

coordinator=true
node-scheduler.include-coordinator=true
http-server.http.port=8080
query.max-memory=5GB
query.max-memory-per-node=1GB
query.max-total-memory-per-node=2GB
discovery.uri=http://127.0.0.1:8080
http-server.https.enabled=false

此外,Trino DB 使用 MySQL 目录,配置如下(文件mysql.properties位于 Trino 服务器的目录文件夹中):

connector.name=mysql
connection-url=jdbc:mysql://localhost:3306
connection-user=root
connection-password=
allow-drop-table=true

最后,这个例子假设 MySQL 服务器上存在一个名为life-expectancy的空模式。

3 VDK 的数据接收

VDK 使用以下配置运行(config.ini):

db_default_type=TRINO
ingest_method_default = trino
trino_catalog = mysql
trino_use_ssl =
trino_host = localhost
trino_port = 8080
trino_user = root
trino_schema = life-expectancy
trino_ssl_verify =

数据摄取将数据源部分中定义的两个 CSV 表上传到数据库中。对于每个表,通过以下步骤执行数据接收:

  • 删除现有的表(如果有)
  • 创建新表格
  • 直接从 CSV 文件中摄取表格值。

前两步将使用 SQL 语言编写,而最后一步将使用 Python 编写。

每个数据集都将被包含在一个名称与数据集名称相似的表中。例如,美国出生时预期寿命(按州和人口普查区域)——2010–2015数据集将被纳入life_expectancy_2010_2015表中。

3.1 删除现有表格

首先,我创建一个脚本,命名为01_delete_table_life_expectancy_2010_2015.sql:

**DROP TABLE IF EXISTS** life_expectancy_2010_2015

脚本名称前面的数字01表示 VDK 框架将把它作为第一个脚本运行。

我还创建了02_delete_table_life_expectancy_2018.sql脚本,它删除了life_expectancy_2018表,其他脚本删除了us_regionsus_gdp表。

3.2 创建新表

现在,我为新表创建模式,并将其存储在03_create_table_life_expectancy_2010_2015.sql脚本中:

**CREATE TABLE** life_expectancy_2010_2015 (
   State varchar(32),
   County varchar(32),
   CensusTractNumber varchar(32),
   LifeExpectancy decimal(4,2),
   LifeExpectancyRange varchar,
   LifeExpectancyStandardError decimal(4,2)
)

与前面的脚本类似,我创建了04_create_table_life_expectancy_2018.sql:

**CREATE TABLE** life_expectancy_2018 (
   State varchar(32),
   Sex varchar(32),
   LEB decimal(3,1),
   SE decimal(3,1),
   Quartile varchar(32)
)

以及us_regionsus_gdp表:

**CREATE TABLE** us_regions (
   State varchar(32),
   StateCode varchar(2),
   Region varchar(32),
   Division varchar(32)
)**CREATE TABLE** us_gdp (
   County varchar(32),
   Year2017 bigint,
   Year2018 bigint,
   Year2019 bigint,
   Year2020 bigint
)

3.2 直接从 CSV 文件摄取表格值

最后,我可以接收life_expectancy_2010_2015表中的表值。我使用 VDK API 提供的IJobInput类。我定义了一个将被 VDK 框架读取的run()函数,并在其中编写了摄取代码:

import pandas as pd
from vdk.api.job_input import IJobInputdef **run**(job_input: IJobInput): # Read CSV file url = "http://data.cdc.gov/api/views/5h56-n989/rows.csv"
   dtypes = {
      "State": str,
      "County": str,
      "Census Tract Number": str,
      "Life Expectancy": np.float64,
      "Life Expectancy Range": str,
      "Life Expectancy Standard Error": np.float64,
   } df = pd.read_csv(url, dtype=dtypes).replace("'", "''", regex=True)
   df.columns = df.columns.str.replace(" ", "") # Ingest CSV file job_input.**send_tabular_data_for_ingestion**(
      df.itertuples(index=False),
      destination_table="life_expectancy_2010_2015",
      column_names=df.columns.tolist()
 )

为了获取数据库中 CSV 的每一行,我使用了IJobInput类的send_tabular_data_for_ingestion()方法。

我们可以用同样的技术摄取其他数据集。

4 VDK 的数据处理

数据处理包括以下任务:

  • 清洁桌子
  • 合并已清理的表

4.1 清洁桌子

清洁life_expectancy_2010_2015工作台包括以下两个操作:

  • 按县分组记录
  • 将列LifeExpectancyRange拆分成两个十进制列MinLifeExpectancyRangeMaxLifeExpectancyRange

life_expectancy_2010_2015工作台的清洗过程的输出被存储在一个名为cleaned_life_expectancy_2010_2015的新表中。

前面的两个操作都可以通过 SQL 语句实现:

**CREATE TABLE** cleaned_life_expectancy_2010_2015 **AS**(**SELECT** State,
   LifeExpectancy,
   cast(split(life_expectancy_2010_2015.LifeExpectancyRange,'-')[1] AS decimal(4,2)) AS MinLifeExpectancyRange,
   cast(split(life_expectancy_2010_2015.LifeExpectancyRange,'-')[2] AS decimal(4,2)) AS MaxLifeExpectancyRange,
   LifeExpectancyStandardError
**FROM** life_expectancy_2010_2015
**WHERE** County = '(blank)'
)

在数据集中,所有带有County = '(blank)'的行包含给定县的预期寿命总值。因此,只需选择这些行,我就可以轻松地按县分组。

下图显示了结果表:

作者图片

现在,我打扫桌子。清洁life_expectancy_2018工作台包括以下操作:

  • 将列LEB重命名为LifeExpectancy
  • 将列SE重命名为LifeExpectancyStandardError
  • 将列Quartile分成两个十进制列MinLifeExpectancyRangeMaxLifeExpectancyRange
  • 仅选择带有Sex = 'Total'的行。

前面的所有操作都可以通过一条 SQL 语句实现:

**CREATE TABLE** cleaned_life_expectancy_2018 AS(**SELECT** State,
    LEB AS LifeExpectancy,
    cast(split(life_expectancy_2018.Quartile,' - ')[1] AS decimal(4,2)) AS MinLifeExpectancyRange,
    cast(split(life_expectancy_2018.Quartile,' - ')[2] AS decimal(4,2)) AS MaxLifeExpectancyRange,
    SE AS LifeExpectancyStandardError **FROM** life_expectancy_2018
  **WHERE** Sex = 'Total' and State <> 'United States'
)

下表显示了cleaned_life_expectancy_2018表的示例:

作者图片

注意,清理之后,cleaned_life_expectancy_2010_2015cleaned_life_expectancy_2018表具有相同的模式。

4.2 合并清理后的表格

最后,我准备合并所有的表。我执行以下操作:

  • 垂直合并cleaned_life_expectancy_2010_2015cleaned_life_expectancy_2018工作台之间的
  • 结果表与us_regionsus_gdp表之间的水平合并

垂直合并意味着将第二个数据集追加到第一个数据集,而水平合并向结果表添加三个新列,称为PeriodGDPRegion,结果表称为merged_life_expectancy。仅针对带有Period = '2018'的记录设置GDP属性。对于其他记录,它被设置为0,因为它不可用。

**CREATE TABLE** merged_life_expectancy **AS**(**SELECT** us_regions.State,
    LifeExpectancy,
    MinLifeExpectancyRange,
    MaxLifeExpectancyRange,
    '2010-2015' AS Period,
    Region,
    0 AS GDP
 **FROM** cleaned_life_expectancy_2010_2015 
     JOIN us_regions 
     ON us_regions.State = cleaned_life_expectancy_2010_2015.State
)**UNION**(**SELECT** us_regions.State,
    LifeExpectancy,
    MinLifeExpectancyRange,
    MaxLifeExpectancyRange,
    '2018' AS Period,
    Region,
    Year2018 AS GDP
**FROM** cleaned_life_expectancy_2018
    JOIN us_regions 
    ON us_regions.State = cleaned_life_expectancy_2018.State
    INNER JOIN us_gdp 
    ON us_gdp.County = cleaned_life_expectancy_2018.State
**WHERE** Year2018 > 100000000
)

在第二个SELECT语句中,WHERE条件指定了Year2018 > 100000000。这允许只选择县。

最终的表格如下表所示:

作者图片

摘要

恭喜你!您刚刚学习了如何在 VDK 接收和处理数据!这可以通过用 SQL 和 Python 编写简单的脚本来实现。

这个例子的完整代码可在这里获得。

下一步是构建显示处理结果的报告。敬请关注,了解如何操作:)

对于多功能数据工具包的问题或疑问,您可以直接加入他们的公共 slack workspace他们的邮件列表在 Twitter 上关注他们

如果你读到这里,对我来说,今天已经很多了。谢谢!你可以在这篇文章中读到更多关于我的信息。

相关文章

https://alod83.medium.com/how-to-represent-and-query-hierarchical-data-in-sql-69b6b77577e8

从原始文本到模型预测,不到 30 行 Python 代码

原文:https://towardsdatascience.com/from-raw-text-to-model-prediction-in-under-30-lines-of-python-32133d853407

快速探索 NLP 管道的快速指南

照片由андрейсизовUnsplash

介绍

自然语言处理(NLP)是处理人类语言数据的机器学习的子领域。在能够用机器学习模型进行预测之前,处理人类文本通常涉及标准的预处理步骤,如数据清理和将文本转换为数字向量。

在这个故事中,我们将带您浏览一个示例,解释如何使用 ATOM 库快速处理大量文本文档,并将它们分类到预先确定的主题中。ATOM 是一个开源的 Python 包,旨在帮助数据科学家加快机器学习管道的探索。如果你想对图书馆有一个温和的介绍,请阅读这个故事。

加载数据集

我们开始获取文本文档并初始化 atom 实例。我们将要使用的数据是 20 个新闻组数据集,可以使用 sklearn 轻松下载。该数据集包含 20 个主题的大约 18000 篇新闻文章,为了加快计算速度,我们只使用了其中的 5 篇。目标是预测每篇文章的主题。

import numpy as np
from atom import ATOMClassifier
from sklearn.datasets import fetch_20newsgroups# Load the dataset (get only 5 topics)
X, y = fetch_20newsgroups(
    return_X_y=True,
    categories=[
        'alt.atheism',
        'sci.med',
        'comp.windows.x',
        'misc.forsale',
        'rec.autos',
    ],
)# atom expects a 2-dimensional array, so reshape to (n_articles, 1)
X = np.array(X).reshape(-1, 1)# Initialize atom
atom = ATOMClassifier(X, y, test_size=0.2, verbose=2)

atom 中的数据存储在熊猫数据帧中。注意,我们拥有的唯一特性(包含文章)被自动调用corpus。这很重要,因为 atom 的所有 NLP 方法只应用于数据集中具有特定名称的列。这种机制允许 atom 将包含文本语料库的数据集与其他非文本特征结合起来(我们不会在本文中使用)。使用dataset属性检查数据。

atom.dataset

文本清理

让我们看看第一篇文章。

atom.corpus[0]

注意它的混乱。文本数据很少干净。无论是从网站上搜集来的还是从纸质文档中推断出来的,通常都填充了与模型无关的信息,比如电子邮件地址、数字、html 标签等..我们可以使用 atom 的text clean方法从新闻文章中去除这种“噪音”。该方法对语料库应用以下变换:

  • 将 unicode 字符解码为 ascii 表示形式
  • 将所有字符转换为小写
  • 从文本中删除电子邮件地址
  • 从文本中删除 URL 链接
  • 从文本中删除 HTML 标签
  • 从文本中删除表情符号
  • 从文本中删除数字
  • 删除文本中的标点符号

atom.textclean()

使用drops属性来检查每篇文章删除了什么。

atom.drops

现在看看第一篇。

atom.corpus[0]

标记化

下一步是将文章转换成令牌序列。在这种情况下,单词。我们需要这样做有两个原因:能够使用像词干化或词汇化这样的算法,这些算法要求文档由标记组成,以便知道应该考虑哪些单独的单词;并且能够将文本映射成模型可以接受的数字。

atom.tokenize()

现在每篇文章都由一系列单词组成。

atom.corpus[0][:7]

提示:使用 atom 的plot _ word cloud方法对语料库中最常见的术语进行可视化处理。

atom.plot_wordcloud()

正常化

规范化是将单词列表转换为更统一的标准的过程。这有助于减少模型必须处理的不同信息量,从而提高效率。像变元化这样的规范化技术的目标是将一个单词的屈折形式和派生相关形式简化为一个共同的基本形式,例如 universal → universe 或 running → run。

我们可以通过删除所有停用词来进一步降低文章的复杂性。停用词是任何自然语言中最常见的词,例如英语中的“the”、“a”、“is”。使用 atom 的文本规格化 方法可以实现规格化和停用词移除。

atom.textnormalize(stopwords="english", lemmatize=True)

还是那句话,我们来看看第一篇文章的前 7 个字。请注意,“从”和“在”不再存在。

atom.corpus[0][:7]

N-grams

有时,单词本身的意思和与相邻单词组合在一起的意思不同。例如,当单词“york”直接跟在单词“new”后面时,它的意思与不跟在后面时完全不同。这两个词的组合被称为二元模型。使用plot_ngrams方法检查语料库中哪些两个单词的组合是最常见的。

atom.plot_ngrams(ngram=2)

Atom 的

atom.tokenize(bigram_freq=200)

二元模型频率为 200 意味着如果二元模型在语料库中至少出现那么多次,就认为它是二元模型。使用bigrams属性查看创建了哪些二元模型。

…向量化…

文本数据不能直接馈送给算法本身,因为它们中的大多数期望具有固定大小的数字特征向量,而不是具有可变长度的文本文档中的单词。矢量化是将一组文本文档转换成数字特征向量的一般过程。

在大型语料库中,一些单词可能非常常见,但携带的关于文档实际内容的有意义信息非常少。如果我们将计数直接输入到分类器中,那些非常频繁的词将会掩盖更罕见、但更有趣的词的频率。使用 TF-IDF 策略将计数特征重新加权为浮点值。创建的列以它们正在计数的单词命名。

atom.vectorize(strategy="tfidf")

由于在完整的语料库中有许多单词,并且每个单词都有自己的列,所以数据集的维度增加了很多。

atom.shape

为了处理这个庞大的数据集,矢量化方法默认返回稀疏数组的数据帧。

atom.dataset.dtypes

信息:您可以使用该方法的return_sparse参数来改变这种行为。请注意,当语料库包含许多单词时,使用 return_sparse=False 会使转换非常缓慢,并占用大量内存。

建模和结果

要为我们的数据选择正确的模型,我们需要检查哪些模型具有对稀疏数据帧的本地支持。

atom.available_models()

看看第 17 行的accepts_sparse栏。看起来随机森林(RF) 很适合我们的任务。

atom.run(models="RF", metric="f1_weighted")

就这样,我们训练了一个模型来对文本数据进行预测。使用以下工具快速评估各种指标的结果:

atom.evaluate()

该模型似乎总体得分较高。使用 atom 的绘图方法进一步分析结果。

atom.plot_confusion_matrix()

ATOM 还集成了 shap 库,可以根据 Shapley 值快速洞察图形。例如,在对第一篇文章进行预测时,查看哪些单词对算法的影响最大。

atom.decision_plot(index=0, target=atom.predict(0), show=10)

或者检查所有文章中哪些词对预测第一个主题最重要:无神论。

atom.beeswarm_plot(target=0, show=15)

结论

我们展示了如何使用 ATOM 包快速探索自然语言数据集。首先,我们将原始文本文档转换成向量,之后,我们训练并评估了一个分类器。以及不到 30 行代码中的所有内容!

关于 ATOM 的更多信息,请看一下软件包的文档。对于 bug 或功能需求,请不要犹豫,在 GitHub 上提出问题或给我发邮件。

相关故事:

参考资料:

从原始视频到 GAN 培训

原文:https://towardsdatascience.com/from-raw-videos-to-gan-training-implementing-a-data-pipeline-and-a-lightweight-deep-learning-6274de917be9

在 AWS 上使用 ClearML 实现数据管道和轻量级深度学习数据湖

介绍

Hour One 是一家以人工智能为中心的初创公司,其主要产品是将文本转换成虚拟人类主持人的视频。

仅基于文本生成真实、流畅和引人注目的人类演讲者用多种语言说话和打手势的视频是一项具有挑战性的任务,这需要训练复杂的深度学习模型——以及大量的训练数据。

这篇文章描述了我使用 ClearML 和 AWS 为 Hour One 构建的数据管道和数据管理解决方案的设计和实现。

该解决方案基于轻量级版本的
Deep Lake 架构模式

视频深度学习的数据管道和数据管理。作者图片

注意:我与 ClearML 项目或其支持者没有任何关系。

从视频到准备好的数据集

Hour One 的人工智能模型需要将文本作为输入,并生成逼真的视频作为输出。

实现这一点的一种方法是通过在真人展示各种文本的视频上训练模型。

该模型然后尝试预测视频中的下一帧或帧序列,同时最小化损失函数,这有助于确保输出是真实的和高质量的。

从数据准备和管理的角度来看,这需要:

将视频数据转换为有用的表示形式— 使训练人员能够专注于输入的正确“特征”。

例如,以一种用于频谱分析的格式表示音频,或者将视频像素编码成一种可以馈入模型的紧凑格式。

丰富提供详细监督的数据层— 天真地,被训练来预测图像的模型可以尝试最小化与地面真实图像的简单像素距离。
然而,这个损失函数可能不是考虑真实性、平滑性或一致性的最佳方式。

为了支持更详细的监督,在训练期间可以使用附加的注释或数据层。
例如,考虑一层关于视频中每一帧中人脸确切位置的信息(“注释”)。

这些层可以由人工注释器以编程方式生成,也可以两者都有。

清理数据以确保其适合训练— 例如,删除不包含对着摄像机说话的人的部分。
一些清洗逻辑需要在转换的甚至是丰富的数据上运行。

捕获元数据 —为了帮助构建多样化且平衡的数据集,我们需要将数据映射到多个领域维度,例如演讲者的性别、照明条件、语音质量等。

元数据可以描述整个视频、视频的片段或视频内非常短的序列,例如帧级别。

作为从源获取数据的一部分,可以提供描述整个视频的一些基本维度。

在其他情况下,需要计算元数据,例如通过附加的深度学习算法对数据进行推断。

长期存储数据+元数据— 所有形式的数据都需要长期存储,包括原始的、经过转换和丰富的以及精选的数据集。

使数据可搜索— 该解决方案需要允许研究人员通过搜索具有所需属性/维度/元数据组合的实例的数据来快速构建数据集,例如“获取 100 个训练实例,其中多达 40%应该具有闪烁的字符”。

构建和存储版本化的训练数据集— 一旦为数据集选择了实例,它就应该以版本化的方式存储,并在需要时被拉入训练机器。

光深湖的视频资料。作者图片

让我们深入了解解决方案每个部分的需求。

要求

管道

数据流水线子系统的目标是执行处理步骤的 DAG,并发出数据,这些数据随后将被存储在数据管理子系统中。

输入和触发

管道的输入是一个文件,其中包含指向原始视频的指针,以及一些关于其内容的元数据。

流水线通常在获取新数据后被触发,并且只处理新的数据增量。

有时,我们可能会选择从头开始对所有数据运行它,或者对输入数据的特定子集运行它。

加工

管道应该运行多个异构的数据处理步骤。一些步骤可以运行外部过程,一些可以运行模型的推断,一些可以执行图像或信号处理。

每个步骤的输出可以被过程中的下一个步骤、训练过程或两者使用。

扩展性和进化

一些低级处理阶段被认为是相对稳定的,不太可能经常改变。

管道的其他部分,如浓缩逻辑,将继续高速发展——我们需要允许研究人员在不依赖工程师的情况下在管道中增加浓缩阶段。

子 DAG 执行和回填

当管道逻辑发展时,新的逻辑需要在整个数据语料库上运行。在数据工程中,这通常被称为“回填”或“填充”。

在我们的案例中,由于数据的大小和处理的复杂性,在整个数据语料库上重新运行整个管道是一项昂贵且耗时的工作。

因此,流水线需要支持触发部分执行,即只运行用户指定的子 DAG

结果缓存

作为一个相关的要求,我们希望管道能够“缓存感知”——即跳过昂贵的处理阶段,以防自上次执行以来数据、代码和配置没有任何变化。

输出处理语义

当对旧数据运行管道时,我们可能会决定覆盖旧数据,或者将输出作为新版本的数据追加。

横向扩展 随着数据量的不断增长,我们需要一个能够在多台机器上运行的解决方案

在这种模式下工作时,应该可以通过 UI 或调度程序调用管道。

本地运行 与此同时,能够在本地将管道作为完全标准的 Python 流程运行非常有用——从源代码、包或 Docker 容器内部运行,而不依赖于云基础设施,也不发布其输出,主要用于开发和本地测试。

CPU 和 GPU

流水线中的一些阶段执行适合 CPU 的视频裁剪或编码/解码等活动,一些阶段执行深度学习模型的推断(例如,检测演员面部周围的边界框),这些活动受益于 GPU 加速。

用户应该能够以声明的方式指定哪些任务应该在哪个处理单元上运行。

数据管理

数据管理子系统的目标是长期存储数据和元数据。此外,它应该:

  1. 使数据可搜索并可访问以构建数据集
  2. 以版本控制的方式支持新数据集的创建
  3. 允许用户下载数据集进行培训

存储

对于长期存储,我们需要一种可扩展的对象存储技术,如 S3。

媒体存储格式

我们希望以标准格式存储较大的媒体文件,包括原始文件和预处理文件,以便尽可能使用标准工具(例如. mp4、.哇哇,还有。png)

元数据存储和模式 遵循 Deep Lake 架构模式,元数据应该使用提供结构的格式存储,并且是可查询的。

同时,我们需要在模式管理中允许高度的灵活性,而不引入复杂或僵化的数据引擎。

数据版本化 媒体文件的底层和繁重的预处理逻辑不会经常改变,如果改变了,通常覆盖以前的版本是安全的。

另一方面,丰富逻辑往往会随着时间而变化,在数据足迹方面更轻(想想边界框和地标坐标),因此它们的输出应该被版本化。

训练数据集应该受版本控制。

ClearML 101

ClearML 是一个开源的 MLOps 项目,它结合了实验跟踪、数据集管理、远程代码执行和用 Python 编写的作业管道。

ClearML HL 架构。图片来自 ClearML github 项目https://raw . githubusercontent . com/allegro ai/clear ml-docs/main/docs/img/clear ml _ architecture . png

任务和实验跟踪

在高层次上,您可以用几行代码来检测 Python 程序,以将其连接到 ClearML。一个被检测的 Python 程序被称为一个 任务

任务执行时,例如在您的本地机器上,插装代码自动收集信息,例如命令行参数、git diff 和 latest commit、解释器可用的 Python 包列表,甚至 ML 工具的特定状态,例如 PyTorch 指标。

然后,被跟踪的元数据被发送到 ClearML 服务器并存储在那里,可以通过 UI 和 API 访问。

您还可以在任务执行期间从代码中显式报告数据。这对于例如在训练过程中跟踪指标是有用的。

远程执行

当 ClearML 跟踪一个任务时,服务器会存储复制它所需的所有信息——包括在远程机器上运行它。

为了在远程机器上执行一个任务,您“克隆”它(通过 UI 或 API 调用),并把它放在一个队列中。

运行在远程机器上的一个 阿根 t 轮询新任务的队列,一旦它将一个任务出队,它就将其作为一个(本地)Python 进程来执行。运行代理的远程机器称为工作器

管道

任务的 DAG 被称为 流水线。 流水线的流程由一个 控制器 任务——另一个触发任务执行的 Python 函数,在它们之间传递参数和信息。

控制器通常会执行任务,将它们发送到 队列 ,在那里它们将被 工作器 拾取。**

数据集

一个 数据集 是一种特殊的任务,其中用户报告“数据”而不是像正常实验中那样报告度量。

数据可以是任何东西,但通常是存储在某个文件系统上的文件,如挂载的磁盘、NFS 或对象存储。

数据集可以用与 Git 类似的方式进行版本化,每次提交版本时,它只存储与以前版本的差异。

关于数据集的元数据存储在 ClearML 服务器中,而实际数据(例如数据集中包含的文件)可以存储在您选择的存储设备中,例如 S3 或 NFS 服务器,只要它可供需要下载和使用它的工作者机器使用。

为什么是 ClearML

功能适用性

ClearML 支持所有主要的功能需求:

  • 在 Python 中定义和运行媒体处理管道
  • 在 CPU 和 GPU 上以远程/分布式执行方式运行管道。
  • 长期存储大量二进制或半结构化文件,并将其管理和保存到数据集中。
  • 允许下游处理步骤和训练过程容易地使用数据集。

ClearML 可以执行所有这些任务。

然而,如果你一直在关注,你会注意到 ClearML 并没有提供一种可以在数据集内存储的数据上运行的查询语言,而这是我们需求的一部分。

然而,正如我们将看到的,我们有一个解决这个限制的方案,成功地完成了工作。

赞成的意见

虽然有许多工具可以实现这一功能,但 Hour One AI 团队已经采用 ClearML 进行实验跟踪。

与此同时,该团队在运行关系数据仓库或云基础设施方面的经验要少得多,因此该工具的 Pythonic 式和熟悉的界面对其有利。

作为一个更普遍的优势,该工具是开源的,并且有一个活跃的社区。

最后,我们知道 ClearML 非常灵活,一旦你掌握了任务和远程执行机制,你就可以构建非常复杂的工作流——所以我们知道我们可以让它工作。

骗局

该工具的自动特性是有代价的——当事情不按预期运行时,需要时间来理解发生了什么。

调试没有按预期执行的 ClearML 代码需要打开工具的代码,通过它进行调试,在 slack 上提出问题,并且通常具有分布式计算、云 API、Python 依赖管理、docker 内部机制等方面的工作知识。

退一步说,文档可能是不完整的。

最后,灵活性也是一个缺点——因为 ClearML 不是一个固执己见的工具。这意味着你通常可以让它做你想做的事情,但你需要知道你在做什么才能让你的工作流有意义。

系统设计

高水平(位)

  • 该工作流被实现为一个 ClearML 管道 (具体来说——使用 PipelineDecorator )。
  • 流水线中的每个 任务 以一个 数据集 ID 为输入,生成一个或多个 数据集 作为输出。
  • 关于生成数据的元数据(包括世系)长期存储在 数据集中。 数据本身以多种不同的格式驻留在 S3 上。
  • 使用 ClearML 队列自动缩放器 来缩放流水线
  • 大多数其他需求(缓存、子 DAG 执行、使用相同的代码库在本地和远程运行)都是通过仔细分离关注点以及使用低级 ClearML API 来实现的。

逻辑流程

按照图表从左到右:

  • 管道由一个参数触发,该参数将管道指向一个包含原始视频链接的文件。
    文件被添加到代表“所有原始数据”的数据集中。
  • 第一个任务是根据文件中存在的元数据将原始视频分割成更短的部分(“片段”)。
    结果是分割的视频和元数据文件,每个都存储为 ClearML 数据集。
  • 下一步是对来自分离视频的视频和音频数据的基本预处理。
    每个都存储到 ClearML 数据集中。
  • 进一步丰富和净化音频和视频信号——另外约 10 项任务。

管道逻辑流和输出数据集。作者图片

数据管理

  • 每个任务的每次运行都会生成一个或多个独立的 ClearML 数据集
  • 每个数据集对象包含一个指向任务的指针,任务为沿袭创建了数据集对象(反之亦然)。
    这使我们能够提取特定管道运行产生的所有不同数据集。
  • 每个数据集包含其包含的视频片段的索引。
  • 大型媒体文件以它们的标准格式存储在 S3 上,ClearML 数据集使用外部文件机制保存它们在 S3 上的位置的引用。
  • 较小的文件被克隆并以 ClearML 格式存储(也在 S3 上)。

元数据模式和查询处理

正如上面所讨论的,我们希望允许研究人员在不需要了解关系数据库和其他外部工具的情况下轻松地发展模式。

此外,管道计算的许多元数据是半结构化的,例如,视频中每一帧的边界框或面部标志。

数据的结构使得通过关系查询引擎进行查询有点困难。

我们决定避免向解决方案中添加另一个移动部分,并保持它纯粹基于 ClearML。下面是我们实现查询的方法:

  1. 研究者获得他们想要查询的数据集 id 的列表。典型地,这些将包括元数据或注释(不是媒体)。
  2. 使用 ClearML 工具,用户下载这些数据集并将其合并到本地数据集副本中。
    例如——获取代表人脸边界框和界标的数据集。
  3. 研究人员使用 numpy 或 Pandas code 等标准工具执行“查询”,以便选择她想要训练的数据。
    例如,迭代表示面部边界框的 Numpy 数组,并且仅过滤掉边界框的总面积大于 X 并且所有界标都落在边界框内的元素。
    该“查询”结果中的每个元素将包含一个指针,该指针指向它所源自的帧和视频。
  4. 研究人员以编程方式创建一个新的数据集,其中包含 ClearML 中经过过滤的视频。
  5. 稍后,训练代码将数据集从(4)下载到本地磁盘,并开始训练。

使用 ClearML 数据集的查询流。作者图片

实际上,构建数据集的过程涉及满足数据集结构约束的线性规划。

基于远程和集群的执行

该过程工作如下:

  1. 用户触发管道执行——通过在她的机器上运行管道或者通过 UI
  2. ClearML 服务器接收呼叫
  3. ClearML 服务器将代表流水线逻辑( 【控制器】 )的任务的执行排入 队列
  4. 运行在某台机器上的一个 代理 拉这个任务
  5. 代理 开始执行管道方法代码。
  6. 控制器 为流水线中的每一步产生 ClearML 任务 ,并将它们放入 队列
  7. 额外的工人机器拉出这些 任务 并在本地启动它们
  8. 每个任务逻辑调用 ClearML 数据集 API 来创建它的输出 数据集 ,其中元数据存储在 ClearML 服务器上,实际数据存储在 S3 上。

带有远程任务执行的 ClearML 管道。作者图片

自动缩放

让数十台机器持续运行以等待任务排队是没有意义的。

ClearML 提供了一个自动缩放器,能够根据队列的状态上下旋转机器。

自动缩放流程非常复杂:

  1. “自动缩放逻辑”实际上是一个 ClearML 任务,它被放在一个专用队列中(例如“DevOps”队列)。
  2. 一台专用的机器(总是运行的)运行一个代理来监听这个队列。
  3. 代理选择自动缩放任务,该任务基本上永远运行
  4. 任务逻辑包括轮询队列和使用配置,为每个队列启动各种类型的机器
  5. 使用云提供商 API(例如 AWS 上的 Boto3)来启动机器。
  6. 衍生的机器有一个用户数据启动脚本,该脚本用凭证设置它们,并以守护模式启动 ClearML 代理
  7. 启动脚本完成后,代理将监听队列

小字:

  • 秘密管理由你负责。ClearML 希望您输入 AWS 凭证和 git。ssh 凭证保存到一个配置文件中,并将其保存在 ClearML 服务器中——从基本的安全实践来看,这是行不通的。
  • 代理需要访问 S3,因此新机器需要能够承担具有适当权限的角色。
  • 用户数据脚本是以非常间接的方式生成的——从配置到自动缩放代码到 AWS API 调用等等。任何错误都很难修复/测试。

我们必须找到替代解决方案,例如,使用适当的实例配置文件,并将机密存储在机密管理解决方案中。

支持 GPU 和 CPU 任务

这是通过两个队列实现的,一个用于 CPU 任务,一个用于 GPU 任务。

每个任务(Python 函数)都用它应该被发送到的队列的名称进行了注释。

代码级设计笔记

管道代码库非常简单。以下是我们构建管道的伪示例。

第 7–8 行— 主控制器逻辑有一个pipeline decorator . pipeline()decorator此外,它的参数(通常解析自命令行参数)应该可以使用 json 或 pickle 进行序列化。**

第 9 行 —任何导入都应该在函数内部执行(远程运行时需要)。

第 13 行 —我们使用一个工厂来创建一个“追踪器”对象。追踪器是大部分魔法发生的地方。它有两个实现——一个本地跟踪器(或多或少是无操作的),和一个 ClearML 跟踪器(实际上针对 ClearML 执行调用)。

基于命令行标志实例化正确的类。

第 15–19 行 —该流程通过在方法(任务)之间传递数据集 id 来实现。

当这些代码以远程模式在 ClearML 上运行时,这些调用触发远程任务的创建,并将它们所依赖的先前任务的结果发送给它们。

现在让我们来分析一下任务:

第 1 行 —该任务是一个带有 ClearML 装饰器的纯 Python 函数。

第 3–5 行 —该函数执行导入,如果我们希望能够远程运行它,就需要执行导入,然后它初始化自己的跟踪器实例。

****第 7–9 行-跟踪器对象负责获取缓存的结果(如果存在),或者,如果不存在,则将输入数据集下载到本地文件夹。

第 14–15 行 —使用跟踪器,我们将第 11–12 行生成的数据上传到名为“split_videos_media”的 ClearML 数据集。

本地运行

要打开本地运行,我们需要在管道方法之前调用pipeline decorator . run _ locally()

还支持其他一些运行模式,比如:在本地运行管道任务,将任务作为本地进程或远程任务运行。

仅在子 Dag 上运行

这也由 tracker 对象处理,它能够遍历 DAG 并自动跳过所有不需要的任务。

血统追踪

tracker 对象用管道运行 ID 标记所有任务,并用它创建的数据集列表标记每个任务——这些数据集作为工件存储在 ClearML 中。

附加功能

跟踪器负责所有的命名、数据收集和报告约定,以便任务作者可以专注于他们的业务逻辑。它还能够将外部任务作为监听器附加到管道执行中,按计划运行等等。

摘要

为训练深度学习模型准备大规模媒体数据需要对原始数据运行多个处理和浓缩步骤。

它还要求以一种结构良好的方式存储处理过的数据,这种方式支持版本控制,并使研究人员能够查询数据以构建数据集。

ClearML 可以用来实现以上所有功能。

它的亮点在于其纯粹的 Pythonic 接口、直观的数据集范式以及对许多非功能性需求的支持,如自动伸缩,尽管这些都是有代价的。

虽然 ClearML 没有提供数据查询机制,但它仍然可以组织数据,以便拉动数据并在本地执行查询可以完成工作,特别是如果查询发生在数据生命周期中定义明确的点上。

从散点图到故事:谷歌数据工作室版

原文:https://towardsdatascience.com/from-scatter-plot-to-story-google-data-studio-edition-ff1bd1ac3d96

通过在 Google Data Studio 中增强您的散点图来讲述完整的故事

TLDR:本文之前的另外三篇配套文章都展示了如何用 Python 或 Stata 代码以编程方式生成散点图。如果你对编写计算机代码不感兴趣,但是你对通过一个简单的散点图讲述一个完整的故事感兴趣,这篇文章适合你。

概观

出于对一个简单想法的极大热情,这篇文章至少有三个其他版本。文章的每个版本都展示了将一个简单的散点图转换成完整故事的过程。每篇文章都关注不同的工具。有一款 Seaborn 版。有一个 MatplotLib 版。而且还有一款 Stata 版

现在我介绍谷歌数据工作室版。正如我先前解释的那样:

散点图是许多分析师和数据专家的重要工具。它们说明了两个(或更多)变量之间的关系。它们很快就会生成。它们很容易解释。

如果你还不知道,Google Data Studio 是一个简单、强大、免费的数据可视化工具。你可以在datastudio.google.com了解更多。谷歌关于这个工具的信息说:

你的数据很漂亮。使用它。
利用交互式仪表盘和漂亮的报告激发更明智的业务决策,释放数据的力量。既轻松又免费。

要理解这篇文章背后的故事,首先要了解一些背景知识。

背景

这个项目的假设目标是审查一个车辆列表,并决定哪一辆车将是为扩大公司车队而购买的最佳车辆。有一个假设的指导方针,确定最低车辆效率为每加仑 16 英里。还有另一个假设的指导方针,确定 29 英里每加仑为最佳车辆效率。耐久性和“韧性”也是车辆选择的一个重点。

古斯塔沃Unsplash 上拍摄的照片

要访问该项目的数据,您需要在 python 中运行以下代码行。运行下面几行代码后,您将拥有 csv 文件中的汽车数据集。

import pandas as pddataurl = 'http://www.stata-press.com/data/r15/auto.dta'df = pd.read_stata(dataurl)df.to_csv('auto.csv', index=False)

下面是数据的前五个观察结果如何出现在大多数 csv 文件阅读器中。

图片鸣谢:作者截屏 auto.csv。

Google Data Studio 入门

图片来源:作者对 datastudio.google.com 的截屏。

如果你还没有使用谷歌数据工作室,在 datastudio.google.com 很容易上手。当您到达并登录后,您将看到以下屏幕和选项。

图片来源:作者登录后 datastudio.google.com 的截屏。显示创建新的“空白报告”、“教程报告”或“acme 营销”报告的选项。

要继续本指南,请选择“空白报告”选项。选择“空白报告”选项后,您将看到另一个类似于下图的屏幕。

图片来源:作者选择“空白报告”后 datastudio.google.com 的截屏显示了连接多个数据源的选项,包括“文件上传”选项。

要继续本指南,请选择“文件上传”选项。点击“文件上传”选项后,你会看到一个类似于下图的屏幕。

图片来源:作者选择“文件上传”后 datastudio.google.com 的截屏显示上传数据源的选项。

上传文件后,你会看到另一个类似如下的屏幕。还需要单击屏幕右下角的“添加”按钮。单击“添加”按钮会将数据添加到您的报告中。

图片来源:作者上传 CSV 文件并选择“添加”数据到您的报告后的 datastudio.google.com 截屏。在继续之前,请务必单击“添加”按钮。

将自动数据添加到报告后,下一步是添加图表。下图显示了添加散点图(或散点图)后报告的外观。要添加此图表,请使用报表顶部菜单/功能区中的“添加图表”按钮。

图片来源:作者在上传 CSV 后以及更改 y 轴和 x 轴以显示车辆效率(mpg)和重量之间的关系后对 datastudio.google.com 的截屏。

要确保 mpg 数据沿垂直 y 轴运行,重量沿水平 x 轴运行,请使用报告右侧的设置选项卡(在红色圆角框内)。

这个过程的下一步将是调整图表的范围。要调整图表的范围,请切换到样式选项卡。如下所示,在红色圆角框中进行编辑。

图片来源:作者在上传 CSV 文件、更改 y 轴和 x 轴变量以及调整 y 轴(10 到 42.5mpg)和 x 轴(1500 到 5,000 lbs)的范围后拍摄的 datastudio.google.com 的屏幕截图。

具体来说,上面显示的是散点图的一个版本,控件上有三个红色圆角框。第一个红色框突出显示“样式”选项卡。“样式”选项卡将提供编辑轴范围的权限。如第二个红框所示,我已经将 y 轴的最小值更改为 10 mpg,将 x 轴的最大值更改为 1,500 lbs。

添加参考线和注释

到目前为止,上述数据可视化提供了关于车辆列表的大量信息。我们知道最小和最大效率以及最小和最大车辆重量。我们还看到,随着重量的增加,效率下降。

这是大量的信息!然而,为了讲述一个完整的故事,这张图表有一点不足。有太多未回答的问题。这个练习的目标是制作一个视觉效果,观众可以看到并快速理解一个完整的故事。

为了完成这个故事,我们可以添加参考线和注释。第一步将是在最低和最佳效率水平添加参考线。下图显示了(在红色圆角框内)如何在 Google Data Studio 中向图表添加参考线。

图片来源:作者在上传 CSV 文件、更改 y 轴和 x 轴变量以及调整 y 轴和 x 轴的范围后拍摄的 datastudio.google.com 的屏幕截图。这张图片上的红框显示你可以在谷歌数据工作室的图表上添加参考线。

在 Google Data Studio 的“style”选项卡中,有一些选项可以让你调整图表,包括添加参考线的选项。在添加参考线的选项中,还有向参考线添加注释或“标签”的选项。这些选项还允许更改参考线的颜色。

注释特定的数据点

最后一步是注释特定的数据点。要注释的特定数据点是最能满足所需车队车辆要求的车辆。在这种情况下,它将是丰田花冠,每加仑效率为 31 英里,重量为 2,200 磅。

为了添加最终的注释,Google Data Studio 提供了添加文本块、线条、箭头和其他形状的机会。下图显示了(在圆角红色框中)文本块、线条、箭头和形状控件。

图片来源:作者截屏 datastudio.google.com,散点图显示了车辆效率和车辆重量之间的关系。红色方框显示了 Google Data Studio 的文本块、线条、箭头和形状工具。

通过添加带有“丰田卡罗拉(31 Mpg 2,200 lbs)租金昂贵,最佳里程”字样的文本块,为最符合车队车辆选择标准的车辆创建注释。

感谢阅读

你准备好了解更多关于数据科学职业的信息了吗?我进行一对一的职业辅导,并有一份每周电子邮件列表,帮助专业求职者获取数据。联系我了解更多信息。

感谢阅读。把你的想法和主意发给我。你可以写信只是为了说声嗨。如果你真的需要告诉我是怎么错的,我期待着尽快和你聊天。推特:@ adamrossnelsonLinkedIn:亚当罗斯尼尔森

从散点图到故事:Seaborn 版

原文:https://towardsdatascience.com/from-scatter-plot-to-story-the-seaborn-edition-fb15f3c4cd72

通过增强散点图讲述完整的故事

概观

就绘图而言,散点图是最受欢迎的。它们迅速揭示了两个(或更多)变量之间的关系。如果做得好,散点图可以很快传达给观众一个完整的故事。你的目标是让你的听众对自己说…

我们知道现在该做什么…

理想情况下,你的散点图将在瞬间激发上述意识。这篇文章将展示如何将你的可视化从一个简单的散点图变成一个完整的故事。这是 Seaborn 版。后续文章将在基础 Matplotlib 和 Stata 中展示相同的内容。

背景

你的工作是帮助决定你的公司应该购买什么样的车辆,因为它希望扩大其企业车队。还有,现在是 1978 年。我们假设现在是 1978 年,因为我们有这一年的数据。

我以前写过这些数据。

这些数据告诉我们大约 74 辆车。我们知道每辆车的品牌、价格、效率(mpg)和其他因素。

我们公司规定最低效率要求是每加仑 16 英里,但我们的最佳效率标准是每加仑 29 英里。分析任务是在最佳效率标准之上找到最具成本效益的车辆,同时也是最重的(将是最重型的)。

入门指南

最容易查看的图是简单的散点图。在 Seaborn 的帮助下,看起来像这样:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as snsdf = pd.read_stata('[http://www.stata-press.com/data/r15/auto2.dta'](http://www.stata-press.com/data/r15/auto2.dta'))plt.figure()
ax = sns.scatterplot(data=df, 
                     x="weight", y="mpg", 
                     color = '#060b3f')
plt.show()

对于下面的结果(这不是很有帮助,也不是有益的,也不是艺术):

作者的再现。使用此处指定的数据和代码。

尺寸、比例、大小和标签

美化这种视觉效果的一些首要任务是:

  • 更改尺寸(更加水平,以适合我们的目标 8.5 x 11 英寸页面报告。
  • 指定将减少混乱的刻度和刻度线。
  • 添加精细的网格线,帮助读者将各个数据点与它们在 y 轴上的位置联系起来。
  • 并更新轴标签以提供更好的信息。
plt.figure(figsize = (15,6))
ax = sns.scatterplot(data=df, 
                     x="weight", 
                     y="mpg", 
                     color = '#060b3f')
ax.set(xlabel='Weight (lbs).', ylabel='Mileage (mpg).')
ax.yaxis.grid(True, color = '#CCCCFF')
plt.xticks(np.arange(2000, 6000, 1000))
plt.yticks(np.arange(10, 45, 10))
plt.show()

对于以下结果:

作者的再现。使用此处指定的数据和代码。

设置上下文(可选)

Seaborn 的一个特性是能够设置你的上下文。粗略地说,上下文指定了你想要显示视觉效果的地方。例如,你可以将你的视觉效果放在一张纸上、一个笔记本上、一次会议上或者一张海报上。

虽然许多人认为这一步是可选的,但我经常支持为海报设置背景。我发现它在任何背景下都是最具可读性的。seaborn.set_context(context='poster')为我们做工作。

plt.figure(figsize = (15,6))
sns.set_context(context='poster')
ax = sns.scatterplot(data=df, 
                     x="weight", 
                     y="mpg", 
                     color = '#060b3f')
ax.yaxis.grid(True, color = '#CCCCFF')
ax.set(xlabel='Weight (lbs).', ylabel='Mileage (mpg).')
plt.xticks(np.arange(2000, 6000, 1000))
plt.yticks(np.arange(10, 45, 10))
plt.show()

对于结果(更大的字体、更大的数据点、更粗的线条等。):

作者的再现。使用此处指定的数据和代码。

添加参考线

这里是商店展示自己的地方。为了勾勒出我们故事的轮廓,我们可以在图形上添加一些额外的信息。我们可以添加与我们公司的效率标准相关的信息。

这里sns.lineplot()让我们添加水平参考线。而ax.text()让我们注释那些参考线。

plt.figure(figsize = (15,6))
sns.lineplot(data=df, x="weight", 
             y=[29]*len(df), color = 'green')
ax = sns.scatterplot(data=df, 
                     x="weight", 
                     y="mpg", 
                     color = '#060b3f')
sns.lineplot(data=df, x="weight", 
             y=[16]*len(df), color = 'red')
ax.text(3200, 30, 
        'Green line indicates optimal efficiency Standards.', 
        color = 'green')
ax.text(1800, 15, 
        'Red line indicates minimum efficiency requirements.', 
        color = 'red')
ax.set(xlabel='Weight (lbs).', 
       ylabel='Mileage (mpg).')
ax.yaxis.grid(True, 
              color = '#CCCCFF')
plt.xticks(np.arange(2000, 6000, 1000))
plt.yticks(np.arange(10, 45, 10))
plt.show()

对于几乎最终的结果:

作者的再现。使用此处指定的数据和代码。

最后,注释特定的数据点

最后的亮点是注释一个特定的数据点。这里的版本表明,丰田卡罗拉(每加仑 31 英里,2200 磅)是我们下一轮车队采购的最佳车辆。

为了注释一个特定的数据点,这段代码使用了ax.plot(),它指定了一个特殊标记的具体位置(坐标)以及其他标记属性。

这个最终版本还使用plt.suptitle()plt.title()添加了标题和副标题。

最终版本是否完美?不。话说回来,没有一幅画是完美的。然而,这个最终版本展示了如何,通过一些深思熟虑的更新,你可以从散点图到故事。

在锡伯恩:

plt.figure(figsize = (15,6))
sns.lineplot(data=df, x="weight", 
             y=[29]*len(df), color='green')
ax = sns.scatterplot(data=df, 
                     x="weight", 
                     y="mpg", 
                     color='#060b3f')
sns.lineplot(data=df, x="weight", 
             y=[16]*len(df), color='red')
ax.text(3600, 30, 
        'Green line indicates optimal efficiency Standards.', 
        color='green')
ax.text(1800, 15, 
        'Red line indicates minimum efficiency requirements.', 
        color='red')
ax.text(2250, 32.2, 
        'Toyota Corolla (31mpg 2,200lbs)',
        color = 'green', size = '14')
ax.text(2250, 30.8,
        'Least expensive above optimal', 
        color = 'green', size = '14')
ax.set(xlabel='Weight (lbs).', 
       ylabel='Mileage (mpg).')
ax.plot(2200, 31, 
        'g', marker='$\\bigoplus$', markersize=15)
ax.yaxis.grid(True, color = '#CCCCFF')
plt.suptitle("The 'Best Value' For Fleet Purchases", 
             fontsize=20)
plt.title("A Preliminary Analysis",
          fontsize=15)
plt.xticks(np.arange(2000, 6000, 1000))
plt.yticks(np.arange(10, 45, 10))
plt.show()

为了最终的结果(如果你愿意,故事):

作者的再现。使用此处指定的数据和代码。

https://adamrossnelson.medium.com/membership

感谢阅读

你准备好了解更多关于数据科学职业的信息了吗?我进行一对一的职业辅导,并有一份每周电子邮件列表,帮助专业求职者获取数据。联系我了解更多信息。

感谢阅读。把你的想法和主意发给我。你可以写信只是为了说声嗨。如果你真的需要告诉我是怎么错的,我期待着尽快和你聊天。推特:@ adamrossnelsonLinkedIn:亚当罗斯尼尔森

从沙普利到 Shapley 理解数学

原文:https://towardsdatascience.com/from-shapley-to-shap-understanding-the-math-e7155414213b

如何计算 SHAP 要素贡献的概述

克拉拉·库利科娃在 Unsplash 上的照片

假设你(玩家 1)和一个朋友(玩家 2)参加了一场猜拳比赛。你最终赢得了一等奖,10,000 美元。现在,你想公平地分配这笔钱。你的朋友建议你平分它。但是,你的超参数调优技术更胜一筹。你认为你应该得到更大的份额,因为你对团队的贡献更大。考虑到这一点,你怎么能公平地分配这笔钱呢?

很方便,你的朋友有一台时光机。你们各自回到过去,独自重新进行卡格尔竞赛。你最终获得第二名,并赢得 $7,500 。你的朋友仅获得第三名,赚了 $5,000 。如果你们都不玩,就不会赢得任何奖金( $0 )。我们将这 4 个玩家联盟的价值记录如下。很明显,你应该得到更多的奖金,但是如何分配还不清楚。

联盟价值观(来源:作者)

一种方法是通过计算每个玩家的预期边际贡献。这是玩家对其可以加入的所有联盟贡献的加权平均值。

例如,参与人 1 (P1)可以加入只有参与人 2 (P2)的联盟。P2 从第三名升至第一名,奖金增加了 $5000 。P1 也可以加入一个没有玩家的联盟,增加 7500 美元的奖金。这些是 P1 的边际贡献。这些因素的平均值为我们提供了预期边际贡献 $6,250

参与人 1 的预期边际贡献(来源:作者)

我们可以按照类似的过程来计算 P2 的预期边际贡献。这次的价值是 3750 美元。最终,P1 将获得 6250 美元,P2 将获得 3750 美元。这两个值也被称为沙普利值。请注意,这些值加起来的总奖金为 10,000 美元。

参与人 2 的预期边际贡献(来源:作者)

沙普利价值观被认为是一种公平的奖金分配方式。这是一个来自博弈论的概念。我们只计算了两个人的团队。我们能够使用它们来计算任何规模的团队的公平分配。我们将花时间来理解这个的广义沙普利值公式。这个公式可能看起来很可怕。然而,当我们得到引擎盖下你会看到它有一个直观的解释。

沙普利值=预期边际贡献

这是玩家对其可以加入的所有联盟贡献的加权平均值。

从划分奖金到解释机器学习模型,这似乎是一个很大的飞跃。然而,Shapley 值可以用来理解每个模型特征(玩家)如何对预测(奖金)做出贡献。我们将解释 Shapley 值如何扩展到解释模型预测。我们将以探索对这一领域研究的贡献来结束。也就是说,它们极大地提高了我们逼近 Shapley 值的速度。****

3 人游戏的 Shapley 值

在我们继续之前,让我们再看一个例子。这将使一般方程更容易理解。这次我们有一个由三名球员组成的队伍。计算很复杂,我们还得再回溯几次。

现在将有 8 个可能的联盟,如下所示。你们一起赢得一等奖( \(10,000** )。现在有 3 个 2 人联盟。例如,P1 和 P3 的联盟将获得第二名(**\) 7500)。也会有一个玩家的联盟。例如,如果 P3 独自比赛,他们将不会获得任何奖金( $0 )。也许他们应该投资一个更好的 GPU。

联盟价值观(来源:作者)

我们可以用这些联盟值来计算 P1 的沙普利值。现在 P1 可以加入 4 个联盟。P1 可以加入 P2 和 P3 的联合政府,或者只有 P2 或 P3 的联合政府,或者没有成员的联合政府。像以前一样,我们计算 P1 对每个联盟的边际贡献。最后,我们取加权平均值。这给了我们一个沙普利值$ 5000

参与人 1 的预期边际贡献(来源:作者)

你可能会问,我们从哪里得到这些重量?这就是为什么我们把第一个边际贡献的权重定为 1/3,第二个边际贡献的权重定为 1/6,以此类推……这些就是 P1 做出这些特定贡献的概率。通过概率加权给我们一个 预期 边际贡献。

这些概率从何而来并不明显。首先,我们需要找出三个人组成联盟的方法。这是因为只有所有 3 名成员齐心协力才能赢得全部奖金(10,000 美元)。

为了做到这一点,我们假设每个成员都有平等的机会依次加入团队。例如,P1 加入,然后 P3,然后 P2。这样我们总共有 3 个!=形成联盟的 6 种方式。你可以在下面看到所有这些。一般来说,有 n 个!组建 n 人团队的方法。

形成 3 人联盟的 6 种方式(来源:作者)

上面我们看到,如果 P1 加入 P2 和 P3 的联盟,他们将贡献 5000 美元的边际贡献。这可能以两种方式发生。要么 P2 加入,然后是 P3,然后是 P1,要么 P3 加入,然后是 P2,然后是 P1。换句话说,P1 将为团队组建的 6 种方式中的 2 种做出边际贡献。这给了我们 2/6 = 1/3 的概率,P1 做出了这一贡献。

只有一种方法可以让 P1 做出第二笔捐款(【2,500 美元】)。也就是说,如果 P2 加入,然后是 P1,然后是 P3。这给了我们 1/6 的概率。同样,第三次贡献($ 7500)的概率是 1/6。第四次贡献( $5000 )有 1/3 的概率。就像第一次捐款一样,P1 有两种方式。先是 P1 加入,然后是 P2,然后是 P3,或者是 P3,然后是 P2。

我们可以对 P2 和 P3 采取同样的做法。对于这些玩家,Shapley 值分别为 $3,750$1,250 。同样,所有 Shapley 值加起来就是总奖金。Shapley values 将始终公平地分配所有奖金。现在让我们看看如何推广 Shapley 值。

广义 Shapley 值

等式 1 给出了 p 玩家游戏中玩家 i 的 Shapley 值的公式。从求和符号开始,我们对所有联盟 S 求和。其中 S 是不包括参与人 I 的联盟的子集。换句话说,S 包含参与人 I 能够做出边际贡献的所有联盟。回到三人组的例子,有 4 个联盟不包括 P1。

等式 1:p 玩家游戏中玩家 I 的 shapley 值(来源:作者)

在方括号中,我们有参与人 I 对联盟 S 的边际贡献。具体来说,我们有联盟 S 的价值(val ),包括参与人 I 减去联盟 S 的价值。价值函数取决于正在进行的特定游戏。在我们三个玩家的例子中,我们使用了不同的符号。我们谈到了联盟的价值观,并使用了字母 C 和玩家下标。这些联盟值给出了 Kaggle 游戏的价值函数。

最后,我们对边际贡献进行加权。下面,你可以看到重量的每个组成部分代表什么。这里 |S| 是联盟 S 中玩家的数量,这意味着 p-|S|-1 是在玩家 I 之后需要加入联盟的玩家数量

在权重分子中,我们有联盟可以形成的方式的数量。分母是整个团队可以形成的方式的数量。因此,当博弈中有 p 个参与者时,权重给出了参与者 I 对规模为|S|的联盟做出贡献的概率。如果你代入我们 3 人游戏的值,你会得到和以前一样的权重。

把 Shapley 值分解一下,可以看到它有一个直观的解释。我们用参与人 I 做出贡献的概率来衡量他们的边际贡献。然后我们将参与人 I 可以加入的所有联盟的加权贡献相加。这给了我们一个预期边际贡献。使用这些值,我们可以在所有玩家之间分配游戏的总价值。

凭直觉,看起来预期边际贡献是一种公平的方式。我们考虑对所有联盟的贡献。这意味着我们考虑了玩家的个人贡献和玩家之间的互动。也就是说,一些玩家可以很好地合作,增加他们的共同价值。问题是可能有其他看起来公平的价值分配方式。我们需要证明 Shapley 值是公平的。

匀称的公理

Shapley 值实际上来源于 3 个公理。我们只对它们进行总结,但它们也可以用数学来定义。这些公理可以被认为是公平的定义。因此,满足这一定义的价值分割方法可以被认为是公平的。

如果两个玩家对所有联盟做出相同的贡献,他们就被认为是可以互换的。如果两个玩家可以互换,那么他们必须得到游戏总价值的同等份额。

****无效玩家财产如果一个玩家对所有联盟的边际贡献为零,那么他们将一无所获。

****可加性如果我们合并两个游戏,那么一个玩家的总贡献是两个单独游戏贡献的总和。这个公理假设任何游戏都是独立的。

我们可以从数学上证明 Shapley 值是满足这 3 个公理的唯一有效值。所谓高效,我们的意思是游戏的价值没有剩余。最终,在这个定义下,沙普利值是唯一公平的价值分配方式。令人惊讶的是,这样一个直观的公式可以从 3 个简单的公理推导出来。

机器学习的 Shapley 值

我们可以使用 Shapley 值来理解模型是如何做出预测的。现在,游戏的价值就是模型预测特征值是玩家。要明确的是,玩游戏的不是特征,而是用于特定观察的特征的 。但是,我们将这些称为特性。我们使用 Shapley 值来计算每个特征对预测的贡献。

例如,在之前的一篇文章中,我们训练了一个模型来预测鲍鱼的年龄。您可以在图 1 的中看到特定观察的 Shapley 值。这些给出了平均预测年龄 E[f(x)]和观测值预测值 f(x)之间的差值。例如,去壳重量的值使预测年龄增加了 1.81。我们称之为特性的贡献。****

图 1:SHAP 价值观的例子(来源:作者)

为了计算这些,我们可以使用与前面相同的 Shapley 值公式。我们需要做的就是改变价值函数。等式 2** 给出了我们用于特定观察的函数 x 。这里 S 是特征值的联合, f 给出模型预测,模型具有 p 个特征。 S 的值是不在 S 中的所有特征的模型预测值。我们使用 S 中的实际值。**

等式 2:机器学习的价值函数 Shapley 值(来源:作者)

我们实际上是在上式中做多重积分。为了忽略某个特征,我们将预测函数与特征值的概率相结合。我们对 s 中的所有特征都这样做,为此我们需要知道特征分布或使用经验分布。

上面的公式里有很多运动的部分。在本节的下一部分,我们将通过一个 ML 示例来更好地理解它。然后我们将继续讨论如何近似 ML 的 Shapley 值。为了结束这一节,我们将讨论 Shapley 值的性质。这些来自公理,我们将讨论它们在机器学习环境中的含义。****

ML 示例

假设我们想预测某人的收入。我们有两个特征——年龄(特征 1)和学位(特征 2)。我们以下面的模型 f 结束。对于年龄,我们假设特征在 18 到 60 岁之间均匀分布。类似地,对于学位,我们假设某人拥有学位(1)或没有学位(0)的机会是均等的。在我们的观察中,我们有一个 20 岁有学位的人。该模型将预测此人的收入为 5000 美元。

(来源:作者)

我们可以计算功能 2 {2}对功能 1 的联合的边际贡献,S = {1}。这可用于帮助计算特征 2 的 Shapley 值。换句话说,计算特征 2(度=1)对预测的贡献。

我们首先计算两个特征的联合的值,S = {1,2}。这相对简单。s 包含这两个特性,所以我们不必忽略任何特性。我们可以使用特性的实际值。如下所示,这与该观察的预测相同。

(来源:作者)

然后我们需要计算联盟的价值,S = {1}。在这种情况下,S 不包含{2}。我们忽略功能 2,使用功能 1 的实际值。记住,特征 2 不是连续的。为了忽略特性的价值,我们不需要使用集成。我们只是将每个值的预测值乘以该值的概率(即 50%)相加。

(来源:作者)

我们现在可以计算特征 2 对 S={1}的边际贡献。该贡献的权重以与标准 Shapley 值相同的方式计算。为了清楚起见,我们使用联合中特征的数量和特征值的总数。我们有|S| = |{1}| = 1 和 p = 2。这给了我们一个权重(1!)(0!)/2!= 1/2.

(来源:作者)

这仅给出了特征 2 的 Shapley 值所需的部分计算。我们还需要计算特征 2 对 S={}的边际贡献。为此,我们需要计算 S = {}的价值函数。这将要求我们忽略这两个特性的分布。

Shapely 值的近似值

计算精确的 Shapley 值在计算上是昂贵的。在上面的例子中,我们只有两个特征。随着我们添加更多的功能,可能的联盟数量呈指数增长。在实践中,只有接近 Shapley 值才是可行的。

一种方法是使用蒙特卡罗抽样。对于特征 I,我们先用特征值(+i)计算预测。我们在没有值(-i)的情况下做同样的事情。也就是说,我们为特征 I 取一个随机值。其余的特征值也将全部被随机采样。我们取这两个预测的差值。如下所示,我们这样做 M 次,并找出所有这些差异的平均值。通过随机采样和平均,我们隐含地根据特征的分布进行加权。

(来源:作者)

上述过程仍然不切实际。我们可能需要许多样本来获得一个合理的 Shapley 值的近似值。这就是 SHAP 的用武之地。正如我们在上一节中讨论的,这是一种更快的近似 Shapley 值的方法。在我们继续之前,我们将在机器学习的背景下讨论 Shapley 值的属性。

Shapley 值的性质

沙普利是解释预测的一种方法。它因其令人满意的特性而广受欢迎。这些大部分是从 Shapley 值的公理中得出的。

效率如前所述,沙普利值是有效率的。以前,这意味着游戏的全部价值在玩家之间分配。对于最大似然,这意味着预测在特征之间划分。具体地,Shapley 值满足下面的等式。所有 Shapley 值和平均预测值之和等于观测值的预测值。我们在前面的图 1 中看到了这一点。

(来源:作者)

另一种流行的当地解释方法是石灰。相比较而言,石灰不一定有效。计算出的权重将不会与原始预测相加。对 Shapley 来说,我们知道每个特征对预测的贡献有多大。对于 LIME,我们只知道哪个特征对预测最重要。

****对称性如果两个特征对所有联盟做出相同的贡献,那么它们将具有相同的 Shapley 值。

Dummy 如果一个特征从未改变预测,则其 Shapley 值为 0。换句话说,模型中不使用的特征将不具有 Shapley 值。

****可加性机器学习的 Shapley 值也是可加的。这仅与集合模型相关。在这种情况下,可以通过对集合中每个模型的 Shapley 值进行加权平均来计算总 Shapley 值。其中权重将与给予每个模型的预测的权重相同。例如,在随机森林中,来自每个决策树的预测被赋予相等的权重。

****一致性这个属性来源于前面的 3 个属性。假设我们把一个模型从 M1 换成 M2。如果一个特征现在比以前增加了更多的预测,那么它的 Shapley 值将增加。这意味着我们可以可靠地比较不同模型的特性贡献。

SHAP 价值观

SHAP python 包已经成为使用 Shapley 价值观的代名词。这个包广泛实现的关键是它可以进行近似的速度。我们在下面讨论一些方法。速度的提高意味着我们也能够计算许多 Shapley 值。这允许对值进行不同的聚合,从而为我们提供模型的全局视图。

KernelSHAP

内核 SHAP 将 Shapley 值重新定义为线性模型中的参数。简单地说,近似方法通过首先置换特征值来工作。在足够的排列之后,使用线性回归联合估计 Shapley 值。与其他采样方法相比,一起估计这些值需要更少的计算。例如蒙特卡罗采样,其中单独计算每个特征的 Shapley 值。

KernelSHAP 也是一种模型不可知的近似 Shapley 值的方法。这意味着它可以用于任何型号。前提是 SHAP 已经为你的建模包实现。

树形

与模型无关的近似方法是 TreeSHAP。它利用了集合模型中个体树的结构。因此,它只能用于基于树的算法,如随机森林和 XGBoost。TreeSHAP 的优点是明显比 KernelSHAP 快。

使用 KernelSHAP,我们可以在指数时间内估计 Shapley 值与特征数量的关系。而 TreeSHAP 可以在线性时间内估计它们。我们将在下面的文章中详细讨论这种差异。我们还探讨了模型的其他方面如何影响近似时间。这包括集合中的树的数量、最大深度和叶子的数量。

**

如前所述,这些方法可以近似计算大量的 Shapley 值。我们可以用不同的方式将它们结合起来,以了解模型作为一个整体是如何工作的。一个例子是图 2 中给出的蜂群图。

在这里,我们对每个特性的值进行分组(例如壳重)。我们可以看到倾向于具有大的正负 Shapley 值的特征。这些是倾向于对预测做出重要贡献的特征。我们还根据特征的值给点着色。这样我们就可以开始理解特征和目标变量之间的关系。

图 2:蜂群图的例子(来源:作者)

这些类型的地块易于实施是 SHAP 一揽子计划被广泛采用的另一个原因。我们将在下面的文章中探索如何使用这个包。我们讨论 Python 代码,并探索该包提供的一些其他聚合。

我希望这篇文章对你有帮助!如果你想看更多,你可以成为我的 推荐会员 来支持我。你可以访问 medium 上的所有文章,我可以得到你的部分费用。

https://conorosullyds.medium.com/membership

你可以在|Twitter|YouTube|时事通讯上找到我——注册免费参加 Python SHAP 课程

图像来源

所有图片都是我自己的或从www.flaticon.com获得的。在后者的情况下,我拥有他们的保费计划中定义的“完全许可”。

参考

南伦德伯格, SHAP 蟒包 (2021) https://github.com/slundberg/shap

南 Lundberg & S. Lee,解释模型预测的统一方法 (2017),https://arxiv.org/pdf/1705.07874.pdf

C.Molnar,可解释机器学习(2021)https://christophm . github . io/Interpretable-ml-book/shap . html

南 Masís,用 Python 进行可解释的机器学习 (2021)

L.S. Shapley,对博弈论的贡献,第一章为 n 人对策的价值。( 1953)

了解沙普利值

正文 S1:沙普利值的公理化基础**

人工智能的未来:从统计学习到想象空间中的行动和思考

原文:https://towardsdatascience.com/from-statistical-learning-to-acting-and-thinking-in-an-imagined-space-7149ad51e64a

建造具有人类思维的机器需要抛弃统计学,支持因果关系

iStock 上的艾夫林拉德科夫拍摄的照片

尽管人工智能领域近年来蓬勃发展,但我们仍远未开发出具有人类思维的机器。事实上,机器还不能像人类一样轻松地适应新的不同环境。此外,计算机系统还不具备对人类进化至关重要的想象力。这些限制来自于当前在该领域中采用的学习范例,该范例仅仅基于相关性学习。在本文中,我们将首先回顾人工智能领域的历史,看看该领域多年来是如何发展的,然后,我们将再次论证,该领域的革命是强制性的。具体来说,如果我们真的想建造一台接近人类智能水平的机器,我们需要抛弃当前的统计和数据驱动的学习范式,转而采用基于因果的方法。

在 20 世纪 70 年代和 80 年代初,计算机科学家认为,对人类事先提供的符号的操作足以让计算机系统表现出智能并解决看似困难的问题。这个假说后来被称为 符号规则假说

然而,尽管最初取得了一些令人鼓舞的进展,如计算机象棋和定理证明,但很快人们就发现,基于规则的系统无法解决对人类来说似乎很简单的问题。正如汉斯·莫拉维克所说:

让计算机表现出成人水平的性能相对容易……,而让它们掌握一岁儿童的技能却很难或不可能。

此外,基于规则的系统无法在不确定性或矛盾数据下很好地工作,由于随机和系统误差,这在自然界中是普遍存在的。由于这些限制和缺乏前景,人们对 AI 的兴趣下降,该领域进入了一个被称为 AI 寒冬 的时期。

最终,几年后,在很大程度上独立于经典人工智能领域之外,一个被称为 机器学习 的新领域开始出现。像罗森布拉特早期关于感知机的工作一样,机器学习建立在这样的观察基础上,即自然智能系统的表示和规则是通过进化和学习过程从经验中获得的,而不是通过符号规则假设获得的。自那以后,机器学习,尤其是基于人工神经网络的机器学习的子领域 深度学习 ,在人工智能领域取得了最显著的成功。

然而,尽管这些巨大的发展让许多科学家感到惊讶,并使许多人相信强人工智能的到来已经不远了,但我们仍然远远没有开发出一台接近人类智能水平的机器,而且,也许,除非人工智能研究发生重大转变,否则我们将无法实现这一目标。事实上,当前最先进的人工智能系统的泛化能力仍然非常差,这限制了它们在狭窄和特定任务中的应用。相比之下,人类可以轻松适应新的完全不同的环境。

最引人注目的是,像 “如果我做了……会怎么样?”“怎么…?”“为什么…?”“如果我做了…?” ,人类觉得相对容易回答的问题,对计算机系统来说却是望而却步。其直接结果是,机器无法思考它们的行为对外部环境的可能影响,也无法在这些有意的改变中做出选择,以产生想要的结果。此外,他们缺乏想象力和反思力,因为他们不能反思自己过去的行为,也不能设想其他的情景。

也许,这些限制不足为奇;毕竟,当前的机器学习系统完全以纯粹的联想模式运行,最终它们的成功归结为四个主要因素: (i)独立且同分布的随机变量假设,(ii)海量数据,(iii)高容量模型和(iv)高性能计算 。简而言之,他们只是试图通过捕捉统计相关性来拟合原始数据的函数,而不是对复杂的因果关系网进行推理,他们通过吃掉大量原始数据和计算资源来做到这一点。例如,撑开的雨伞和雨天是相互关联的现象,但只有后者与前者有直接的因果联系。因此,当看到人们打着伞时,表明正在下雨,但合上伞并不能停止下雨。虽然这对人类来说似乎微不足道,但机器还没有这种关系的线索,因此,它们会预测合上雨伞实际上会阻止下雨。因此,就像柏拉图的洞穴寓言中的囚犯一样,机器学习程序学习预测洞穴中阴影的运动,但他们未能理解那些阴影仅仅是三维物体的投影。

即使我们的祖先最初也缺乏因果知识,但正如尤瓦尔·赫拉利在他的书 智人 中所假设的那样,一旦人类开始意识到某些事情会导致其他事情,并且玩前者可以改变后者,我们就以惊人的速度进化。这个进化过程被称为 认知革命 。所有这些考虑都表明,除非我们将一个基于因果知识的心智模型嵌入其中,否则我们无法实现建造一台能够在康拉德·劳伦兹意义上的想象空间中行动的人类思维机器的雄心。

为了实现这一目标,被称为的新科学最杰出的倡导者之一 Judea Pearl 提出在未来的人工智能系统中植入一个 【因果推理机】 。这个因果推理机是一个机器,它接收一个查询和一堆数据作为输入,从而生成一个估计命令和对答案的估计。虽然估计需求可以被认为是回答查询的方法,并且它是根据基本的因果模型产生的,但是估计是根据输入数据的实际答案。因此,与传统的统计方法不同,数据的作用只限于估算的计算。这与基于数据驱动学习的机器学习形成了深刻的对比。**

这种设计背后的基本原理是原始数据天生是愚蠢的。事实上,尽管当前的研究趋势似乎希望每当因果问题出现时,以数据为中心的方法将引导我们找到正确的答案,但可以证明,因果问题无法直接从原始数据中得到答案。事实上,因果推理需要一些关于潜在数据生成过程的假设,因果领域已经表明,我们可以通过一组称为 因果模型 的数学对象将这些假设正式化。

在这一点上,我们应该指出,因果关系领域传统上假设因果模型是由人类先验给出的。此外,由基本因果模型生成的因果变量被假定为可直接观察的。然而,这些假设通常是不现实的。
的确,在某些领域,我们的知识还处于胚胎状态,以至于我们对世界如何运转毫无头绪。此外,现实世界的观察通常不会被结构化为因果变量单位。例如,首先需要提取图像中允许因果推理的对象。因此,随着机器学习超越了符号规则假设,不需要先验地给出符号,因果关系的新兴领域将努力学习现实世界现象的因果模型,并以自动的方式从现实世界的观察中发现它们的因果变量单元。毕竟,未来配备了因果推理机的强人工智能机器应该能够对世界做出一些假设,并在获得更多经验后对其进行微调。

这些缺点可以通过受益于机器学习的进步来解决。事实上,从非结构化原始数据中发现因果变量并连续学习底层因果模型都是以数据为中心的操作,而机器学习擅长于此。此外,现代机器学习技术可以帮助我们在因果推理机的统计估计步骤中克服维数灾难。所有这些都让我们得出一个结论,如果我们真的想造一个接近人类智能的机器,那么我们需要将因果性和机器学习融合到一个单一的领域: 因果性机器学习

总而言之,要建造一台具有人类思维的机器,我们还有很长的路要走,要实现这一目标,当前人工智能研究趋势的转变是必不可少的。就像人工智能领域通过接受机器学习的进步超越了符号规则假设一样,现在有必要抛弃纯粹的统计和数据驱动的学习范式,转而采用基于因果的方法。然而,新兴因果领域的工具不足以赋予机器因果思维的天赋。这就是为什么,虽然这两个领域是分开产生和发展的,但因果关系和机器学习需要合并成一个新的有前途的领域,称为因果机器学习。也许,当我们人类开始问自己因果问题时,我们的进化速度惊人地快,一旦我们发现如何成功地将因果关系与机器学习配对,那么 奇点 将指日可待。

建议阅读:

  • j .珀尔和麦肯齐博士(2019 年)。原因之书。企鹅图书。
  • schlkopf,b .,Locatello,f .,Bauer,s .,Ke,N. R .,Kalchbrenner,n .,Goyal,a .,& Bengio,Y. (2021)。迈向因果表征学习 2021。 arXiv 预印本 arXiv:2102.11107
  • schlkopf,b .和 von kugel gen,J. (2022 年)。从统计学习到因果学习。 arXiv 预印本 arXiv:2204.00607
  • schlkopf,B. (2022 年)。机器学习的因果关系。在概率和因果推理:朱迪亚珀尔的作品(第 765-804 页)。

从监督学习到非监督学习:计算机视觉中的范式转变

原文:https://towardsdatascience.com/from-supervised-to-unsupervised-learning-a-paradigm-shift-in-computer-vision-ae19ada1064d

慢慢地从训练过程中去除人类知识的注入

自从现代计算机视觉方法出现以来,这些技术的成功应用只能在监控领域中看到。为了使模型有助于执行图像识别、对象检测或语义分割等任务,人工监督曾经是必要的。在一个重大转变中,过去几年的计算机视觉研究已经改变了该领域的焦点:从人类监督下的保证成功转向新的前沿:自我监督和非监督学习。

以非监督方式对不同类进行聚类的动画。来源:【1】

让我们踏上一个已经开始的新时代的征程。

监督学习的成功

原始 AlexNet 架构的插图。来源:【2】

AlexNet 标志着神经网络应用于图像任务的第一次突破,更具体地说是 ImageNet 挑战。从那时起,游戏开始了,计算机视觉研究界开始致力于完善多种计算机视觉任务的监督技术。

对于图像分类,自最初的 AlexNet 论文以来,已经出现了许多模型的变体。ResNet 已经无可争议地成为卷积神经网络中的经典。诸如 EfficientNet 之类的高效架构已经出现。甚至是针对移动设备优化的网络,如 MobileNet 架构。最近,视觉变压器获得了越来越多的关注(无意的玩笑),并显示出在正确的设置下(大量数据和计算)优于卷积神经网络。最初是为语言任务而发明的,它们在计算机视觉方面的应用取得了巨大的成功。另一个有趣的方法是设计网络设计空间,其中量化的线性函数定义称为 RegNet 的网络架构。

监督学习成功解决的下一个任务是对象检测和语义分割。r-CNN 在第一个领域引起了第一次轰动,随后在计算效率和准确性方面取得了许多进步。值得注意的方法是 Fast、Faster 和 Mask R-CNN,还有 YOLO 算法和单次检测器,如 SSD MobileNet。语义分割领域的一个里程碑是 U-Net 架构。

此外,不要忘记基准数据集使监督技术更具可比性。ImageNet 为图像分类设立了标准,MS COCO 对于对象检测和分割任务仍然很重要。

所有这些技术都有一个共同点:它们依赖于经过提炼的人类知识和技能,以标记数据的形式表现良好。事实上,他们是围绕这个资源建立的,并依赖于这个资源。

在某种程度上,所有这些技术都采用模拟人类生物神经网络的人工神经网络。但是,这些模型学习感知的方式与人类学习感知的方式非常不同。为什么只模仿人脑的生物形态,而不模仿学习识别和分类背后的认知过程?

这就是下一次进化的切入点:自我监督学习。

将自我监督引入流程

想想你是如何学会观察的。你如何学会识别苹果?当你年轻的时候,你见过许多苹果,但并不是所有的苹果上都有一个标志,上面写着“这是一个苹果”,也没有人告诉你每次你看到一个苹果时它就是一个苹果。你学习的方式是通过相似性:你一次又一次地看到这个物体,每周多次,甚至每天。你认出来了:哎…这是一回事!

然后,有一天,有人教你这是苹果。突然间,这个抽象的物体,这个视觉表现,现在变成了你所知道的“苹果”。这是在自我监督学习中使用的类似过程。

SimCLR 培训过程的图示。来源:【3】

最先进的技术如 SimCLRSwAV 复制了这一过程。对于预训练,所有标签都被丢弃,模型在不使用人类知识的情况下进行训练。向模型显示同一图像的两个版本,图像可能被裁剪、颜色扭曲或旋转,它开始学习尽管它们的视觉表示不同,但这些对象是相同的“东西”。事实上,这在它们相似的潜在向量表示中是可见的(记住这一点以备后用)。因此,该模型学习为每一类对象产生一个一致的矢量。

接下来是“教学”步骤:这次向预训练的模型显示一些带有标签的图像。它可以更快更有效地学习对不同种类的物体进行分类。

如此多的人类知识已经从训练过程中移除,但不是全部。但下一步就在眼前。

走向无监督学习

为了使一个模型完全不受监督,它必须在没有人类监督(标签)的情况下进行训练,并且仍然能够完成预期的任务,例如对图像进行分类。

请记住,自我监督模型已经朝着这个方向迈出了一步:在向它们展示任何标签之前,它们已经能够为不同的对象计算一致的矢量表示。这是去除所有人类监督的关键。

以非监督方式对不同类别进行聚类的示例。来源:【1】

这个向量通常表示的是维数减少的图像。事实上,自动编码器可以被训练来重建图像像素。因为它的降维,我们可以使用一种在计算机视觉中长期被忽略的技术:k 近邻分类器。如果我们的向量表示很好,只有相同的对象形成一个簇,而不同的对象在远处聚集,我们可以给模型一个新的未知图像,模型会将它分配到正确类别的簇中。模型将不能告诉你类名是什么,但是它属于哪组图像。如果为该组指定一个类名,则该组中的所有对象都可以被分类。毕竟类名是人类人工创造的(有人定义这个东西叫苹果),只是被人类赋予了意义。

由于所有标签都从训练过程中移除,并且像 DINO 这样的论文中的结果非常有希望,这是我们从计算机视觉模型的训练过程中移除所有监督的最接近的方式。

但是还有更多的事情要做,还有更大的提升空间。

包装它

如果你一直读到这里,我非常感谢你花时间阅读。我特意没有在这个故事中加入任何图片,因为它们会转移你对这篇文章的注意力。我是说,我们都想成为一个好的变形金刚,对吧?(这次是有意的)

衷心感谢您阅读本文。如果你对自我监督学习感兴趣,可以看看我的其他故事,在这些故事中,我试图向任何感兴趣的人解释这个领域中最先进的论文。如果你想在高级计算机视觉领域更深入地探索,考虑成为我的追随者。我试着每周发一篇文章,让你和其他人了解计算机视觉研究的最新进展。

参考资料:

[1] Meta AI 研究博文。https://ai . Facebook . com/blog/dino-paws-computer-vision-with-self-supervised-transformers-and-10x-more-efficient-training/

[2] Krizhevsky、Alex、Ilya Sutskever 和 Geoffrey E. Hinton。"使用深度卷积神经网络的图像网络分类."神经信息处理系统进展25(2012):1097–1105。https://proceedings . neur IPS . cc/paper/2012/file/c 399862d 3 b 9d 6 b 76 c 8436 e 924 a 68 c 45 b-paper . pdf

[3]陈,丁等:“视觉表征对比学习的一个简单框架。”机器学习国际会议。PMLR,2020 年。https://arxiv.org/pdf/2002.05709.pdf

从技术项目经理到机器学习工程师

原文:https://towardsdatascience.com/from-technical-project-manager-to-machine-learning-engineer-ecb185c16968

差距、陷阱和技巧

图片由作者提供;它只显示普通路径,而不是所有可能的路径;

这是一个具有挑战性的转变,需要付出巨大的努力,而人工智能/机器学习对于组织来说仍然是一个时髦的词,数据团队通常是第一个被解雇的人。另一方面,人工智能世界正在快速发展,从 AlphaFold 、文本到图像(稳定扩散 / Dall-E 2 )到多模态机器学习的突破性进展,人工智能的采用继续增长,收益仍然显著。

我在两年半的时间里过渡到了机器学习工程师,并从 2020 年开始担任商业分析硕士候选人的行业导师。这篇文章中分享的陷阱和技巧是基于我的经验和来自校友、同事和各行各业演讲者的见解。

希望我的故事能帮助激励你的转变,并指导你的决定。

职业转型前

我的职业生涯始于一家跨国电信公司的系统集成工程师,最终成为一名高级技术项目经理,后来我决定辞职,作为联合创始人加入一家初创公司,建立一个金融科技 SaaS 解决方案。

新的旅程

我的家人在 2017 年移居澳大利亚,和许多新移民一样,这意味着挑战和机遇,让你重新审视自己的职业规划。自 2013 年以来,我一直关注人工智能和云的发展,我相信云和人工智能,因为它们已经开始全面重塑行业,而这只是一个开始。

缺口

知识差距显然很大,所以不考虑非全日制学习。我一直在寻找一个密集、实用、核心、贴近业务且不超过 1 年的过渡。

知识缺口,先于商业分析硕士学位

墨尔本商学院的全日制商业分析硕士 (MBS)是最合适的人选:

按作者分类的图片,工作数据/业务分析的类型可能导致

  • 核心和实用课程——统计学习、预测分析、决策和优化、机器学习、编程以及如何在商业中应用数据分析。
  • 高强度的快节奏训练,速度比正常训练快 3 倍,将你推向极限。
  • 为期 5 周的团队实习,整合了学术学习和在组织中实施数据分析的实际挑战。
  • 非凡而聪明的候选人,大多数在知名咨询公司、金融机构或财富 500 强公司开始他们的数据职业生涯。

该计划为您的职业生涯建立了坚实而广泛的数据分析基础,更不用说强大的校友社区,因为许多校友已经开始成为行业领导者。

从英国帝国理工学院商学院到美国麻省理工学院索兰商学院,全球各地的商学院都在提供商业分析硕士课程。

如果你正在寻找一个密集和高质量的过渡,并且已经拥有业务领域的知识,这条道路可能很适合你作为起点。

行业差距,张贴商业分析硕士学位

我的数据职业生涯始于在澳大利亚一家领先的数据咨询公司担任数据顾问,这确实加速了我在该行业的职业生涯,并弥合了“Jupyter 笔记本中的结构化和静态数据”与“云中的非结构化和海量数据”之间的差距。

除了从客户项目中获得经验,破解认证/在线课程是获得丰富行业知识和实践的一种高效方式。我在两年内破解了 19 个证书/在线课程,以下是一些强烈推荐的证书:

图片由作者提供;每个工作范围的认证;

破解认证并不意味着你是该领域的主题专家,所以不要高估它的价值。然而,这些证书确实有助于建立对现代数据/机器学习解决方案的整体理解。凭借强大的云工程和数据分析技能,我后来从数据顾问转型为机器学习工程师。

这不是关于证书,而是关于你在这个行业中的知识/经验差距的意识,并成为一个快速的学习者,用你自己的策略缩小差距。

陷阱:

注意事项:本节分享的见解可能有失偏颇。

  • 头衔膨胀:许多数据科学家头衔的工作与建模、模型分析或特征工程无关,但面向数据分析师(仪表板报告、即席 SQL 查询等)。拥有强大数据科学背景的候选人(尤其是博士候选人)可能会对那些职位感到失望和失落。如果你有很强的领域知识并接近业务部门,成为一名数据分析师是一个非常有吸引力的职业。但我假设大多数数据科学或博士毕业生更喜欢机器学习建模/实现——了解现实中的工作范围,并妥善管理你的期望。
  • 机器学习建模工作很少,由机器学习驱动的组织通常实施成熟的行业级模型/解决方案。数据工程、系统设计和软件开发技能远比构建自己的模型更重要(你团队的模型击败正在推动人工智能军备竞赛的大型技术公司的尖端模型的可能性很低)。而是作为一名数据科学家或机器学习工程师,你的主要职责是:提高数据质量(与数据工程师团队一起);识别用例(与业务涉众一起);设计和实施基于云的机器学习端到端解决方案(与软件工程师 DevOps 一起)以证明用例——远离 Jupyter 笔记本并学习软件开发实践。
  • 对利益相关者的 AI 101 教育是必不可少的,不是因为他们是项目发起人,而是更重要的是,他们需要了解人工智能的局限性,而不是将其视为解决一切问题的神奇技术——在许多情况下,你不需要花哨的机器学习模型,企业需要可解释的人工智能。
  • 数据团队通常处于第一波裁员中,尤其是数据分析师、数据科学家和机器学习工程师,而数据工程师相对更安全——数据科学家可能不是“21 世纪最性感的工作”,不幸的是,至少目前不是。

小贴士:

注意事项:本节分享的小技巧可能会有偏差。

  • 避免头衔膨胀陷阱的一个技巧是在面试时问这个职位是与机器学习产品化相关还是与内部报告相关。
  • 云知识(AWS/Azure/GCP)是非常可取的,无论你的工作角色如何,它都是必备的,而大多数数据科学/分析项目都不涉及它。对于应届毕业生和大三学生来说是一大利好。
  • 坦诚面对自己的优势和劣势,从第一天开始规划自己的数据职业生涯。

按作者分类的图像,按角色分类的主要工作范围

  • 个人品牌很重要,知识分享也很贴心——建立你的影响力要从分享你的知识开始,越早越好。
  • 该行业正朝着以数据为中心的人工智能方法发展,数据工程和 MLOps 将更加关键。垃圾进,垃圾出。我们最近通过 8% 改进了一个模型在生产中的性能——通过简单地修复训练集中不一致的数据注释。

以下是帮助我转型的书籍/博客列表:

技术

  • 使用 Scikit-Learn、Keras 和 TensorFlow 进行机器实践学习
  • 用 Chip Huyen 设计机器学习系统
  • 系统设计访谈—内部人员指南:第 2 卷
  • 彼得的数据科学家实用统计学。b 安德烈。B&·彼得。G
  • 设计数据密集型应用程序:可靠、可伸缩和可维护系统背后的重要思想
  • AWS 解决方案库由 AWS

软技能

最终想法:

数字化转型和人工智能的采用仍处于早期阶段,人工智能项目的高失败率并不新鲜。释放数据的价值并实现成功的 ML 项目需要对强大的基础进行投资,特别是用例一致性、文化转变、人工智能教育和流程变革管理,这实际上比实施人工智能技术本身更重要。对于那些拥有商业领域知识并随后转向数据职业的人来说,除了你的技术技能之外,培养“分析翻译”技能可以让你更具竞争力,正如我所观察到的那样,它们对成功的 ML/AI 项目更为关键。

[1]报道于 IBM优步Airbnb 和 Sejuti Das 的分析《数据科学家如何也容易受到危机中裁员的影响》、Analytics India Magazine ,2020 年 5 月 21 日、https://oreil.ly/jobmz

[2]2021 年的 AI 状态

https://www . McKinsey . com/capabilities/quantum black/our-insights/global-survey-the-state-of-ai-in-2021

[3]与 Andrew 就 MLOps 的对话:从以模型为中心到以数据为中心的人工智能

https://www.youtube.com/watch?v=06-AZXmwHjo

从理论到实践用贝叶斯神经网络,用 Python

原文:https://towardsdatascience.com/from-theory-to-practice-with-bayesian-neural-network-using-python-9262b611b825

以下是如何用几行代码将不确定性融入你的神经网络

Unsplash 上由Towfiqu barb huya拍摄的照片

我拥有物理学硕士学位,是一名航空航天工程研究员。

物理学和工程学是两种截然不同的科学,它们都渴望了解自然,并有能力模拟自然。

物理学家的方法更多的是理论上的。物理学家观察这个世界,并试图以尽可能精确的方式对其建模。物理学家模拟的现实是不完美的,有一些近似,但是一旦我们考虑到这些不完美,现实就变得整洁、完美、优雅。

工程师的方法更加实用。工程师意识到物理学家的模型的所有限制,并试图在实验室中尽可能顺利地体验。工程师可能会做更残酷的近似(例如 pi = 3),但它的近似在现实生活的实验中实际上更有效。

戈登·林赛·格莱格的这段话总结了工程师的实践方法和物理学家优雅的理论方法之间的差异

科学家可以发现一颗新星,但他不能制造一颗。他将不得不请一位工程师为他做这件事。

在研究人员的日常生活中,事情是这样的。物理学家是对特定现象有理论的人。工程师是一个科学家,他可以进行实验,看看理论是否可行。

实际上,当我开始从物理学家到工程师的转变时,我经常被问到的一个问题是:

“好吧,你的模型似乎是可行的……但它有多稳健?”

这是典型的工程师问题。

当你有了一个物理模型,给定一定的条件,这个模型理论上是完美的

作者图片

尽管如此,当你进行实验时,还是有一定程度的误差,你必须能够正确地估计它。

作者图片

在这个例子中,我们如何估计理论输出和实验结果之间的能量差?

两个选项:

A.如果模型是确定性的,您可以通过某个增量改变初始条件(例如,将确定性规则应用于输入的噪声版本)

B.如果模型是概率的,对于一些给定的输入,你从输出中提取一些统计信息(例如,平均值、标准偏差、不确定性边界……)

现在让我们进入机器学习的语言。在这种特殊情况下:

A.如果机器学习模型是确定性的,我们可以通过改组训练集和验证集来测试它的鲁棒性。

B.如果机器学习模型是概率,对于一些给定的输入,您从输出中提取一些统计信息(例如,平均值、标准偏差、不确定性边界……)

现在,假设我们要使用的模型是神经网络。
第一个问题:需要神经网络吗?如果答案是肯定的,那么你一定要用(你不说)。问题:

“你的机器学习模型健壮吗?”

神经网络的原始定义是“纯粹确定性的”。
我们可以调整训练、验证和测试集,但我们需要考虑到神经网络可能需要很长时间来训练,如果我们想进行多次测试(假设 CV = 10,000),那么,你可能需要等待一段时间。

我们需要考虑的另一件事是,使用一种称为梯度下降的算法来优化神经网络。这个想法是我们从参数空间中的一个点开始,顾名思义,沿着损失的负梯度指示的方向下降。这将理想地把我们带到一个全球最小值(剧透:它实际上从来不是全球性的)。

不切实际的简单 1D 损失函数的理想情况如下:

作者图片

现在,在这种情况下,如果我们改变起点,我们仍然收敛到唯一的全局最小值。

更现实的情况是这样的:

作者图片

所以,如果我们从不同的起点随机重启训练算法,我们会收敛到不同的局部最小值

作者图片

所以如果我们从点 1 或者点 3 开始,我们会到达一个比起点 2 更低的点。

损失函数可能充满了局部最小值,因此找到真正的全局最小值可能是一项艰巨的任务。我们可以做的另一件事是从不同的起点重新开始训练,并比较损失函数值。这种方法和以前一样,我们也有同样的问题:我们只能做这么多次。

有一种更健壮、更严格、更优雅的方法,以一种概率的方式使用神经网络的相同计算能力;它被称为贝叶斯神经网络。

在本文中,我们将了解:

  1. 贝叶斯神经网络背后的想法
  2. 贝叶斯神经网络背后的数学公式
  3. 使用 Python 的贝叶斯神经网络的实现(更具体地说 Pytorch )
  4. 如何使用贝叶斯神经网络解决回归问题

开始吧!

1.什么是贝叶斯神经网络?

正如我们前面所说,贝叶斯神经网络的思想是给典型的神经网络增加一种概率“感觉”。我们如何做到这一点?

在理解贝叶斯神经网络之前,我们可能应该回顾一下贝叶斯定理。

观察贝叶斯定理的一个非常有效的方法如下:

“贝叶斯定理是一个数学定理,它解释了为什么如果世界上所有的汽车都是蓝色的,那么我的汽车也必须是蓝色的,但仅仅因为我的汽车是蓝色的,并不意味着世界上所有的汽车都是蓝色的。”

在数学术语中,已知事件“A”和“B”,已知事件“B”已经发生,事件“A”发生的概率如下:

作者图片

假设事件“A”已经发生,事件“B”发生的概率如下:

作者图片

连接第一个和最后一个表达式的等式如下:

作者图片

明白了吗?太好了。现在,假设你有了你的神经网络模型。这个神经网络只不过是一组将给定输入转换为期望输出的参数。

前馈神经网络(最简单的深度学习结构)通过将输入乘以参数矩阵来处理您的输入。然后,非线性激活函数(这是神经网络的真实能力)被逐项应用于该矩阵乘法的结果。结果是下一层的输入,其中应用相同的过程。

我们现在将模型的参数集称为 w 。现在我们可以问自己这个棘手的问题。

假设我有一个数据集 D,它是一组成对的输入 x_i 和输出 y_i,例如,第 I 个动物的图像和第 I 个标签(猫或狗):

作者图片

给定某个数据集 D,有一组参数的概率是多少?

这个问题你大概需要看 3、4 遍才能领会,但是思路是有的。如果你在输入和输出之间有某种映射,在极端的确定性情况下,只有一组参数能够处理输入并给你带来想要的输出。在概率方式中,会有一个概率参数集比另一个更有可能。

所以我们感兴趣的是数量。

作者图片

现在,有三件事很酷:

  1. 当考虑给定该分布的平均值时,您仍然可以将其视为标准的神经网络模型。例如:

作者图片

等式的左手代表计算的平均输出,右手代表所有可能的参数结果集(N)的平均值,概率分布提供每个结果的权重。

2.虽然 p(w|D) 显然是个谜,但 p(D|w)是我们可以一直研究的东西。如果我们对一个巨大的 N 使用上面的等式,就不需要机器学习了。你可以简单地说:“尝试给定某个神经网络的所有可能的模型,并使用上面的等式权衡所有可能的结果”

3.当我们得到 p 时,我们得到的不仅仅是一个机器学习模型;我们实际上获得了无限的机器学习模型。这意味着我们可以从你的预测中提取一些不确定性边界和统计信息。结果不仅是“10.23”,更像是“10.23,可能的误差为 0.50。”

我希望我让你兴奋了。让我们进入下一章

2.一些数学

我不想让这篇文章成为闲聊,但我也不想让它成为一种痛苦。如果你对贝叶斯神经网络有所了解,或者你已经知道它们背后的数学原理,可以跳过这一章。如果想有个参考,下面是一个不错的。(动手操作贝叶斯神经网络——深度学习用户教程)

现在这一切看起来很酷,但我认为,如果你是一个机器学习用户,你应该有这样的想法:

"我怎么才能优化这样一个奇怪的生物?"

简单的回答是,“通过最大化:

作者图片

但我不认为这是不言自明的。

在这种情况下,优化原则是找到分布 p(w|D)的最佳估计。我们称这个分布为 q,我们需要两个分布函数之间距离的度量。

我们将使用的指标称为kull back–lei bler散度

作者图片

一些有趣的事实:

  1. 对于两个相等的分布,它是 0
  2. 如果两个分布的分母趋向于零,而分子仍然不为零,那么它就是无穷大
  3. 它是不对称的。

现在,你在上面看到的损失函数是 Kullback-Leibler 散度的替代量,它被称为证据下限(ELBO)

权重 q 的分布被认为是具有均值μ和方差σ2 的正态分布:

作者图片

因此,优化是为了确定该分布的最佳 mu 和 sigma 值。

作者图片

在实际的 PyTorch 实施中,分布的平均值和目标值之间的 MSE 也被添加到我们的 L (mu,sigma)中。

3.Pyt(orch)hon 实施

使用 PyTorch 在 Python 中实现贝叶斯神经网络非常简单,这要归功于一个名为torchbn的库。

安装非常简单,因为:

*pip install torchbnn*

正如我们将看到的,我们将建立一个非常类似于标准 Tor 神经网络的东西:

*model = nn.Sequential(
    bnn.BayesLinear(prior_mu=0, prior_sigma=0.1, in_features=1, out_features=1000),
    nn.ReLU(),
    bnn.BayesLinear(prior_mu=0, prior_sigma=0.1, in_features=1000, out_features=1),
)*

实际上,有一个库可以将您的 torch 模型转换成它的贝叶斯代理:

*transform_model(model, nn.Conv2d, bnn.BayesConv2d, 
                args={"prior_mu":0, "prior_sigma":0.1, "in_channels" : ".in_channels",
                      "out_channels" : ".out_channels", "kernel_size" : ".kernel_size",
                      "stride" : ".stride", "padding" : ".padding", "bias":".bias"
                     }, 
                attrs={"weight_mu" : ".weight"})*

但让我们来做一个实际操作的详细示例:

4.动手回归任务

首先要做的是导入一些库:

之后,我们将制作非常简单的二维数据集:

因此,给定我们的 1D 输入 x(范围从-2 到 2),我们想找到我们的 y

Clean_target 是我们的地面真相生成器,而 target 是我们的噪声数据生成器。

现在我们将定义我们的贝叶斯前馈神经网络:

正如我们所看到的,它是一个具有贝叶斯层的两层前馈神经网络。这将允许我们有一个概率输出。

现在我们将定义我们的 MSE 损失和剩余的 Kullback-Leibler 散度:

这两个损失都将用于我们的优化步骤:

已经用了 2000 个纪元了。

让我们定义我们的测试集:

现在,从模型类得出的结果是概率。这意味着,如果我们运行我们的模型 10,000 次,我们将得到 10,000 个略有不同的值。对于从-2 到 2 的每个数据点,我们将得到平均值和标准偏差,

我们会画出置信区间。

5.包装它

在本文中,我们看到了如何建立一个机器学习模型,该模型结合了神经网络的力量,并仍然保持对我们预测的概率方法。

为了做到这一点,我们可以建立所谓的贝叶斯神经网络。这个想法不是优化一个神经网络的损失,而是优化无限个神经网络的损失。换句话说,我们正在优化给定数据集的模型参数的概率分布。

我们使用了一个损失函数,这个函数包含了一个叫做 Kullback-Leibler 散度的度量。这用于计算两个分布之间的距离。

在优化我们的损失函数后,我们能够使用一个概率模型。这意味着,如果我们重复这个模型两次,我们会得到两个不同的结果,如果我们重复 10k 次,我们能够提取结果的稳健统计分布。

我们使用 torch 和一个名为 torchbnn 的库实现了这一点。我们建立了简单的回归任务,并使用两层前馈神经网络来解决它。

6.结论

如果你喜欢这篇文章,你想了解更多关于机器学习的知识,或者你只是想问我一些问题,你可以:

A.在 Linkedin 上关注我,我在那里发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大数量的故事”,你可以阅读我(以及数千名其他机器学习和数据科学顶级作家)写的任何关于最新可用技术的文章。

从训练到部署:用机器学习停止咬指甲

原文:https://towardsdatascience.com/from-training-to-deployment-stop-biting-your-nails-with-machine-learning-ffed31a59040

了解如何通过几次点击来训练机器学习模型,在简单的 web 应用程序中将其付诸实践,并将其部署到云上与他人共享。

为了完成所有这些,我们将使用 Teachable Machine、ReactJS、GitHub Actions 和开发者沙箱。

照片由帕特里克·福尔Unsplash 拍摄

介绍

Teachable Machine 是一个令人兴奋的工具,它提供了一种简单的方法来训练和测试机器学习模型,而无需该领域的专业知识。数据集可以直接从网络摄像头或麦克风创建,并用于训练图像、声音或姿势分类器。

训练本身是由工具自动完成的,并且在训练完成后可以立即测试生成的模型。如果您对刚刚构建的模型满意,可以将其导出为 Tensorflow 格式,并在应用程序中使用。

这里有一个值得一看的展示可教机器的视频。

现在你已经对可教机器有了更好的了解,我相信你已经绞尽脑汁在想用这样一个神奇的工具可以做的许多应用了。查看这个资源库,它提供了一个用可教机器建造的项目的精选列表。

因此,我想展示如何使用由可教机器训练的模型来构建一个简单的 web 应用程序。这个应用程序也将被部署到云中,使它成为一个更真实的应用程序,并使您能够与其他人共享它。

别担心,我们只会使用免费的东西。此外,一切都将是客户端的唯一,这意味着没有个人数据将流向服务器。

让我们开始吧!

游戏攻略

第一步:考虑一个应用程序

我们的应用将是一个咬指甲探测器

该检测器的目的是在用户工作/学习/玩耍等时通过他们的网络摄像头监控用户,并在手指靠近嘴时发出恼人的警报。希望这将把一个不愉快的触发器映射到用户的大脑中,这将使他们在再次向嘴移动手指时三思而行。

由于我们将构建一个 web 应用程序,并将其部署到云中,因此几乎任何带有摄像头和浏览器的设备都可以用来进行这种令人不安的现代监控。然而,重点将放在坐在电脑前的人身上,即面对他们的网络摄像头,这使得我们的模型更容易构建。

我理解有些人努力摆脱这种强迫性习惯,所以让我们试着建立一些可能帮助他人的东西,对吗?!

第二步:训练机器学习模型

训练我们的咬指甲探测器用可教的机器很容易。

使用网络摄像头,我们需要收集两类图像:(1) 咬指甲类,由手指靠近嘴的图像表示;(2) 正常类,由手指离开嘴的图像表示。

因此,一旦您开始使用可示教机器(转到开始图像项目标准图像模型),继续按下录制按钮,直到您每节课获得大约 500 张图像,这对该分类器来说应该绰绰有余。

在收集图像时改变位置(主要是手和脸)以在数据集中创建多样性是很重要的。此外,在 nnormal类中包括一些没有人在镜头前的图像,只是为了确保模特明白没有人在镜头前意味着一切都很好。你甚至可以创建一个 nobody 类!

使用从网络摄像头拍摄的图像构建训练数据集

一旦收集了数据集,就只需要训练和测试模型了。如果结果看起来不错,那么将训练好的模型导出为 Tensorflow.js 格式。输出将是一个 zip 文件,包含两个 JSON 文件(model.jsonmetadata.json)和权重(weights.bin)。

测试得到的训练模型

注意:请记住,最终的训练模型非常简单,可能最适合在创建数据集时记录的同一个人,并且条件相似。然而,你可以通过更精细的训练来改进它,比如收集更多的数据和调整训练超参数。

步骤 3:创建 web 应用程序

我不打算从头开始实现 ReactJS 应用程序,因为这不是本文的目标。相反,我已经为我们的 web 应用程序准备了样板代码。这是一个简单的 ReactJS 应用程序,我称之为可教机器游乐场,在这里你可以很容易地添加你的新页面来练习你的新模型。

代码是开源的,可以在 GitHub 库上获得,供你克隆或派生。您将发现一个简单的 ReactJS 应用程序,其中包含一些随时可以使用的组件。脚本和 GitHub 工作流也可以让生活变得更简单。

在继续之前,克隆并构建应用程序。确保已经安装了 Node.jsYarn 。您可以简单地在应用程序的根文件夹中运行yarn install && yarn build:dev来构建代码。此命令将安装所有尚未安装的依赖项,并在开发模式下构建代码。

现在让我们看看如何向代码中添加一个新的模型。

3a)添加训练好的模型文件

解压缩从可示教机器中导出的文件,并将其放在/static/models文件夹下的新文件夹中。

在我的例子中,我创建了一个名为nail-biting-detector 的文件夹,并将模型文件放在那里。

添加训练好的模型文件

3b)配置路线

寻找Routes.tsx文件(/src/app/Routes.tsx)。您必须分别向与您的新页面路径和模型文件相对应的navmodels对象添加条目。这些条目将允许您为您的模型创建一个新的路由页面,并使其易于访问模型文件。

在我的例子中,我用nbd键添加条目,这是nail-biting detector的缩写。当所有步骤完成后,我可以通过/nail-biting-detector路径访问我的新页面。

为页面和模型文件创建路线

3c)设置型号信息

在将模型文件添加为应用程序的一部分并相应地配置了路线之后,让我们创建一个对象,该对象将包含一些关于模型的有用信息,并使前面的步骤中加载模型变得容易。

查找Model.tsx文件(/src/app/Model.tsx)并导出一个ModelDescriptor常量。在我的例子中,我用与咬指甲检测器模型相关的信息添加了NAIL_BITING_DETECTOR_MODEL常量。

设置模型信息(仅显示代码的相关部分)

3d)添加模型页面

既然路线已经准备好了,您只需要在/src/pages文件夹下实现页面的逻辑。我已经准备了一些组件和挂钩,以便于开发使用网络摄像头和火灾通知的页面。

在我的例子中,当检测到类nail-biting时,页面NailBitingDetectorPage简单地呈现网络摄像头并发出烦人的通知(浏览器和声音)。很简单,是吧?!

注意:实施的通知行为可能因操作系统/浏览器而异。我已经在 Ubuntu/Chrome、Android/Chrome、Windows/Chrome 和 macOS/Safari 上验证过了。

创建 NailBitingDetectorPage(仅显示代码的相关部分)

最后,你需要去App.tsx ( /src/app/App.tsx)和地图的路线与网页,以便它可以访问。

用 NailBitingDetectorPage 映射路线(只显示代码的相关部分)

3e)构建并运行

一旦前面的步骤都完成了,你就可以开始尝试了!

在应用程序的根文件夹中执行yarn install && yarn build:dev来构建代码。此命令将安装所有尚未安装的依赖项,并在开发模式下构建代码。

然后执行yarn start启动开发服务器,在浏览器中打开 https://localhost:9001 。您甚至可以在连接到同一网络的其他设备上加载应用程序,但您需要使用您的 IP 地址,而不是localhost

可教机器游乐场主页

注:每次添加新路线,都会在主页上显示为要导航到的链接。

步骤 4:构建并推送容器映像

对于这一步,您必须用yarn build:prod构建生产代码,用yarn build:image构建容器映像,并将其推送到 quay.io 或 docker hub 等注册中心。

我使用podman来构建图像,但是如果你喜欢的话,也可以随意使用docker。如果你不使用podman,你将不得不对yarn build:image脚本(/package.json)做一些小的改动。

要自定义映像注册表、名称或标记,您可以在构建映像之前导出以下预定义的环境变量:

IMAGE_REGISTRY (default value: quay.io/caponetto)

IMAGE_NAME (default value: teachable-machine-playground)

IMAGE_TAG (default value: latest)

如您所见,使用默认值将准备一个名为teachable-machine-playground的容器图像,该图像将被推送到我的quay.io帐户,标签为latest,这里的是可用的

如果您计划将代码推送到您的 GitHub 帐户,我还准备了一个 GitHub 工作流(/.github/workflows/publish.yml),它构建并将容器映像推送到quay.io。您只需定制环境变量,并添加 quay.io 密码作为存储库密码(REGISTRY_PASSWORD)。

将容器图像推送到 quay.io 的环境变量(仅显示代码的相关部分)

步骤 5:将您的 web 应用程序部署到开发人员沙箱中

现在,让我们使用已经发布到 quay.io 的容器映像,在 Red Hat OpenShift 的开发人员沙箱(或简称为开发人员沙箱)上创建一个应用程序。

如果你不熟悉开发者沙盒,这里有一段来自官方网站的引言,它几乎概括了它的所有功能:

沙盒在一个共享的多租户 OpenShift 集群中为您提供了一个私有的 OpenShift 环境,该集群预先配置了一组开发人员工具。您可以轻松地从源代码或 docker 文件创建容器,使用提供的示例和堆栈构建新的应用程序,从我们的模板目录添加数据库等服务,部署 Helm charts,等等。

此外,如果您想了解更多,请查看我的一次演讲,我在演讲中展示了如何将开发者沙箱与托管的卡夫卡和 DMN 连接起来。

一旦您登录到您的开发人员沙箱实例,转到Developer透视图并单击Add。您将看到创建应用程序的各种方法。然后点击Container images卡,在表格中填入适当的信息。

您必须提供图像 URL 并确保您的路线在 HTTPS 可用(移动通知仅适用于 HTTPS)。下面的 GIF 详细展示了怎么做。

将容器映像部署到开发人员沙箱

或者,您可以使用我之前提到的 GitHub 工作流(/.github/workflows/publish.yml)来部署您的映像。在这种情况下,您必须设置与您的 OpenShift 实例(OPENSHIFT_SERVEROPENSHIFT_TOKEN)和名称空间(OPENSHIFT_NAMESPACE)相关联的秘密。

注意:来自开发者沙箱的令牌每天到期。

部署到 OpenShift 的环境变量(仅显示相关的代码部分)

一旦这些信息准备就绪,您就可以在触发发布工作流时选择“部署”复选框。除了构建 web 应用程序并将容器映像推送到 quay.io,工作流还将在 OpenShift 实例上部署应用程序。自动化❤

好了,我们快到了!既然已经从容器映像创建了应用程序,那么只需等待几秒钟,应用程序就会启动。一旦准备就绪,您就可以访问和测试您的应用程序了。

测试部署的应用程序

正如你所看到的,当我把手放在嘴边时,浏览器会触发一个桌面通知(部分显示在 GIF 的顶部)。你听不到,但是浏览器也会发出烦人的声音警报。

如果您构建了这个应用程序,并且想要监控您自己,请让这个浏览器窗口保持打开,正常地做您的事情。如果你走神,把手放在嘴附近,你会收到通知。😄

重要提示:同样,如果您打算将该应用程序用作您的个人咬指甲探测器,请确保在专用浏览器窗口中加载该应用程序。只要不最小化应用程序窗口,您就可以正常使用电脑(在应用程序窗口上方打开其他窗口)。如果您最小化浏览器窗口或在同一个浏览器窗口中切换选项卡,网络摄像头将停止拍摄帧。

注意:开发者沙箱可以让你的应用程序运行 8 个小时。过了这段时间,你必须再次扩大规模。

结束语

在这篇文章中,我展示了创建包含从机器学习模型派生的功能的 web 应用程序是多么容易。我打算激励人们去思考机器学习模型的适用性,而不是把时间花在编码本身上。这就是为什么我选择了 Teachable Machine 并提供了样板代码(但尽可能保持简单)。

希望您可以运用一些想法,并使用这些工具轻松创建概念证明。如果它们变得适用,那么您可以将更多的时间集中在编码部分(训练一个更高级的模型和构建一个好看的应用程序)。

我会试着想出更多的应用程序,并将它们添加到。此外,拥有桌面版和移动版的 playground 也不错,这样我们就可以拥有操作系统的全部功能。

也许我们可以在打响指、拍手甚至做一些手势时触发设备上的某些东西?你放在抽屉底部的那部旧手机可以变成一个监控管家,对你做的一些手势或声音做出反应!自动化工作并节省时间是很棒的,不是吗?

所以一定要尝试你的想法并分享结果!

注意:如果您发现了任何问题或想要对代码做出贡献,请不要犹豫,提出问题、讨论或请求。你将非常受欢迎!另外,这是我的个人网站,你可以在那里找到我的社交媒体链接来联系我。

今天到此为止。感谢阅读!😃

从模糊到有价值—数据从业者的见解

原文:https://towardsdatascience.com/from-vague-to-value-data-science-analytics-practitioner-insights-fed92a4bda08

作为数据专业人员,我们都想解决很酷的数据问题,并在这些项目中取得成功。然而,令人惊讶的是,当你从学校进入行业时,酷的定义和衡量成功的标准也发生了变化。当我们从在受控环境(如学校、训练营等)中从事数据项目时,就会发生范式转变。)到处理现实世界中的数据项目。凭借我多年的数据专业经验,参与各种对话,并担任有抱负的数据科学家的导师,我想在本文中分享一些从业者的见解。

1.数据是答案,但问题是什么?

数据专家不断地重新评估,不仅是我们是否正确地解决了问题,更重要的是,我们是否正确地解决了问题。在学校,总有人知道问题;这个问题很明确,至少在老师看来是这样。然而,我们的利益相关者很少确切地知道需要做什么。他们通常会带着担忧或希望来找你,希望你提供数据背景,填补空白,必要时进行反驳,并形成整体的问题陈述。我们的工作是将模糊的想法转化为可量化的问题陈述,然后再转化为数学语言。

一旦你得出一个可量化的问题陈述,这也不是路的尽头。可用数据很可能不支持您最初设想的分析类型(缺失值、隐藏的混杂变量、稀疏特征、稀疏数据点等)。).像这样的情况会进一步调整和完善你的问题陈述。

适应性、实用性和适应模糊性是数据科学家工具箱中最有价值的三项技能。

2.价值>准确性

数据科学的目标不是寻找和调整最准确的机器学习模型;目标是为组织提供价值。价值可以定义为金钱、时间、客户商誉、市场信任等。我们与业务和产品利益相关方合作,了解业务需求并量化 。通常,您会发现简单性和可解释性比模型的准确性和复杂性更受重视,因为前者与降低风险和增加部署模型成功的信心相关。

在学校里,你被鼓励学习和尝试越来越先进的技术来优化准确性,而在解决实际数据问题时,你必须找到,有时提倡,在准确性与成本或准确性与花费的时间之间进行权衡。技术的复杂性需要与技术解决问题的能力以及它为业务提供的价值相平衡。

认识到机器学习是解决方案,而不是解决方案,这一点很重要。在许多情况下,多变量统计分析、基于启发式的 case 语句和行为状态机等成本较低的技术可以提供我们想要的结果。

3.更多!=更好

数据的稀疏性给我们带来了许多众所周知的挑战。然而,很少被讨论的是,丰富的数据并不一定使分析或分析师的生活变得更容易。

在现实世界的数据科学应用中,选择正确的数据和正确的数据量至关重要。在学校项目中,我们通常试图获得尽可能多的数据,并从这些数据中提取尽可能多的信息,因为(a)这是我们获得额外学分的原因,(b)如果这是一个基于研究的项目,探索越多越好。

然而,在现实世界的数据项目中,我们在业务和产品的约束下运作,时间和金钱是其中的两个,并且关注的是效率而不是完整性。限制探索范围和缩小所需数据集的范围是很有价值的技能。添加到分析中的数据越多,分析就变得越复杂。对于数据清洁度、完整性、插补、大数据的分布式处理、代码复杂性、测试要求等问题,复杂性不是线性增长,而是呈指数增长。在复杂性呈指数增长的情况下,价值增益通常是对数的。认识到并停在甜蜜点是至关重要的。

4.当心沉没成本谬论

沉没成本谬误是这样一种现象,即如果一个人已经投资了一项努力,他可能会继续下去,即使很明显放弃会更有益。

数据专业人员处理的问题通常是开放式的,结论也不总是直截了当的。例如,您可能必须优化彼此反向相关的指标。或者你的项目可能朝着一个好的方向发展,但是没有确保足够的价值让涉众再花一个季度在你的工作上。如果你和其他主要利益相关者认为一个项目不再可行,可以继续进行。继续前进并不表示失败或浪费;相反,成熟到为了每个人的最佳利益而放手是专家和领导者的标志。在这些情况下,我们严格审查整个项目,做笔记和学习,与相关利益相关者召开回顾会议,然后继续前进!

数据科学和分析是一个迷人的领域,我们的任务是从模糊的走向有价值的。毫无疑问,这需要专业的技术知识,但更重要的是,这需要超越算法、代码和混淆矩阵的思维定势。数据专业人员的思维模式在模糊和权衡的土地上茁壮成长;不那么死板,更灵活,对每一个假设都持开放态度。我们希望这篇文章有助于您在开始数据科学和分析专业之旅时建立这种心态。

关注作者 @DrBushraAnjum

注意:文章的更新版本已经由内置专家贡献者网络发布。


图像属性:
女工分析数字数据、图表和图形。由 redgreystock 创建的背景矢量—www.freepik.com/矢量/背景

Fugue 和 duck db:Python 中的快速 SQL 代码

原文:https://towardsdatascience.com/fugue-and-duckdb-fast-sql-code-in-python-e2e2dfc0f8eb

使用 Python 和 DuckDB 优化您的 SQL 代码

动机

作为一名数据科学家,您可能对 Pandas 和 SQL 都很熟悉。然而,可能有一些查询和转换,您会觉得用 SQL 而不是 Python 来做比较舒服。

如果你能使用 SQL 查询熊猫数据帧,那不是很好吗…

作者图片

…同时在处理大量数据时还能加速您的代码

这时候 Fugue + DuckDB 就派上用场了。

在上一篇文章中,我展示了如何使用 pandas 引擎通过 SQL 查询 pandas 数据帧。

在本文中,我将向您展示如何使用 DuckDB 引擎来加速您的查询。

为什么是赋格和 DuckDB?

Fugue 是一个 Python 库,允许用户组合 Python 代码和 SQL 命令。这使得用户可以在 Jupyter 笔记本或 Python 脚本中灵活地切换 Python 和 SQL。

默认情况下,Fugue 会将您的 SQL 代码映射到 pandas。但是,当数据大小超过几个 GB 时,使用 pandas 并不理想,因为它:

  • 一次只允许您使用一个内核
  • 创建大量数据的中间副本,这会增加内存使用

Fugue 还允许您使用 Spark 或 Dask 来扩展 SQL 代码。但是,较小的组织可能没有可以向其移植工作负载的集群。

DuckDB 简介

DuckDB 是一个进程内 SQL OLAP 数据库管理系统。在本地机器上,即使是千兆字节的数据,速度也非常快。

因此,FugueSQL 和 DuckDB 的结合允许你使用 SQL 和 Python无缝地加速你的代码

要使用 DuckDB 引擎安装 FugueSQL,请键入:

pip install -U fugue[duckdb,sql] 

建立

首先,我们为赋格导入一些设置函数。这将让我们使用 Jupyter 笔记本中的%%fsql细胞魔法。我们还导入了 DuckDB 引擎。

加载数据

本文将使用币安加密小猫数据集。这可以通过知识共享许可从 Kaggle 下载。

文件夹crypto-binance包含 1000 多个不同的文件,总内存超过 5GB。我合并了这些文件并将新文件保存为raw.parquet

下载完加工文件后,一个文件名raw.parquet会保存在你的本地机器上。从获取新文件的位置开始:

现在我们来比较一下熊猫和 DuckDB 的加载速度。

熊猫

从使用 pandas 加载数据开始:

作者图片

请注意,加载数据花费了我们 10.5 秒。这太慢了。让我们看看是否可以通过使用 FugueSQL 和 DuckDB 来加快这个过程。

赋格+ DuckDB

要在 Jupyter 笔记本单元格中使用 DuckDB 作为引擎编写 SQL,只需在单元格的开头添加%%fsql duck:

作者图片

在上面的代码中,

  • PRINT允许我们打印输出
  • 双括号{{}}允许我们在 SQL 中使用 Python 变量。

上面的代码在 303 毫秒内加载数据!使用 DuckDB 作为引擎比使用 pandas 作为引擎快 34 倍以上。

处理

我们来对比一下熊猫和 DuckDB +神游的数据处理速度。

熊猫

作者图片

赋格+ DuckDB

作者图片

注意 :类似于 SQL 临时表,FugueSQL 允许多个 *SELECT* 语句。这使得代码可以自顶向下阅读,并消除了样板代码。如果未指定 *FROM* ,SQL 语句将使用堆栈中的最后一个数据帧。

观察

我们可以看到用 Fugue + DuckDB 比用熊猫快了差不多 4 倍。用 SQL 编写上面的处理代码也比用 pandas 稍微容易一些。

为什么 DuckDB 快了这么多?

DuckDB 更快,因为它使用了惰性求值。

作者图片

例如,在上面的代码中,PRINT语句默认返回 10 条记录。DuckDB 知道最终结果只需要 10 条记录,所以它只获取那些记录

另一方面,熊猫被急切地处决了。这意味着只有在整个文件加载到后,获取前 10 行的操作才会运行。

但是为什么神游呢?

DuckDB 有自己的 Python API ,为什么要用 Fugue 搭配 DuckDB?

这是因为 Fugue 提供了自定义函数,允许您轻松地与 Python 对象进行交互。在接下来的部分中,我们将学习如何使用这些自定义函数来改进您的 SQL 代码。

使用赋格数据帧

在上面的代码中,这一行

YIELD DATAFRAME AS top_symbols

…输出赋格数据帧并保存为top_symbols

你可以很容易地把top_symbols变成熊猫的数据帧:

作者图片

…或者在另一个 SQL 查询中使用top_symbols:

作者图片

为中间输出指定名称

有时,您可能希望为中间输出指定名称,以便它们可以被同一个 SQL 查询中的其他流程使用。神游允许你使用=给你的中间输出命名:

作者图片

在上面的代码中,我将中间输出保存到src,然后将srctop_symbols连接起来。

Python 扩展

同时使用 Fugue 和 DuckDB 还允许您通过扩展在 SQL 代码中使用 Python 逻辑。让我们来看看这些扩展。

输出

SQL 不允许您绘制输出。然而,我们可以用 Python 创建一个绘图函数,然后在我们的 SQL 代码中使用它。

要使用上面的plot_by功能,只需在plot_by旁边添加OUPUT USING:

作者图片

作者图片

改变

有些函数用 Python 比用 SQL 更容易编写。如果您想使用 Python 转换 SQL 查询的输出,请使用TRANSFORM

要了解这个扩展是如何工作的,首先创建一个名为macd的函数。该函数使用 pandas-ta 来获取时间序列的某个趋势。

我们还将模式提示作为注释(# schema: *,macd:double)添加到函数macd之上,以便 Fugue 可以读取这个模式提示并应用该模式。

现在我们可以使用这个函数来转换查询中的数据:

作者图片

作者图片

酷!我们刚刚使用 Python 函数转换了 SQL 输出。

点击了解更多关于赋格TRANSFORMPREPARTITION的信息。

神游+生产中的 DuckDB

要将 FugueSQL 从 Jupyter 笔记本中取出并放入 Python 脚本中,我们需要做的就是将 FugueSQL 查询包装在一个fsql类中。然后我们可以调用.run()方法并选择一个执行引擎作为"duck"

结论

恭喜你!您刚刚学习了将 FugueSQL 和 DuckDB 一起用作后端,以充分利用本地执行。由于 DuckDB 提供的惰性评估,我们可以在将数据提交给 Pandas 进行进一步分析之前快速预聚合数据,这在 SQL 中是很难做到的。

使用 Fugue 作为界面还可以让我们无缝地使用 DuckDB 的优势。

随意发挥,并在这里叉这篇文章的源代码:

https://github.com/khuyentran1401/Data-science/blob/master/productive_tools/Fugue_and_Duckdb/Fugue_and_Duckdb.ipynb

我喜欢写一些基本的数据科学概念,并尝试不同的数据科学工具。你可以通过 LinkedInTwitter 与我联系。

如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:

💔-tools-to-track-and-visualize-the-execution-of-your-python-code-666a153e435e> [## 3 个跟踪和可视化 Python 代码执行的工具

towardsdatascience.com](/3-tools-to-track-and-visualize-the-execution-of-your-python-code-666a153e435e)

参考

币安加密线。2018–01–16.币安。
CC0:公共领域。从 https://www.kaggle.com/binance/binance-crypto-klines检索到 2022–03–01

全栈数据科学家现在是趋势:这里是你如何成为一个

原文:https://towardsdatascience.com/full-stack-data-scientists-are-trending-right-now-heres-how-you-can-become-one-d5398dff60e7

软件工程和数据工程是你需要温习的两项技能

迈克尔·D 在 Unsplash 上的照片

2019 年,大家都想成为数据科学家。

2020 年,每个人都想成为一名数据工程师。

2021 年,大家都想成为机器学习工程师。

2022 年,事情又回到了原点——几乎是。

现在,公司希望有人能做所有的事情——翻译业务问题,编写生产就绪的代码,开发机器学习模型,设计数据管道,向 C 级高管演示,等等。公司的愿望和需求开始推动下一代数据驱动型技术人员的未来,他们希望在该领域找到一份工作。然而,这一次,公司倾向于更倾向于技术领域软件方面的个人,只是有数据科学的天赋。

这一次,2022 年,公司在找全栈数据科学家。

什么是全栈数据科学家?

我们从未见过这么多全栈数据科学家的招聘广告。但是到底什么是一呢?

一个 f 全栈数据科学家是一个独角兽,他能够履行软件工程师、数据工程师、商业分析师、机器学习工程师和数据科学家的角色,所有这些都包装在一个包中。这些人拥有不同的技能,甚至超过了普通数据科学家的技能,可以成为公司管理数据科学项目整个生命周期的一站式商店。

这种全生命周期方法意味着全栈数据科学家能够识别业务需求(或与 C 级高管合作确定需要解决哪个问题),建立项目所需的数据架构,分析数据和构建模型,并最终将模型部署到生产环境中。

从本质上讲,这个人是一个单人数据科学团队,可以满足一家小公司的所有数据需求。

全栈数据科学家与数据科学通才有何不同?

全栈数据科学家可能比你想象的要简单一点。

本质上,大多数有前途、有经验的数据科学家都具备成为全栈数据科学家所需的大部分技能。

让全栈数据科学家与众不同的一点是他们的软件和数据工程技能。这就是数据科学通才和全栈数据科学家的不同之处。数据科学通才将在多个领域拥有多种技能(如果你愿意,可以称之为多面手),但可能不具备在整个团队中执行端到端工作的丰富经验。

公司不再需要为他们最初的目的雇佣数据科学家,而是希望数据科学家能够带来各种各样的技能。这导致数据科学家希望通过扩展他们的数据和软件工程技能来适应现在摆在桌面上的所有工作要求,从而变得更有影响力。

如何成为一名全栈数据科学家

全栈数据科学家拥有普通数据科学家的所有基本技能,还具备强大的数据和软件工程技能。

在基础层面,全栈数据科学家将具备解决任何一般数据科学问题所需的数学、分析、设计和编码技能。这些基础知识超出了本文的范围,但是可以在这里找到更多信息:

从那里,数据科学家可以扩展他们的数据和软件工程技能,成为完整的包。

软件工程

最容易提高的技能是软件工程。所有这些都需要你写出比现在更好的代码。

全栈数据科学家需要了解的软件工程围绕着能够端到端地进行数据项目,这意味着您可以在最后将它发布到生产环境中。这将包括开发模块化、文档化和自动化测试的技能。

模块化指的是编写代码,将它的功能分成独立的、可互换的模块。这些模块应该被分成可访问的类和函数,允许你写一次代码,提高你的代码的性能,并保持你的代码文件小且易于导航。

提高软件工程技能的下一步是学习如何编写好的代码文档。令人惊讶的是,将新代码集成到现有的生产环境中,却没有代码文档来帮助您清理混乱,这种情况非常普遍。好的代码文档很容易创建,并且围绕着突出逻辑中的任何关键点,提前排除故障,并且通常给出代码做什么以及它应该如何工作的良好概述。

提高软件工程技能的最后一步是培养自动化测试的感觉。与普通测试相比,普通测试包括手动运行代码,并在每次输入一段逻辑时查看是否会抛出错误,自动化测试是使用为您执行这些任务的工具来执行的。您可以执行的自动化测试类型包括单元测试、冒烟测试、集成测试、回归测试、API 测试、安全测试、性能测试、验收测试等等。您将熟悉的一些工具包括 Selenium、LambaTest 和 QMetry Automation Studio。这里有一个很棒的视频,可以帮助你开始自动化测试:

数据工程

数据工程是你在称自己为全栈数据科学家之前需要提高的另一项技能。

数据工程涉及“设计和构建收集、存储和分析大规模数据的系统”。更广泛地说,这可以扩展到包括获取数据集,开发算法来清理数据,创建数据验证模型,确保符合数据安全政策,等等。

数据工程围绕着使用编程、数据库、分布式系统和云工程技能的组合来开发数据管道,这对一个好的数据科学项目来说是至关重要的。

数据工程技能,就像软件工程技能一样,可以通过免费的在线课程轻松掌握。获得全面的数据工程教育的最佳来源是 DataTalks 的免费在线课程。名为数据工程 Zoomcamp 的俱乐部。本课程将带您了解基础知识,从设置您的环境到学习工作流程编排,再到在本地和云中创建数据管道。还涵盖了数据仓库、批处理和端到端项目。

https://github.com/DataTalksClub/data-engineering-zoomcamp

订阅将我的故事直接发送到您的收件箱:故事订阅

请成为会员,使用我的推荐链接获得无限制的媒体访问权限(我将收取少量佣金,无需额外费用):媒体会员

通过捐赠来支持我的写作,以资助更多像这样的故事的创作:捐赠

从浓缩咖啡中充分提取咖啡

原文:https://towardsdatascience.com/fully-extracting-coffee-from-espresso-e2310dc6c289

咖啡数据科学

为另一个实验做准备的有趣数据

当我在准备一个实验时,我很好奇我是否能完全取出一个冰球。我的目标是为另一个实验生产废咖啡,但以前的实验总是显示一些可溶物仍然存在。所以我在制作这个用过的咖啡时做了一些测量,并在圆盘中放了很多水。

设备/技术

意式咖啡机 : 像样的意式咖啡机

咖啡研磨机:利基零

咖啡:家庭烘焙咖啡,中杯(第一口+ 1 分钟)

镜头准备:断奏夯实

预灌注:长,约 25 秒

输液:压力脉动

过滤篮 : 20g VST

其他设备: Atago TDS 计Acaia Pyxis 秤

发射

我开了一枪:

所有图片由作者提供

然后,我以 2、4、6 和 8 毫升/秒的流速,分四个阶段倒入 3 个杯子中。

结果看起来像奶油。

三杯及其级数

最终产品看起来像几瓶淡啤酒。

冰球看起来很奇怪。通常,颜色是均匀的,但是现在,上面的颜色比下面的颜色深。

倒置的用过的冰球。

所以我把这个冰球放回篮子里,并让更多的水通过它。

当然流出的水少了。

所以我把冰球拿出来,混在一起,然后把它塞回篮子里。

所有这些都是为了从咖啡中提取所有的可溶物。

然而,结果却很奇怪。

收集数据

我收集了每个阶段的 TDS。

这个测量可以告诉我们提取了多少咖啡。通常情况下,30%的咖啡是可溶的,但更多的咖啡不断从圆盘中流出。

我想过继续,但是我不需要为了我的实验而继续。我降到了 0% TDS,我想进行更大的实验。然而,这个数据是如此有趣和引人注目,因为它违背了我所假设的最大提取量。数据还表明,有微通道导致一些咖啡碎片没有暴露在足够的水流中进行提取。

如果你愿意,可以在推特、 YouTubeInstagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在订阅

我的进一步阅读:

我未来的书

我的链接

浓缩咖啡系列文章

工作和学校故事集

Python 中的函数包装器:模型运行时和调试

原文:https://towardsdatascience.com/function-wrappers-in-python-model-runtime-and-debugging-243da483b9d6

使用函数包装器进行机器学习

图片由Acharaporn Kamornboonyarush像素上拍摄

函数包装器是修改函数行为的有用工具。在 Python 中,他们被称为装饰者。Decorators 允许我们扩展函数或类的行为,而不改变包装函数的原始实现。

decorators 的一个特别有用的应用是用于监控函数调用的运行时,因为它允许开发人员监控一个函数成功执行和运行需要多长时间。这一过程对于管理时间和成本等计算资源至关重要。

函数包装器的另一个应用是调试其他函数。在 Python 中,定义打印函数参数和返回值的调试器函数包装器非常简单。这个应用程序对于使用几行代码检查函数执行失败的原因非常有用。

Python 中的 functools 模块使得定义定制装饰器变得容易,它可以“包装”(修改/扩展)另一个函数的行为。事实上,正如我们将看到的,定义函数包装器与在 Python 中定义普通函数非常相似。一旦定义了函数装饰器,我们只需在想要修改或扩展的函数之前的代码行中使用“@”符号和包装函数的名称。定义定时器和调试器函数包装器的过程遵循类似的步骤。

在这里,我们将考虑如何为简单分类模型定义和应用函数包装器来剖析机器学习模型运行时。我们将使用这个函数包装器来监控简单机器学习工作流中的数据准备、模型拟合和模型预测步骤的运行时。我们还将看到如何定义和应用函数包装器来调试这些相同的步骤。

我将使用 Deepnote ,这是一个数据科学笔记本,它使管理机器资源变得容易,并提供各种数据科学工具之间的无缝切换。这些特性使得运行可重复实验变得简单。我们将使用虚构的电信客户流失数据集,该数据集在 Kaggle 上公开。数据集在 Apache 2.0 许可下可以自由使用、修改和共享。

监控机器学习工作流的运行时间

让我们通过一个例子来看看这个过程是如何工作的。

数据准备

让我们通过导航到 Deepnote 平台开始数据准备过程(如果您还没有帐户,注册是免费的)。让我们创建一个项目。

作者截图

并将我们的项目命名为 function_wrappers,将我们的笔记本命名为 profiling_debugging_mlworkflow:

作者截图

让我们将数据添加到 Deepnote:

作者截图

我们将使用熊猫图书馆来处理我们的数据。我们来导入一下:

import pandas as pd

接下来,让我们定义一个函数,我们称之为 data_preparation:

def data_preparation():
  pass

我们来补充一些基本的数据处理逻辑。该功能将执行五项任务:

  1. 读入数据
  2. 选择相关列
  3. 该函数将接受列名列表作为输入
  4. 清理数据
  5. 指定列数据类型
  6. 为培训和测试拆分数据
  7. 该函数将把测试大小作为输入
  8. 返回训练和测试集

让我们首先添加读入数据的逻辑。让我们也添加显示前五行的逻辑:

def data_preparation(columns, test_size):
  df = pd.read_csv("telco_churn.csv")
  print(df.head())

让我们调用我们的数据准备函数。现在,让我们传递“none”作为列和测试大小的参数:

def data_preparation(columns, test_size):
  df = pd.read_csv("telco_churn.csv")
  print(df.head())
  data_preparation(None, None)

作者截图

接下来,在我们的 data_preparation 方法中,让我们使用 columns 变量来过滤我们的数据框,定义我们将使用的列名列表,并使用 columns 变量调用我们的函数:

def data_preparation(columns, test_size):
  df = pd.read_csv(“telco_churn.csv”)
  df_subset = df[columns].copy()
  print(df_subset.head())
columns = ["gender", "tenure", "PhoneService", "MultipleLines","MonthlyCharges", "Churn"]
data_preparation(columns, None)

作者截图

接下来,让我们指定另一个函数参数,我们将使用它来指定每一列的数据类型。在我们函数的循环的中,我们将为每一列指定数据,这些数据将从我们的数据类型映射输入字典中获得:

def data_preparation(columns, test_size, datatype_dict):
  df = pd.read_csv(“telco_churn.csv”)
  df_subset = df[columns].copy()
  for col in columns:
  df_subset[col] = df_subset[col].astype(datatype_dict[col])
  print(df_subset.head())
columns = ["gender", "tenure", "PhoneService", "MultipleLines","MonthlyCharges", "Churn"]
datatype_dict = {"gender":"category", "tenure":"float", "PhoneService":"category", "MultipleLines":"category", "MonthlyCharges":"float", "Churn":"category"}
data_preparation(columns, None, datatype_dict)

在另一个 for 循环中,我们将把所有分类列转换成机器可读的代码:

def data_preparation(columns, test_size, datatype_dict):
  df = pd.read_csv("telco_churn.csv")
  df_subset = df[columns].copy()

  for col in columns:
     df_subset[col] = df_subset[col].astype(datatype_dict[col])

  for col in columns:
   if datatype_dict[col] == "category":
     df_subset[col] = df_subset[col].cat.codes

columns = ["gender", "tenure", "PhoneService", "MultipleLines","MonthlyCharges", "Churn"]
datatype_dict = {"gender":"category", "tenure":"float", "PhoneService":"category", "MultipleLines":"category", "MonthlyCharges":"float", "Churn":"category"}
data_preparation(columns, None, datatype_dict)

最后,让我们指定我们的输入和输出,分割我们的数据用于训练和测试,并返回我们的训练和测试集。首先,让我们从 Scikit-learn 中的模型选择模块导入训练测试分割方法:

from sklearn.model_selection import train_test_split

接下来,让我们指定我们的输入、输出、训练和测试集:

def data_preparation(columns, test_size, datatype_dict):
  df = pd.read_csv("telco_churn.csv")
  df_subset = df[columns].copy()

  for col in columns:
     df_subset[col] = df_subset[col].astype(datatype_dict[col])

  for col in columns:
   if datatype_dict[col] == "category":
     df_subset[col] = df_subset[col].cat.codes
  X = df_subset[["gender", "tenure", "PhoneService", "MultipleLines","MonthlyCharges",]]
  y = df_subset["Churn"]
  X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=42)
  return X_train, X_test, y_train, y_test

columns = ["gender", "tenure", "PhoneService", "MultipleLines","MonthlyCharges", "Churn"]
datatype_dict = {"gender":"category", "tenure":"float", "PhoneService":"category", "MultipleLines":"category", "MonthlyCharges":"float", "Churn":"category"}
X_train, X_test, y_train, y_test = data_preparation(columns, 0.33, datatype_dict)

模特培训

现在我们已经准备好了训练和测试数据,让我们来训练分类模型。为简单起见,让我们定义一个函数,用默认参数训练一个随机森林分类器,并设置一个随机状态再现性。该函数将返回经过训练的模型对象。让我们从导入随机森林分类器开始:

from sklearn.ensemble import RandomForestClassifier

接下来,让我们定义我们的拟合函数并存储训练好的模型对象:

def fit_model(X_train,y_train):
   model = RandomForestClassifier(random_state=42)
   model.fit(X_train,y_train)
   return model

model = fit_model(X_train,y_train)

模型预测和性能

让我们也定义我们的预测函数,它将返回模型预测

def predict(X_test, model):
   y_pred = model.predict(X_test)
   return y_pred

y_pred = predict(X_test, model)

最后,让我们定义一个报告分类性能指标的方法

def model_performance(y_pred, y_test):
   print("f1_score", f1_score(y_test, y_pred))
   print("accuracy_score", accuracy_score(y_test, y_pred))
   print("precision_score", precision_score(y_test, y_pred))

model_performance(y_pred, y_test)

作者截图

现在,如果我们想使用函数包装器来定义我们的计时器,我们需要导入 functools 和 time 模块:

import functools
import time

接下来,让我们定义我们的定时器函数。我们称之为 runtime_monitor。它将采用一个名为 input_function 的参数作为自变量。我们还将把输入函数传递给 functools 包装器中的 wrappers 方法,我们将把它放在实际的计时器函数之前,称为 runtime_wrapper:

def runtime_monitor(input_function):
  @functools.wraps(input_function)
  def runtime_wrapper(*args, **kwargs):

接下来,在运行时包装器的范围内,我们为输入函数指定计算执行运行时的逻辑。我们定义了一个开始时间值,函数的返回值(我们执行函数的地方)一个结束时间值,以及运行时间值,即开始时间和结束时间之差

 def runtime_wrapper(*args, **kwargs):
     start_value = time.perf_counter() 
     return_value = input_function(*args, **kwargs)
     end_value = time.perf_counter()
     runtime_value = end_value - start_value 
     print(f"Finished executing {input_function.__name__} in {runtime_value} seconds")
     return return_value

我们的计时器函数(runtime_wrapper)是在 runtime_monitor 函数的范围内定义的。完整的功能如下:

作者创建的嵌入

然后,我们可以使用 runtime_monitor 来包装我们的 data_preparation、fit_model、predict 和 model_performance 函数。对于数据准备,我们有以下内容:

@runtime_monitor
def data_preparation(columns, test_size, datatype_dict):
  df = pd.read_csv("telco_churn.csv")
  df_subset = df[columns].copy()
  for col in columns:
     df_subset[col] = df_subset[col].astype(datatype_dict[col])
  for col in columns:
   if datatype_dict[col] == "category":
     df_subset[col] = df_subset[col].cat.codes
  X = df_subset[["gender", "tenure", "PhoneService", "MultipleLines","MonthlyCharges",]]
  y = df_subset["Churn"]
  X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=42)
  return X_train, X_test, y_train, y_test

columns = ["gender", "tenure", "PhoneService", "MultipleLines","MonthlyCharges", "Churn"]
datatype_dict = {"gender":"category", "tenure":"float", "PhoneService":"category", "MultipleLines":"category", "MonthlyCharges":"float", "Churn":"category"}
X_train, X_test, y_train, y_test = data_preparation(columns, 0.33, datatype_dict)

作者截图

我们看到我们的数据准备函数需要 0.04 来执行。对于 fit_model,我们有:

@runtime_monitor
def fit_model(X_train,y_train):
   model = RandomForestClassifier(random_state=42)
   model.fit(X_train,y_train)
   return model

model = fit_model(X_train,y_train)

作者截图

我们预测:

@runtime_monitor
def predict(X_test, model):
   y_pred = model.predict(X_test)
   return y_pred

y_pred = predict(X_test, model)

作者截图

最后,对于模型性能:

@runtime_monitor
def model_performance(y_pred, y_test):
   print("f1_score", f1_score(y_test, y_pred))
   print("accuracy_score", accuracy_score(y_test, y_pred))
   print("precision_score", precision_score(y_test, y_pred))

model_performance(y_pred, y_test)

作者截图

我们看到 fit 方法是最耗时的,这是我们所期望的。在构建像这样简单的机器学习工作流时,能够可靠地监控这些函数的运行时对于资源管理是必不可少的。

调试机器学习模型

定义调试器函数包装器也是一个简单的过程。我们先来定义一个叫做调试方法的函数。类似于我们的定时器函数,iit 将接受一个函数作为输入。我们还将把输入函数传递给 functools 包装器中的 wrappers 方法,我们将把它放在实际的调试器函数之前,称为 debugging_wrapper。debugging_wrapper 将参数和关键字参数作为输入:

def debugging_method(input_function):
  @functools.wraps(input_function)
  def debugging_wrapper(*args, **kwargs):

接下来,我们将把参数的表示、关键字及其值分别存储在名为 arguments 和 keyword_arguments 的列表中:

def debugging_wrapper(*args, **kwargs):
       arguments = []
       keyword_arguments = []
       for a in args:
          arguments.append(repr(a))    
       for key, value in kwargs.items():
          keyword_arguments.append(f"{key}={value}")

接下来,我们将连接参数和 keyword_argument,然后将它们连接成一个字符串:

 def debugging_wrapper(*args, **kwargs):
    ...#code truncated for clarity
    function_signature = arguments + keyword_arguments
    function_signature = "; ".join(function_signature) 

最后,我们将打印函数名、它的签名和它的返回值:

 def debugging_wrapper(*args, **kwargs):
    ...#code truncated for clarity
     print(f"{input_function.__name__} has the following signature: {function_signature}")
     return_value = input_function(*args, **kwargs)
     print(f"{input_function.__name__} has the following return: {return_value}") 

debugging_wrapper 函数也将返回输入函数的返回值。完整的功能如下:

作者创建的嵌入

数据准备

现在,我们可以用调试方法包装数据准备函数:

作者创建的嵌入

模特培训

我们可以对 fit 函数做同样的事情:

作者创建的嵌入

模型预测和性能

对于我们预测函数:

作者创建的嵌入

最后,对于我们的性能函数:

作者创建的嵌入

这篇文章中的代码可以在 GitHub 上找到。

Python 中使用的函数包装器

函数包装器在软件工程数据分析机器学习中有着广泛的应用。当开发机器学习模型时,涉及数据准备、模型训练和预测的操作的运行时间是主要关注的领域。在数据准备的情况下,根据数据的大小和操作的复杂性,诸如读入数据、执行聚合和输入缺失值等操作在运行时会有所不同。记住这一点,当数据改变时,监视这些操作的运行时如何改变是有用的。

此外,将模型拟合到训练数据可以说是机器学习管道中最昂贵的步骤。根据数据对模型进行定型(拟合)的运行时间可能会随着数据大小的不同而有很大的变化,包括包含的要素数量和数据中的行数。在许多情况下,机器学习的训练数据会随着更多的数据而刷新。这导致模型训练步骤在运行时增加,并且通常需要更强大的机器来成功完成模型训练。

模型预测调用也可能因预测输入的数量而异。尽管几十到几百个预测调用可能不会有很长的运行时间,但在某些情况下,需要进行几千到几百万次预测,这会极大地影响运行时间。能够监控预测函数调用的运行时对于资源管理也是必不可少的。

除了监控运行时,使用函数包装器进行调试在构建机器学习模型时也很有用。与运行时监控类似,该过程对于解决数据准备、模型拟合调用和模型预测调用的问题非常有用。在数据准备步骤中,数据刷新可能导致曾经可执行的功能失败。此外,当数据被刷新或用于训练的模型输入被修改时,问题和错误可能出现。使用函数包装器进行调试有助于指出输入、数组形状和数组长度的变化是如何导致 fit 调用失败的。在这种情况下,我们可以使用函数包装器来找到这个 bug 的来源并解决它。

Python 中的函数包装器使得运行时监控和调试变得简单明了。虽然我只讨论了一个非常简单的例子的数据准备、模型拟合和模型预测,但是这些方法对于更复杂的数据会变得更加有用。在数据准备的情况下,运行时监控和调试功能对于其他类型的数据准备非常有用,例如预测缺失值、合并数据源以及通过规范化或标准化来转换数据。此外,在拟合模型和进行预测时,模型类型和模型超参数会对运行时间和错误产生重大影响。拥有可靠的运行时监控和调试工具对于数据科学家和机器学习工程师来说都是很有价值的。

本帖原载于 内置博客 。原片可以在 这里找到

功能数据分析:维数灾难的解决方案

原文:https://towardsdatascience.com/functional-data-analysis-a-solution-to-the-curse-of-dimensionality-f83dd19fa6e8

使用梯度增强和 FDA 对 Python 中的 ECG 数据进行分类

马库斯·斯皮斯克Unsplash 上拍摄的照片

维度的诅咒

维数灾难是指在机器学习中处理高维数据集时出现的挑战和困难。随着数据集中维度(或特征)数量的增加,准确了解特征和目标变量之间的关系所需的数据量呈指数增长。这使得在高维数据集上训练高性能的机器学习模型变得困难。

维数灾难是机器学习中的一个问题的另一个原因是,它可能导致过度拟合。在处理高维数据集时,很容易包含不相关或冗余的特征,这些特征对模型的预测能力没有贡献。这可能会导致模型过于接近定型数据,从而导致对看不见的数据的泛化能力很差。

功能数据分析

功能数据分析(FDA) 是一种统计分析,用于分析连续曲线或函数形式的数据,而不是统计分析中常用的传统表格数据。在函数数据分析中,目标是通过检查函数本身之间的关系,而不仅仅是单个数据点,来建模和理解数据的底层结构。这种类型的分析对于复杂或依赖于时间的数据集特别有用,并且可以提供传统统计技术无法提供的洞察力。

函数数据分析在许多不同的情况下都很有用。例如,它可用于对可能具有大量底层结构的复杂数据集进行建模,如时序数据或连续测量的数据。它还可用于识别数据中的模式和趋势,这些模式和趋势在查看单个数据点时可能不明显。[1]此外,函数数据分析可以提供对数据集中不同变量之间关系的更详细和细微的理解,这对于进行预测或开发新理论是有用的。功能数据分析的使用可以帮助研究人员更深入地理解他们正在处理的数据,并揭示从更传统的统计技术中可能不明显的见解。

功能数据表示

可以将数据从离散集合x₂…xₜx₁转换成函数形式。换句话说,我们可以将数据表示为函数,而不是离散的点。

在函数数据分析中,基是用来表示连续曲线或函数的一组函数。这个过程也叫基平滑。平滑过程如公式 1 所示。它包括将统计单位 xᵢ表示为系数 cᵢₛ和基函数φₛ.的线性组合

等式 1。功能数据表示。[2]

基本类型

根据数据的性质和分析的具体目标,可以使用不同类型的基础。一些常见类型的基包括傅立叶基、多项式基、样条基和小波基。每种类型的基础都有其独特的属性,可用于不同类型的数据和分析。例如,傅立叶基常用于具有周期性结构的数据,而多项式基则适用于由多项式函数近似的数据。一般来说,基础的选择将取决于数据的具体特征和分析的目标。

B 样条 基础

在函数数据分析中,B 样条基是一种使用 B 样条函数构造的基。 B 样条函数是计算机图形学和数值分析中常用的分段多项式函数。在 B 样条基中,函数以特定的方式排列,以便它们可以用来表示任何连续的曲线或函数。b 样条基常用于函数数据分析,因为它们有许多有用的性质。b 样条基是 FDA 研究中使用最多的基。[3]
图 1 显示了一个三次 B 样条基的例子。

图一。三次 B 样条基,使用 n = 15。图片作者。

FDA 如何降低数据的维度?

让我们来看一个 FDA 的 Python 实现,以展示这种强大的技术如何在一些数据集上很好地工作,从而既降低维度又提高准确性。您可以在文章末尾找到完整的代码链接。该过程如下:

1。选择一个数据集 对于下面的例子,我使用的是 BIDMC 充血性心力衰竭数据库【4】【5】数据集。该分析基于一个名为 ECG5000 的预处理版本。如图 2 所示,数据集是一个具有 140 个特征(时刻)和 5000 个实例(500 个用于训练集,4500 个用于测试集)的时间序列。在目标变量中有五个等级,有四种不同类型的心脏病。对于这种分析,我们将只考虑一个二元目标,如果心跳正常,则为 0,如果受到心脏病的影响,则为 1。训练集大小为 500x140,是一个高维数据集。

图二。ECG5000 数据集。数据集的目的是预测心脏病。图片作者。

2。选择一个基础。 我选择了如图 1 所示的基础。

3。以函数形式表示数据。 图 3 显示了使用具有 15 个函数的 B 样条将数据转换为函数形式后的结果。我已经用 python 库 scikit-fda 用下面的代码完成了。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from skfda.representation.basis import BSpline
from skfda.representation import FDataGrid, FDataBasis

train = pd.read_csv(f'ECG5000_TRAIN.arff', header=None)
test = pd.read_csv(f'ECG5000_TEST.arff', header=None)
y_train = train.iloc[:, -1]
y_train = [1 if i == 1 else 0 for i in y_train]
X_train = train.iloc[:, :-1]
y_test = test.iloc[:, -1]
y_test = [1 if i == 1 else 0 for i in y_test]
X_test = test.iloc[:, :-1]

basis = BSpline(n_basis=15, order = 3)
X_train_FDA = FDataBasis.from_data(X_train, grid_points= np.linspace(0, 1, 140), basis=basis)
X_test_FDA = FDataBasis.from_data(X_test, grid_points= np.linspace(0, 1, 140), basis=basis)

X_train_FDA = X_train_FDA.coefficients
X_test_FDA = X_test_FDA.coefficients

图 3。用函数表示的 ECG5000。图片作者。

4。最后,提取系数。这是你的新数据集。 基础集中有 15 个功能。因此,要使用 15 个系数。该过程有效地将维数从 140 减少到 15 个特征。现在让我们训练一个 XGBoost 模型来评估准确性是否受到影响。

图 4。ECG5000 数据集上的 FDA 之前和之后。图片作者。

可以观察到,在将特征的数量减少了几乎 10 倍之后,准确度提高了!

还有一步:添加衍生品

导数是一个数学概念,用来衡量函数相对于它的一个自变量的变化率。在函数数据分析的背景下,导数可用于定量描述函数数据集的平滑度和形状。例如,函数的一阶导数可用于识别局部最大值和最小值,而二阶导数可用于识别拐点。

导数也可以用来确定函数的斜率或曲率的变化。这对于识别一段时间内数据的趋势或变化非常有用。此外,导数可用于通过多项式展开来逼近原始函数,这对于进行预测或对数据执行其他分析非常有用。
图 5 显示了添加一阶和二阶导数后精度的提高。导数以同样的方式加入。首先,我们对函数形式求导,并将基系数添加到数据集。对二阶导数重复这一过程。

由于我们添加了额外的功能,维度增加了。然而,它还不到原始数据集的三分之一。混淆矩阵显示,衍生工具为模型增加了重要信息,显著减少了假阴性的数量,并且实现了对健康个体近乎完美的分类。

图 5。特征加导数后的结果。图片作者。

结论

总的来说,FDA 是分析功能数据的强大工具,在工程、经济和生物学等领域有广泛的应用。它能够使用函数形式对函数数据进行建模,并应用广泛的统计方法,这使它成为在某些情况下降低维度和提高准确性的重要工具。

参考

您可以在 GitHub 上找到数据集以及图和模型的完整代码。

[1] Ramsay,j .,& Silverman,B. W. 功能数据分析 (2010) (统计学中的 Springer 系列)(精装第 2 版软装再版。2005).斯普林格。

[2] Maturo,f .,& Verde,r .生物医学信号监督分类的汇集随机森林和功能数据分析:心电图数据的理论和应用。(2022).医学统计学41 (12),2247–2275。https://doi.org/10.1002/sim.9353

[3]s .乌拉和 f .芬奇。功能数据分析的应用:系统综述。(2013) BMC 医学研究方法论13 (1)。【https://doi.org/10.1186/1471-2288-13-43】

[4] Baim DS,Colucci WS,Monrad ES,Smith HS,Wright RF,Lanoue A,Gauthier DF,Ransil BJ,Grossman W,Braunwald E .口服米力农治疗严重充血性心力衰竭患者的生存率。美国心脏病学会,1986 年 3 月;7(3):661–670.http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db = PubMed&list _ UIDs = 3950244&dopt = Abstract

[5] Goldberger,a .,Amaral,l .,Glass,l .,Hausdorff,j .,Ivanov,P. C .,Mark,r .,…和 Stanley,H. E. (2000 年)。生理银行、生理工具包和生理网:复杂生理信号新研究资源的组成部分。循环[ 在线。101 (23),第 e215–e220 页。

功能和 DAGs: Hamilton,一个用于熊猫数据帧生成的通用微框架

原文:https://towardsdatascience.com/functions-dags-introducing-hamilton-a-microframework-for-dataframe-generation-more-8e34b84efc1d

一种防止代码变得过于复杂而无法导航的方法

创建数据框架很容易,但是管理代码库来实现这一点可能会变得困难和复杂。当一个数据科学团队寻求帮助来解决这个问题时,Stitch Fix Platform 团队一起工作来构建 Hamilton 【开源代码】!这篇文章是汉密尔顿的背景介绍。

注:这篇帖子是最初出现在 Stitch Fix 多线程博客上的两篇帖子(帖子 1帖子 2 )的合并。

背景

数据科学家的一项常见任务是生成一个数据框架(也称为特征工程),用作创建模型的输入。我敢肯定,对于本博客的大多数读者来说,以下是熟悉的景象:

df **=** load_some_data('location/SQL query')
df['column_b'] **=** SOME_CONSTANT ***** df['column_a']
df['column_c'] **=** df['column_b'].apply(a_transform)
*# … some more feature engineering* model **=** fit(df)

对于简单的领域和模型,上面的代码可能不会太难管理。但是,如果您在时间序列预测这样的领域中,您创建的许多列都是其他列的函数,那么这方面的代码会变得非常复杂。

问题是

现在想象一下上面的代码帮助创建某种类型的模型的情况。这些模型是成功的,对业务至关重要,需要每周更新和使用。例如,它正在做一些重要的运营预测。没问题,上面的代码存在于一个脚本中,该脚本可以由一个预定的任务执行。为了便于创建和管理更好的模型,围绕这一业务关键任务组建了一个团队,他们遵循最佳实践来维护工作秩序,例如,代码是受版本控制的,以便可以审查任何更改,可以恢复任何重大更改,等等。

现在,五年过去了,团队的成长刚好能够跟上业务的发展。你认为上面的代码库会发生什么?它可能变得更加复杂;适应业务变化、新模式、新功能。但是它是一种很好的综合体吗,是你愿意日复一日与之共事的综合体吗?不太可能。这并不是因为恶意或糟糕的软件工程实践而变得复杂,它只是因为每次需要改变时,都需要触及数据框架(本例中的 df)。这导致了一些问题:

列(即特征)依赖关系很难确定。

  • 例如,如果我创建列 a,它随后被列 c、d 使用,然后这些列又被用作创建其他列的输入,这些列又被用作输入,等等。除非您阅读所有代码,否则没有简单的方法来确定这些依赖关系。这在大型代码库中是困难的。

文档很难。

  • 如果有许多行代码在做:
    df['column_z'] **=** df['column_n'] ***** log(df['column_o']) **+** 1 那么很难自然地放置文档。

单元测试很难。

  • 与文档示例类似,如何轻松地对内联数据帧转换进行单元测试?

您需要执行并理解整个脚本来获得任何列(即特性)。

  • 如果您只需要数据帧的一部分就需要计算整个数据帧,这可能会成为开发的负担。例如,你正在开发一个不需要所有这些柱子的新模型……让我去煮点咖啡……

在应用程序中,这意味着一个新加入团队的人有很多准备工作要做,任期与调试一个问题或添加一个新列(特性)所花费的时间直接相关。生产率的学习曲线变得陡峭而漫长;每一个新的修改都加剧了这条曲线——经过多年的这种开发,学习曲线看起来更像黎明之墙。

你们中的一些人可能会读到这里,并想知道——“这些人到底怎么了?”— 显然解决办法是增加功能。可以记录功能,并进行单元测试。在这一点上,我们不同意你的意见。功能很棒!然而,将代码组织成函数仅仅是一种工具,并不能提供足够的防护来防止我们上面看到的问题。例如,您将什么作为输入传递给这些函数?整个数据框架?数据帧的特定列?十有八九,这仍然意味着您必须执行整个脚本才能得到任何东西。它也没有解决知道哪些列被使用和未被使用,以及它们之间的关系的问题。

缝合固定方法

在 Stitch Fix,需求预测和评估(FED)团队负责业务决策所依据的运营预测。作为工作流程的一部分,他们需要大型复杂的数据框架来训练和执行他们的模型。很自然,他们会遇到我们上面描述的每一个问题。谢天谢地,现在他们不用自己解决这个问题了。

Stitch Fix 的 Algo 平台团队的职能之一是帮助构建库、工具和平台,以使数据科学家能够更快地开展工作。Algo 平台团队分为多个子团队,专注于数据科学与业务相结合所需的不同功能。每个子团队在创建解决方案时都有很大的自由度。我们的模型生命周期团队解决了这个问题,他们的目标是简化模型生产——包括简化特征创建。

在继续之前,我们首先要提到的是,虽然我们探索了各种产品,但我们没有发现任何开源工具可以显著提高我们解决上述问题的能力。第二,我们在这里没有解决大数据挑战,所以基本假设是所有数据都可以放在内存中,但是 Hamilton 可以在多个 CPU 上运行,并扩展到大数据集大小。

结果是:汉密尔顿

为了解决美联储的问题,美联储和模型生命周期团队之间形成了合作,一个项目诞生了,以帮助重新思考他们的代码库。由此产生了用于生成数据帧的 python 微框架 Hamilton 。这个框架是专门为解决创建数据框架(包含成千上万的工程特性和多年的业务逻辑)所带来的难题而设计的,这些数据框架将用作时间序列预测的输入。Hamilton 的主要“技巧”是如何改变数据帧创建和操作的范式,以处理这种令人痛苦的复杂性。更多信息请见下文。

随着 2019 年 11 月汉密尔顿的出现,美联储团队的数据科学家专门编写了特殊形状的 python 函数来产生他们的数据框架。如前所述,简洁的函数有助于解决单元测试和文档方面的问题,我们在这里并没有开拓新的领域。然而,如何解决跟踪数据帧内的依赖性和选择性执行的问题呢?容易;我们构建了一个有向无环图,或者简称为 DAG,使用 python 函数如何形成的属性。

: 等等,我糊涂了?解释一下!

不是让数据科学家编写如下的列转换:

df['COLUMN_C'] **=** df['COLUMN_A'] **+** df['COLUMN_B']

Hamilton 使数据科学家能够将它们表达为类似于 following⁴:的函数

**def** **COLUMN_C**(COLUMN_A: pd.Series, COLUMN_B: pd.Series) **->** pd.Series:
      """Documentation"""
	**return** COLUMN_A **+** COLUMN_B

函数的名等同于列名。输入参数是该函数所依赖的输入列(或其他输入变量)的名称。功能文档 string⁵* 成为仅用于这部分业务逻辑的文档。函数主体照常进行计算。对于大多数意图和目的来说,输出应该是一个序列或者一个数据帧,但是这不是一个硬性要求。*

注意范式的转变!汉密尔顿没有让数据科学家编写代码,然后在一个大规模的过程中执行,而是利用函数的定义方式来创建 DAG,并为数据科学家执行它。使用这种方法,列关系和业务逻辑可以很容易地进行文档化和单元测试。

显示代码在前、代码在后和 DAG 构造的示例。图片作者。

列定义与函数定义对等,然后我们利用 python 的内置 inspect ⁷模块来创建所有这些声明函数的 DAG。这使我们能够确定需要 DAG 的哪些部分来计算任何给定的节点,即列。这反过来又使我们能够削减给定所需输出列列表所需的输入和执行集。

数据科学家现在可以通过两个简单的步骤创建他们想要的数据框架:初始化和执行。这些步骤的内部工作被框架巧妙地抽象出来。数据科学家需要为 DAG 的初始化指定的所有内容是一些初始配置参数和 python 包/模块,以获取函数定义。为了执行,他们只需要指定在最终的数据帧中需要哪些列。

功能示例 1:

import importlib
from hamilton import driverinitial_columns **=** {  *# load from actuals or wherever 
*                     *# this is our initial data we use as input.
*    'signups': pd.Series([1, 10, 50, 100, 200, 400]),
    'spend': pd.Series([10, 10, 20, 40, 40, 50]),
}*# module to import functions from; could just do import my_functions* module_name **=** 'my_functions'
py_module **=** importlib.import_module(module_name)*# create the DAG* dr **=** driver.Driver(initial_columns, py_module)*# determine what we want in the end* output_columns **=** ['signups', 'avg_3wk_spend', 'some_column']# create the dataframe
df **=** dr.execute(output_columns, display_graph**=**False)

能够将列关系表示为 DAG 还有其他一些好处:

  • 通过函数上的类型提示,我们可以在运行 DAG 之前编译它,以进行基本的类型检查和验证。
  • 我们可以想象匕首。这是快速理解复杂关系的好方法——对新老团队成员都是如此。
  • 我们可以执行其他图形分析,例如,我们可以删除什么?例如,未使用的列将被表示为孤立的 Dag。
  • 通过使用 DAG 对函数执行进行逻辑建模,这为我们针对不同的执行环境编译执行提供了可能性。例如 Spark,或者适当地利用系统上的多个内核等。

就是这样。它非常简单,相对来说也很轻便。

如果你仍然不相信,这里有一份来自 Stitch Fix 的数据科学家的证明,他加入了美联储团队:

我之前在一个处理多层信息依赖的组织工作过。“数据产品”是多年来多位作者在没有系统检查循环引用等坑洞的情况下添加图层的结果。正因为如此,该产品是一个非常脆弱的产品,知识转移是通过一个新成员的一系列特别的试验和错误来进行的;他们遇到一个问题,要求他们的主管澄清,然后他们得到一个不透明的解释和解决方法,然后通过电话游戏传播给下一个人(解决方案的机制被传播,但解决方案背后的原因没有被传播)。在我自己的经历中,直到我顽强地跟随信息的线索并自己建立了一个 dag,啊哈时刻才出现。

有过这样的经历后,拥有一个已经有图形结构来体现复杂依赖性的数据产品是一件令人高兴的事情;在它的众多好处中,最主要的是该产品可以适用于其他图形的一般分析方法。此外,由于将数据帧结构与定量说明分开的抽象,它有助于新人处理信息,而不必具有先验领域知识,因为依赖性被清楚地指定,并且功能简单而简明。

学习

让我们回顾一下汉密尔顿自成立以来的一些学习和发展。

使 Hamilton 代码更易于维护

随着移植数据科学代码以使用 Hamilton 的工作的进展,我们定义函数以使用 Hamilton 的方式显然还有改进的余地。这里有两个我们认为读者会觉得最有趣的例子:

  • 我们如何处理条件执行?我们是需要一个带有 if else 语句的函数,还是需要多个函数来处理依赖于某些输入的情况?例如,Stitch Fix 有多个独立建模的业务线,但是我们希望尽可能共享代码。在每个业务线可能需要一些不同的逻辑的情况下,我们如何最好地处理它,同时保持代码简单、易于理解和遵循?
  • 这里有一个人为的例子。这里的要点是,每个业务线的逻辑可能会变得任意复杂,并需要不同的输入,从而混淆依赖结构,并带回我们最初试图与 Hamilton 一起解决的问题。

人为的例子 1:

**def** **total_marketing_spend**(business_line: str,
                        tv_spend: pd.Series, 
                        radio_spend: pd.Series, 
                        fb_spend: pd.Series) **->** pd.Series:
   """Total marketing spend."""
   **if** business_line **==** 'womens':
       **return** tv_spend **+** radio_spend **+** fb_spend
   **elif** business_line **==** 'mens':
       **return** radio_spend **+** fb_spend
   **elif** business_line **==** 'kids':
       **return** fb_spend
   **else**:
      **raise** ValueError(f'Unknown business_line {business_line}')
  • 我们如何在变换之间保持相似的逻辑?相差一个值的重复函数使得代码重复且不必要地冗长。例如,为节假日或特殊事件发生时设置指标变量,或重新设计网站,或推出新产品。

人为的例子 2:

**def** **mlk_holiday_2020**(date_index: pd.Series) **->** pd.Series:
   """Indicator for MLK holiday 2020"""
   **return** (date_index **==** "2020-01-20").astype(int)

**def** **us_election_2020**(date_index: pd.Series) **->** pd.Series:
   """Indicator for US election 2020"""
   **return** (date_index **==** "2020-11-03").astype(int)

**def** **thanksgiving_2020**(date_index: pd.Series) **->** pd.Series:
   """Indicator for Thanksgiving 2020"""
   **return** (date_index **==** "2020-11-26").astype(int)

幸运的是,我们正在处理函数,所以我们用来帮助上述情况的一个常用策略是创建装饰器。这些作为语法糖帮助保持代码更简洁,写起来更愉快。让我们描述一下改进上述例子的装饰者。

用@config.when*避免 if else 语句

在上面的“虚构示例 1”中,我们有一堆基于业务线的 if else 语句。为了使依赖关系更加清晰,我们定义了三个独立的函数,并用@config.when修饰它们,以描述该定义适用的条件。

**@**config.when(business_line**=**'kids')
**def** **total_marketing_spend__kids**(fb_spend: pd.Series) **->** pd.Series:
     """Total marketing spend for kids."""
     **return** fb_spend **@**config.when(business_line**=**'mens')
**def** **total_marketing_spend__mens**(business_line: str,
                                radio_spend: pd.Series, 
                                fb_spend: pd.Series) **->** pd.Series:
     """Total marketing spend for mens."""
     **return** radio_spend **+** fb_spend **@**config.when(business_line**=**'womens')
**def** **total_marketing_spend__womens**(business_line: str,
                                  tv_spend: pd.Series, 
                                  radio_spend: pd.Series, 
                                  fb_spend: pd.Series) **->** pd.Series:
     """Total marketing spend for womens."""
     **return** tv_spend **+** radio_spend **+** fb_spend

当我们构造 DAG 时,我们只保留满足装饰器中指定的过滤标准的函数。过滤标准来自实例化时提供的配置/初始数据(功能示例 1,变量initial_columns)。这使我们能够根据一些配置参数有选择地包含或排除功能。

您可能还注意到了函数名中的__后缀指示器。我们希望目标列的名称在不同的配置中保持一致,但是一个函数在一个给定的文件中只能有一个定义,所以我们被迫使用不同的名称。在框架中强加 decorator + dunder 命名约定,我们可以有条件地定义输出列的底层函数;如果我们检测到函数名中的后缀,框架知道要去掉它,从而适当地创建输出列名和定义。

使用@config.when有助于确保我们避免复杂的代码,并且可以在 DAG 构建时发现错误的配置。例如,在以前的版本中,如果我们提供了一个不正确的 business_line 值,我们直到代码执行时才知道(如果曾经知道的话!).在使用@config.when时,如果我们传入一个不正确的 business_line 值,我们将在请求输出列时出错,因为没有这样的方法来满足这个请求。

用@参数化减少代码维护

天真地使用 Hamilton 会使简单的函数感觉不必要的冗长:一行定义函数,另几行用于文档,然后是简单的主体。为了减少这种感觉,我们可以创建一个函数来跨多个列定义共享代码。让我们能够做到这一点的是用@parametrized来修饰这个函数,以告诉 Hamilton 它代表哪些列(或者更确切地说,可以用来调用这个函数的所有参数)。例如,我们可以用下面的方式重写“人造例子 2 ”:

*# we define the input here* SOME_DATES **=** {
     *# (output name, documentation): value to pass
*    ("mlk_holiday_2020", "MLK 2020"): "2020-01-20",
    ("us_election_2020", "US 2020 Election Day"): "2020-11-03",
    ("thanksgiving_2020", "Thanksgiving 2020"): "2020-11-26",
}**@**function_modifiers.parametrized(parameter**=**"single_date", assigned_output**=**SOME_DATES)
**def** **create_date_indicators_**(date_index: pd.Series, 
                            single_date: str) **->** pd.Series:
    """Helper to create an indicator series from a single date."""
    **return** (date_index **==** single_date).astype(int)

除了更简洁之外,我们更喜欢“人造示例 2 ”,主要有两个原因:

  1. 如果我们改变逻辑(例如,在我们的例子中日期指示器是如何创建的),我们只需要在一个地方改变它。
  2. 如果我们要创建更多这样的代码,只需要添加一行代码。

我们喜欢这个装潢师的额外原因:

  1. 文档!即使我们只添加了一行,我们仍然可以适当地考虑文档,这确保了每个功能仍然是文档化的。

这里我们只讨论了汉密尔顿带来的众多装饰家中的两个。我们创建的其他 decorators 在其他上下文中提供了帮助,这些上下文来自于我们对框架的其他学习。

代码审查更简单

创建 Hamilton 时,简化代码审查过程并不是预期的目标。碰巧的是,当您强制将业务逻辑更紧密地封装到功能中时,审查变更要容易得多。

例如,由于逻辑没有很好地封装,Hamilton 的事情要简单得多,而不是必须跟踪涉及大量文件和代码行的更改。迫使数据科学家编写清晰的函数来解释他们需要什么输入和他们创建什么输出,对于评审者来说是一个更简单、更难理解的工件。这样做的结果是,代码评审者更有效率,并且通过评审过程的错误更少。

缩放汉密尔顿

当我们谈论缝合固定时的比例时,我们通常指以下之一:

  • 复杂性的扩展(额外的模型架构、更多可参数化的管道、额外的业务线/区域),以及…
  • 数据规模和计算的扩展(由于客户群的增长、更大的培训数据、更多的 CPU 内核可供利用,数据集变得更大)

虽然 Hamilton 最初设计时考虑的是前者,但在使用过程中,它发展到解决后者,而不限制分布式计算的方法!使用图形适配器的概念,这是可行的,图形适配器是负责处理 Hamilton DAG 并确定各个节点应该如何执行的抽象。不严格地说,这允许你在任何你想要的平台上运行你的 Hamilton DAG 使你能够利用 ray 的并行性,以及 spark 的可伸缩性(使用 pandas API ,又名考拉dask )。如果你正在使用 pandas ,好消息是,你现在有三种方法来扩展你的代码!

这一切听起来很酷,也许好得难以置信。可能有一两个警告(阅读文档了解更多!),但是让我们深入一个例子来说服您,您既可以简单地编写您的管道,又可以让它们伸缩。我们将从一个简单的管道开始,即计算营销支出的汉密尔顿数据流:

# my_functions.py
import pandas **as** pd

**def** **avg_3wk_spend**(spend: pd.Series) **->** pd.Series:
    """Rolling 3 week average spend."""
    **return** spend.rolling(3).mean()

**def** **acquisition_cost**(avg_3wk_spend: pd.Series, signups: pd.Series) **->** pd.Series:
    """The cost per signup in relation to a rolling average of spend."""
    **return** avg_3wk_spend **/** signups

**def** **spend_mean**(spend: pd.Series) **->** float:
    """Shows function creating a scalar. In this case it computes the mean of the entire column."""
    **return** spend.mean()

**def** **spend_zero_mean**(spend: pd.Series, spend_mean: float) **->** pd.Series:
    """Shows function that takes a scalar. In this case to zero mean spend."""
    **return** spend **-** spend_mean

我们可以使用下面的“driver”代码在 pandas 中轻松运行它:

import pandas **as** pd
from hamilton import driver

import my_functions  *# we import the module here!**# load from actuals or wherever
#  -- this is one way to provide inputs to Hamilton*
initial_columns **=** {      'signups': pd.Series([1, 10, 50, 100, 200, 400]),
    'spend': pd.Series([10, 10, 20, 40, 40, 50]),
}*# creates the DAG* dr **=** driver.Driver(initial_columns, my_functions)  outputs **=** ['spend','signups','acquisition_cost','spend_zero_mean']*# let's create the dataframe!* df **=** dr.execute(outputs)  **print**(df)

通过对驱动程序代码做如下小小的修改(总共五处),我们可以在 dask 上运行它!请注意,定义业务逻辑的函数根本不需要修改(感谢 dask 实现了 Pandas API 的一个好部分)!

import pandas **as** pd
from hamilton import base
from hamilton import driver
from hamilton.experimental import h_dask

import my_functions  *# we import the module here!**# import dask components (1)* from dask.distributed import Client, LocalCluster  from dask import dataframe *# need to adjust how we load data -- (2)*
initial_columns **=** {      'signups': 
    dataframe.from_pandas(pd.Series([1, 10, 50, 100, 200, 400]), 
                          name**=**'signups', npartitions**=**2),
    'spend': 
    dataframe.from_pandas(pd.Series([10, 10, 20, 40, 40, 50]),
                          name**=**'spend', npartitions**=**2),
}*# Setup connection to dask (3)* client **=** Client(LocalCluster())*# Create dask adapter for Hamilton (4)*  **adapter** **=** h_dask.**DaskGraphAdapter**(client,
                                  base.PandasDataFrameResult()) 
*# and pass in the Adapter (4)*dr **=** driver.Driver(initial_columns, my_functions, adapter**=adapter**)  outputs **=** ['spend','signups','acquisition_cost','spend_zero_mean']*# This will now execute using dask!* df **=** dr.execute(outputs)  **print**(df) *# shut things down (5)* client.shutdown() 

就这么简单。

补充说明,PyData 社区为确保 python 数据 libraries/frameworks⁷.之间更好的互操作性所做的工作让我们很兴奋在这个例子中,在 dask 上进行缩放是因为他们的工作!

开放 Hamilton 成为通用数据流框架

预测团队的管道都围绕着创建熊猫数据框架,汉密尔顿就是在这种假设下成长起来的。然而,通过函数对数据流建模的概念比最初的应用更普遍。

因此,我们引入了结果 Mixin ,这是一个帮助告诉 Hamilton 如何将 DAG 的输出转换成可用的 python 对象的抽象。这使得汉密尔顿用户能够操作的不仅仅是熊猫数据帧!它与图形适配器一起使用,使用简单。

这是一个例子,汉密尔顿数据流适合一个模型,并预测它。

import numpy **as** np
from sklearn import base, linear_model
from hamilton import function_modifiers

*# This dataflow is abbreviated for this blog post -- see the full example in the examples folder.* 
**@**function_modifiers.config.when(clf**=**'logistic')
**def** **prefit_clf__logreg**(penalty: str) **->** base.ClassifierMixin:
    """Returns an unfitted Logistic Regression classifier object"""
    **return** linear_model.LogisticRegression(penalty)

**def** **fit_clf**(prefit_clf: base.ClassifierMixin, X_train: np.ndarray, y_train: np.ndarray) **->** base.ClassifierMixin:
    """Calls fit on the classifier object; it mutates it."""
    prefit_clf.fit(X_train, y_train)
    **return** prefit_clf

**def** **predicted_output**(fit_clf: base.ClassifierMixin, X_test: np.ndarray) **->** np.ndarray:
    """Exercised the fit classifier to perform a prediction."""
    **return** fit_clf.predict(X_test)

**def** **predicted_output_with_labels**(predicted_output: np.ndarray, target_names: np.ndarray) **->** np.ndarray:
    """Replaces the predictions with the desired labels."""
    **return** np.array([target_names[idx] **for** idx **in** predicted_output])

**def** **y_test_with_labels**(y_test: np.ndarray, target_names: np.ndarray) **->** np.ndarray:
    """Adds labels to the target output."""
    **return** np.array([target_names[idx] **for** idx **in** y_test])

我们可以使用下面的驱动程序代码轻松运行它;它请求一个具有两列的 numpy 矩阵,一列具有预测标签,另一列具有基本事实标签。

from hamilton import base, driver
import my_train_evaluate_logic

dag_config **=** {'clf': 'logistic'}
**adapter** **=** base.**SimplePythonGraphAdapter**(**base.NumpyMatrixResult()**)
dr **=** driver.Driver(
     dag_config, my_train_evaluate_logic, adapter**=adapter**)
*# the following is abbreviated code:* inputs **=** {'penalty': 'l1', 
          'X_train': ..., 
          'y_train': ..., 
          'X_test': ..., 
          'y_test': ..., 
          'target_names': ...}
result **=** dr.execute(
         ['predicted_output_with_labels', 
          'y_test_with_labels'], 
         inputs**=**inputs)
*# result is a np.ndarray that represents a matrix with two columns.
# One could then go and compute performance metrics with this output...*

但是,如果我们想调试输出呢?我们可以很容易地修改代码,返回一个字典作为结果,然后检查汉密尔顿数据流的阶段。我们只需要更新图形适配器,并指定我们想要检查的额外输出。

from hamilton import base, driver
import my_train_evaluate_logic

dag_config **=** {'clf': 'logistic'}*# (1) we changed the following line:* **adapter** **=** base.SimplePythonGraphAdapter(**base.DictResult()**)
dr **=** driver.Driver(
     dag_config, my_train_evaluate_logic, adapter**=adapter**)
inputs **=** {'penalty': 'l1', 
          'X_train': ..., 
          'y_train': ..., 
          'X_test': ..., 
          'y_test': ..., 
          'target_names': ...}*# result will now be a dictionary*
result **=** dr.execute(
         ['predicted_output_with_labels', 
          'y_test_with_labels', 
          '**fit_clf**'],     *# (2) add this to get more output*
         inputs**=**inputs)  *

# One could now debug/inspect the fit model and iterate/develop further...*

总结和未来工作

Hamilton 的诞生是为了帮助一个数据科学家团队通过编写特殊形状的函数,在共享代码库中管理复杂数据帧的创建。通过处理如何,汉密尔顿允许数据科学家专注于什么。这是模型生命周期和 Stitch Fix 的 FED 团队之间成功的跨职能合作的结果,自 2019 年 11 月以来一直在生产中运行。我们认为我们创建 dataframe 或任何 python 对象的新颖方法非常适合我们在 Stitch Fix 的环境,并邀请您亲自尝试一下!

pip install sf-hamilton # See [https://hamilton-docs.gitbook.io/docs/](https://hamilton-docs.gitbook.io/docs/)

我们对汉密尔顿的发展方向感到非常兴奋,并且正在积极地进行扩展。作为一个引子,我们想分享我们对汉密尔顿如何帮助解决数据质量问题的想法。我们可以添加一个简单的装饰器来运行基本的检查吗?也许像这样的 API 就足够了…

**@**check_output({'type': float, 'range': (0.0, 10000.0)})
**def** **SOME_IMPORTANT_OUTPUT**(input1: pd.Series, input2: pd.Series) **->** pd.Series:
    """Does some complex logic"""
    ...

你怎么想呢?请在我们的 github 问题中留下您的想法。

如果你对汉密尔顿感兴趣,我们已经在 slack 上开发了一个社区📣。如果您对如何最好地使用汉密尔顿有任何疑问或需要建议,请加入我们。您也可以通过探索 Hamilton 知识库(请⭐️它),或者在这里浏览我们的文档来了解更多信息📚

脚注

[1]在我们的说法中,列是特征。出于本文的目的,我们将坚持把特性描述为数据帧中的列。

[2]黎明墙是约塞米蒂国家公园中一条攀登埃尔卡皮坦山的路线的名字。它非常陡峭,很难攀登。

[3]该框架比仅仅创建数据框架更强大,但这是它的第一个应用。

[4]因为用 Hamilton 编写函数会稍微增加一些冗长,我们有装饰者来帮助保持代码干燥。继续阅读,看看一些例子。

[5]你知道吗,有了 sphinx ,一个 python 文档工具,你也可以轻松地展示这个函数文档。我们有一个合并后的工作,从这个代码构建 sphinx 文档,以帮助它更好地浮出水面。

[6]TL;inspect 模块上的 DR 是它允许你非常容易地访问函数的名字和签名。

[7]例如定义一个 Python 数组 API 标准的工作。

posted @ 2024-10-18 09:29  绝不原创的飞龙  阅读(388)  评论(0)    收藏  举报