函数参数:默认值、关键字和任意值
函数参数。图片由作者
给 PYTHON 开发者的提示
定义一个接受可变数量参数的函数
假设您正在编写一个接受多个参数的函数,其中一些参数通常有一个公共值。例如,您希望能够在不显式定义每个参数的情况下调用函数。换句话说,您希望一些参数有默认参数。
接下来,将向您介绍如何编写一个带有默认参数和灵活参数的函数,它允许您向函数传递任意数量的参数。
先决条件
如果你不熟悉定义自己的函数,下面的文章会给你更多的信息。
数据科学家需要具有特定功能的函数
towardsdatascience.com](/writing-your-own-functions-40d381bd679) [## 变量范围和 LEGB 规则
变量的作用域指的是你可以看到或访问变量的地方
towardsdatascience.com](/scope-of-variable-and-legb-rule-4d44d4576df5)
具有默认参数值的函数。图片由作者
首先,定义一个带有默认参数值的函数。在函数头中,我们给感兴趣的参数加上一个等号和默认的参数值。请注意,此函数计算第一个参数的第二个参数的幂,默认的第二个参数值是 1。这意味着我们可以像你所期望的那样用两个参数来调用这个函数。但是,如果只使用一个参数,函数调用将使用默认参数 1 作为第二个参数。
通过本文,您将获得编写带有单个和多个默认参数的函数的专业知识。
现在让我们看看灵活的论点。假设您想编写一个函数,但是不确定用户想要传递多少个参数。例如,一个函数接受浮点数或整型数,并将它们相加。这就是所谓的灵活的论点。
带有可变参数(args)的函数。图片作者作者
在这个例子中,我们编写了一个函数,它总结了传递给它的所有参数。在函数定义中,我们使用参数*args
,然后将传递给函数调用的所有参数转换成函数体中的元组。如果我们使用这个方法,*args
调用函数时,参数没有最大值。
带有任意关键字参数(kwargs)的函数。图片作者作者
您还可以使用一个**
来传递任意数量的名为kwargs
的关键字参数。也就是说,前面有标识符的参数。我们将编写一个名为print_all
的函数,打印出标识符和传递给它们的参数。为了编写这样一个函数,我们使用前面带一个**
的参数kwargs
。这将标识符-关键字对转化为函数体内的字典。然后,在函数体中,我们需要打印字典kwargs
中存储的所有键值对。
供大家参考,我们可以组合使用 args 和 kwargs。让我们看看下面的例子。
args 和 kwargs 的组合。图片由作者
使用灵活参数时,名称args
和kwargs
并不重要。你可以定义任何你想要的变量名。但是,它们前面必须分别有一个*
和**
。我知道这很难接受,所以是时候自己解决了。
**Other Interesting Articles**#1 [Writing Your Own Functions](/writing-your-own-functions-40d381bd679)#2 [Scope of Variable and LEGB Rule](/scope-of-variable-and-legb-rule-4d44d4576df5)#3 [Python: Procedural or Object-Oriented Programming?](/python-procedural-or-object-oriented-programming-42c66a008676)#4 [Data Science with Python: How to Use NumPy Library](/data-science-with-python-how-to-use-numpy-library-5885aa83be6b)#5 [Do you have the Software Engineer and Data Scientist skills?](/do-you-have-the-software-engineer-and-data-scientist-skills-probably-not-7e8fb069e067)
关于作者
Wie Kiang 是一名研究人员,负责收集、组织和分析意见和数据,以解决问题、探索问题和预测趋势。
他几乎在机器学习和深度学习的每个领域工作。他正在一系列领域进行实验和研究,包括卷积神经网络、自然语言处理和递归神经网络。
连接上LinkedIn
张量流函数估计
使用优化器来估计函数变量和回归
梯度下降是微积分中的一种数学方法,它考虑空间中变量的导数,以实现值的最佳组合来最小化函数。在机器学习中,我们通常使用定义为 预测-实际 的损失函数来最小化,从而给出预测问题的一组变量。有兴趣可以看这里的。
在本文中,我们将看到如何在 Tensorflow 中使用梯度下降来估计我们在日常研究生活中碰巧看到的函数变量。此外,我将展示一个例子,说明如何使用张量流优化器来寻找函数的最小值及其参数的最小值。
函数估计的示例曲线
让我们考虑以下三种情况。前两个场景用于函数估计,最后一个场景将演示评估函数最小值的能力。我将使用下面的代码来生成数据。
# imports
import tensorflow as tf
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
np.random.seed(50) # For reproducability
集合 1
x = np.arange(10)
y = 4 * x + 3
noise = np.random.normal(0,1,10)
y = y + noisefig = plt.figure(figsize=(5,5))
ax = sns.lineplot(x, y)
plt.xlabel("X")
plt.ylabel("Y")
带噪声的线性函数
集合 2
x = np.arange(-10, 10)
y = x**3 + x**2 + 5
noise = np.random.normal(0,20,20)
y = y + noisefig = plt.figure(figsize=(5,5))
ax = sns.lineplot(x, y)
plt.xlabel("X")
plt.ylabel("Y")
带噪声的 3 次多项式
第三组
在这种情况下,我们将尝试在函数最小时估计 X 和 Y 的值。
def f(x, y):
return (np.sin(x) * np.cos(y)) ** 2x = np.linspace(0, 3, 100)
y = np.linspace(0, 3, 100)X, Y = np.meshgrid(x, y)
Z = f(X, Y)fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection='3d')
ax.contour3D(X, Y, Z, 100)
# ax.scatter(0,0,0, color='r', marker='^')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
plt.xlim(-1, 4)
plt.ylim(-1, 5)
ax.set_zlim(-0.5,1)
ax.view_init(25, 20)
三维绘图
使用张量流优化器
根据文档,有几个优化器供我们使用。让我们来看看如何为集合 1 估计变量。
def init():
M = tf.Variable(5.0)
C = tf.Variable(0.0)
return M, CM, C = init()
opt = tf.keras.optimizers.Adam(learning_rate=0.001)for epoch in range(1000):
opt.minimize(lambda: sum(abs(y - M*x - C)), var_list=[M, C])
print(sum((y - M*x - C)**2).numpy(), M.numpy(), C.numpy(), end="\r")
print()print(M.numpy())
print(C.numpy())
在上面的代码中,我将尝试估计函数Y = Mx + C
的M
和C
参数。因此,作为第一步,我创建初始化函数来返回M
和C
作为张量流变量。这意味着优化器将计算 GRAD(多变量梯度),并使用提供的学习率将其应用于张量流变量。
注意损失函数是lambda: sum(abs(y — M*x — C))
。这转化为平均绝对误差,与使用均方误差的回归模型非常相似。经过 1000 次迭代后,对于 M 和 C ,我们得到值 4.4131455 和 0.29607454 。同一平面上的两个图如下所示。
原始和估计
请注意,我们有一个非常类似于原始数据集的估计。接下来,我们将看看如何解决第二个问题。
def init():
A = tf.Variable(1.0)
B = tf.Variable(1.0)
C = tf.Variable(1.0)
D = tf.Variable(1.0)
return A, B, C, DA, B, C, D = init()
opt = tf.keras.optimizers.Adam(learning_rate=0.01)for epoch in range(1000):
opt.minimize(lambda: sum(abs(y - (A*x**3+ B*x**2+C*x+D))), var_list=[A, B, C, D])
print(sum(abs(y - (A*x**3+ B*x**2+C*x+D))).numpy(), A.numpy(), B.numpy(), C.numpy(), D.numpy(), end="\r")
print()print(A.numpy())
print(B.numpy())
print(C.numpy())
print(D.numpy())
我们使用估计量Y = Ax^3 + Bx^2 + Cx + D
作为目标函数。因此,我们将有四个张量变量。与前面的场景类似,我们将使用平均绝对误差进行函数估计。我们分别得到 A,B,C 和 D 的系数 1.0356606,1.1082488,-2.7969947 和 8.258981 。我们的图如下所示。
原始和估计
计算最小值
def init():
X = tf.Variable(1.0)
Y = tf.Variable(1.0)
return X, YX, Y = init()
opt = tf.keras.optimizers.Adam(learning_rate=0.001)for epoch in range(1000):
opt.minimize(lambda: (tf.math.sin(X) * tf.math.cos(Y)) ** 2, var_list=[X, Y])
print(((tf.math.sin(X) * tf.math.cos(Y)) ** 2).numpy(), X.numpy(), Y.numpy(), end="\r")
print()print(X.numpy())
print(Y.numpy())minX = X.numpy()
minY = Y.numpy()
这里我们直接使用优化器的函数值,并要求它最小化。经过 1000 次迭代后,我们分别得到 X 和 Y 的值 0.5938998 和 1.5066066 。再次绘图给了我们下面标有最小值的图。
def f(x, y):
return (np.sin(x) * np.cos(y)) ** 2x = np.linspace(0, 3, 100)
y = np.linspace(0, 3, 100)X, Y = np.meshgrid(x, y)
Z = f(X, Y)fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection='3d')
ax.contour3D(X, Y, Z, 100)
ax.scatter(minX, minY, f(minX, minY), color='r', marker='o', s=100)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
plt.xlim(-1, 4)
plt.ylim(-1, 5)
ax.set_zlim(-0.5,1)
ax.view_init(25, 150) # Rotate to see minima better
标有最小值的图
注意,这个图的最小值实际上是一个平面。然而,我们只计算点,因此,我们不能指望找到一个平面。一个数学上更合理的方法将为这项任务建立一个更好的近似模型。
笔记
- 可能存在多个最小值,尽管梯度下降法只报告一个最小值/最大值,该值在起始点附近。随机梯度下降试图减轻这种影响。
- 同样可以使用神经网络进行建模。参见文档。
我希望你喜欢阅读这篇关于梯度下降的实际应用的文章。这里,我们使用函数来获取数据进行演示。然而,这同样适用于只存在具有多个参数的数据的情况。
干杯!
Python 中的函数包装器
将包装器放在函数周围
软件编程的一个重要原则是 DRY 原则。DRY 是“不要重复自己”的缩写。DRY 的目标是避免软件编程中不必要的重复。DRY 的应用包括通过函数、类、装饰器、类装饰器和元类实现抽象。在这篇文章中,我们将使用一个函数装饰器来包装和添加额外的处理到现有的用于模型构建的函数中。
我们开始吧!
对于我们的例子,我们将定义一个装饰函数来报告输入函数的执行时间。作为一名数据科学家,我经常需要考虑 fit 的执行时间,并预测生产中的调用。让我们考虑这个用例。
我们将使用来自医疗费用个人数据集的合成医疗数据,可以在这里找到。我们将定义读取数据、拟合数据和进行预测的函数。然后我们将定义一个装饰函数,它将报告每个函数调用的执行时间。
首先,让我们将数据读入熊猫数据框:
import pandas as pd
df = pd.read_csv("insurance.csv")
让我们打印前五行数据:
print(df.head())
现在让我们来构思一下我们的预测问题。让我们使用“年龄”、“bmi”和“儿童”列作为输入特征,使用“费用”作为目标。让我们也拆分我们的数据用于训练和测试。首先,让我们导入一些必要的包:
import numpy as np
from sklearn.model_selection import train_test_split
接下来,让我们定义我们的输入和输出。让我们将数据分成训练集和测试集:
X = np.array(df[['children', 'bmi', 'age' ]])
y = np.array(df['charges'])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)
这里,我们选择一个测试大小,对应于 20%数据的随机样本。现在,让我们将所有这些放入一个函数中:
def read_and_split(self, test_size):
df = pd.read_csv("insurance.csv")
print(df.head())
X = np.array(df[['children', 'bmi', 'age' ]])
y = np.array(df['charges'])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)
return X_train, X_test, y_train, y_test
接下来,让我们定义一个函数“fit_model”,我们将使用它来使我们的模型符合我们的训练数据。让我们导入“线性回归”模块:
from sklearn.linear_models import LinearRegression
在我们的“fit_model”函数中,让我们定义一个“LinearRegression”对象,并根据训练数据拟合我们的模型:
def fit_model():
model = LinearRegression()
model = model.fit(X_train, y_train)
return model
最后,让我们定义一个对测试集进行预测的函数:
def predict(input_value):
result = model.predict(X_test)
return result
现在我们已经定义了函数,让我们定义将报告执行时间的装饰函数。我们的装饰函数将是一个计时器函数,称为“timethis ”,它将接受一个函数作为输入:
def timethis(func):
...
接下来,我们将在“timethis”函数中定义一个“wrapper”函数:
def timethis(func):
def wrapper(*args, **kwargs):
...
在我们的“包装器”函数中,我们将定义“开始”和“结束”变量,我们将使用它们来记录运行的开始和结束。在定义“开始”和“结束”变量之间,我们将调用输入函数,并将其存储在一个名为“结果”的变量中:
def timethis(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
我们需要做的最后一件事是将“@ wraps”装饰符放在“wrapper”函数之前的行中:
def timethis(func):
[@wraps](http://twitter.com/wraps)(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
return result
“@ wraps”装饰器获取传递给“@timethis”的函数,并复制函数名、文档字符串、参数列表等…
然后,我们将打印函数的名称和运行时间(’ end’ — 'start ')。我们还返回输入函数,它存储在“结果”变量中:
def timethis(func):
[@wraps](http://twitter.com/wraps)(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(func.__name__, end-start)
return result
最后,“timethis”函数返回“包装器”:
def timethis(func):
[@wraps](http://twitter.com/wraps)(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(func.__name__, end-start)
return result
return wrapper
现在我们可以在任何函数上使用“@timethis”装饰器。让我们将“@timethis”应用于我们的“read_split”方法。我们只需在我们想要包装的函数前面的行中放上“@timethis ”:
@timethis
def read_and_split(self, test_size):
df = pd.read_csv("insurance.csv")
X = np.array(df[['children', 'bmi', 'age' ]])
y = np.array(df['charges'])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)
return X_train, X_test, y_train, y_test
现在,如果我们调用我们的“read_split”方法,我们的装饰器“@timethis”应该打印执行时间:
X_train, X_test, y_train, y_test = read_and_split(0.2)
让我们对我们的 fit 方法做同样的事情:
[@timethis](http://twitter.com/timethis)
def fit_model():
model = LinearRegression()
model = model.fit(X_train, y_train)
return modelmodel = fit_model()
对于我们预测方法:
[@timethis](http://twitter.com/timethis)
def predict():
result = model.predict(X_test)
return resultprediction = predict()
我将在这里停下来,但是您可以自己随意摆弄代码和数据。我鼓励您分析一些其他回归模型的执行时间,您可以使用这些数据构建这些模型,如随机森林或支持向量回归。
结论
总之,在这篇文章中,我们讨论了 python 中的函数包装器。首先,我们定义了三个函数来构建线性回归模型。我们定义了一些函数,用于读取和拆分我们的数据以进行训练,使我们的模型适合训练数据,并对我们的测试集进行预测。然后我们定义了一个函数包装器,允许我们报告每个函数调用的执行时间。我希望你觉得这篇文章有用/有趣。这篇文章中的代码可以在 GitHub 上找到。感谢您的阅读!
数据科学项目中的函数式编程
至少是一个我如何使用它和我学到了什么的小故事
我想分享一下我日常使用函数式编程(FP)的经验。我在这里的主要目的是挑战这种方法,并从数据科学界获得反馈。作为一名数据科学家,我大部分时间都花在 POCs(概念验证)和代码产品化之间。这种反复会带来很多麻烦,尤其是在 POC 阶段没有满足生产代码的约束时。当然,我的大部分代码并没有在生产中结束。如果我必须在调查期间编写一个生产就绪的代码,这将是一个巨大的开销。对我来说,有三种方法:
- 编写大块的代码。这很酷,因为你可以很容易地测试一个假设。我通常用这种方法来测试一个想法,我希望最多在一两天内得到答案。大多数时候,当我得到答案时,我会扔掉(99%)这段代码。如果我不得不在这段代码上工作更长的时间,复杂性将会每天增加,并且这段代码在进入生产阶段时会变成一场噩梦。我也称之为“笔记本方法”。
- 编写 OOP 代码。我喜欢 OOP 代码有很多原因。首先,当设计简单明了时,它易于使用。一个很好的例子是 Scikit-Learn 库。如果你理解了
fit
—predict
的概念,你基本上就理解了这个库的 90%。还有许多其他的库,其中的基本概念简单而有效。然而,当我开始一个新项目时,我不知道包装我的代码的最佳“概念”是什么。从一个概念开始,并在测试假设的同时维护它,这可能是对时间的极大浪费。 - 写入 FP 代码。我不是 FP 大师,我每天都在学习更多的东西。对我来说,FP 的主要优势是代码组织和可维护性。此外,当您需要加速代码时,并行化几乎是免费的。FP 在开始的时候可能有点难学,但是我认为这是更糟糕的投资。
无论如何,这三种方法各有利弊,根据你所从事的团队/项目的类型,任何一种都可能更适合你。我选择用第三种方法开始大部分时间,主要是因为开销非常低,并且回报是为项目的未来阶段赢得惊人的时间。正如我所说的,我不是一个 FP 大师,如果你认为我错了或者你觉得你可以用更好的方式来做,请添加评论和反馈。
数据科学家(至少我)花了很多时间来创建管道。换句话说,我们创建代码从数据到预测。管道中的步骤顺序可以在项目之间改变,但我或多或少总是做解析、清理、格式化、特征工程、培训等工作。在 POC 阶段,这些步骤中的每一步都可以分解为更小的步骤(格式化一些列、填充缺失值等),我们还希望有一个适当的测试步骤来评估管道。POC 的目标基本上是找到管道中最合适的步骤。在将 FP 用于我的研究并将代码转移到产品中之后,我学到了几个关键点,使得这个过渡更加平稳。
定义:什么是 FP?
函数式编程最简单的例子就是使用一个标准函数。
第 8 行,我使用函数map
将函数add_2
应用于列表中的所有值。这里没有什么新的东西,我们可以很容易地用 lambda 函数替换add_2
函数:lambda x: x+2
。
在这个例子中,有一个硬编码的值+2
要添加。那么,如果我想做同样的操作,但是要添加不同的值,会怎么样呢?然后,理论上,我需要创建一个不同的函数,比如说add_3()
。或者使用 lambda 风格:lambda x: x+3
。您还可以创建一个函数,该函数通过适当的操作返回一个函数:
在本例中,函数add(to_add)
返回一个函数,该函数可以使用map
以与上例相同的方式应用于列表。主要区别是值to_add
神奇地保存在创建的函数my_custom_add_function
中。这个概念对于分离参数和数据非常有用。在这个例子中,函数的参数是3
,数据对应于列表的值。
好的,但是 FP 和管道之间有什么联系呢?
让我们回到数据科学。在下面的例子中,我有一个数据集,我想在其中应用一些修改。
- 删除缺少太多值的列。
- 从 1 小时间隔到两个 2 小时间隔对数据进行重采样。
- 填充缺失值。
没有什么疯狂的复杂,只是时间序列的标准预处理步骤。您可以简单地编写如下代码:
如果你看一下preprocess
函数,你会发现我提到的 3 个步骤。这太棒了!您可以继续扩展preprocess
功能或使用您的特征工程添加新功能等
现在,让我们考虑使用函数式编程风格编写的相同代码:
没什么区别,对吧?只是多了几行代码…但是,您可以注意到,直到第 34 行才开始计算。还有一点,在这段代码中,你可以清楚地看到参数和数据的区别。参数被提供给create_...
函数,而数据稍后提供。第一个例子叫做命令式,第二个例子叫做声明式(以防万一你想谷歌一下)。
因此,现在考虑您想要更新您的代码,因为您发现 2 小时重采样不合适,并且您想要保留原始采样。轻松点。在命令式代码(example_pandas.py)中,您只需删除第 22 行并更改preprocess
签名。嗯,这可能是一件痛苦的事…尤其是如果preprocess
函数被许多其他代码共享的话。在声明性代码(example_pandas_FP.py)中,您可以删除第 32 行,将第 34 行改为fill_na(remover(random_df))
,这只会影响这段代码。直到这里,没有太多的差异。
现在,考虑这个函数create_pipeline
:
这个函数以一个函数列表作为参数,并返回一个函数,这个函数逐个调用每个函数。
现在,example_pandas_FP.py 变成了:
更干净的代码不是吗?步骤和参数被清楚地突出显示。您可以通过修改传递给create_pipeline
的函数列表来简单地添加或删除步骤。
当您要为每个单独的功能创建单元测试时,这种范例也会有所帮助。额外收获:如果你习惯了 FP 风格,你将会有一个清晰的模块化,可以跨项目重用。
Scikit-learn 还提供了一个管道对象。我经常使用它,我发现它很棒。我不再使用它的主要原因是它与fit
- transform
范式联系太紧密了。很多时候,我最终只是在fit
函数中传递数据,我发现这既没用又不方便。我发现我的实现更加灵活,但是,您仍然可以将这两种方法结合起来。
序列化
一般来说,管道和 FP 的一个很酷的技巧是序列化函数的能力。这是缓存中间计算的好方法,也是在生产中重用代码的简单方法。总的来说,我更喜欢用dill
而不是pickle
,因为它消除了很多麻烦,而且几乎总是对我有效。例如:
Spark 的(几乎)自由并行化
现在,如果我想将相同的管道应用于 1000 个数据集呢?一个快速的解决方案是并行执行。因为管线的参数化在创建管线时一次性完成,所以不需要传递参数。你只需要传递数据。如果不是 Spark 专家,您可以简单地使用 RDD 的map
— collect
功能。想法是使用 RDD 并行化我们的 1000 个数据集,并使用这些函数来获得结果。
这并不难:d .这对你来说非常有效。然而,在内存中加载 1000 个数据集可能不是一个好主意。为了避免这种情况,我在管道中加载数据集。所以sc.parallelize
不是用在数据集列表上,而是用在获取数据的键列表上。例如,CSV 文件名列表。然后,管道的第一个函数可以解释这些文件名,并在需要时加载数据。在这个例子中,为了简单起见,我展示了数据集的并行化,但是同样的概念也可以用于按列或按行并行化数据集。
函数估计量
你们中的一些人可能会想,“这里有一个明显的限制!如果管道不是线性的呢?”换句话说,如果您需要从一个数据集推断出一些结果,并将其应用到另一个数据集,该怎么办?例如,标准化或训练模型。我刚刚描述的范式打破了…事实上,并不完全是。这就是你想要使用函数估值器的地方。这是我从我在威盛科技的前任主管杰里米·泰勒那里学到的。这个想法是链函数创建。 *F(参数)——>G(训练 _ 集)——>H(预测)。*在 python 中:
现在,我们有 3 条管道可以协同工作。pipeline_preprocessor
是预处理数据的函数,pipeline_rf_creator
是创建随机森林预测器的管道,pipeline_create_prediction
是推断外部数据集预测的管道。最后一个管道可以序列化,并在以后用于推理。
业务逻辑= >组合
总的来说,一件棘手的事情是理清业务逻辑和计算。经验法则是避免在封装函数中使用if parameter == something
。需要的地方还是可以有if data == something
的。为了处理业务逻辑,您可以利用组合。当你使用create_pipeline
功能时就变得简单了。例如:
在本例中,管道的组成取决于use_sampling
参数。你可以想象各种各样的检查来定义管道。然而,从测试的角度来看,这个函数是最难测试的,因为您需要尝试所有的参数组合,以确保它不会在特定的用例中出错。这就是为什么我尽量保持简单的原因。
产品化
当我对我的调查感到满意时,我可以将我的代码转移到生产中。为此,我简单地删除了我创建的所有未使用的函数,并保留了相关的测试。过去,这一阶段需要我花费数周的时间,而现在,我可以在一两天内完成。没有债务要付,我的代码已经组织得很清楚了。事实上,我将代码集中于产生预期的结果,并且通过避免复杂的设计节省了大量时间。这种 FP 范式在我所有的代码中都是一样的,我的同事可以很容易地理解它并修改它。
无国籍的
转移到生产时,重要的一点是尽可能无状态。这确保了幂等性,这在云上部署时非常重要。我发现 FP 范型非常适合创建无状态代码,而且过了一段时间后,维护和调试变得容易多了。
我希望你喜欢阅读这篇文章。如果您有任何反馈或意见,请随时联系我或对本文发表评论。
我的 LinkedIn: 这里
Python 函数,解释过。
下面是理解 Python 函数的一种更直观的方式。
由布鲁纳托里诺通过坎瓦
什么是功能?
我们首先在高中数学中学习函数。数学中函数的一般概念是有一个输入(姑且称之为 x)和一个输出(姑且称之为 y)。我们表示这样一个数学函数:
y = f(x)
其中 f(x)可以是对我们称之为 x 的输入的任何变换(如加、减、乘、指数运算…),例如:
y = 2x + 1
其中 f 代表我们将输出的等式,乘以 2 再加 1,最重要的是,我们希望将它返回为 y。
为什么我要走这么远?编程语言总是使用基本的数学概念来构建它们的结构和逻辑。如果你理解了数学中的函数,你就已经理解了 Python(以及其他编程语言)中的函数。
我们如何在 Python 中表示函数?
让我们用上面的数学解释来帮助我们理解如何写函数。试着把编码想象成与计算机的对话,把 Python 想象成一种逐步表达命令的方式。
首先,我们需要定义函数的名称和输入 x(就像我们的数学例子一样)。我们从 define 中写出 def ,函数名(你可以把它想成数学中的 f),输入变量 x。我们称 x 为函数的参数。
def my_function_name(x):
在数学中,只写 f(x)会转换 x,但不会把 x 的值赋给任何我们以后可以引用的变量。所以我们需要添加一个返回语句到函数的末尾:
def my_function_name(x):
return y
我们正在使用 x 并返回 y,但是这两者之间有什么联系呢?我们如何计算 y?
如果你认为编码是一步一步地解释你的命令,那么在定义函数之后,在返回 y 值之前进行函数计算是有意义的。
def my_function_name(x):
y = 2*x + 1
return y
完事了吗?还没有!
定义函数和直接编写函数的区别在于:
y = 2*x + 1
print(y)
如果我们想在以后使用函数,我们可以用不同的 x 值保存它们。如果我们想在脚本中使用函数,我们需要调用它:
variable1 = my_function_name(4)
注意到这里有什么不同吗?当我们调用函数时,我们用 4 代替 x。实际上,此时我们希望 x 等于 4。我们称 4 为函数的自变量。函数的美妙之处在于我们从来不需要全局定义 x 是什么:当我们调用函数时,x 可以取我们写在括号中的任何值。
变量 1 呢?用 x 解释的同样的概念在这里也适用:在这一次我们调用函数,我们希望变量 1 是 y 。这意味着无论函数中的 y 是(2*4 + 1 = 9),我们都希望将该值赋给变量 1。
您可以继续在脚本的其余部分使用 variable1,就像您编写了以下代码一样:
variable1 = 9
酷,我还能用函数做什么?
回到数学,假设你有一个有两个输入的函数:
y = f(x,y)
你可以用 Python 函数做同样的事情,就像数学一样:
def my_function_name(x,y):
y = x + y
return y
调用函数时,需要与定义函数时最初使用的输入数量保持一致:
variable2 = my_function_name(1,2)
你也可以在定义你的函数时,通过写*arg 作为参数,不指定参数的数量。这就是它与数学有一点不同的地方:你可以在函数中调用任意多的参数。这里,我们创建一个列表,并将函数参数中的所有数字追加到列表中。
def my_function_name(*arg):
list_of_numbers = []
list_of_numbers.append(arg)
return list_of_numbers
例如,如果我们这样调用函数:
my_function_name(1,2,3,4,5,6,7,8,9)
您可以期待这样的输出:
Out: [1,2,3,4,5,6,7,8,9]
示例:矩阵计算器
为了说明我们如何在更高级的主题中使用函数,让我们看一个如何构建矩阵计算器的例子,它将返回 2x2 矩阵的行列式。
这里,我将函数名定义为 matrix_calculator ,并传递 4 个输入(也称为参数):x1、x2、y1 和 y2,它们用于创建名为 my_matrix 的矩阵。接下来,我计算行列式并返回它。
如果我用参数 1,2,3 和 4 调用函数:
matrix_calculator(1,2,3,4)
我将得到以下输出:
-2
让我们把计算器变得更先进。如果函数能够理解你是否想计算 2x2 或 3x3 矩阵的行列式,取决于你调用函数时使用的参数数量,那会怎样?
我们将通过 numpy 函数 np.linalg.det(A) 来计算行列式,因此我们将从导入 numpy 包开始。接下来,我将定义名为smart _ matrix _ calculator的函数,并通过使用*arg 作为参数向该函数传递尽可能多的参数。然后,我将把数字转换成一个列表,这样我们就可以对列表进行切片并创建矩阵。
如果我们传递的数字的长度是 9 ( if len(arg) == 9) 这意味着我们想要一个有 9 个元素的 3x3 矩阵。
我们通过以下方式对列表进行切片,将其转换为一个矩阵:
- 前三个数字分别是 x1、y1 和 z1
- 接下来的三个数字将是 x2、y2 和 z2
- 最后三个数字将是 x3、y3 和 z3
我们把它转换成同一行的数组,计算行列式,最后在函数结束时返回。
如果我们使用数字(1,2,3,4,1,2,2,1,9)调用函数来生成一个 3x3 矩阵:
smart_matrix_calculator(1,2,3,4,1,2,2,1,9)
我们可以期待它的回报:
Out: -51.0
如果我们使用数字(1,2,3,4)调用函数来生成一个 2x2 矩阵:
smart_matrix_calculator(1,2,3,4)
我们可以期待它的回报:
-2.0
感谢您的阅读。我希望我对什么是函数以及它们是如何工作的理解能够帮助你更好地理解这个基本的编程技巧。
如果你想要更多关于其他 Python 主题的教程,请在评论中告诉我!
Power BI 中的函数
这是什么?我们什么时候使用它?我们如何制造一个?
埃里克·克鲁尔在 Unsplash 上的照片
清洗,扯皮,总结。我不知道你怎么想,但是我喜欢这个数据分析的过程。Power Query 是我在这方面使用的主要工具。以后我会写更多关于权力查询的内容。
我清理是因为数据不干净。我打扫卫生是因为我需要好的数据来分析。我努力为连接找到正确的“形状”。如果您必须手动清理数据,那就太可怕了,而且很容易出现清理错误。想象错误之上的错误?你真正要分析的是什么?
别担心。功能——就像上面那些可爱的小机器人可以来救援一样。
作者图片
究竟什么是函数?谷歌快速搜索给出了这个定义。
“功能是一组有组织的、可重复使用的代码,用于执行单个相关的动作。…不同的编程语言对它们的命名不同,例如,函数、方法、子例程、过程等。”
这里的关键词是可重用。要成为一个功能,它必须是可重用的。这意味着您可以使用这段代码,并将其应用于不止一个文件,还可以应用于其他文件。我认为函数就是接受输入,执行一组定制的过程,然后给出输出。
你今天用的很多东西,比如,BI 中的 sum 函数到 joins 都是函数。
例如,看看 M(超级查询语言)中的这个函数
图片来自作者
问题是…有时我们没有可以满足我们需求的功能。我们希望对各种文件应用一组自定义的过程。这就是我们必须使用它的原因和时间。
让我们在 Power Query 中过一遍如何在 Power BI 中创建函数。生活中有那么多清洁和争吵,我相信这对我们会很有用。
正如简笔画所提到的,这些年来我们有几个不同的文件。我们需要对所有文件执行相同的操作。
作者图片
我们不想对每个文件都执行这个过程,但是我们希望函数为我们执行这个过程。
首先,让我们将所有文件放入一个文件夹中。
作者图片
加载完所有文件后,您应该会看到它们如下所示。
让我们从创建主文件的“副本”开始——我们想从这个副本构建一个函数。
作者图片
现在有了一个副本,我们只保留第一行来构建函数。
作者图片
Power BI 只保留第一排。很好。
作者图片
这里有一个巧妙而重要的技巧——您可以键入引用该列的[Content]和引用实际“单元格”本身的{0}。如果你有更多的技术理解,我引用列并返回一个列表,然后引用列表值 0 返回二进制文件。
如果你不喜欢打字,你也可以点击二进制文件本身的“向下钻取”。
作者图片
Power BI 干净利落地加载文件,不需要那些自动转换。
作者图片
现在,让我们使用分组方式进行快速总结。您可以在面板顶部的“变换”功能区下找到“分组依据”。
作者图片
这是我们对其中一个文件的结果。
作者图片
这正是我们所需要的。
嗯…简笔画。
作者图片
还没做,让我们把它做成一个实际的函数。
你可以通过转换功能区下的高级编辑器选项来实现。
Power BI 把所有的步骤都记录下来,变成 M 脚本给我们看。如果你熟悉 Excel,它就像一个录制的宏。我们只需要更改引用,以便它可以应用于其他文件。
你不需要知道如何把下面的 M 代码全部打出来。Power BI 的妙处在于,它为你记录了所有这些步骤。在不久的将来,我会写更多关于如何阅读 M 脚本的内容。
这是当前的脚本。
let
Source = Folder.Files("MY_DRIVE"),
#"Removed Top Rows" = Table.Skip(Source,1),
#"Removed Other Columns" = Table.SelectColumns(#"Removed Top Rows",{"Name", "Content"}),
#"Kept First Rows" = Table.FirstN(#"Removed Other Columns",1)[Content]{0},
#"Imported CSV" = Csv.Document(#"Kept First Rows",[Delimiter=",", Columns=5, Encoding=65001, QuoteStyle=QuoteStyle.None]),
#"Promoted Headers" = Table.PromoteHeaders(#"Imported CSV", [PromoteAllScalars=true]),
#"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"Year", Int64.Type}, {"Sex", type text}, {"Name", type text}, {"Job Title", type text}, {"Salary", Int64.Type}}),
#"Grouped Rows" = Table.Group(#"Changed Type", {"Year", "Sex"}, {{"Average Salary", each List.Average([Salary]), type nullable number}}),
#"Rounded Off" = Table.TransformColumns(#"Grouped Rows",{{"Average Salary", each Number.Round(_, 0), type number}})
in
#"Rounded Off"
不要让这个压倒你,我们唯一关心的是 Csv。文档部分。
让我们删除 Csv 上面的所有内容。文档部分并声明我们的变量。我选择(X 为二进制)是因为我们希望这个函数有一个二进制输入。
还记得文件夹里的那些 csv 文件吗?它们是二进制文件。
作者图片
这是我们的功能!
作者图片
现在让我们将它应用到我们的文件中。让我们“调用”我们的自定义函数。您可以在“添加列”功能区下找到“调用”。
听起来像是《哈利·波特》里的情节,对吗?
作者图片
在这里!
作者图片
让我们点击“扩展”进入决赛桌。
作者图片
哒哒!
按年平均工资分列的男性和女性。
现在,这是一个简单的数据集,但你也可以将连接和其他更复杂的转换构建到一个函数中,并将其应用于所有文件。
TL:DR ?我为你做了这个。
作者图片
希望你喜欢这篇文章。
如果你想看视频——Curbal 是一个真正教会我很多关于功能的频道,当然还有 Power BI。
注意安全,希望这能帮助您的数据之旅!
幂 BI 中的函数—(表。FindText)
…使用表格。FindText 可在文件中的任意位置搜索任何内容。
Rabie Madaci 在 Unsplash 上拍摄的照片
我一直对简单的查找功能感到惊讶。您可以找到任何符合条件的内容,即使您的搜索项目隐藏在不同的工作表、打开的文本或表格中。
想象一下,你可以搜索原始数据中出现的任何内容。即使它隐藏在打开的文本字段中的某个地方。相当神奇!
这有什么帮助?如果你正在寻找一个关键词或一组重要的数字,但你不知道去哪里找,这将非常有帮助。该号码可以在开放文本字段中,也可以在名称字段中,等等。
一旦找到,你就可以把这些搜索结果结合起来,用它讲一个故事。有时,做一个发现只是出于好奇,但有时你可能会对你的发现感到惊讶。
我用过这张桌子。FindText 功能是一个欺诈检测练习,它非常方便,因为我的表很大,而我只是在寻找一个人。这个简单的函数还有其他用处。您可以使用它作为帐户记录的历史搜索,并回答您可能有的一些问题。
我们开始吧!
我们来看看表。微软的 FindText 函数。
作者图片
简单地说,这个函数使用一个表作为输入,并在该表中搜索一个条目。然后,它返回一个表,其中包含与您的条件匹配的行。
让我们试试这个。
拿桌子
第一步很棘手——也许你很幸运,已经为你设置了一列表格,但是我必须将文件夹中的二进制文件转换成表格格式。
这并不困难,我使用这个功能。
(FOLDER_PATH , FILENAME ) =>
let
Source = Csv.Document(File.Contents(FOLDER_PATH&FILENAME)),
#"Promoted Headers" = Table.PromoteHeaders(Source, [PromoteAllScalars=true])
in
#"Promoted Headers"
如果您使用 folders 选项连接 Power BI 并有一个二进制文件列表,您可以使用上述函数将这些二进制文件转换成表格。
要添加新功能,只需进入电力查询 > 新建空白查询 > 高级编辑 > 粘贴到脚本
这个查询中的 FOLDER_PATH 和 FILENAME 参数是 Power Query 在您连接到它时为您准备的列。
这里有一个例子。
作者图片
现在,我将使用调用函数选项来应用该函数。如果你对如何使用函数感到困惑,这里有一篇文章给你,帮助你入门。功能很棒!
在我应用这个函数之后,我得到了这个输出。
作者图片
太好了!现在我有一列表来应用我的表。FindText()。
应用函数
我将删除不需要的列,只反映表格列。
我们现在需要做的就是通过创建一个新列来应用我们的函数。我想看看“Apple”这个词是否出现在我的表中。
作者图片
现在,当你向下钻取时,你可以看到“苹果”出现在“口味”列下的表格中。
作者图片
如果我将它移动到另一个选项,您可以看到“Apple”也出现在“Appleton”下的“Stand”列中。
作者图片
现在,为了分析您的结果,您可以将所有文件组合起来,这些文件将包含只包含单词“Apple”的行。
是的,我的例子很简单,我只有 10 个表,但是这样想,如果你能够理解如何构建一个简单的原型,你可以在你的日常工作中使用任何数量的表:)
带走:
- 桌子。FindText()是一个简洁的函数。发挥你的想象力,你可以在任何列和表格中找到你的搜索选项。
2.删除一些开始时不需要的列,这样运行起来会快很多!
3.棘手的部分不是使用搜索功能,而是将二进制文件转换成表格。
你可能会发现还有其他有用的功能。
如果你已经知道你要找的关键词或数字,并且你知道要找哪一栏,这些功能将会很有用。他们是的表。包含(),表。ContainsAll() 表。ContainsAny() 。
我将来也会写一篇关于这些的文章。
注意安全,感谢您的阅读!
创建和使用 R 函数
关于如何使用它们的基础知识
你经历过下面这种情况吗?
作者图片
我似乎总是碰到它。也许只是我的运气…
我喜欢函数。我喜欢建造它们,和它们一起工作,创造它们。没有什么比有人说一项任务需要一周时间更好的了,但是实际上,你知道它可能在 10 分钟内就能完成。
功能很棒。就像小机器人一样,它们可以把你从错误和重复的工作中拯救出来。如果你还没有看过我写的关于在 Power BI 中使用函数的文章,在这里。它将为你提供使用函数的基本知识,如果你使用平面文件,它很可能会节省你大量的时间。
我们用的很多东西已经是函数了。我们只是不去想它。
以 R 中的均值函数为例,它先取函数名,然后取输入。所以 mean()是一个函数。与 Power BI 非常相似,AVERAGE([列名])是一个函数。
在这篇文章中,我希望我能向你展示如何用 r 中的函数做一些工作。
让我们开始吧!
作者图片
我主要处理大量平面文件(如果有一天我可以连接到数据仓库,那就太好了,但那是另一回事了)。
我想在 r 中对一组平面文件执行一组转换,我们该怎么做呢?
不难。这非常类似于我们如何创建 Power BI 函数。关键是我们需要对一个文件执行一组转换,并将其设置为构建我们功能的原型。
首先,让我们使用 setwd 设置我们的工作目录并加载我们的包。我们将使用 tidyverse 和 rlist 。
需要设置工作目录,因为 R 需要知道它正在工作的文件夹。r 需要知道将文件导出到哪里,以及从哪里读取文件。我们将把我们的工作目录分配给一个变量调用 File_Directory
library(tidyverse) #loading packages
library(rlist)File_Directory = setwd("Your folder containing your files")
#setting up the work directory
现在,让我们使用 rlist 的 package list.filter 函数和 str_detect 来过滤。csv”平面文件。我们将把它赋给变量 All_Files 。
library(tidyverse) #loading packages
library(rlist)File_Directory = setwd("Your folder containing your files")
#setting up the work directory All_Files = File_Directory %>%
list.files() %>%
list.filter(str_detect(., ".csv")) //just want the csv files and the "." here is a place holder for the output in the list.files.
list.files 返回 File_Directory 变量中所有文件的名称。然后 list.filter 用 str_detect 对 csv 文件进行过滤。此外,如果您不熟悉% > %,它被称为“管道”,它将结果从一行传输到另一行。
All_Files 的结果是我们想要清除的文件名的列表。
这是关键——我们需要手动加载其中一个文件,并应用我们的转换步骤。
我想按年份和性别分组,按平均工资汇总。这些是我想应用于所有文件的转换步骤。
Dataset_2007 %>% # I manually loaded this file from Import Dataset on R Studio
group_by(Year,Sex) %>%
summarize(Avg_Sal = mean(Salary, na.rm = T))
让我们检查它是否工作。您必须选择脚本并按下 cntrl+R 来运行脚本。
作者图片
它工作了。
既然原型起作用了,那就把它变成函数吧。
My_Function = function(X) {
read_csv(X) %>%
group_by(Year, Sex) %>%
summarize(Avg_Sal = mean(Salary))
}
这里我们添加了 read_csv ,因为我们希望我们的函数读取目录中的文件并应用转换。
这样做比把每个文件都加载到 R Studio 中更有效率。我们可以把这个函数叫做 My_Function 。我对函数名没什么想象力。
在这里,该函数采用“X”作为我们的输入,使用 read_csv 读取文件,然后将表传递到下一行,即 group_by、,然后根据平均工资汇总。
这里它读取 X,这是我们所有文件中的第一个文件,并执行集合转换,等等。
现在让我们使用这个函数并应用到 All_Files 变量中的所有文件。记住变量 All_Files 在一个列表中保存我们的文件名。
在 R 中,有一个简洁的小东西叫做 map ,基本上, map 接受你的函数并把它映射到你所有的输入,你也可以选择返回一个数据框、一个列表或其他东西中的所有结果。使用 map 超出了本文的范围,但是让我们在这里使用 map_df 。 map_df 代表地图数据帧。
map_df(All_Files, My_Function)
以下是数据框中的结果。
作者图片
现在您可以选择使用 r 中的 write.csv 调用将结果导出到 csv 格式的同事。
map_df(All_Files, My_Function) %>%
write.csv("My_Results.csv", row.names = F)
这是所有的一切。
library(tidyverse) #loading packages
library(rlist)File_Directory = setwd("Your folder containing your files")
#setting up the work directoryAll_Files = File_Directory %>%
list.files() %>%
list.filter(str_detect(., ".csv")) #filtering for csvDataset_2007 %>% # function prototype
group_by(Year,Sex) %>%
summarize(Avg_Sal = mean(Salary, na.rm = T))My_Function = function(X) {
read_csv(X) %>%
group_by(Year, Sex) %>%
summarize(Avg_Sal = mean(Salary))
} #creating the functionmap_df(All_Files, My_Function) %>%
write.csv("My_Results.csv", row.names = F) #mapping it exporting to a csv file call My_Results
这只是在 r 中使用函数的一个非常简单的例子。
老实说,生活中的事情从来不会这样发展。总会在某个地方打嗝。您今天可能会尝试这样做,并意识到您的数据集都在不同的列中,您必须首先清理它。也许一个包不会为你加载。可能是电脑的问题。
总会有问题需要解决。
我希望你保持鼓励,保持安全,并在你的旅程中继续前进!
强化学习的基本迭代方法
学习价值和策略迭代能掌握多少强化学习?很多。
本文着重于理解基本的 MDP(在这里简单回顾一下),并将其应用于与基本强化学习方法的关系。我将重点介绍的方法是值迭代和策略迭代。这两种方法支撑了 Q 值迭代,直接导致 Q 学习。
来源——加州大学伯克利分校 CS188。
可以在这之前,或者之后看的我的一些相关文章(有意独立):
- 什么是马尔可夫决策过程?
- 强化学习的隐藏线性代数。
Q-Learning 开启了我们正在进行的深度强化学习浪潮,因此它是强化学习学生手册中的一个重要部分。
回顾马尔可夫决策过程
马尔可夫决策过程是支持强化学习的随机模型。如果你熟悉,你可以跳过这一部分,但是我增加了解释为什么每个元素在强化学习环境中都很重要。
定义(对 RL 有影响)
- 一组状态 s ∈ S ,动作 a ∈ A 。状态和动作是代理所有可能的位置和动作的集合。 在高级强化学习 中,状态和动作变得连续,这就需要我们重新思考算法。
- 一个转移函数 T(s,a,s’)。给定当前位置和提供的动作,决定下一个状态跟随的频率。 在强化学习 中,我们不再能够访问这个函数,所以这些方法试图近似它或者在采样数据上学习隐含。
- *一个奖励函数 **R(s,a,s’)。*这个函数表示每一步获得多少奖励。 在强化学习 中,我们不再能访问这个函数,所以我们从采样值 r 中学习,引导算法探索环境,然后利用最优轨迹。
- *[0,1]中的折扣因子 γ (gamma) ,将当前(下一步)的价值调整为未来奖励。 在强化学习 *中,我们不再能够访问这个函数,****γ(gamma)**通过类似贝尔曼的更新来控制大多数所有学习算法和规划优化器的收敛。
- 起始状态 s0 ,也可能是终止状态。
MDP 就是一个例子。来源——我在 CS188 做的一个讲座。
重要的价值观
MDP 有两个重要的特征效用-状态值和机会节点的 q 值。任何 MDP 或 RL 值中的 ***** 表示一个 最优量 。
- 一个状态的价值:一个状态的价值是从一个状态出发的奖励的最优递归和。
- 一个状态、行动对的 Q 值:Q 值是与一个状态-行动对相关的折扣奖励的最优和。
最佳值与最佳动作条件 q 值相关。然后值和 q 值更新规则非常相似(加权转换、奖励和折扣因子)。顶部)值与 q 值的耦合;mid) Q 值递归,bot)值迭代。加州大学伯克利分校的 cs188。
来源—作者。
走向强化学习
价值迭代
了解所有状态的值,然后我们可以根据梯度采取行动。值迭代直接从贝尔曼更新中学习状态的值。在一些非限制性条件下,贝尔曼更新保证收敛到最优值。
学习一个政策可能比学习一个价值观更直接。学习一个值可能需要无限长的时间才能收敛到 64 位浮点数的数值精度(想想每次迭代中常数的移动平均,从估计值 0 开始,它将永远添加越来越小的非零值)。
策略迭代
学习与价值观一致的政策。策略学习递增地查看当前值并提取策略。因为动作空间是有限的,所以希望能比值迭代收敛得更快。从概念上讲,动作的最后一次改变将在小的滚动平均更新结束之前发生。策略迭代有两个步骤。
第一个称为策略提取,这是如何从一个值到一个策略——通过采取最大化超过期望值的策略。
策略提取步骤。
第二步是政策评估。策略评估采用一个策略,并根据策略运行价值迭代。样本永远与策略联系在一起,但是我们知道我们必须运行迭代算法来减少提取相关 动作 信息的步骤。**
政策评估步骤。
像值迭代一样,由于底层的贝尔曼更新,策略迭代保证收敛到最合理的 MDP。
q 值迭代
知道最优值的问题是很难从中提炼出政策。argmax
操作符明显是非线性的,难以优化,因此 Q 值迭代向直接策略提取迈进了一步。每个状态下的最优策略就是该状态下的最大 q 值。
MDP 的 q 学习。
大多数指令以值迭代开始的原因是,它更自然地嵌入到贝尔曼更新中。 Q 值迭代需要将两个关键 MDP 值关系替换在一起。这样做了之后,离 Q-learning 就差一步了,这个我们会了解的。
这些迭代算法是怎么回事?
让我们确保你理解所有的条款。本质上,每次更新由求和后的两项组成(可能还有一个选择动作的max
项)。让我们把括号去掉,并讨论它们与 MDP 的关系。
状态空间奖励映射。
第一项是对乘积 T(s,a,s’)R(s,a,s’)的求和。该术语代表给定状态和转换的潜在价值和可能性。 T 术语或转换,支配从转换中获得给定奖励的可能性(回忆,atuple,a,s’确定一个元组,其中一个动作将一个代理从状态带到状态s’【T42 这将做一些事情,比如用高回报来衡量低概率状态,用低回报来衡量频繁状态。****
来自数据的递归更新。****
下一项决定了这些算法的、。它是迭代算法最后一步数据的一个加权— V ,上面有项。这从邻近的州提取了关于价值的信息,这样我们就可以理解长期的转变。将这一项视为大多数递归更新发生的地方,第一项是由环境决定的加权先验。
收敛条件
所有的迭代算法都被告知“在某些条件下收敛到最优值或策略。”你问的那些条件是什么?
- 总状态空间覆盖率。条件是在有条件的策略下达到所有状态、动作、next_state 元组。否则,来自 MDP 的一些信息将会丢失,值可能会停留在初始值。
- ****贴现因子γ < 1。这是因为任何可以重复的循环的值都可以并且将趋向于无穷大。
谢天谢地,在实践中,这些条件很容易满足。大多数探索都有一个ε贪婪性,包括随机行动的机会,总是(因此任何行动都是可行的),非 1 折扣因子导致更有利的性能。最终,这些算法可以在很多环境下工作,所以它们绝对值得一试。
强化学习
我们如何把我们所看到的变成一个强化学习的问题?我们需要使用样本,而不是真正的 T(s,a,s ')和 R(s,a,s ')函数。
基于样本的学习——如何解决隐藏的 MDP
MDPs 中的迭代方法与解决强化学习问题的基本方法之间的唯一区别是 RL 从 MDP 的底层转移和奖励函数中采样,而不是将其包含在更新规则中。我们需要更新两个东西,一个是对 T(s,a,s’) 的替换,另一个是对 R(s,a,s’) 的替换。
首先,让我们将转换函数近似为每个观察到的元组的平均动作条件转换。所有我们没见过的值都用随机值初始化。这是基于模型的强化学习(我的研究领域)最简单的形式。****
转移函数的近似。基于高级模型的强化学习研究的基础。
现在,剩下的就是记住如何处理奖励,对吗?但是,我们实际上对每一步都有奖励,所以我们可以侥幸逃脱(方法通过许多样本平均出正确的值)。考虑用一个采样奖励来近似 Q 值迭代方程,如下所示。
基于样本的 Q 学习(实际 RL)。
上式为 Q-learning 。我们从一些填充了随机值的向量 Q(s,a)开始,然后我们收集与世界的交互并调整 alpha。α是一个学习率,所以当我们认为我们的算法收敛时,我们会降低它。
结果表明,Q 学习的收敛与 Q 值迭代非常相似,但我们只是在用不完整的世界观运行算法。
机器人和游戏中使用的 Q 学习是在更复杂的特征空间中,用神经网络逼近所有状态-动作对的大表。关于深度 Q-Learning 如何震惊世界的总结,这里有一个很棒的视频,直到我写下自己的相关文章!
这些是如何融合的?
有什么简单的收敛界限吗?
towardsdatascience.com](/convergence-of-reinforcement-learning-algorithms-3d917f66b3b7)
更多?订阅我关于机器人、人工智能和社会的时事通讯!
一个关于机器人和人工智能的博客,让它们对每个人都有益,以及即将到来的自动化浪潮…
robotic.substack.com](https://robotic.substack.com/)**
基础营销分析:RFM 分析
威廉·艾文在 Unsplash 上的照片
通过最近频率货币模型和用 Python 实现的管理细分机器学习模型
如今,要成为一名成功的营销专业人士,营销分析是一项必备技能。因此,具有经济学和统计学背景的营销专家通常被认为比其他人更有优势。但是不管你的背景如何,仍然有一个简单的分析技术你可以利用——RFM(最近频率货币)模型。
RFM 分析是一种营销分析工具,用来确定你的客户是最好的。RFM 模型基于 3 个定量因素:首先是最近度(R ),它显示了客户购买的时间;然后是频率(F ),它告诉客户多久购买一次;最后,货币(M),它显示了客户花了多少钱。
在这篇短文中,我们将通过 Python 环境探索零售数据集,向您展示这种营销分析技巧。(将使用一个非常简单的 SQL 语句来实现这个目标)
作为一个读者,没有什么比弄脏自己的手更有收获的了,走吧!
数据
- 分析中使用的数据集可从这里下载。
- 我们没有使用所有的列。我们将主要关注客户 id、订单日期和销售额。
- 销售额:每笔订单的总金额。
RFM 分析和分割
- 像往常一样,首先要做的是导入必要的库。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pandasql import sqldf
from sklearn.preprocessing import scale
from scipy.cluster.hierarchy import linkage, dendrogram, cut_tree
- 让我们导入数据并检查它的前 5 行。
df = pd.read_csv('data.csv')
df.head()
检查数据的前 5 行
- 将 order_date 列转换为日期格式。
- 然后我们需要找出数据集的持续时间。
df["order_date"] = df["order_date"].astype('datetime64[ns]')
print(df["order_date"].max(),"\n",df["order_date"].min())
- 创建一个新列,并将其命名为 days_since。这是为了显示最后一次购买的日期。
df['days_since'] = (pd.Timestamp('2020-01-01') - df['order_date']).dt.days
个人认为这是文章最有趣的部分。我们将只使用一行 sql 代码来计算最近、频率和平均购买量。
- “GROUP BY 1”表示结果集的第一列。这与我们的 python 语言完全不同,如您所见,别名为 1 的列是不存在的。
- 然后我们检查新的数据集“rfm”。
rfm = sqldf("SELECT customer_id, MIN(days_since) AS 'recency', COUNT(*) AS 'frequency', AVG(sales) AS 'amount' FROM df GROUP BY 1", globals())rfm.describe()
数据帧“rfm”
- 现在将数据可视化。
rfm.recency.hist(bins=20)
崭新
customers.frequency.hist(bins=6)
频率
customers.amount.hist()
数量
观察:
- 大多数顾客是最近买的。
- 大多数顾客一年中只来购物一次,很少光顾超过两次,这意味着在顾客满意度方面需要做很多工作。
- 平均订单值位于图表(金额)的左侧,这是合理的。
- 现在,我们将客户数据复制到一个新的数据框中。
- 并将 customer_id 设置为索引
new_data = rfm
new_data.head()
new_data = new_data.set_index(new_data.customer_id).iloc[:,1:4]
new_data.head()
- 然后我们对数量和图形进行对数变换。
new_data.amount = np.log(new_data.amount)new_data.amount.hist(bins=7)
对数转换后的数量
- 标准化变量。
new_data = pd.DataFrame(scale(new_data), index=new_data.index, columns=new_data.columns)
new_data.head()
新数据数据框架
运行分层分段
- 取标准化数据的 10%样本。
new_data_sample = new_data.iloc[::10, :]
- 对距离度量执行分层聚类。
c = linkage(new_data_sample, method='ward')
- 将分段设置为 3,
- 并查看频率表。
members = pd.DataFrame(cut_tree(c, n_clusters = 3), index=new_data_sample.index, columns=['ClusterNumber'])members.ClusterNumber.value_counts(sort=False)
- 展示前 10 名顾客。
members.iloc[0:10]
客户表
结论:
是不是很简单,简单的脚本,简单的工作流程!
有了这个客户表(上表),营销经理就能够针对每个客户的细分市场和相关的营销活动。
例如,客户群 2 是那些具有较高购买频率和中等消费金额的客户群。营销人员可以通过积极的优惠券营销或频繁的激励电子邮件来瞄准这一客户群。你也可以用各种各样的参数,如折扣价值、持续时间、打折产品等,开展小规模的活动,来测试这群客户。
而那些在第一部分的人,他们是那些很少花钱的休眠者。要恢复它们并不容易,因此暂时跳过它们可能是个不错的选择。
最后,分析的 Jupyter 笔记本可以在 Github 上找到。
编码快乐!
要查看我的其他中国传统营销博客,这里有链接,
概率推理的基本问题
肖恩·辛克莱在 Unsplash 上拍摄的照片
如果你是机器学习从业者,为什么要关心采样?
到目前为止,我知道很多人从事 ML 研究或者研究机器学习算法。然而,他们中的大多数人不知何故没有意识到机器学习所基于的基本问题,即概率推理的问题。这篇文章的重点可能是将你的注意力转向你在编写机器学习算法时可能没有考虑到的问题。
为什么我们首先要讨论概率?随机性从何而来?真的有随机变量这种东西吗?最后,我们希望预测一些相对具体的东西,给定图像的类别标签,给定马尔可夫决策过程中某种状态描述的最佳行动。可以说,这些事情没有什么随机性。从随机的意义上来说,一个对象实际上并没有被赋予一个类标签。一头牛可能不是一头牛,它肯定是一头牛。
另一方面,我们有一些问题,比如不同风格的无监督学习,我们可能希望降低数据的维度,对数据进行聚类,学习反映数据概率分布的生成模型。所有这些味道都可以用概率来表示。但是,将一个潜在的低维表示分配给一个数据点并不是真正随机的。我们希望将输入数据点直接映射到潜在表示(一个聚类,一个低维的潜在变量)。
那么,我们最后为什么要用随机性和概率呢?主要论点是**不存在随机性。**当我们谈论机器学习世界中的概率分布、密度时,我们实际上谈论的是什么。**我们实际上谈论的是信息或不确定性。**概率度量反映了我们对给定事件有多少信息。让我们看一个监督的例子,一个包含一头牛的图像。当我说,图像包含一头牛的概率是 0.9。这并不意味着奶牛有时就在那里,这意味着我 90%确定这张照片里有一头奶牛。也许是因为图像没有足够的信息来完全确定它包含一头奶牛,或者也许是我的模型是错误的…谁知道呢。
**随机性从何而来?**笑点在于没有随机性。让我们讨论一下先验分布。这意味着我有一个模型参数的先验分布,或者说假设空间?这意味着我比其他人更确定特定的配置,特定的假设。这并不意味着模型是随机的,它可以根据潜在的随机过程而变化。
当我们进行机器学习时,我们的目标主要是(以不同的形式)以下,在最一般的意义上来说。给定某种类型的数据,我们希望推断出最能描述数据的模型的一些参数。如果你想成为贝叶斯,那么你将坚持这些不确定性的概念。下面是贝叶斯法则给定数据 D 来推断模型参数 θ 。
p(θ) 我们称之为先验, p(D|θ) 似然,LHS(左手边)就是后验。后验仅仅表达了我们对参数的确定程度。分母中是边际超过的 D 。请注意,我们不能神奇地访问这个边际,我们需要以某种方式评估它。为了得到分母的积分公式,我们可以从 θ 和 D 的联合分布中忽略θ。在这种情况下,我们有下面的贝叶斯规则公式:
等效地,我们可以在积分中分离分母中的项,使得它包含 θ 上的似然和先验。
为什么这是一个难题?ML 从业者说评估这个积分是困难的,但是理解它为什么困难有时可能是棘手的。首先,分母中的 θ 和分子中的不是同一个。积分基本上意味着我们对所有可能的参数 θ 的联合分布求和。现在,假设这个 θ 是神经网络的参数,它们是实数。分母中有无限多的参数配置需要评估。这显然很难处理。即使我们认为实数仅限于 32 位精度,可能的配置数量也是用于存储模型的位数的指数。此外,评估积分的过程涉及到采样,显然,我们的采样越好,我们就能更好地预期我们的估计。
当你继续阅读时,记住以下几点是很有用的:
在估计和采样的情况下,计算复杂度是重要的。
然而,情况并非没有希望。虽然有些积分不能求值,但是我们还是可以估计的。估计一个积分有不同的方法,其中一种是(最基本的一种)蒙特卡罗抽样(注意名字的抽样部分)。所以我们能做的就是画出一定数量的参数 θ 并在积分内对函数求值(注意我们假设我们有访问函数的权限对其求值),看起来是这样的:
这是一个相对简单的估计量,它是无偏的,这意味着它是正确的,并在无限数量的样本下产生精确的正确结果。然而,它也有一个缺点。
可以证明,对于蒙特卡罗估计量,估计误差随着样本数的平方根而渐近下降。
这意味着,如果我们想要对目标价值有一个大概的估计,这可能已经足够好了。但是如果我们想要一个高精度的估计,也许存在更好的具有更快收敛特性的选择。此外,我们可能不喜欢经常从参数分布中取样,因为取样可能很昂贵。
从分布中取样的计算考虑
到目前为止,我们已经讨论了计算后验概率的贝叶斯规则中分母的计算问题。在蒙特卡罗估计中,我们需要抽取θ的样本来估计积分。但是在 θ 上的分布到底是什么样的呢?你有没有想过采样实际上是如何工作的,当使用 Python 或 R 库时,我们如何得出一个“随机”数?即使我给你一个能够从均匀分布中采样的采样器,你如何使用它从更复杂的分布中采样呢?事实证明,采样问题本身就是一个迷人的问题,无论是计算上还是数学上。
取样时,我们想问自己的主要问题如下。如果我们有可能从一个简单的概率分布中有效地抽样,我们如何从一个更复杂的概率分布中抽样呢?事实证明,从复杂分布中采样是一个重要的问题,并且构成了 ML 中许多方法的基础。举几个例子:离线强化学习、规范化流程、变分推理……但是这个讨论我留到以后的文章中。
相关文章
信息论中的“信息”是什么 —了解更多不确定性和信息概念的关系。
基础统计学
探索时间序列建模
用 Python 代码进行时间序列建模
时间序列数据在我们身边无处不在,从股票市场价格到你所在城市的每日气温。顾名思义,时间序列数据的 x 轴就是时间。我们总是站在当前的时间点。在 x 轴的左侧,我们正在回顾过去。如果幸运的话,简单的一瞥,也许能发现过去的一些周期性模式。或者,如果我们付出更多的努力,并引入一些其他变量,我们可能会得到一些“相关性”来解释我们过去的数据。但是,我们能评估这些其他变量对数据建模的效果吗?这些“相关性”在未来会成立吗?我们能指望这些过去的“相关性”并做出预测吗?
如果你也有这些问题,让我们一起探索时间序列数据的分析和建模!我将使用 Python 进行一些实验和统计测试。使用 Python 学习统计学的一个好处是,我们可以使用已建立的库并绘制漂亮的图表来更好地理解复杂的统计概念。
未来,我还计划拥有以下职位:
用 Python 代码进行时间序列建模:如何分析单个时间序列变量 。
用 Python 代码进行时间序列建模:如何分析多个时间序列变量 。
用 Python 代码进行时间序列建模:如何用线性回归进行时间序列数据建模 。
由于我将从非常基本的统计概念开始,到更复杂的分析和建模,如果你是时间序列数据的初学者,我强烈建议你从这篇文章开始。
1.总体、样本和估计量
人口有一个潜在的分布过程,我们通常无法准确知道。我们能做的是从总体中抽样,并用样本来估计总体。但是如何选择合适的估计量呢?定义一个好的估计量有三个特性:无偏、一致和有效。
当样本参数的期望值等于总体参数时,估计量是无偏的:
如果样本参数的方差随着样本量的增加而减小,则估计量是一致的。
在样本容量相同的情况下,方差越小的估计量越有效。
一个好的评估者是无偏的、一致的和有效的。
2.概率密度分布
概率密度分布(PDF)用于指定随机变量落在特定值范围内的概率。某一 x 处的概率密度表示为 f(x)。通过在(x1,x2)的范围内对 f(x)应用积分函数,可以计算 x 落入(x1,x2)的概率。
正态分布的概率密度函数和概率密度函数
3.中心极限定理和大数定律
中心极限定理指出,当样本容量较大时,独立随机变量的样本均值服从正态分布。通常,当样本量大于 30 时,大样本量的要求被认为是满足的。独立随机变量可以服从任何分布,而这些独立随机变量的样本均值服从正态分布。
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import normmeanList = []num_trials = 10000
num_observations = 1000for i in range(num_trials):
# sample from uniform distribution
numList = np.random.randint(1,7,num_observations)
# sample from normal distribution
#numList = np.random.normal(loc=0,scale=1,size=num_observations)
# sample from poisson distribution
#numList = np.random.poisson(lam=1,size=num_observations)
meanList.append(np.mean(numList))mu, std = norm.fit(meanList)
fig, ax = plt.subplots()
ax.hist(meanList, bins=20, density=True, alpha=1, color='#4495c9')xmin, xmax = ax.get_xlim()
x = np.linspace(xmin, xmax, 100)
p = norm.pdf(x, mu, std)
ax.plot(x, p, 'k', linewidth=4)ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.set_yticks([])
plt.show()
大数定律表明,给定大量试验,估计值的平均值会更接近理论值。对于上面的实验,如果我们只重复试验 10 次,分布将与图非常不同。如果您感兴趣,您可以快速测试结果,直观地了解大数定律是如何产生影响的。
4.假设检验
假设只能计算样本参数,我们需要使用假设检验对总体参数进行推断。在假设检验中,提出一组互补的假设,包括一个零假设和一个备择假设。当进行假设检验时,我们选择相信零假设成立。如果观测值很可能出现在原假设为真的条件下,那么我们不拒绝原假设。然而,如果观察值不可能出现,那么我们拒绝零假设,接受替代假设。
假设矩阵
5.显著性水平和 P 值
在进行假设检验之前,我们需要首先定义一个显著性水平。显著性水平决定了我们对零假设的置信水平。如果我们将显著性水平设置为 0.05,那么只要观察值的概率高于 5%,我们就不拒绝零假设。然而,如果观察的概率低于 5%,我们拒绝零假设,接受替代假设。在类型 I 和类型 II 误差之间有一个折衷。基本上,较高的显著性水平更容易拒绝零假设。虽然以这种方式,较高的显著性水平减少了类型 II 误差,但同时也导致了较高的类型 I 误差。减少 I 型和 II 型误差的唯一方法是增加样本量。
观察值的概率称为 p 值。较低的 p 值意味着在零假设成立的情况下,观察不太可能发生。当 p 值低于显著性水平时,我们拒绝零假设。但是,有一点需要注意,p 值应该解释为二进制:它只是大于或小于显著性水平。
如何解释显著性水平和 p 值
摘要
在这篇文章中,我强调了一些基本的统计概念,这些概念对于理解未来关于时间序列数据分析和建模的文章非常重要。希望您有信心在我们的期刊上更进一步,探索时间序列数据!敬请关注下面这篇关于如何分析单个时间序列变量的文章!
数据架构基础,帮助数据科学家更好地理解架构图
在假装你理解你聪明的同事给你看的图表之前。
贾里德·穆雷在 Unsplash 上的照片
介绍
在一家使用数据获取商业价值的公司中,尽管您可能并不总是被赏识您的数据科学技能,但当您管理好数据基础架构时,您总是会被赏识的。每个人都希望数据存储在一个可访问的位置,清理好,并定期更新。
在这些不引人注目但稳定的需求的支持下,数据架构师的工资与数据科学家一样高,甚至更高。事实上,根据 PayScale(https://www . PayScale . com/research/US/Country = United _ United States/Salary)进行的薪酬研究显示,美国数据架构师的平均薪酬为*$ 121816*,而数据科学家的平均薪酬为*$ 96089*。
并不是说所有的数据科学家都应该改变他们的工作,至少学习数据架构的基础知识对我们来说会有很多好处。实际上,有一个简单(但有意义)的框架可以帮助您理解任何类型的真实世界的数据架构。
目录
- 数据架构的三个组成部分:数据湖- >数据仓库- >数据集市
- 每个组件中使用的工具
- 案例研究—构建预定的&从 BigQuery(数据仓库)到 Google Sheets(数据集市)的自动数据馈送
- 结尾注释
数据架构的三个组成部分:数据湖->数据仓库->数据集市
“数据湖”、“数据仓库”和“数据集市”是数据平台架构中的典型组件。按照这种顺序,业务中产生的数据被处理和设置,以创建另一个数据含义。
作者使用 Irasuto-ya(https://www.irasutoya.com/)的材料制作的图表
三个组件负责三种不同的功能:
- 数据湖:保存业务中产生的数据的原始副本。来自原始数据的数据处理即使有也应该是最小的;否则,万一某些数据处理最终被证明是错误的,将不可能追溯性地修复错误。
- 数据仓库:保存由托管数据模型处理和结构化的数据,反映数据最终用途的全局(非特定)方向。在许多情况下,数据是表格形式的。
- 数据集市:保存特定业务功能使用的子部分和/或聚合数据集,例如特定业务单位或特定地理区域。一个典型的例子是当我们准备一个特定业务线的 KPI 摘要,然后在 BI 工具中可视化。特别是,当用户希望定期和频繁地更新数据集市时,在仓库之后准备这种单独和独立的组件是值得的。相反,在用户只想对某组数据进行一次特别分析的情况下,可以跳过这一部分。
三个数据架构组件的摘要(由作者创建的图表)
除了这种简单的描述之外,更多真实世界的例子,请享受谷歌“数据架构”来找到大量的数据架构图。
你所看到的,当你用“数据架构”谷歌一下。(图片由作者拍摄)
为什么我们需要分成这三个部分?
因为流程中的不同阶段有不同的要求。
在数据湖阶段,我们希望数据接近原始数据,而数据仓库旨在保持数据集更加结构化,通过清晰的维护计划进行管理,并具有清晰的所有权。在数据仓库中,我们也希望数据库类型是面向分析的,而不是面向事务的。另一方面,数据集市应该能够方便地接触到非技术人员,他们可能会使用数据旅程的最终输出。
不同用途的系统组件往往在不同的时间进行重新设计。然后,配置松散连接的组件在将来的维护和扩展中具有优势。
数据工程师和数据科学家如何处理这三个部分?
粗略地说,数据工程师涵盖了从业务中产生的数据抽取到数据仓库中的数据湖和数据建模以及建立 ETL 管道;而数据科学家则负责从数据仓库中提取数据,构建数据集市,并引导进一步的业务应用和价值创造。
当然,数据工程师和数据科学家之间的这种角色分配有些理想,许多公司不会为了符合这一定义而同时聘用两者。实际上,他们的工作描述有重叠的趋势。
超越三组分方法的新趋势
最后但并非最不重要的一点是,值得注意的是,这种由三部分组成的方法是二十多年来一直存在的传统方法,而且新技术一直在不断出现。例如, " 数据虚拟化%20of) " 是一种允许对数据源进行一站式数据管理和操作接口的思想,而不管它们的格式和物理位置。
每个组件中使用的工具
现在,我们了解了三个数据平台组件的概念。那么,人们用什么工具呢?基于这份“数据平台指南”(日语),以下是一些想法:
数据湖/仓库
数据湖和数据仓库有以下选项。
作者根据“数据平台指南”(日文)精心制作
ETL 工具
ETL 发生在数据到达数据湖并被处理以适应数据仓库的地方。数据实时到达,因此 ETL 更喜欢事件驱动的消息传递工具。
作者根据“数据平台指南”(日文)精心制作
工作流引擎
工作流引擎用于管理数据的整体流水线操作,例如,通过流程图可视化流程进行的位置,在出错时触发自动重试等。
作者根据“数据平台指南”(日文)精心制作
数据集市/商务智能工具
以下工具可以用作数据集市和/或 BI 解决方案。选择将取决于业务环境、贵公司熟悉的工具(例如,您是 Tableau 人还是 Power BI 人?),聚合数据的大小(例如,如果数据大小很小,为什么像 Excel 或 Google Sheets 这样的基本解决方案达不到目标?)、您使用什么数据仓库解决方案(例如,如果您的数据仓库在 BigQuery 上,Google DataStudio 可能是一个简单的解决方案,因为它在 Google circle 内有天然的链接)等等。
作者根据“数据平台指南”(日文)精心制作
案例研究—构建从 BigQuery(数据仓库)到 Google Sheets(数据集市)的预定和自动数据馈送
当数据大小保持在几十兆字节左右或不到几十兆字节,并且不依赖于其他大型数据集时,坚持使用基于电子表格的工具来存储、处理和可视化数据是很好的,因为它成本较低,并且每个人都可以使用它。
一旦数据变得更大,并开始与其他数据表有数据依赖性,那么从云存储开始作为一站式数据仓库是有益的。(当数据变得更大,达到数十 TB 时,使用本地解决方案来实现成本效益和可管理性是有意义的。)
在这一章中,我将演示一个案例,当数据作为数据仓库存储在 Google BigQuery 中时。 BigQuery 数据实时或短频率处理存储。最终用户仍然希望在电子表格上看到高度汇总的每日 KPI。这意味着数据集市可以很小,甚至适合电子表格解决方案。让我们在这里使用 Google Sheets 而不是 Excel,因为它可以与 BigQuery 中的数据源在同一个环境中。哦,顺便说一下,不要想着每天手动运行查询。尝试找到一种解决方案,让一切自动运行,无需你采取任何行动。
案例研究中的数据管道(作者使用 IRA suto-ya(https://www.irasutoya.com/)的材料制作的图表)
本案例研究中使用的数据
在本案例研究中,我将使用一个样本表数据,其中包含每次乘坐纽约出租车的乘客记录,包括以下数据字段:
- 汽车 ID
- 驱动程序 ID
- 乘车日期
- 乘客人数
- 票价金额
- 等等。
示例数据作为数据仓库存储在 BigQuery 中。
Google Sheets 可以从 BigQuery 表中提取数据吗?
从技术上来说是的,但目前这只能通过连接的表获得,并且您需要一个 G Suite Enterprise、Enterprise for Education 或 G Suite Enterprise Essentials 的帐户。
作者创建的图表。
Connected Sheets 允许用户操纵 BigQuery 表数据,就像在电子表格上播放一样。参见“BenCollins”博客文章本页的 GIF 演示。
使用 Google Sheets 通过 Connected Sheets 连接到 BigQuery 的示例(由作者捕获)
连接的工作表还允许自动调度和刷新工作表,这是作为数据集市的自然需求。
虽然这是一个很好的选择,但一个可能的问题是,欠 G Suite 帐户并不常见。
关于设置的更多细节,见这篇来自“Ben Collins”的博文。
我们可以做些什么来将数据从 BigQuery 推送到 Google Sheets?
要从 BigQuery 中提取数据并将其推送到 Google Sheets,单靠 BigQuery 是不够的,我们需要服务器功能的帮助来调用 API 向 BigQuery 发送查询,接收数据,并将其传递给 Google Sheets。
作者创建的图表。
服务器功能可以在 GCP 外部或内部的服务器机器上(例如 GCP 的“计算引擎”实例;或 AWS 上的“EC2”实例)。可以使用 unix-cron 作业来调度代码运行。但是这里的一个缺点是它需要实例的维护工作和成本,并且对于运行一个小程序来说太多了。
“谷歌云功能”是一种所谓的“无服务器”解决方案,在不启动服务器的情况下运行代码。将代码放入云函数并设置触发事件(例如,本案例研究中的预定时间,但也可以是来自一些互联网用户的 HTML 请求),GCP 会自动管理代码的运行。
我案例研究中的设置
使用纽约出租车数据配置我的案例研究有两个步骤。
第一步:设置调度——设置云调度器和发布/订阅来触发云功能。
这里,“发布/订阅”是一个消息服务,由云功能订阅,并在每天的某个时间触发其运行。“云调度程序”是基于 unix-cron 格式以用户定义的频率启动的功能。结合这两者,我们可以创建由云函数订阅的常规消息。参见这份关于如何做的官方说明。这是我在 GCP 拍摄的截图。
在云调度程序中设置(由作者捕获)
在发布/订阅中设置(由作者捕获)
第二步:设置代码——在云函数上准备代码,查询 BigQuery 表,推送给 Google Sheets。
下一步是设置云功能。在云函数中,您定义 1)什么是触发器(在这个案例研究中,从 Pub/Sub 发送的“cron-topic ”,链接到云调度程序,它在每天早上 6 点提取触发器)和 2)当检测到触发器时您想要运行的代码。
更多细节见这个官方说明,这里是我设置的截图。
在云函数中设置(由作者捕获)
云函数中的代码输入——在这里,您还可以设置 requirements.txt 来使用 mail.py 程序中的可安装库。(作者捕捉)
要运行的代码必须包含在一个函数中,这个函数的名称可以是任何您喜欢的名称(在我的例子中是“nytaxi_pubsub”)。)代码内容由两部分组成:第 1 部分在 BigQuery 上运行查询,以将原始 BigQuery 表简化为 KPI,并将其保存为 BigQuery 中的另一个数据表,以及使其成为 Pandas 数据框,第 2 部分将数据框推送到工作表。
以下是我实际使用的代码。重要的是,对 BigQuery 的认证是自动的,只要它与云函数驻留在同一个 GCP 项目中(参见本页了解解释)。)然而,Google Sheets 的情况并非如此,它至少需要一个通过服务帐户共享目标工作表的过程。更多细节参见gspread 库中的描述。
main.py(由作者编码)
requirements.txt(由作者编码)
Google Sheets 上的最终数据集市
最后,我在 Google Sheets 中得到这样的汇总数据:
经过漫长的设置过程后,自动更新数据集市。(作者捕捉)
该表每天早上自动更新,当数据仓库通过 ETL 从数据湖接收新数据时,我们可以轻松地跟踪纽约出租车 KPI。
结尾注释
在雇用数据工程师和/或数据架构师以及数据科学家的大公司中,数据科学家的主要角色不一定是准备数据基础设施并将其放置到位,但至少了解数据架构的要点将有助于了解我们在日常工作中所处的位置。
数据湖->数据仓库->数据集市是一个典型的平台框架,用于处理从起点到用例的数据。将过程分成三个系统组件对于维护和目的性有很多好处。
在工具的选择上有很多选项。应根据数据环境(大小、类型等)明智地选择它们。)和企业的目标。
最后,在这篇文章中,我讨论了一个案例研究,我们在 Google Sheets 上准备了一个小型数据集市,从 BigQuery 中提取数据作为数据仓库。通过使用云调度程序和发布/订阅,更新变得自动化。
参考
- “数据湖 vs 数据仓库 vs 数据集市”, Jatin Raisinghani ,整体博客(https://www . holistics . io/Blog/Data-Lake-vs-Data-Warehouse-vs-Data-Mart/)
- 一张幻灯片《数据平台指南》(日文),@yuzutas0(推特),https://speakerdeck.com/yuzutas0/20200715
- “连接的工作表:分析谷歌工作表中的大数据”,BenCollins,【https://www.benlcollins.com/spreadsheets/connected-sheets/
生成对抗网络的基础
入门
GANs——图解、解释和编码
由 GAN 生成的合成手写数字。在本教程中,我们将创建自己的 GAN,它可以像这样生成数字,以及创建上面这个动画的代码。请继续阅读!
介绍
2014 年,一位名不见经传的博士生 Ian Goodfellow 向世界介绍了生成性对抗网络(GANs)。GANs 不同于 AI 社区见过的任何东西,Yann LeCun 将其描述为“在 ML 的过去 10 年中最有趣的想法”。
从那时起,许多研究工作都倾注在 GANs 上,许多最先进的人工智能应用程序,如英伟达的超现实人脸生成器都源自 Goodfellow 对 GANs 的研究。
作者注:本文所有图片和动画均由作者创作。如果你想把这些图片用于教育目的,请在评论中给我留言。谢谢大家!
什么是甘,他们能做什么?
在高层次上,GANs 是一种神经网络,它学习如何生成真实的数据样本,并根据这些样本对其进行训练。例如,给定手写数字的照片,GANs 学习如何生成更多手写数字的逼真照片。更令人印象深刻的是,GANs 甚至可以学习生成人类的逼真照片,如下图所示。
GAN 生成的人脸。以上这些面孔都不是真实的。来源:https://thispersondoesnotexist.com/
那么 GANs 是如何工作的呢?从根本上说,GANs 学习兴趣主题的分布。比如说。受过手写数字训练的 GANs 学习数据的分布。一旦学习了数据的分布,GAN 可以简单地从分布中采样以产生真实的图像。
数据的分布
为了巩固我们对数据分布的理解,让我们考虑下面的例子。假设我们有下面的 6 张图片。
每个图像都是一个浅灰色的盒子,为了简单起见,让我们假设每个图像只包含一个像素。换句话说,每个图像中只有一个灰色像素。
现在,假设每个像素都有一个介于-1 和 1 之间的可能值,其中白色像素的值为-1,黑色像素的值为 1。因此,6 幅灰度图像将具有以下像素值:
关于像素值的分布,我们知道些什么?嗯,通过检查,我们知道大多数像素值都在 0 左右,只有少数值接近极值(-1 和 1)。因此,我们可以假设分布是高斯分布,平均值为 0。
注意:对于更多的样本,通过计算平均值和标准偏差来导出该数据的高斯分布是很简单的。然而,这不是我们的重点,因为计算复杂主题的数据分布是很困难的,不像这个简单的例子。
我们像素的基本分布是平均值为 0 的高斯分布
这种数据分布是有用的,因为它允许我们生成更多的灰色图像,就像上面的 6。为了生成更多相似的图像,我们可以从分布中随机取样。
从高斯分布中随机独立抽取 10 个像素。请注意,大多数像素值都接近平均值(0),只有极少数异常值(-1 和 1)。
虽然计算出灰色像素的基本分布可能是微不足道的,但计算猫、狗、汽车或任何其他复杂对象的分布通常是数学上难以处理的。
那么,我们如何学习复杂对象的底层分布呢?显而易见的答案是使用神经网络。给定足够的数据,我们可以训练神经网络来学习任何复杂的功能,例如数据的基本分布。
生成器——分布式学习模型
在 GAN 中,生成器是学习数据底层分布的神经网络。更具体地说,生成器将随机分布(在 GANs 文献中也称为“噪声”)作为输入,并学习将输入映射到期望输出的映射函数,期望输出是数据的实际底层分布。
但是,请注意,上面的体系结构中缺少一个关键组件。我们应该用什么损失函数来训练发电机?我们如何知道生成的图像实际上是否类似于实际的手写数字?一如既往,答案是“使用神经网络”。第二个网络被称为鉴别器。
鉴别器——生成器的对手
鉴别器的作用是判断和评估发生器输出图像的质量。从技术上讲,鉴别器是一个二元分类器。它接受图像作为输入,并输出图像是真实的(即实际的训练图像)还是虚假的(即来自生成器)的概率。
最初,生成器很难产生看起来真实的图像,鉴别器可以轻松区分真假图像,而不会犯太多错误。由于鉴别器是二进制分类器,我们可以使用二进制交叉熵(BCE)损失来量化鉴别器的性能。
鉴频器的 BCE 损耗是发生器的一个重要信号。回想一下,生成器本身并不知道生成的图像是否与真实图像相似。然而,发生器可以使用鉴别器的 BCE 损失作为信号来获得对其生成的图像的反馈。
它是这样工作的。我们将生成器输出的图像发送到鉴别器,它预测图像是真实的概率。最初,当生成器很差时,鉴别器可以很容易地将图像分类为假的,从而导致 BCE 损失很低。然而,生成器最终会改进,鉴别器开始犯更多的错误,将假图像误分类为真实图像,这导致了更高的 BCE 丢失。因此,鉴频器的 BCE 损耗表示发生器输出的图像质量,并且发生器寻求最大化该损耗。
鉴别器的 BCE 损失是发生器输出图像质量的指标
从上面的动画中我们可以看到,鉴频器的 BCE 损耗与发生器产生的图像质量相关。
发生器使用鉴频器的损耗作为其生成图像质量的指标。生成器的目标是调整其权重,使得来自鉴别器的 BCE 损失最大化,有效地“愚弄”鉴别器。
训练鉴别器
但是鉴别器呢?到目前为止,我们从一开始就假设我们有一个完美工作的鉴别器。然而,这个假设是不正确的,鉴别器也需要训练。
由于鉴别器是一个二元分类器,它的训练过程很简单。我们将向鉴别器提供一批标记的真实和虚假图像,并且我们将使用 BCE 损失来调整鉴别器的权重。我们训练鉴别器来识别真假图像,防止鉴别器被生成器“愚弄”。
GANs——两个网络的故事
现在让我们把所有的东西放在一起,看看 GANs 是如何工作的。
基本 GAN 的体系结构
到目前为止,您已经知道 GANs 由两个相互连接的网络组成,即生成器和鉴别器。在传统的 GANs 中,发生器和鉴别器是简单的前馈神经网络。
甘斯的独特之处在于,生成器和鉴别器轮流接受训练,彼此对立。
为了训练生成器,我们使用从随机分布中采样的噪声向量作为输入。在实践中,我们使用从高斯分布中抽取的长度为 100 的向量作为噪声向量。输入通过前馈神经网络中一系列完全连接的层。生成器的输出是一个图像,在我们的 MNIST 例子中,是一个28x28
数组。发生器将其输出传递给鉴频器,并使用鉴频器的 BCE 损耗来调整其权重,目的是最大化鉴频器的损耗。
为了训练鉴别器,我们使用来自生成器的标记图像以及实际图像作为输入。鉴别器学习将图像分类为真或假,并且使用 BCE 损失函数来训练。
在实践中,我们依次训练生成器和鉴别器。这种训练方案类似于两个玩家的 minimax 对抗游戏,因为生成器的目标是最大化鉴别器的损失,而鉴别器的目标是最小化它自己的损失。
创造我们自己的 GAN
现在我们已经理解了 GAN 背后的理论,让我们通过使用 PyTorch 从头开始创建我们自己的 GAN 来将其付诸实践!
首先,让我们引入 MNIST 数据集。torchvision
库让我们可以轻松获得 MNIST 数据集。在将28x28
MNIST 图像展平为784
张量之前,我们将对图像进行一些标准归一化。这种扁平化是必需的,因为网络中的层是完全连接的层。
接下来,让我们编写生成器类的代码。从我们前面看到的,生成器只是一个前馈神经网络,它接受一个100
长度张量并输出一个784
张量。在生成器中,密集层的大小通常会在每层之后翻倍(256、512、1024)。
那很容易,不是吗?现在,让我们为 discriminator 类编写代码。鉴别器也是一个前馈神经网络,它接受一个784
长度张量,并输出一个1
大小的张量,表示输入属于类别 1(真实图像)的概率。与生成器不同,我们在每个层(1024、512、256)之后将密集层的大小减半。
现在,我们将创建一个包含生成器和鉴别器类的 GAN 类。根据我们之前讨论的训练方案,这个 GAN 类将包含依次训练生成器和鉴别器的代码。为了简化我们的代码并减少样板代码,我们将使用 PyTorch Lightning 来实现这一点。
上面的代码是注释过的,根据我们到目前为止所讨论的内容,它是非常简单明了的。请注意,使用 PyTorch Lightning 将我们的代码模块化是如何让它看起来如此整洁和易读的!
我们现在可以训练我们的 GAN 了。我们将使用 GPU 训练它 100 个纪元。
可视化生成的图像
现在剩下的就是可视化生成的图像。在上面我们的 GAN 类的training_epoch_end()
函数中,我们在每个训练时期之后将生成器输出的图像保存到一个列表中。
我们可以把这些图像绘制在网格上,使之形象化。下面的代码随机选择了在第 100 个训练时期后生成的 10 幅图像,并将它们绘制在一个网格上。
这是输出结果:
那挺好的!输出类似于真正的手写数字。我们的发电机肯定学会了如何骗过鉴别器。
最后,正如承诺的那样,我们将创建帖子顶部显示的动画。使用matplotlib
中的FuncAnimation
函数,我们将一帧一帧地制作图上图像的动画。
下一步是什么?
恭喜你。您已经完成了本教程的学习。我希望你喜欢读这篇文章,就像我喜欢写这篇文章一样。幸运的是,这不是我们旅程的终点。在 Goodfellow 推出最初的 GAN 后不久,科学界在这一领域投入了巨大的努力,这导致了基于 GAN 的 AI 模型的激增。
我正在开始一系列这样的教程,在这些教程中,我将举例说明、解释和编码 GAN 的不同变体,包括一些重要的变体,如深度卷积 GAN (DCGAN) 和条件 GAN (CGAN) 。一定要关注我(如果你还没有!)以便在新教程发布时得到通知。
其他资源
这里的代码也可以在我的 Github 库中找到。我将不断更新这个库,在将来包含 GANs 的其他变体。
[## jamesloyys/py torch-Lightning-GAN
使用 py torch Lightning github.com 实现各种 GAN 架构](https://github.com/jamesloyys/PyTorch-Lightning-GAN)
数字基础
在 5 分钟内学习 NumPy 的基础知识
Emile Perron 在 Unsplash 上的照片
在本文中,我们将学习 Python 库 NumPy 的各种特性和功能
NumPy 是 Python 库之一,它支持多维、实体数组以及矩阵。它还支持大量的数学函数来操作这些数组。NumPy 为许多其他数据科学和数据可视化库提供了强大的基础。
在本文中,我们将使用 NumPy 创建、索引和操作数组。(你需要先了解如何在 Python 中使用元组和列表。)
首先,我们来看看 NumPy 数组。NumPy 数组由可以用切片索引的值组成,也可以用布尔或整数数组(掩码)索引。数组的形状表示数组在每个维度上的大小,也可以用整数元组来表示。对于标准的 2D 数组,形状给出了行数,后跟列数。
在我们开始之前,请确保您的系统中安装了所有必需的库。
使用 conda:
conda install numpy
使用画中画:
pip install numpy
使用 NumPy 创建数组
>>> import numpy as np
>>> a = np.array([1,22,333])
>>> print(a)
[1 22 333]
在深入了解 NumPy 的更多特性和功能之前,让我们先研究一下数组。我们可以在 Python 中使用。“形状”功能。
>>> print(a.shape)
(3,)
这里我们看到形状是(3),这意味着我们有一个大小为 3 的一维数组。如果我们想查看数组中的特定元素,我们使用与 Python 列表中相同的符号。
>>> print(a[0])
1>>> print(a[1])
22>>> print(a[2])
333
更改特定索引处的值:
>>> a[0] = 11
>>> print(a)
[ 11 22 333]
如您所见,第一个位置的元素现在已经从 1 变成了 11。
使用 NumPy 创建 2D 数组
>>> b = np.array([[4,5,6],[7,8,9]])
>>> print(b)
[[4 5 6]
[7 8 9]]
打印形状
>>> b.shape
(2, 3)
这表明我们有 2 行 3 列。
现在,为了查看 2D 数组中的各个元素,我们可以执行以下操作:
>>> print(b[0,0])
4
我们可以创建不同值的 numpy 数组,而不仅仅是整数
>>> c = np.array([9.0, 8.7, 6.5])
>>> print(c)
[9\. 8.7 6.5]
构造数组的其他 NumPy 函数
NumPy 提供了不同的函数来创建数组。例如,如果我想创建一个用零填充的数组,我可以很容易地使用 NumPy’s。“零点”功能如下所示
>>> d = np.zeros((5,6))
>>> print(d)
[[0\. 0\. 0\. 0\. 0\. 0.]
[0\. 0\. 0\. 0\. 0\. 0.]
[0\. 0\. 0\. 0\. 0\. 0.]
[0\. 0\. 0\. 0\. 0\. 0.]
[0\. 0\. 0\. 0\. 0\. 0.]]
类似地,我们可以使用。某人的功能
>>> e = np.ones((5,6))
>>> print(e)
[[1\. 1\. 1\. 1\. 1\. 1.]
[1\. 1\. 1\. 1\. 1\. 1.]
[1\. 1\. 1\. 1\. 1\. 1.]
[1\. 1\. 1\. 1\. 1\. 1.]
[1\. 1\. 1\. 1\. 1\. 1.]]
此外,您可以通过使用。“full”函数,您需要在括号的第一部分提到数组的大小,在括号的第二部分提到常量值。下面显示了一个这样的例子,我们创建了一个填充了值“7.8”的 3x3 数组。
>>> f = np.full((3,3), 7.8)
>>> print(f)
[[7.8 7.8 7.8]
[7.8 7.8 7.8]
[7.8 7.8 7.8]]
NumPy 还提供了随机函数。使用它,我们可以创建一个由 0 到 1 之间的随机值组成的数组
>>> g= np.random.random((4,4))
>>> print(g)
[[0.21243056 0.4998238 0.46474266 0.24573327]
[0.80314845 0.94159578 0.65609858 0.0559475 ]
[0.80367609 0.35230391 0.91716958 0.03513166]
[0.37717325 0.00882003 0.82166044 0.7435783 ]]
使用 NumPy 索引
Numpy 提供了几种索引数组的方法。与 python 列表类似,NumPy 数组可以切片。由于数组可以是多维的,您需要为数组的每个维度指定切片。
让我们用随机值创建一个二维数组。
>>> i = np.array([[12,23,34], [45,56,67], [78,89,90]])
>>> print(i)
[[12 23 34]
[45 56 67]
[78 89 90]]
对于这个例子,让我们使用切片来提取数组的一部分,它由前 2 行和第 1、2 列组成
>>> j = i[:2, 1:3]
>>> print(j)
[[23 34]
[56 67]]
数组的切片(如上所示)是同一数据的视图。
>>> print(i[0,1])
23
您可以在数组中的任何位置更改该值。
>>> j[0,0]=75
>>> print(i[0,1])
75>>> print(i)
[[ 1 75 3]
[ 4 5 6]
[ 7 8 9]]
NumPy 中的布尔数组索引
布尔数组索引索引类型用于选择满足某些条件的数组元素。因此,让我们创建一个由一些随机值组成的数组,应用特定的条件,看看布尔数组索引是如何工作的。
>>> k = np.array([[26,78], [51,42], [30,89]])
>>> print(k)
[[26 78]
[51 42]
[30 89]]>>> print(k>50)
[[False True]
[ True False]
[False True]]
我们还可以使用布尔数组索引来创建一个新的一维数组,它包含所有实际上大于 50 的元素
>>> print(k[k>50])
[78 51 89]
NumPy 数学函数
基本数学函数对数组进行元素操作。这意味着一个数组中的一个元素对应于另一个数组中相同位置的一个元素。您可以同时使用数学运算符或 NumPy 提供的函数。两者给我们相同的输出。让我们对数组执行一些数学运算。
- 添加
>>> l = np.array([[1,2], [3,4]])
>>> m = np.array([[5,6], [7,8]])
>>> print(l+m)
[[ 6 8]
[10 12]]
这将根据元素在数组中的相应位置添加元素。我们可以使用 NumPy 函数获得类似的结果。
>>> print(np.add(l,m))
[[ 6 8]
[10 12]]
除此之外,NumPy 还具有减法、乘法、除法和计算数组和的功能。
2.减法
# Subtraction using math operators
>>> print(l-m)
[[ -4 -4]
[-4 -4]]# Subtraction using NumPy functions
>>> print(np.subtract(l,m))
[[ -4 -4]
[-4 -4]]
3.增加
# Multiplication using math operators
>>> print(l*m)
[[ 5 12]
[21 32]]# Multiplication using NumPy functions
>>> print(np.multiply(l,m))
[[ 5 12]
[21 32]]
4.分开
# Division using math operators
>>> print(l/m)
[[0.2 0.33333333]
[0.42857143 0.5 ]]# Division using NumPy functions
>>> print(np.divide(l,m))
[[0.2 0.33333333]
[0.42857143 0.5 ]]
5.总和
>>> print(l)
[[1 2]
[3 4]]#sum of all the elements
>>> print(np.sum(l))
10#sum of columns
>>> print(np.sum(l, axis=0))
[4 6]#sum of row
>>> print(np.sum(l, axis=1))
[3 7]
你可以在这里找到这个教程的代码。
我希望你喜欢这篇文章。谢谢你阅读它!
参考文献
[1] NumPy 文档:【https://numpy.org/doc/
回归的基础:什么,为什么和如何
回归技术的变体和真实世界用例
Katie Rodriguez 在 Unsplash 上的照片
凯文,如果你知道一个男孩的年龄,你能预测他的身高吗?
可以;如果你知道什么是回归以及它是如何工作的。
假设您有一个游泳班 5 名学生的记录,他们的年龄和身高如下。
然后一个新学生凯文(6 号学生)加入进来,他 5 岁。但他的身高未知。没问题!由于回归分析,可以根据班上其他学生的年龄-身高关系来估计他的未知身高。
回归只不过是两个变量之间的统计关系——在这种情况下是年龄和身高——这样,如果你知道另一个,你就可以预测其中一个。
在本文中,我将给出回归背后的高级统计直觉,并讨论它如何工作以及为什么工作。最后,我概述了一些真实世界的用例。
什么是回归?
让我们用一个正规的、教科书式的回归定义。
回归分析是一种调查变量之间关系的统计工具。通常,调查者试图确定一个变量对另一个变量的因果影响——例如,价格上涨对需求的影响,或者货币供应量的变化对通货膨胀率的影响。”资料来源:塞克斯(1993 年)。
里面装了不少东西。如果我们稍微打开包装,我们可以提取一些东西:
a)回归是一套工具/技术
b)回归确定两个或多个变量之间是否存在关系
c)回归测量关系的大小/强度
回归中的一些变化
我上面举的例子——年龄和身高——是两个变量之间的关系。身高是因变量,年龄是预测变量或自变量。从数学角度来说:
身高= f(年龄)
在统计学的行话中,它被称为“简单回归”,因为它只有一个预测因子(年龄)。但是如果需要几个预测器来预测一个因变量呢?我们来考虑一下房价,想想影响价格的因素有哪些?
回归模型的概念公式
房子的价格取决于几个因素——卧室和浴室的数量,建筑面积是其中的一部分(当然还有位置!).由于价格受到这些因素的影响,你需要所有这些因素作为预测因素。这种带有多个预测因子的回归称为“多元回归”。数学表示如下:
房价= f(#床位数、#卫生间数、建筑面积)
我们也可以把它写成回归方程的形式:
***房价= a+ b1 (床位数)+ b2 (浴池数)+ b3 (建筑面积)
回归的目的是通过某种统计程序找出 a,b1,b2 和 b3 的参数值,这样只需知道模型中的 3 个变量就可以预测未知房屋的价格。
刚刚讨论的简单和多重回归技术被称为“线性回归”——因为你用一条线性线来拟合数据。接下来,我将在下一节讨论一些非线性回归技术。
不同种类的回归
回归:统计与机器学习
取决于你是从事统计学还是机器学习,回归对你来说可能意味着稍微不同的东西。
很大一部分学统计学和计量经济学的学生倾向于认为回归是关于找出影响因变量的因素及其解释力的程度。这大概就是为什么在很多经典教材里,相关和回归是并行教授的。相关性告诉你一段关系的方向和强度,而回归则量化了效果。
然而,在机器学习中,一切都与应用有关。回归是许多进行预测的算法之一。
机器学习问题大致分为(1)分类和(2)回归问题。分类预测离散的类别(例如,狗对猫),回归预测数量(例如,在给定一些因素的情况下预测销售收入)。
机器学习中的回归算法
除了预测结果的线性回归技术之外,机器学习领域还使用了广泛的算法。以下是一些经常被引用的方法,并对每种方法进行了简要描述:
1)逻辑回归 是用来预测一个二元结果;例如对图片中的动物是狗还是猫等进行分类。
2)多项式回归 用于两个变量之间的关系不是线性的时候。换句话说,如果线性回归拟合数据不足(下图左),则拟合非线性线(右图)。
线性和多项式回归模型中的拟合线
3)岭回归 是一种降低模型复杂度的方法。如果有太多的预测因子,尤其是相关的预测因子,一些预测因子会被惩罚以减少它们的影响,而不是完全消除它们。
4)拉索 *,*的简称最小绝对收缩和选择算子,在概念上类似于岭回归,但系数可以被罚以将它们设置为接近于零。
5)支持向量回归: 支持向量机是分类问题的一种流行算法,但在回归问题中使用了类似的原理。虽然正常回归使误差最小化,但是 SVR 拟合模型使得误差在阈值内。
支持向量回归
6)面板数据回归: 面板数据回归是一种非常专业化的回归技术,用于时间序列数据的建模。这是一种预测时间相关观测值的强大技术(无耻之徒:请看下面我的一篇旧文章)。
在数据科学和机器学习问题中应用计量经济学
towardsdatascience.com](/panel-data-regression-a-powerful-time-series-modeling-technique-7509ce043fa8)
一些使用案例
现在是讨论的最后也是最重要的部分——回归的用例。作为一名数据科学家,你懂多少算法,能建立多精密的模型都不重要,真正的价值在于现实世界的应用。这里是我们到目前为止讨论过的各种技术的一些用例。
- 确定二手车的价格:假设你是一名汽车经销商,你要给一辆刚刚入库的汽车贴上价格标签。传统上,价格是根据经销商的经验和市场趋势确定的。但数据科学改变了一切,你可以根据车型、品牌、年份、里程、颜色、车门数量等特征来设定汽车价格……尽可能多的功能。
- 房屋定价:概念差不多。如果你曾经在房地产市场,你会注意到房价是如何起作用的——基于最重要的特征,如建造年份、建筑面积、位置、学区等。你可以进行回归拟合,从而轻松估算即将上市的房子的价格。
- 影响结果的因素:不是为了预测,而是在一些统计应用中,回归技术被用来找出影响特定结果的因素。例如,测量气候变化对 GDP 的影响。有一门完全不同的学科——计量经济学——专门研究这类问题。
数据科学家如何利用经济学家的工具箱
towardsdatascience.com](/econometrics-101-for-data-scientists-584f4f879c4f)
- 确定保险费:你有没有想过保险费是怎么定的?当你注册汽车保险时,你会被问及一系列关于汽车和司机的问题——汽车的品牌和型号、你的年龄、驾驶记录等等。所有的信息都用在回归模型中,以确定你将支付多少保费。
一锤定音
本文的目的是通过例子讨论回归背后的一些基本的统计直觉。我们还讨论了机器学习中使用的不同种类的线性和非线性回归技术。在最后一部分,我介绍了回归的一些实际用例。
当然,关于回归技术及其使用标准编程库的实现,还有很多可以说的。不过那个又搁置了一天,敬请关注!
【注:所有数字均由作者制作
监督情感分析的基础
NLP 预处理、BoW、TF-IDF、朴素贝叶斯、SVM、Spacy、Shapely、LSTM 等等
克里斯·j·戴维斯在 Unsplash 拍摄的照片
在这篇文章中,我将解释一些基本的机器学习方法来对推特情感进行分类,以及如何在 Python 中运行它们。
情感分析
情感分析用于识别数据的影响或情绪(积极的、消极的或中性的)。对于一个企业来说,这是一个简单的方法来确定客户对产品或服务的反应,并迅速发现任何可能需要立即关注的情绪变化。解决这个问题的最基本的方法是使用监督学习。我们可以让真正的人来确定和标记我们数据的情感,并像对待文本分类问题一样对待它。这正是我将在这篇文章中讨论的,并且将在后面的文章中重新讨论这个话题,讨论无监督的方法。
数据
- 你可能会在 data.world 上找到一些人为标注的推文数据。数据包含超过 8000 条被标记为积极、消极、中立或未知(“我不能说”)的推文。
- 斯坦福大学的一个团队提供了训练数据。此数据集的标签被自动标注。
自然语言处理预处理
在清理数据(删除带有缺失文本的观察结果、删除“未知”类、删除 RTs)并将我们的数据分成训练集、测试集和验证集之后,我们需要预处理文本,以便它们可以在我们的分析中正确量化。我将一个一个地检查它们。
1.仅保留 ASCII 字符
不同的编码会导致数据中出现一些奇怪的字符。因此,首先我们将确保我们只保留 ASCII 字符。下面的代码将过滤掉所有非 ASCII 字符。
**def** ascii_only(str_):
**return** str_.encode("ascii", "ignore").decode()
2.全部小写
这个很简单。
**def** make_lower(str_):
**return** str_.lower()
3.删除 HTML 符号、提及和链接
由于编码错误,一些推文包含 HTML 符号,如
。在我们删除标点符号之前,我们将首先删除这些单词,这样我们就不会在删除标点符号后留下胡言乱语。我们要删除的其他字母单词是用户名(例如@stereopickle)和超链接(在这个数据集中写成{link})。我们将使用 regex 来完成这项工作。
**import** **re
def** remove_nonwords(str_):
**return** re.sub("[^A-Za-z0-9 ]\w+[^A-Za-z0-9]*", ' ', str_)
这个表达式的意思是用空格替换所有不以字母数字字符开头的单词。这些单词可能以非字母数字字符结尾,也可能不以非字母数字字符结尾。
4.删除品牌词
这是特定于该数据集的步骤。因为我们的数据集包含关于产品的推文,所以有很多对实际品牌和产品名称的引用。我们不希望不同品牌或产品的总体情绪分布影响我们的模型,因此我们将删除其中一些。
**def** remove_brandwords(str_):
p = '''#?(iphone|ipad|sxsw|hcsm|google|apple|cisco|austin|
atari|intel|mac|pc|blackberry|android|linux|ubuntu)[a-z0-9]*'''
**return** re.sub(p, ' ', str_)
5.删除标点符号
又一次简单的清洗。请注意,为了简单起见,我在这里删除了所有标点符号,但在现实中,许多推文可能会使用标点符号来表达情绪,例如:)和:(。
**import** **string**
punctuations = string.punctuation
punctuations = punctuations + '�' + string.digits
**def** remove_punctuations(str_, punctuations):
table_ = str.maketrans('', '', punctuations)
**return** str_.translate(table_)
如果你对上面的方法感到好奇,请看看我的另一篇关于如何有效去除标点符号的帖子。
Python 中清理字符串的 8 种不同方法
towardsdatascience.com](/how-to-efficiently-remove-punctuations-from-a-string-899ad4a059fb)
6.词汇化&删除停用词
最后,我们将使用 NLTK 对我们的词汇表进行词汇化并删除停用词。总之,lemmatizer 将把词汇表还原成它的基本形式 lemma。
**from** **nltk.stem** **import** WordNetLemmatizer
**from nltk.corpus import** stopwordssw = stopwords.words('english')**def** lemmatize(str_, sw):
wnl = WordNetLemmatizer()
**return** ' '.join([wnl.lemmatize(w) **for** w **in** x.split() **if** w **not** **in** sw])
在这里阅读更多关于词汇化或词干化的内容。
使用 Python 预处理艺术描述数据
medium.com](https://medium.com/swlh/understanding-art-through-art-description-data-part-1-2682e899dfe5)
7.特征选择
在将上述函数应用到我们的文本数据之后,我们应该有一组非常干净的单词可以使用。但我喜欢在这里添加一个特征选择步骤,因为当每个词汇或二元或三元语法被标记化时,NLP 问题往往会以太多的特征结束。
当然,有许多方法可以清除特征,但是我喜欢使用 Spacy 来组合相似的单词。基本思想是遍历只出现几次的单词,并找到一个具有高相似性(在向量空间中更接近)的现有单词。
这个自定义函数返回一个字典,将出现频率低的单词作为关键字,将它们的替换词作为值。这一步通常会修复一些拼写错误,并纠正术语化可能遗漏的地方。我们可以使用这个替换词典将单词转换为更频繁出现的对应词,并删除出现次数少于指定次数的单词。
模型评估
估价
通常我们的数据会有很高的阶层不平衡问题,因为在大多数情况下,人们更可能写中立或积极的推文,而不是消极的推文。但对于大多数商业问题,我们的模型必须检测到这些负面推文。因此,我们将通过观察宏观平均 f1 分数以及精确召回曲线来关注这些问题。下面的函数将绘制 ROC 曲线和精度-召回曲线,并打印关键评估指标。
基线模型
我们可以使用 scikit-learn 的DummyClassifier
来首先查看我们的基线度量是什么,如果我们只是根据每个类出现的频率来分类的话。
**from sklearn.dummy import** DummyClassifier
dummy_classifier = **DummyClassifier**()
dummy_classifier.**fit**(tweets_train, labels_train)y_pred_p = dummy_classifier.predict_proba(tweets_validation)
y_pred = dummy_classifier.predict(tweets_validation)
词袋模型(计数向量)
量化文本数据的一个最简单的方法是计算每个单词的频率。scikit-learn 的CountVectorizer
可以轻松完成这项工作。
**from** **sklearn.feature_extraction.text** **import** CountVectorizer
countvec = **CountVectorizer**(ngram_range = (1, 2), min_df = 2)
count_vectors = countvec.**fit_transform**(tweets_train)
这将返回至少出现两次的单个词汇和二元模型的计数向量。然后我们可以使用这些计数向量来训练不同的分类算法。
TF-IDF 载体
计数向量的一个问题是,它只关注单个单词的频率,而不关心单词出现的上下文。没有办法评估一条推文中的特定单词有多重要。这就是术语频率-逆文档频率(TF-IDF)得分的由来。TF-IDF 得分对在一条推文中出现频率更高的词的权重大于在所有推文中出现频率更高的词。
**from** **sklearn.feature_extraction.text** **import** TfidfVectorizer
tfvec = **TfidfVectorizer**(ngram_range = (1, 2), min_df = 2)
tf_vectors = tfvec.fit_transform(tweets_train)
现在我们有了两个矢量化的文本,我们可以为每个矢量测试不同的分类器。
朴素贝叶斯
朴素贝叶斯是文本分类中比较流行的选择之一。这是对每个类和预测器的简单应用贝叶斯定理,并且它假设每个单独的特征(在我们的例子中是单词)是相互独立的。
比方说,我们有一条推特,上面写着… *“我爱我的新手机。真的很快,很可靠,设计的很好!”。*这条推文明显带有积极的情绪。在这种情况下,朴素贝叶斯模型假设像“爱”、“新”、“真的”、“快”、“可靠”这样的单个单词都独立地贡献给它的正类。换句话说,当使用“可靠”一词时,推文是正面的可能性不会因其他词而改变。这并不意味着这些词在外观上是独立的。一些单词可能经常一起出现,但这并不意味着每个单词对其类别的贡献是独立的。
当上述假设成立时,朴素贝叶斯算法使用简单且可靠。因为在我们的模型上测试需要矢量化,所以我们可以将管道构建到我们的模型中。
**from** **sklearn.naive_bayes** **import** MultinomialNB
**from** **sklearn.pipeline** **import** Pipelinemn_nb = **MultinomialNB**()# change countvec to tfvec for tf-idf
model = **Pipeline**([('vectorize', countvec), ('classify', mn_nb)])# fitting training count vectors (change to tf_vectors for tf-idf)
**model**['classify'].**fit**(count_vectors, labels_train)y_pred_p = **model**.predict_proba(tweets_validation)
y_pred = **model**.predict(tweets_validation)
**evaluating**(labels_validation, y_pred, y_pred_p)
由于朴素贝叶斯假设要素之间相互独立,因此它高估了每个要素对标注贡献的置信度,从而使其成为一个糟糕的估计器。所以对预测的概率要有所保留。
支持向量机(SVM)
文本分类算法的另一个流行选择是支持向量机(SVM)。简而言之,SVM 找到了一个超平面,这个超平面以最大的间距来划分这些类。在文本分类中 SVM 是首选的主要原因是我们倾向于以大量的特征结束。如果我们在这样一个拥有我们所有特征的高维空间中工作,就会导致一个被称为维度诅咒的问题。基本上,我们的空间太大了,我们的观察开始失去意义。但是 SVM 在处理大量特性时更加健壮,因为它使用了内核技巧。SVM 实际上并不在高维度中工作,它只是看着观察之间的成对距离,就好像它们在高维度中一样。做这项工作确实需要很长时间,但它很健壮。
**from** **sklearn.svm** **import** SVCsvm_classifier = SVC(class_weight = 'balanced', probability= **True**)
# don't forget to adjust the hyperparameters! # change countvec to tfvec for tf-idf
svm_model = **Pipeline**([('vectorize', countvec), ('classify', svm_classifier)])# fitting training count vectors (change to tf_vectors for tf-idf)
**svm_model**['classify'].**fit**(count_vectors, labels_train)y_pred_p = **svm_model**.predict_proba(tweets_validation)
y_pred = **svm_model**.predict(tweets_validation)
**evaluating**(labels_validation, y_pred, y_pred_p)
SHAP 评估
当 SVM 使用内核技巧时,就可解释性而言,事情就进入了一个灰色地带。但是我们可以使用 Shapley 值来解释单个特征是如何对分类起作用的。我们将使用 SHAP 的友好界面来可视化 Shapley 值。关于这方面的详细教程,我推荐阅读关于 SHAP 的文档。
**import** **shap** shap.initjs()
sample = shap.**kmeans**(count_vectors, 10)
e = shap.**KernelExplainer**(svm_model.predict_proba, sample, link = 'logit')
shap_vals = e.**shap_values**(X_val_tf, nsamples = 100)
shap.**summary_plot**(shap_vals,
feature_names = countvec.get_feature_names(),
class_names = svm_model.classes_)
唷,太多了。让我们休息一下。 freestocks 在 Unsplash 上拍照
LSTM
让我们再深入一点(字面上)。到目前为止,我们用两种不同的频率度量来量化我们的文本数据。但是每个单词的出现频率只讲述了故事的一小部分。理解语言及其意义需要理解句法,或者至少是单词的顺序。因此,我们将研究一种关心词汇序列的深度学习架构:长短期记忆(LSTM) 架构。
对于 LSTM,我们需要按顺序输入文本。以下步骤概述了运行和评估 LSTM 分类器的步骤。我解释了代码中的每一步。
单词嵌入(手套)
我们的 LSTM 模型的一个缺点是,它只包含我们训练数据中存在的信息,而词汇具有推文之外的语义。了解每个词汇在语义相似性方面的相互关系可能有助于我们的模型。我们可以基于预先训练的单词嵌入算法对我们的词汇应用权重。
为此,我们将使用由斯坦福大学的一个团队获得的矢量表示法 GloVe 。我用他们在 20 亿条推特上训练的 200 维词汇向量。你需要从他们的网站上下载载体。
然后,您可以将获得的向量矩阵作为嵌入权重添加到我们的 LSTM 架构的嵌入层中。
# adding the bolded part
model.add(Embedding(num_vocab, 200, **weights = [vector_matrix]**,
input_length = max_len))
通过使用单词嵌入和 LSTM,我的模型显示整体准确性提高了 20%,宏观平均 F1 分数提高了 16%。
我们回顾了使用 tweets 数据构建情感分析模型的基础。我将以一些需要思考和扩展的问题来结束这篇文章。
- 如果我们没有标签,我们如何解决同样的问题?(无监督学习)
- 在保持其可解释性的同时,还有哪些方法可以降低维度?
快乐学习!
照片由 Jonathan Daniels 在 Unsplash 上拍摄