使用 PySpark 和 MLlib 构建线性回归
Photo credit: Pixabay
Apache Spark 已经成为机器学习和数据科学最常用和最受支持的开源工具之一。
在这篇文章中,我将帮助你开始使用 Apache Spark 的 spark.ml 线性回归来预测波士顿房价。我们的数据来自 Kaggle 竞赛:波士顿郊区的房屋价值。对于每个房屋观察,我们有以下信息:
各城镇的人均犯罪率。
ZN —面积超过 25,000 平方英尺的住宅用地比例
INDUS —每个城镇非零售商业亩数比例。
CHAS —查尔斯河虚拟变量(= 1,如果区域边界为河流;否则为 0)。
NOX —氮氧化物浓度(百万分之一)。
RM —每个住宅的平均房间数。
年龄—1940 年前建造的自有住房比例。
DIS —到五个波士顿就业中心的加权平均距离。
RAD —放射状公路可达性指标。
税 —每 1 万美元的全价值财产税税率。
pt ratio——按城镇划分的学生/教师比率。
黑人 — 1000(Bk — 0.63)其中 Bk 是按城镇划分的黑人比例。
LSTAT —较低的人口地位(百分比)。
MV —以千美元为单位的自有住房的中值。这是目标变量。
输入数据集包含各种房屋的详细信息。根据提供的信息,目标是提出一个模型来预测该地区给定房屋的中值。
加载数据
from pyspark import SparkConf, SparkContext
from pyspark.sql import SQLContext
sc= SparkContext()
sqlContext = SQLContext(sc)house_df = sqlContext.read.format('com.databricks.spark.csv').options(header='true', inferschema='true').load('boston.csv')
house_df.take(1)
【Row(CRIM = 0.00632,ZN=18.0,INDUS=2.309999943,CHAS=0,NOX=0.537999988,RM=6.574999809,AGE = 65.19999695,DIS=4.090000153,RAD=1,TAX=296,PT = 15.30000019
数据探索
以树格式打印模式。
house_df.cache()
house_df.printSchema()root
|-- CRIM: double (nullable = true)
|-- ZN: double (nullable = true)
|-- INDUS: double (nullable = true)
|-- CHAS: integer (nullable = true)
|-- NOX: double (nullable = true)
|-- RM: double (nullable = true)
|-- AGE: double (nullable = true)
|-- DIS: double (nullable = true)
|-- RAD: integer (nullable = true)
|-- TAX: integer (nullable = true)
|-- PT: double (nullable = true)
|-- B: double (nullable = true)
|-- LSTAT: double (nullable = true)
|-- MV: double (nullable = true)
执行描述性分析
house_df.describe().toPandas().transpose()
Figure 1
散点图是粗略确定多个自变量之间是否存在线性相关性的一个很好的方法。
import pandas as pdnumeric_features = [t[0] for t in house_df.dtypes if t[1] == 'int' or t[1] == 'double']
sampled_data = house_df.select(numeric_features).sample(False, 0.8).toPandas()
axs = pd.scatter_matrix(sampled_data, figsize=(10, 10))
n = len(sampled_data.columns)
for i in range(n):
v = axs[i, 0]
v.yaxis.label.set_rotation(0)
v.yaxis.label.set_ha('right')
v.set_yticks(())
h = axs[n-1, i]
h.xaxis.label.set_rotation(90)
h.set_xticks(())
Figure 2
很难看到。让我们找出自变量和目标变量之间的相关性。
import six
for i in house_df.columns:
if not( isinstance(house_df.select(i).take(1)[0][0], six.string_types)):
print( "Correlation to MV for ", i, house_df.stat.corr('MV',i))Correlation to MV for CRIM -0.3883046116575088
Correlation to MV for ZN 0.36044534463752903
Correlation to MV for INDUS -0.48372517128143383
Correlation to MV for CHAS 0.17526017775291847
Correlation to MV for NOX -0.4273207763683772
Correlation to MV for RM 0.695359937127267
Correlation to MV for AGE -0.37695456714288667
Correlation to MV for DIS 0.24992873873512172
Correlation to MV for RAD -0.3816262315669168
Correlation to MV for TAX -0.46853593528654536
Correlation to MV for PT -0.5077867038116085
Correlation to MV for B 0.3334608226834164
Correlation to MV for LSTAT -0.7376627294671615
Correlation to MV for MV 1.0
相关系数范围从–1 到 1。当接近 1 时,说明有很强的正相关性;例如,当房间数量增加时,中值往往会增加。当系数接近–1 时,说明有很强的负相关性;当处于较低地位的人口比例上升时,中间值往往会下降。最后,系数接近零意味着没有线性相关性。
目前,我们将保留所有变量。
为机器学习准备数据。我们只需要两列—功能和标签(“MV”):
from pyspark.ml.feature import VectorAssemblervectorAssembler = VectorAssembler(inputCols = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PT', 'B', 'LSTAT'], outputCol = 'features')
vhouse_df = vectorAssembler.transform(house_df)
vhouse_df = vhouse_df.select(['features', 'MV'])
vhouse_df.show(3)
Figure 3
splits = vhouse_df.randomSplit([0.7, 0.3])
train_df = splits[0]
test_df = splits[1]
线性回归
from pyspark.ml.regression import LinearRegressionlr = LinearRegression(featuresCol = 'features', labelCol='MV', maxIter=10, regParam=0.3, elasticNetParam=0.8)
lr_model = lr.fit(train_df)
print("Coefficients: " + str(lr_model.coefficients))
print("Intercept: " + str(lr_model.intercept))
系数:【0.0,0.007302310571175137,-0.03286303124593804,1.413473328268,-7.919323668,5.21692169246
对训练集的模型进行总结,并打印出一些指标:
trainingSummary = lr_model.summary
print("RMSE: %f" % trainingSummary.rootMeanSquaredError)
print("r2: %f" % trainingSummary.r2)
RMSE:4.675914
R2:0.743627
RMSE 测量模型预测值和实际值之间的差异。然而,RMSE 本身是没有意义的,直到我们与实际的“MV”值进行比较,如平均值、最小值和最大值。经过这样的比较,我们的 RMSE 看起来还不错。
train_df.describe().show()
Figure 4
r 的平方为 0.74 表明,在我们的模型中,大约 74%的“MV”可变性可以用该模型来解释。这与 Scikit-Learn 的结果一致。还不错。然而,我们必须小心,训练集上的性能可能不是测试集上性能的良好近似。
lr_predictions = lr_model.transform(test_df)
lr_predictions.select("prediction","MV","features").show(5)from pyspark.ml.evaluation import RegressionEvaluator
lr_evaluator = RegressionEvaluator(predictionCol="prediction", \
labelCol="MV",metricName="r2")
print("R Squared (R2) on test data = %g" % lr_evaluator.evaluate(lr_predictions))
Figure 5
test_result = lr_model.evaluate(test_df)
print("Root Mean Squared Error (RMSE) on test data = %g" % test_result.rootMeanSquaredError)
测试数据的均方根误差(RMSE)= 5.52048
果然,我们在测试集上取得了更差的 RMSE 和 R 平方。
print("numIterations: %d" % trainingSummary.totalIterations)
print("objectiveHistory: %s" % str(trainingSummary.objectiveHistory))
trainingSummary.residuals.show()
操作数:11
目标历史记录:【0.49999999999956,0.4281126976,069,304,0.22593628,5989,17,0.20326326,5959,582,0.1。546667
Figure 6
使用我们的线性回归模型进行一些预测:
predictions = lr_model.transform(test_df)
predictions.select("prediction","MV","features").show()
Figure 7
决策树回归
from pyspark.ml.regression import DecisionTreeRegressordt = DecisionTreeRegressor(featuresCol ='features', labelCol = 'MV')
dt_model = dt.fit(train_df)
dt_predictions = dt_model.transform(test_df)
dt_evaluator = RegressionEvaluator(
labelCol="MV", predictionCol="prediction", metricName="rmse")
rmse = dt_evaluator.evaluate(dt_predictions)
print("Root Mean Squared Error (RMSE) on test data = %g" % rmse)
测试数据上的均方根误差(RMSE)= 4.39053
特征重要性
dt_model.featureImportances
SparseVector(13,{0: 0.0496,1: 0.0,4: 0.0118,5: 0.624,6: 0.0005,7: 0.1167,8: 0.0044,10: 0.013,12: 0.1799})
house_df.take(1)
【Row(CRIM = 0.00632,ZN=18.0,INDUS=2.309999943,CHAS=0,NOX=0.537999988,RM=6.574999809,AGE = 65.19999695,DIS=4.090000153,RAD=1,TAX=296,PT = 15.30000019
显然,在我们的数据中,房间数量是预测房屋中值价格的最重要特征。
梯度推进的树回归
from pyspark.ml.regression import GBTRegressor
gbt = GBTRegressor(featuresCol = 'features', labelCol = 'MV', maxIter=10)
gbt_model = gbt.fit(train_df)
gbt_predictions = gbt_model.transform(test_df)
gbt_predictions.select('prediction', 'MV', 'features').show(5)
Figure 8
gbt_evaluator = RegressionEvaluator(
labelCol="MV", predictionCol="prediction", metricName="rmse")
rmse = gbt_evaluator.evaluate(gbt_predictions)
print("Root Mean Squared Error (RMSE) on test data = %g" % rmse)
测试数据的均方根误差(RMSE )= 4.19795
梯度推进的树回归在我们的数据上表现最好。
源代码可以在 Github 上找到。我很高兴听到任何反馈或问题。
用 Python 构建逻辑回归
假设你得到了不同申请人的两次考试的分数,目标是根据分数将申请人分为两类,即如果申请人可以被大学录取,则分为 1 类;如果申请人不能被大学录取,则分为 0 类。这个问题可以用线性回归解决吗?让我们检查一下。
**注意:**我建议你在继续写这篇博客之前,先阅读一下线性回归。
目录
- 什么是逻辑回归?
- 数据集可视化
- 假设和成本函数
- 从头开始训练模型
- 模型评估
- Scikit-learn 实现
什么是逻辑回归?
如果你记得线性回归,它是用来确定一个连续因变量的值。逻辑回归通常用于分类目的。与线性回归不同,因变量只能取有限数量的值,即因变量是分类的。当可能的结果只有两个时,称为二元逻辑回归**。**
让我们看看逻辑回归是如何用于分类任务的。
在线性回归中,输出是输入的加权和。逻辑回归是一种广义线性回归,因为我们不直接输出输入的加权和,而是通过一个可以映射 0 到 1 之间任何实数值的函数来传递。
如果我们像线性回归一样将输入的加权和作为输出,则该值可能大于 1,但我们希望该值介于 0 和 1 之间。这就是为什么线性回归不能用于分类任务的原因。
从下图中我们可以看到,线性回归的输出通过一个激活函数传递,该函数可以映射 0 和 1 之间的任何实数值。
所使用的激活功能被称为s 形功能。sigmoid 函数的图形如下所示
sigmoid function
我们可以看到,sigmoid 函数的值总是位于 0 和 1 之间。X=0 时,该值正好为 0.5。我们可以使用 0.5 作为概率阈值来确定类别。如果概率大于 0.5,我们将其归类为 Class-1(Y=1) 或者 Class-0(Y=0) 。
在我们建立模型之前,让我们看看逻辑回归的假设
- 因变量必须是分类变量
- 独立变量(特征)必须是独立的(以避免多重共线性)。
资料组
这篇博客中使用的数据取自吴恩达在 Coursera 上的机器学习课程。数据可以从这里下载。数据由 100 名申请者的两次考试成绩组成。目标值采用二进制值 1,0。1 表示申请人被大学录取,而 0 表示申请人没有被录取。目标是建立一个分类器,可以预测申请是否会被大学录取。
让我们使用read_csv
函数将数据加载到pandas
数据帧中。我们还将把数据分成admitted
和non-admitted
来可视化数据。
既然我们对问题和数据有了清晰的理解,让我们继续构建我们的模型。
假设和成本函数
到目前为止,我们已经了解了如何使用逻辑回归将实例分类到不同的类别中。在这一节中,我们将定义假设和成本函数。
线性回归模型可以用下面的等式来表示。
然后,我们将 sigmoid 函数应用于线性回归的输出
其中 sigmoid 函数由下式表示:
逻辑回归的假设就变成了,
如果输入的加权和大于零,则预测类为 1,反之亦然。因此,通过将输入的加权和设置为 0,可以找到分隔两个类的决策边界。
成本函数
像线性回归一样,我们将为我们的模型定义一个成本函数,目标是最小化成本。
单个训练示例的成本函数可以由下式给出:
成本函数直觉
如果实际类是 1,而模型预测是 0,我们应该高度惩罚它,反之亦然。从下图中可以看出,对于图-log(h(x))
来说,当 h(x)接近 1 时,成本为 0,当 h(x)接近 0 时,成本为无穷大(也就是说,我们对模型的惩罚很重)。类似地,对于图-log(1-h(x))
,当实际值为 0 并且模型预测为 0 时,成本为 0,并且随着 h(x)接近 1,成本变得无穷大。
我们可以将这两个方程结合起来使用:
由 J(θ) 表示的所有训练样本的成本可以通过对所有训练样本的成本取平均值来计算
其中 m 为训练样本数。
我们将使用梯度下降来最小化成本函数。任何参数的梯度可由下式给出
该方程类似于我们在线性回归中得到的结果,只有 h(x)在两种情况下是不同的。
训练模型
现在我们已经准备好了构建模型所需的一切。让我们用代码实现它。
让我们首先为我们的模型准备数据。
X = np.c_[np.ones((X.shape[0], 1)), X]
y = y[:, np.newaxis]
theta = np.zeros((X.shape[1], 1))
我们将定义一些用于计算成本的函数。
def sigmoid(x):
# Activation function used to map any real value between 0 and 1
return 1 / (1 + np.exp(-x))
def net_input(theta, x):
# Computes the weighted sum of inputs
return np.dot(x, theta)
def probability(theta, x):
# Returns the probability after passing through sigmoid
return sigmoid(net_input(theta, x))
接下来,我们定义cost
和gradient
函数。
def cost_function(self, theta, x, y):
# Computes the cost function for all the training samples
m = x.shape[0]
total_cost = -(1 / m) * np.sum(
y * np.log(probability(theta, x)) + (1 - y) * np.log(
1 - probability(theta, x)))
return total_cost
def gradient(self, theta, x, y):
# Computes the gradient of the cost function at the point theta
m = x.shape[0]
return (1 / m) * np.dot(x.T, sigmoid(net_input(theta, x)) - y)
让我们也定义一下fit
函数,它将被用来寻找最小化成本函数的模型参数。在这篇博客中,我们编写了梯度下降法来计算模型参数。这里,我们将使用scipy
库中的fmin_tnc
函数。它可以用来计算任何函数的最小值。它将参数视为
- func:最小化的函数
- x0:我们要寻找的参数的初始值
- fprime:由“func”定义的函数的梯度
- args:需要传递给函数的参数。
def fit(self, x, y, theta):opt_weights = fmin_tnc(func=cost_function, x0=theta,
fprime=gradient,args=(x, y.flatten()))
return opt_weights[0]parameters = fit(X, y, theta)
模型参数为[-25.16131856 0.20623159 0.20147149]
为了了解我们的模型表现有多好,我们将绘制决策边界。
绘制决策边界
因为我们的数据集中有两个要素,所以线性方程可以表示为:
如前所述,可以通过将输入的加权和设置为 0 来找到决策边界。使 h(x)等于 0 得到我们,
我们将在用于可视化数据集的图的顶部绘制决策边界。
x_values = [np.min(X[:, 1] - 5), np.max(X[:, 2] + 5)]
y_values = - (parameters[0] + np.dot(parameters[1], x_values)) / parameters[2]
plt.plot(x_values, y_values, label='Decision Boundary')
plt.xlabel('Marks in 1st Exam')
plt.ylabel('Marks in 2nd Exam')
plt.legend()
plt.show()
看起来我们的模型在预测课程方面做得不错。但是有多准确呢?让我们找出答案。
模型的准确性
def predict(self, x):theta = parameters[:, np.newaxis]
return probability(theta, x)def accuracy(self, x, actual_classes, probab_threshold=0.5):
predicted_classes = (predict(x) >=
probab_threshold).astype(int)
predicted_classes = predicted_classes.flatten()
accuracy = np.mean(predicted_classes == actual_classes)
return accuracy * 100accuracy(X, y.flatten())
模型的准确率为 89%。
让我们使用 scikit-learn 实现我们的分类器,并将其与我们从头构建的模型进行比较。
scikit-learn 实现
from sklearn.linear_model import LogisticRegressionfrom sklearn.metrics import accuracy_score model = LogisticRegression()
model.fit(X, y)
predicted_classes = model.predict(X)
accuracy = accuracy_score(y.flatten(),predicted_classes)
parameters = model.coef_
模型参数为[[-2.85831439, 0.05214733, 0.04531467]]
,准确率为 91%。
为什么模型参数与我们从零开始实现的模型有显著差异? 如果你看一下 sk-learn 的逻辑回归实现的文档,它考虑到了正则化。基本上,正则化用于防止模型过度拟合数据。我不会在这篇博客中深入讨论正规化的细节。但是现在,就这样了。感谢阅读!!
这篇博客中使用的完整代码可以在这个 GitHub repo 中找到。
如果你被困在任何地方或有任何反馈,请给我留言。
下一步是什么?
在下一篇博客中,我们将使用在这篇博客中学习到的概念,在来自 UCI 机器学习知识库的成人数据上构建一个分类器。该数据集包含近 49K 个样本,包括分类值、数值值和缺失值。这将是一个值得探索的有趣数据集。
请继续关注这个空间。
在 Python 中逐步构建逻辑回归
Photo Credit: Scikit-Learn
逻辑回归 是一种机器学习分类算法,用于预测分类因变量的概率。在逻辑回归中,因变量是一个二元变量,包含编码为 1 的数据(是,成功,等等)。)或 0(否,失败等。).换句话说,逻辑回归模型预测 P(Y=1)是 x 的函数。
逻辑回归假设
- 二元逻辑回归要求因变量是二元的。
- 对于二元回归,因变量的因子水平 1 应该代表期望的结果。
- 应该只包括有意义的变量。
- 自变量应该是相互独立的。也就是说,模型应该很少或没有多重共线性。
- 自变量与对数概率呈线性相关。
- 逻辑回归需要相当大的样本量。
记住上面的假设,让我们看看我们的数据集。
数据
数据集来自 UCI 机器学习库,它与一家葡萄牙银行机构的直接营销活动(电话)有关。分类的目标是预测客户是否会订阅(1/0)定期存款(变量 y)。数据集可以从这里下载。
import pandas as pd
import numpy as np
from sklearn import preprocessing
import matplotlib.pyplot as plt
plt.rc("font", size=14)
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
import seaborn as sns
sns.set(style="white")
sns.set(style="whitegrid", color_codes=True)
数据集提供了银行客户的信息。它包括 41,188 条记录和 21 个字段。
Figure 1
输入变量
- 年龄(数字)
- 工作:工作类型(分类:“行政人员”、“蓝领工人”、“企业家”、“女佣”、“管理人员”、“退休人员”、“自营职业者”、“服务人员”、“学生”、“技术人员”、“失业人员”、“未知人员”)
- 婚姻:婚姻状况(分类:“离婚”、“已婚”、“单身”、“未知”)
- 教育(分类:"基础. 4y "、"基础. 6y "、"基础. 9y "、“高中”、“文盲”、“专业.课程”、“大学.学位”、“未知”)
- 违约:有信用违约?(分类:“否”、“是”、“未知”)
- 住房:有住房贷款吗?(分类:“否”、“是”、“未知”)
- 贷款:有个人贷款?(分类:“否”、“是”、“未知”)
- 联系人:联系人通信类型(分类:“手机”、“电话”)
- 月份:一年中的最后一个联系月份(分类:“一月”、“二月”、“三月”、“十一月”、“十二月”)
- 星期几:一周的最后一个联系日(分类:“星期一”、“星期二”、“星期三”、“星期四”、“星期五”)
- duration:上次联系持续时间,以秒为单位(数字)。重要注意事项:该属性对输出目标有很大影响(例如,如果 duration=0,则 y='no ')。在执行呼叫之前,持续时间是未知的,同样,在呼叫结束之后,y 显然是已知的。因此,这种输入应该仅用于基准目的,如果目的是得到一个现实的预测模型,则应该丢弃
- 活动:在此活动期间为此客户执行的联系次数(数字,包括最后一次联系)
- pdays:从上一个活动中最后一次联系客户后经过的天数(数字;999 表示之前没有联系过客户)
- 上一次:在此活动之前为此客户执行的联系次数(数字)
- poutcome:先前营销活动的结果(分类:“失败”、“不存在”、“成功”)
- 员工变动率:员工变动率—(数字)
- cons.price.idx:消费者价格指数—(数字)
- 消费者信心指数—(数字)
- euribor 3m:3 个月 euribor 利率—(数字)
- 受雇人数:雇员人数—(数字)
预测变量(期望目标):
y —客户是否认购了定期存款?(二进制:“1”表示“是”,“0”表示“否”)
数据集的教育列有许多类别,为了更好地建模,我们需要减少类别。“教育”栏有以下类别:
Figure 2
让我们把“基本. 4y”、“基本. 9y”、“基本. 6y”组合在一起,称之为“基本”。
data['education']=np.where(data['education'] =='basic.9y', 'Basic', data['education'])
data['education']=np.where(data['education'] =='basic.6y', 'Basic', data['education'])
data['education']=np.where(data['education'] =='basic.4y', 'Basic', data['education'])
分组后,这是列:
Figure 3
数据探索
Figure 4
count_no_sub = len(data[data['y']==0])
count_sub = len(data[data['y']==1])
pct_of_no_sub = count_no_sub/(count_no_sub+count_sub)
print("percentage of no subscription is", pct_of_no_sub*100)
pct_of_sub = count_sub/(count_no_sub+count_sub)
print("percentage of subscription", pct_of_sub*100)
无认购百分比为 88.73288821988
认购百分比 11.265417111780131
我们的类是不平衡的,非订阅实例和订阅实例的比例是 89:11。在我们继续平衡这些类之前,让我们做一些更多的探索。
Figure 5
观察值 :
- 购买定期存款的客户的平均年龄高于没有购买的客户。
- 对于购买该产品的客户来说,pdays(自上次联系客户后的天数)更短是可以理解的。时间越短,对最后一次通话的记忆就越好,因此成交的机会就越大。
- 令人惊讶的是,对于购买定期存款的客户来说,营销活动(在当前营销活动中联系或致电的次数)更少。
我们可以计算其他分类变量(如教育和婚姻状况)的分类平均值,以获得更详细的数据。
Figure 6
Figure 7
形象化
%matplotlib inline
pd.crosstab(data.job,data.y).plot(kind='bar')
plt.title('Purchase Frequency for Job Title')
plt.xlabel('Job')
plt.ylabel('Frequency of Purchase')
plt.savefig('purchase_fre_job')
Figure 8
购买存款的频率在很大程度上取决于职位。因此,职称可以很好地预测结果变量。
table=pd.crosstab(data.marital,data.y)
table.div(table.sum(1).astype(float), axis=0).plot(kind='bar', stacked=True)
plt.title('Stacked Bar Chart of Marital Status vs Purchase')
plt.xlabel('Marital Status')
plt.ylabel('Proportion of Customers')
plt.savefig('mariral_vs_pur_stack')
Figure 9
婚姻状况似乎不是结果变量的强有力的预测因素。
table=pd.crosstab(data.education,data.y)
table.div(table.sum(1).astype(float), axis=0).plot(kind='bar', stacked=True)
plt.title('Stacked Bar Chart of Education vs Purchase')
plt.xlabel('Education')
plt.ylabel('Proportion of Customers')
plt.savefig('edu_vs_pur_stack')
Figure 10
教育似乎是结果变量的一个很好的预测器。
pd.crosstab(data.day_of_week,data.y).plot(kind='bar')
plt.title('Purchase Frequency for Day of Week')
plt.xlabel('Day of Week')
plt.ylabel('Frequency of Purchase')
plt.savefig('pur_dayofweek_bar')
Figure 11
星期几可能不是预测结果的好方法。
pd.crosstab(data.month,data.y).plot(kind='bar')
plt.title('Purchase Frequency for Month')
plt.xlabel('Month')
plt.ylabel('Frequency of Purchase')
plt.savefig('pur_fre_month_bar')
Figure 12
月份可能是结果变量的一个很好的预测因子。
data.age.hist()
plt.title('Histogram of Age')
plt.xlabel('Age')
plt.ylabel('Frequency')
plt.savefig('hist_age')
Figure 13
在这个数据集中,银行的大多数客户年龄在 30-40 岁之间。
pd.crosstab(data.poutcome,data.y).plot(kind='bar')
plt.title('Purchase Frequency for Poutcome')
plt.xlabel('Poutcome')
plt.ylabel('Frequency of Purchase')
plt.savefig('pur_fre_pout_bar')
Figure 14
Poutcome 似乎是一个很好的预测结果变量。
创建虚拟变量
也就是只有两个值的变量,0 和 1。
cat_vars=['job','marital','education','default','housing','loan','contact','month','day_of_week','poutcome']
for var in cat_vars:
cat_list='var'+'_'+var
cat_list = pd.get_dummies(data[var], prefix=var)
data1=data.join(cat_list)
data=data1cat_vars=['job','marital','education','default','housing','loan','contact','month','day_of_week','poutcome']
data_vars=data.columns.values.tolist()
to_keep=[i for i in data_vars if i not in cat_vars]
我们的最终数据列将是:
data_final=data[to_keep]
data_final.columns.values
Figure 15
使用 SMOTE 进行过采样
有了我们创建的训练数据,我将使用 SMOTE 算法(合成少数过采样技术)对非订阅进行上采样。在高水平上,击打:
- 通过从 minor 类(无订阅)创建合成样本而不是创建副本来工作。
- 随机选择 k 个最近邻中的一个,并使用它来创建类似的、但随机调整的新观察值。
我们要用 Python 实现 SMOTE。
X = data_final.loc[:, data_final.columns != 'y']
y = data_final.loc[:, data_final.columns == 'y']from imblearn.over_sampling import SMOTEos = SMOTE(random_state=0)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
columns = X_train.columnsos_data_X,os_data_y=os.fit_sample(X_train, y_train)
os_data_X = pd.DataFrame(data=os_data_X,columns=columns )
os_data_y= pd.DataFrame(data=os_data_y,columns=['y'])
# we can Check the numbers of our data
print("length of oversampled data is ",len(os_data_X))
print("Number of no subscription in oversampled data",len(os_data_y[os_data_y['y']==0]))
print("Number of subscription",len(os_data_y[os_data_y['y']==1]))
print("Proportion of no subscription data in oversampled data is ",len(os_data_y[os_data_y['y']==0])/len(os_data_X))
print("Proportion of subscription data in oversampled data is ",len(os_data_y[os_data_y['y']==1])/len(os_data_X))
Figure 16
现在我们有了一个完美的平衡数据!您可能已经注意到,我只对训练数据进行了过采样,因为通过只对训练数据进行过采样,测试数据中的任何信息都不会用于创建合成观察,因此,没有任何信息会从测试数据流入模型训练。
递归特征消除
递归特征消除(RFE) 基于重复构建模型并选择最佳或最差性能特征的思想,将该特征放在一边,然后对其余特征重复该过程。此过程将一直应用到数据集中的所有要素都用完为止。RFE 的目标是通过递归地考虑越来越小的特征集来选择特征。
data_final_vars=data_final.columns.values.tolist()
y=['y']
X=[i for i in data_final_vars if i not in y]from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegressionlogreg = LogisticRegression()rfe = RFE(logreg, 20)
rfe = rfe.fit(os_data_X, os_data_y.values.ravel())
print(rfe.support_)
print(rfe.ranking_)
Figure 16
RFE 帮助我们选择了以下特征:“euribor3m”、“job _ 蓝领”、“job _ 女佣”、“婚姻 _ 未知”、“教育 _ 文盲”、“默认 _ 否”、“默认 _ 未知”、“联系人 _ 手机”、“联系人 _ 电话”、“month_apr”、“month_aug”、“month_dec”、“month_jul”、“month_jun”、“month_mar”、“month_may”、“month_nov”、“month_oct”、“poutcome_failure”、“poutcome_success”。
cols=['euribor3m', 'job_blue-collar', 'job_housemaid', 'marital_unknown', 'education_illiterate', 'default_no', 'default_unknown',
'contact_cellular', 'contact_telephone', 'month_apr', 'month_aug', 'month_dec', 'month_jul', 'month_jun', 'month_mar',
'month_may', 'month_nov', 'month_oct', "poutcome_failure", "poutcome_success"]
X=os_data_X[cols]
y=os_data_y['y']
实施模型
import statsmodels.api as sm
logit_model=sm.Logit(y,X)
result=logit_model.fit()
print(result.summary2())
Figure 17
除了四个变量,大多数变量的 p 值都小于 0.05,因此,我们将删除它们。
cols=['euribor3m', 'job_blue-collar', 'job_housemaid', 'marital_unknown', 'education_illiterate',
'month_apr', 'month_aug', 'month_dec', 'month_jul', 'month_jun', 'month_mar',
'month_may', 'month_nov', 'month_oct', "poutcome_failure", "poutcome_success"]
X=os_data_X[cols]
y=os_data_y['y']logit_model=sm.Logit(y,X)
result=logit_model.fit()
print(result.summary2())
Figure 18
逻辑回归模型拟合
from sklearn.linear_model import LogisticRegression
from sklearn import metricsX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
logreg = LogisticRegression()
logreg.fit(X_train, y_train)
Figure 19
预测测试集结果并计算准确度
y_pred = logreg.predict(X_test)
print('Accuracy of logistic regression classifier on test set: {:.2f}'.format(logreg.score(X_test, y_test)))
逻辑回归分类器在测试集上的准确率:0.74
混淆矩阵
from sklearn.metrics import confusion_matrix
confusion_matrix = confusion_matrix(y_test, y_pred)
print(confusion_matrix)
【6124 1542】
【2505 5170】]
结果告诉我们,我们有 6124+5170 正确的预测和 2505+1542 不正确的预测。
计算精度、召回、F-测量和支持
引用 Scikit Learn 的话:
精度是 tp / (tp + fp)的比值,其中 tp 是真阳性的数量,fp 是假阳性的数量。精确度直观上是分类器在样本为阴性时不将其标记为阳性的能力。
召回率是 tp / (tp + fn)的比值,其中 tp 是真阳性的数量,fn 是假阴性的数量。召回直观上是分类器找到所有肯定样本的能力。
F-beta 分数可以解释为精确度和召回率的加权调和平均值,其中 F-beta 分数在 1 时达到其最佳值,在 0 时达到最差分数。
F-beta 分数通过 beta 因子对召回率的加权大于对精确度的加权。beta = 1.0 意味着召回率和准确率同等重要。
支持度是 y_test 中每个类的出现次数。
from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred))
Figure 20
解读:在整个测试集中,74%的推广定期存款是客户喜欢的定期存款。在整个测试集中,74%的客户首选定期存款得到了推广。
受试者工作特征曲线
from sklearn.metrics import roc_auc_score
from sklearn.metrics import roc_curve
logit_roc_auc = roc_auc_score(y_test, logreg.predict(X_test))
fpr, tpr, thresholds = roc_curve(y_test, logreg.predict_proba(X_test)[:,1])
plt.figure()
plt.plot(fpr, tpr, label='Logistic Regression (area = %0.2f)' % logit_roc_auc)
plt.plot([0, 1], [0, 1],'r--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic')
plt.legend(loc="lower right")
plt.savefig('Log_ROC')
plt.show()
Figure 21
受试者工作特性(ROC) 曲线是二元分类器使用的另一个常用工具。虚线代表纯随机分类器的 ROC 曲线;一个好的分类器尽可能远离那条线(朝向左上角)。
用来写这篇文章的 Jupyter 笔记本可以在这里找到。我将很高兴收到关于上述任何反馈或问题。
构建具有交互式可视化的 Microsoft Power BI web 应用程序
我想为初学者提供一个简单、简洁的指南,让他们动手操作 MS Power BI,并使用 Power BI 中的可视化功能执行数据集。这是我今天想到的一个方法,使用简单的工具和方法。
对于 Windows 设备:转到 Microsoft 应用程序商店并获得 Power BI desktop
Power BI desktop from Microsoft Store (Instead of Launch, you should have a Get button)
下载并在您的系统中启动它
您可能会看到此对话框打开。我们需要在这里选择获取数据选项,并导航到下载的数据集。但是在那之前,我们需要下载一个数据集用 Power BI 探索。一旦我们下载了数据集,我们可以回到这一步。
我想在世界卫星数据集上测试。忧思科学家联盟为所有卫星发射提供了一个非常好和干净的所有格式的数据集:excel/csv/text 文件。于是我接着用下面的链接从他们的网站下载了一些数据集(提供了网站截图,以防万一)由此下载 excel 格式的数据库。
目前围绕地球运行的 1,886 颗卫星的详细信息,包括它们的来源国、用途和…
www.ucsusa.org](https://www.ucsusa.org/nuclear-weapons/space-weapons/satellite-database#.W-DqNpNKiM8)
下载完成后,您可以进入 Power BI 窗口中的获取数据步骤并导航至下载的卫星 excel 数据集。
然后选择同一数据集的工作表 1,并单击 Load。
Windows 完成数据集加载后,您可能会看到以下内容:
所以右边是可视化和字段列。可视化栏有一个图标列表,对应于您可以获得的各种图,而字段栏显示 excel 数据表中的列列表,如发布日期、发布地点、目的、来源等。
为了从这些数据中得出一个简单而有力的故事,我列出了一些相关特征的重要列表,如国家、运载火箭和预期寿命。现在的挑战是选择正确的可视化,然后将这些选定的字段放置在选定的可视化下的相应位置。
因此,从各种基于地图的可视化中,我发现 ArcGIS 地图是展示该数据集的最酷的地图。因此,我们从可视化列中选择 ArcGIS graph 图标。
现在我们必须根据选择的可视化来排列字段。例如,从字段中,选择国家,然后将该特征拖放到可视化栏下的位置空间中。然后从字段列中选择运载火箭字段,并将其拖放到颜色空间中。
然后一旦放下,再次点击运载火箭选项卡并选择计数,这将设置每个国家运载火箭计数总数的使用。
然后,为了显示更多信息,我们可以添加每个国家卫星的平均预期寿命。我们通过从 fields 列中选择Expected Lifetime并将其放到 visualization 下的 Tooltips 空间中来实现这一点。点击预期寿命选项卡,并选择平均值作为与图的其余部分整合的操作值。
因此,在不到 10 分钟的时间里,我们已经将简单的卫星发射 excel 文件演变成了一个在世界地图上讲述的故事,讲述了世界上目前领先的国家作为航天大国的实力。我们可以显示各自国家的运载火箭总数,以及各自卫星在太空中的平均预期寿命。
Satellite launches in world with ArcGIS map visualization and proper Fields selected
最后,你会看到这个美丽的世界地图视图,当你将光标悬停在一个国家时,会弹出额外的信息——给出国家名称、运载火箭总数和预期寿命。虽然颜色的强度给出了该国运载火箭数量的概念,但我们不难想象,毫不奇怪,美国、中国和俄罗斯的卫星发射数量最多。
您可以使用焦点模式查看整个页面中的图表,而不查看任何其他图表:
用户也可以点击右上角,将数据导出为. csv 文件,或者在可视化或地图旁边查看数据[选择右上角更多选项按钮旁边的水平布局视图图标]以查看如下所示的屏幕。
人们可以在地图上摆弄不同的按钮和缩放级别,也许还能得到更吸引人的信息图。
我知道你会用它来创作更有创意和更有趣的东西,所以急切地等待你的惊人回应。
这是可视化的嵌入式代码。点击它打开一个新的网页,与我为卫星数据集创建的可视化交互。
你也可以这样做,进入文件->点击发布到网站->嵌入代码到你的博客或 html 网站
以下是基于我的报告中选择的图表和字段的数据集的网站可视化,当我在我的 Power BI 应用程序 dasahboard 上更改它们时,它也会实时更改。
**[## 电源 BI 报告
由 Power BI 支持的报告
app.powerbi.com](https://app.powerbi.com/view?r=eyJrIjoiYmJhYmM0ODItZWFjMC00ZGFjLWJjNGQtZDNjNjc1MGU4NWVkIiwidCI6IjlmZWE3NjAwLWE0YWYtNGZlZC05NTZlLTYxODk0ZjY0MWE3MCIsImMiOjN9)**
要了解更多关于商业智能/深度学习/机器学习的最新作品和教程,请访问:
这里有一些最好的人工智能或深度学习或数据科学链接和资源可以免费获得…
rajshekharmukherjee.wordpress.com](https://rajshekharmukherjee.wordpress.com/hottest-resources/)
干杯!**
在纽约出租车数据上建立一个机器学习集成分类器,用 Python 和 Google BigQuery 预测没有小费和慷慨的小费
我通过构建一个分类器来展示 Google BigQuery 引擎的强大功能,该分类器将预测乘坐纽约市的出租车是会得到丰厚的小费还是根本没有小费。作为这项工作的一部分,我探索数据集并查看数据集中的关系。我还想象了城市周围的拾音器,结果是一个散点图,基本上画出了纽约的街道。
我主要介绍了 BigQuery UI、python API 和 pandas 库,这些库与直接从 Jupiter notebook 和 sklearn 执行 SQL 查询相关。我描述了在 BigQuery 中克服的挑战,包括通过 API 和 UI 执行的查询之间的一些语法差异。很多在线文档都过时了,因此我的示例代码应该是执行自己项目的有用资源。
最后,对于那些对构建分类器和元分类器感兴趣的人来说,我使用 simple 来理解标签和输入,这对那些希望实现自己的分类器的人来说应该是一个有用的参考。我还演示了如何处理缺失值。
big query 背景
BigQuery 是 Google 内部使用的查询服务 Dremel 的公共实现。
Dremel 可以在几十秒内扫描 350 亿行而不用索引。云驱动的大规模并行查询服务 Dremel 共享谷歌的基础设施,因此它可以并行化每个查询,并在数万台服务器上同时运行。
使用 BigQuery 的第一步是授权一个 google cloud 帐户。这是通过访问 https://console.cloud.google.com并遵循授权协议来完成的。您还需要提供一张信用卡来支付账单。注册开始时,您将获得价值 300 美元的积分,这对于探索和运行示例查询来说绰绰有余。
然后,您将登录到 google cloud dashboard,下一步是创建一个项目(您可以有多个项目)。
然后,您可以通过单击左侧的菜单并选择“bigquery”来访问 BigQuery
这将加载 BigQuery WebUI
我们看到有 130GB 关于纽约出租车旅行的数据和 11 亿行。BigQuery 处理所有这些数据大约只需要 30 秒。
我首先导入 pandas,因为 pandas 中有一个专门设计的方法来查询 bigquery,并以 pandas dataframe 的形式返回结果。
我还导入 matplotlib 进行可视化。
我使用的数据已经存储在谷歌云中,所有人都可以使用——我使用它是为了演示,因为任何人都可以访问它并运行查询。
作为对数据的初步演示和探索,我展示了通过将 SQL 代码直接传递给 pandas 方法进行查询的能力,方法是按月查看数据中的出租车出行次数。
然后,我研究了这些数据,并分析了小费和不同特征之间的关系。
然后,我汇总了纽约每个经度和纬度坐标上的皮卡数量
然后,我能够将这些数据输入散点图,根据出租车搭载乘客的地点,得出纽约相当清晰的轮廓。
根据我对数据的研究,我发现只有 3%的用信用卡支付的旅行没有小费,所以我决定为了分类的目的,我们将尝试并预测两种极端情况,这两种情况需要人们在纽约出租车上手动取消——没有小费和非常慷慨的小费(“慷慨”是指超过车费的 30%)
然后,我着手提取相关信息,并给它们贴上标签。
我首先创建两个独立的数据集,在每个数据集上做标记,然后将它们合并。
我平衡数据,所以一半的数据将是慷慨的,另一半没有提示,所以我的分类器只需要做得比 50%更好就可以被认为是有用的。
现在已经得到了我的标签(Y)和特征(X),我建立分类器。我使用 sklearns 分类器 KNN,贝叶斯,逻辑回归和决策树,并使用它们的输出来输入一个集成或“元分类器”,然后给出结果。
当我们运行培训和测试时,我们会看到以下结果:
准确度:0.67(+/-0.12)[decision tree classifier]
准确度:0.91(+/-0.14)【LogReg】
准确度:0.77(+/-0.10)【KNN】
准确度:0.67(+/-0.01)【NB】
准确度:0.83 (+/- 0.12)【系综】
结论:
鉴于上述结果,我已经能够成功地建立一个元分类器,它将预测哪里的旅行更有可能导致司机没有小费或“慷慨”的小费。
诸如此类的分类器可能有助于捕捉欺诈。例如,该数据仅使用信用卡交易,因为所有小费信息都被捕获,但当使用现金时,由于税务后果,司机可能不会报告小费-像所构建的分类器这样的分类器可用于了解我们何时应该看到小费被记录,或不被记录,并识别异常。
github Repo:https://github . com/cjflanagan/ny taxi _ Tips _ Classifier _ with _ big query-/blob/master/ny taxi . ipynb
构建分子电荷分类器
化学和人工智能的交集
A.在过去的几年里,我看到了前所未有的增长。尽管像神经网络(NN)这样的机器学习架构由于 Geoffrey Hinton 这样的顶级研究人员的突破而闻名已久,但直到最近神经网络才成为人工智能专家工具箱中的强大工具。这主要归功于 21 世纪的到来带来的三个变化:
- 增强的计算能力
- 增加可用数据
- 对自动化越来越感兴趣
目前在互联网、政府系统和个人电脑上流动的大多数数据都是未标记的。**标记数据(即对应于另一个现有数据集的数据)更难获得。**数据类型包括数字、图像、音频和文本。
Code, Math, and Molecules; the trinity of difficult things to use in A.I
要克服的巨大挑战是离散分子数据是多么脆弱。
考虑一下这个:
- 在数学方程式中,如果一个数字或运算被改变,则该方程式无效。
- 在代码块中,如果任何变量、方法、函数等。被更改,代码将以不同的方式执行。
- 在一个分子中,如果一个键、原子或电子被改变,这个分子就完全不同了。
神经网络有一个非常独特的过程,使它们不同于其他算法。输入的数据必然会在神经网络中被处理,也就是说,它会受到权重、偏差、激活函数以及一系列其他可调、可学习和特定参数的影响;
进入神经网络的数据将被改变。
**操纵脆弱的离散数据如履薄冰。**目前,有 4 种主要的方法来表示一种分子,按照损失的顺序列出:
- 分子指纹
- 字符串表示
- 分子图
- 模拟
每一种都有自己独特的过程,但我用来构建分子电荷分类器的是字符串表示和分子指纹。大多数化学信息数据和分子数据都是以 InChi 字符串的形式提供的,或者我更喜欢的格式,SMILES 字符串。大多数时候,微笑字符串看起来像一种化学胡言乱语:
Molecules with their respective SMILE strings
这些字符串是在语法的帮助下生成的。和英语一样,单个分子可以有不同的微笑串;就像一个词可以有很多定义,或者一个想法可以用不同的句子来表达。
The many faces of Toluene; all the strings represent the same molecule
图表由 Esben Bjerrum 博士提供,他是用人工智能征服化学之旅的领先专家和研究人员。要了解更多关于 canonical SMILES 枚举的信息,请查看他的博客帖子。
为了防止重复的微笑串,每个分子仅使用一个微笑串变体。值得注意的是,每个分子都有一个初级字符串表示,也称为规范微笑。有了 SMILES 格式的干净的标记分子数据集,我开始构建分子电荷分类器。
项目收费
我用微笑字符串和神经网络用 python 和 Keras 库构建了一个分子电荷分类器。每个分子都有特定的电荷 0、1、-1 和-2(还有更多,但我的数据集中的所有分子都有 4 种可能的电荷)。
该项目分为 4 个步骤:
- 导入 SMILES 数据(带标签,即费用)
- 将数据转换为 256 位二进制代码
- 构建深层神经网络
- 编译和拟合模型
对于这个项目,该模型用 250,000 个数据集的 1000 个微笑字符串进行训练。我将每个微笑字符串转换成 256 位二进制代码,这样我就可以用相同数量的输入神经元构建神经网络;256.该模型将每个前进层的神经元数量减半,从 256 → 128 → 64 → 34 → 16,最后到 4 个输出神经元,每个神经元对应数据集中的一个可能电荷(0,1,-1,-2)。
从训练集中抽取 10%的数据(100 个微笑字符串)用作验证数据。该模型使用 relu 作为其输入层激活函数,使用 sigmoid 作为隐藏层的激活函数,最后使用 softmax 函数以 0 到 1 之间的概率形式输出结果。因此,分类以概率小数的形式呈现,由神经网络根据其训练计算。
该模型用 10 个新分子进行了测试,并成功预测了所有 10 个分子的电荷,其中一些产生了较高的可能性百分比(80%),而另一些产生了较低的可能性百分比(40%)
Charged 项目的所有代码和数据集都可以在我的 Github 上找到,在这个库里。
化学数据面临的挑战是找到代表你正在研究的分子的方法。理想的表示考虑到了分子数据的脆弱性,同时仍然保持固有的特征和模式完整无缺。这种表示应该在准确性和计算效率之间取得适当的平衡。
关键要点
- 像代码和数学一样,分子在被操纵时容易变得脆弱
- 用计算术语表示分子有 4 种方法;指纹、字符串表示、分子图或模拟
- SMILES 是一种流行的字符串表示;一个分子可以有许多串,但只有一个规范的微笑
- 将一个分子转换成二进制指纹,并将它们输入到神经网络中,这是在化学中利用人工智能的一种方法
- 分子表示和化学信息学作为一个整体,是准确性和计算能力的平衡
还在看这个?想要更多吗?不确定下一步该做什么?
向前!
在 MATLAB 中建立 NARX 来预测时间序列数据。
The Kenduskeag at high flows. At lower flows (you can literally walk out to that rock in the middle).
去年我发表了一篇文章(在 PaddleSoft 账号上),在文章中我描述了使用 MATLAB 中内置的 NARX 预测 Kenduskeag 流的流量。然而,我故意没有包括那么多技术细节,因为我试图为普通读者写。好了,我现在已经创建了一个完整的教程,关于如何在 MATLAB 中建立一个带有外源输入的非线性自回归网络(NARX)来预测时间序列数据。
教程
这个过程实际上相当简单:导入数据,创建模型,训练模型,然后进行预测。
这个 CSV 是我们将在教程中使用的 CSV 类型的一个例子。基本上有三列:高度、温度和降雨量。我们希望使用先前的高度以及温度和降雨量来预测当前的高度。
- 导入数据
首先,我们必须导入数据。我们将使用一个简单的函数来实现:
我们用[X_input,height]=load_data('height.csv')
调用这个函数(注意,即使我们使用 xlsread,它仍然可以处理 CSV 文件)。
2。培训
现在让我们看看我们的培训代码:
让我们稍微分解一下这段代码。我们要做的第一件事是使用 tonndata 函数将我们的输入放入单元格数组。接下来,我们必须选择我们的训练函数。我个人在贝叶斯正则化(即 trainbr)方面取得了最大的成功,但是,这可能需要更长的时间。之后,p reparets 将为我们的 NARX (24)准备正确格式的数据。
输入延迟和反馈延迟参数非常重要,将极大地影响您的预测。我最初选择的方法是绘制降雨量与高度变化的曲线图,以确定降雨量何时对河流高度影响最大。然后我试验了各种配置。也有更多的数学方法来确定它使用互相关(见 MATLAB 论坛回答这里)。然而,现在,你可以试着看看什么是最好的。
然后,我们在第 28–30 行做出相当标准的决策,如训练/验证/测试分割,最后训练网络(第 30 行)。代码的其余部分是可选的,所以我不会详细介绍。但是,请随意尝试,了解一下有哪些可用的东西。
3。做出预测
现在,为了在 CSV 上进行预测,我们将使用新创建的 height_net。我个人发现最简单的方法是将测试数据分成两个 CSV:一个包含 stamps 0:length-240 的输入(temp 和 rain)和高度值,另一个只包含 length-240:length 的输入(其中 length 是测试 CSV 中的行数)。这大致类似于 10 天天气预报的预测(即使用一个已知的长时间序列,然后预测从天气 API 获得的 1024 个时间戳)。你可以很容易地在你的 MATLAB 代码中分割数据,但是我发现这样做最简单。考虑到这一点,下面是我们的预测代码:
我们将已知的时间戳序列传入 open height net,得到xf
和af
。然后我们关闭网络进行多重预测(12)。最后,我们进行实际预测(13)。你现在可以通过看 y2 或者我们重新分配的 unknownY 来看预测。
我希望这个例子足以让你熟悉 MATLAB 中的 NARX(s)。在未来,我希望更深入地研究 NARXs 相对于 LSTMs 等其他 rnn 的优势。
你可以关注我,也可以查看我写的更多内容。
构建自然语言课程
我是一名软件工程师,在应用创新团队的 Kainos 工作,我们与客户一起使用和开发超越眼前视野的工具,以发现他们的商业价值。这包括研究新兴技术,如人工智能及其应用。
问题是
我们经常会发现自己的文本数据集从一开始就没有足够的数据,没有足够的变化,或者很可能有偏差。为了让我们的机器学习模型有效地提供准确的预测,需要大量的数据。对于图像,没有足够数据的解决方案是增加数据集(我将在后面展示一个例子),从而在不增加方差的情况下创建我们正在寻找的额外数据量。
如果我的任务是对狗和猫进行分类,而我只有一只猫和 9 只狗来训练我的模型,那么这个模型就不擅长寻找猫。
所以……(鼓声 please)🥁
我们如何为我们的文本数据集做到这一点?
有没有一种方法可以自动化增加数据集大小的过程,这些数据集的数据足够多样,可以进行良好的训练?
基础
我的研究让我寻找可以在不损失质量或上下文的情况下扩展这些数据集的方法。我看了问题和答案的结构,因为那是我正在处理的数据;有趣的是,我们与技术互动的最常见方式基本上是通过提问和回答。因此,随着对机器学习的兴趣和发展,我们如何使用这项技术来使这些交互更加流畅?
Airbnb chatbot which organises accomodation for you via a natural conversation
我相信每个人都知道我们可以有许多不同的方式来“问或回答一个问题”;问自己这个问题让我开始关注一些技术,比如文本生成、文本预测、文本变形以及最后的文本增强。
An example of how essentially the same answer can have multiple questions
这项研究
文本生成
T ext generation 或自然语言生成(NLG) 是机器学习的一种应用,将在未来几年在商业中大量出现。
高德纳公司对 NLG 做了如下预测:
“到 2018 年,20%的商业内容将由机器创作”
“到 2019 年,自然语言生成将成为 90%的现代商业智能和分析平台的标准功能。”
“到 2020 年,前 20 大网络内容管理(WCM)供应商将提供自然语言生成功能,作为其整体产品的一部分。”
自然语言是一个非常复杂的话题,我们已经到了机器能够理解句子结构、提取关键词或短语、总结文本和将一种语言高精度地翻译成另一种语言的阶段。机器学习使 NLG 成为可能(看看谷歌双工),它需要数据来学习;这里的问题是,我一开始没有多少数据,所以它违背了目的。任何模型都无法概括和产生我所寻找的问题的变体。
对于那些想了解模型一般化的人来说,我说的是训练模型的目的,这样它就能够理想地应对训练范围内的任何场景。在这种情况下,由于我要处理的数据量很小,这个模型不可能是一般化的,反而会过度拟合。
文本预测
由于 NLG 不适合我的需要,我简要研究了文本预测;这实质上提供了所谓的“种子文本”,并且该模型将预测下一组单词是什么。然而,这种技术和 NLG 遇到了同样的问题,它需要大量的数据;我看到的问题。
文本变形
接下来,我发现了一篇有趣的研究论文,题目是“从连续空间生成句子”。这是我所能找到的文本变形的唯一证据,它在将两个不同的句子变形到一起后产生了新的句子。
不幸的是,我无法测试这个方法,我的时间是有限的,我在寻找能够让我这样做的工具;我不能花那么多时间重现一篇研究论文的结果。将来,我想测试文本变形,看看它是否可以单独扩展数据集,或者与另一种方法相结合。
Example taken from the “Generating Sentences from a Continuous Space” paper
文本增补
在我的研究进行到一半时,我开始质疑是否值得研究与图像增强的常见实践更密切相关的技术。图像增强是一种扩大数据集中图像数量的技术,同时增加这些图像的变化,这将有助于我们概括我们的模型,并扩大数据暴露,以说明现实世界的细节。方法可以包括旋转、水平或垂直翻转以及缩放。虽然这些方法不能准确地翻译成文本,但它仍然是值得考虑的事情,以前我只关注涉及机器学习的方法。
An example of image augmenation using Keras
为了生成更多的文本,我测试了两种扩充文本的方法,同时努力保持数据集的高质量标准。
同义词替换
首先,我试图提取关键字,然后用它们的同义词替换它们,换句话说(没有双关语的意思),用同义词词典中找到的对应词替换单词。最初我只是用同义词替换名词,还没有测试过用同义词替换英语演讲的其他部分。从结果中可以立即看出,这种方法在增加数据集的规模方面是有效的,即使它无法找到某些句子的同义词。
尽管如此,像“我们”这样的词被提取出来,然后断章取义,因为机器不理解问题的意图,这是有问题的。它取了“我们”,换成了“美国”、“美国”等。用这些替换生成新问题意味着新问题没有意义,特别是对于以前没有见过原始问题的人来说。
您可以通过定义单词集合(语料库)来解决这个问题。语料库将类似于您不想找到同义词并替换的内容。我还建议你在进入替换阶段之前,尝试一下关键词提取的替代方法。对于这项研究,我使用了 Rake 库来提取关键词,甚至可以尝试不同的库。
语言翻译
第二种增强方法更像是一场赌博,从英语翻译成外语再翻译回英语。我在 fast.ai 论坛上看到过这种技术被用于 Kaggle 竞赛“有毒评论分类”。令人惊讶的是,这种方法非常有效,特别是对于离家更远的语言,如日语和阿拉伯语。我相信由于句子结构和单个单词意思的不同,这些语言工作得很好,这意味着它们翻译的句子与原始句子略有不同。我最后使用的语言是日语、乌克兰语、塞尔维亚语、阿拉伯语和保加利亚语。使用 Azure 的翻译服务,我随机选择了 500 个问题,并且能够产生 5 倍的数量,因为我只使用了 5 种语言。将来,我想增加翻译的问题数量,看看它对基准测试有多大影响。
下面是我的一些结果,危险数据集的一个子集被用作基准数据,在使用各种方法后,逻辑回归模型在总体上进行训练。
Table describing the results from each mehod
在表格中,我标记了开始时的原始数据量、使用左侧的标记方法生成的生成量以及两者的总和。第四行以“Translated -> Augmented”开始,显示了将扩充(同义词替换)和翻译结合起来的结果。所有这些都给出了有价值的结果,并显示了应用每种方法的好处。值得在其他数据集上进一步测试这一点,并可能将其实现到聊天机器人中,看看问答系统是否可以处理更多不同的问题。
结论
通过进行这项研究,我意识到这是一个困难的问题,可以通过各种方法或方法的组合来解决,我很有兴趣看到机器学习的这个小而重要的领域如何扩展。
最近,在自然语言处理(NLP)方面取得了一些进展,这可能有助于这一领域,如 DecaNLP、transformer 模型以及最近谷歌对“强化学习的主动问题重构”的研究。如果你对自然语言和机器学习感兴趣,所有这些都值得一看。
联系✉️
如果您有兴趣了解更多我们在应用创新团队的工作,请随时联系我们 appliedinnovation@kainos.com 公司。
在 Tensorflow 中构建下一个单词预测器
ByPriya DWI vedi,数据科学家@ SpringML
下一个单词预测或者也称为语言建模是预测下一个单词是什么的任务。它是自然语言处理的基本任务之一,有许多应用。当你写短信或电子邮件时,你可能每天都在使用它而没有意识到这一点。
我最近在 Tensorflow 上建立了一个下一个单词预测器,在这篇博客中,我想介绍我遵循的步骤,这样你就可以复制它们并建立你自己的单词预测器。
- 数据下载和预处理
我使用了从 2006 年 3 月的英语维基百科转储的数据集。该数据集非常庞大,总共有 16 毫米的单词。为了测试和建立单词预测模型,我随机抽取了一个数据子集,总共有 0.5 毫米的单词,其中 26k 是唯一的单词。正如我将在后面解释的那样,随着唯一单词数量的增加,你的模型的复杂性也会增加很多。
2。生成单词向量
在 NLP 中,首要任务之一是用单词向量替换每个单词,因为这能够更好地表示单词的含义。如需了解更多关于词向量以及它们如何捕捉语义的信息,请点击这里查看博文。
对于这个模型,我用 Glove Vectors 初始化这个模型,本质上是用一个 100 维的单词向量替换每个单词。
3。模型架构
对于此任务,我们使用 RNN,因为我们希望通过查看每个单词之前的单词来预测每个单词,并且 rnn 能够保持隐藏状态,该隐藏状态可以将信息从一个时间步转移到下一个时间步。RNN 的工作原理见下图:
RNN visualization from CS 224N
一个简单的 RNN 有一个权重矩阵 Wh 和一个在每个时间步共享的嵌入到隐藏矩阵 We。每个隐藏状态的计算方法如下
并且任何时间步长的输出取决于隐藏状态
因此,使用这种架构,RNN 能够“理论上”使用过去的信息来预测未来。然而,普通的 rnn 受到消失和爆炸梯度问题的困扰,因此它们很少被实际使用。对于这个问题,我使用了 LSTM ,它使用门及时流回梯度,并减少消失梯度问题。
我在 Tensorflow 中设置了一个多层 LSTM,每层 512 个单位,2 个 LSTM 层。LSTM 的输入是最后 5 个单词,LSTM 的目标是下一个单词。
模型中的最后一层是 softmax 层,它预测每个单词的可能性。正如我之前提到的,我的模型有大约 26k 个唯一的单词,所以这一层是一个有 26k 个唯一类的分类器!这是模型中计算量最大的部分,也是单词语言建模中的一个基本挑战。
4。模型训练和输出
我用的损失函数是 sequence_loss 。该模型被训练了 120 个时期。我观察了训练损失和训练困惑来衡量训练的进展。困惑是用来衡量语言模型性能的典型指标。困惑度是由字数标准化的测试集的逆概率。困惑度越低,模型越好。在训练了 120 个时期后,该模型达到了 35 的困惑度。
我根据一些样本建议测试了这个模型。该模型输出前 3 个最高概率的单词供用户选择。见下面截图。考虑到该模型是在仅有 26k 个单词的有限词汇量上进行训练的,因此它工作得相当好
Trained model output
后续步骤
- 探索允许在更大词汇量上进行培训的替代模型体系结构。你可以在论文中看到这些策略——https://arxiv.org/abs/1512.04906
- 将模型更好地推广到新的词汇或罕见的单词,如不常见的名称。最近的一个发展是使用指针哨兵混合模型来做这个——见论文https://arxiv.org/abs/1609.07843
SpringML 是谷歌云平台的主要合作伙伴,专注于机器学习和大数据分析。我们已经在多家财富 500 强企业中实施了预测和分析解决方案。请联系了解更多:info@springml.comwww.springml.com
从头开始构建问答系统—第 1 部分
系列文章的第一部分着重于脸书嵌入句
随着我的硕士学位即将结束,我想从事一个有趣的 NLP 项目,在那里我可以使用我在 USF 学到的所有技术(不完全是)。在我的教授的帮助下,通过与同学们的讨论,我决定从头开始建立一个问答模型。我使用的是斯坦福问答数据集(SQuAD) 。这个问题非常有名,所有的大公司都试图在排行榜上跳起来,并使用先进的技术,如基于注意力的 RNN 模型,以获得最佳的准确性。我发现的其他人做的与 SQuAD 相关的所有 GitHub 库也都使用了 RNNs。
然而,我的目标并不是达到最先进的准确性,而是学习不同的 NLP 概念,实现它们并探索更多的解决方案。我一直相信从基本模型开始了解基线,这也是我的方法。这一部分将重点介绍脸书语句嵌入以及如何将其用于构建问答系统。在未来的部分,我们将尝试实现深度学习技术,特别是针对这个问题的序列建模。所有代码都可以在这个 Github 库上找到。
但是我们先来了解一下问题。我先简单概述一下,不过,对问题的详细了解可以在这里找到。
小队数据集
SStanfordQuestionAnsweringDataset(SQuAD)是一个新的阅读理解数据集,由一组维基百科文章上的众包工作者提出的问题组成,其中每个问题的答案都是相应阅读文章中的一段文字,或 span 。SQuAD 拥有 500+篇文章上的 100,000+问答对,比以前的阅读理解数据集大得多。
问题
对于训练集中的每个观察,我们都有一个**上下文、问题和文本。**这样一个观察的例子——
目标是为任何新问题和提供的上下文找到文本。这是一个封闭的数据集,意味着问题的答案总是上下文的一部分,也是上下文的一个连续跨度。我现在把这个问题分成两部分-
- 获得有正确答案的句子(突出显示黄色)
- 一旦句子最终确定,从句子中获得正确答案(突出显示为绿色)
引入阴差阳错,脸书句嵌入
这些天我们有各种类型的嵌入 word2vec 、 doc2vec 、 food2vec 、 node2vec ,那么为什么没有 sentence2vec 呢?所有这些嵌入背后的基本思想是使用各种维度的向量来用数字表示实体,这使得计算机更容易理解它们以用于各种下游任务。解释这些概念的文章被链接以便你理解。
传统上,我们习惯于平均一个句子中所有单词的向量,称为单词袋方法。每个句子被标记为单词,这些单词的向量可以使用手套嵌入来找到,然后取所有这些向量的平均值。这种技术表现不错,但是这不是一种非常准确的方法,因为它没有考虑单词的顺序。
下面是 推断 ,它是一个句子嵌入方法,提供语义句子表示。它在自然语言推理数据上进行训练,并很好地推广到许多不同的任务。
这个过程是这样的-
从训练数据创建词汇表,并使用该词汇表训练推理模型。一旦模型被训练,提供句子作为编码器函数的输入,该函数将返回 4096 维向量,而不管句子中的单词数。[ 演示
这些嵌入可以用于各种下游任务,如寻找两个句子之间的相似性。我已经为 Quora-问题对 kaggle 竞赛实现了同样的功能。你可以点击查看。
说到团队问题,下面是我尝试使用句子嵌入来解决前一部分问题的第一部分的方法
- 将段落/上下文分成多个句子。我知道的处理文本数据的两个包是-Spacy&text blob。我也使用了包 TextBlob。它进行智能拆分,不像 spacy 的句子检测可以根据周期给你随机的句子。下面提供了一个例子:
Example: Paragraph
TextBlob is splitting it into 7 sentences which makes sense
Spacy is splitting it into 12 sentences
- 使用推理模型获得每个句子和问题的向量表示
- 根据每个句子-问题对的余弦相似度和欧几里德距离,创建距离等特征
模型
我用两种主要方法进一步解决了这个问题-
- 不使用目标变量的无监督学习。这里,我从与给定问题距离最小的段落中返回句子
- 监督学习-训练集的创建对于这一部分来说非常棘手,原因是每个部分没有固定数量的句子,答案可以从一个单词到多个单词不等。我能找到的唯一一篇实现了逻辑回归的论文是由发起这个竞赛&数据集的斯坦福团队发表的。他们使用了这篇论文中解释的多项式逻辑回归,并创建了1.8 亿个特征(该模型的句子检测准确率为 79%) ,但不清楚他们是如何定义目标变量的。如果任何人有任何想法,请在评论中澄清。稍后我会解释我的解决方案。
无监督学习模型
在这里,我首先尝试使用欧几里德距离来检测与问题距离最小的句子。这个模型的精确度大约为 45%。然后,我切换到余弦相似度,准确率从 45%提高到 63% 。这是有意义的,因为欧几里德距离不关心向量之间的对齐或角度,而余弦则负责这一点。在矢量表示的情况下,方向很重要。
但是这种方法没有利用提供给我们的带有目标标签的丰富数据。然而,考虑到解决方案的简单性质,这仍然是一个没有任何培训的好结果。我认为体面的表现归功于脸书句子嵌入。
监督学习模型
这里,我已经将目标变量从文本转换为包含该文本的句子索引。为了简单起见,我把我的段落长度限制在 10 句以内(大约 98%的段落只有 10 句或更少)。因此,在这个问题中,我有 10 个标签要预测。
对于每个句子,我都建立了一个基于余弦距离的特征。如果一个段落的句子数量较少,那么我将把它的特征值替换为 1(最大可能余弦距离),这样总共有 10 个句子。用一个例子来解释这个过程会比较容易。
我们来看看训练集的第一个观察/行。有答案的句子在上下文中被加粗。:
问题——“1858 年在法国卢尔德,圣母玛利亚据说是向谁显现的?”
背景——“从建筑上来说,这所学校具有天主教的特征。在主楼的金色圆顶上是一座金色的圣母玛利亚雕像。在主建筑的正前方,正对着它的是一尊铜制的基督雕像,双臂高举,上面写着“Venite Ad Me Omnes”的字样。主楼旁边是圣心大教堂。长方形会堂的后面是一个洞穴,一个祈祷和沉思的地方。这是一个法国卢尔德石窟的复制品,据说圣母玛利亚于 1858 年在这里出现在圣贝尔纳黛特·索比罗斯面前。在主车道的尽头(在一条直线上,连接着三座雕像和金色穹顶),是一座简单、现代的玛丽石像。
正文——《圣贝尔纳黛特·索比罗斯》
在这种情况下,目标变量将变成 5,因为这是粗体句子的索引。我们将有 10 个特征,每个特征对应于段落中的一个句子。column_cos_7、column_cos_8 和 column_cos_9 的缺失值用 1 填充,因为这些句子在段落中不存在
依赖解析 我为这个问题使用的另一个特性是**“依赖解析树”**。这将模型的精确度略微提高了 5%。这里,我使用了空间树解析,因为它有丰富的 API 来导航树。
For more details: Please check out Stanford Lecture
单词之间的关系在句子上方用有向的、有标记的弧线从首字母到从属字母来表示。我们称之为类型依赖结构,因为标签是从固定的语法关系清单中提取的。它还包括一个根节点,该节点显式地标记了树的根,即整个结构的头部。
让我们使用空间树解析来可视化我们的数据。我使用的是上一节中提供的同一个例子。
**问题—“**1858 年在法国卢尔德,圣母玛利亚据称是向谁显现的?”
有答案的句子— “这是一个法国卢尔德石窟的复制品,据说圣母玛利亚于 1858 年在那里出现在圣贝尔纳黛特·索比罗斯面前”
段落中所有句子的词根
想法是将问题的词根匹配到句子的所有词根/子词根。由于一个句子中有多个动词,所以我们可以得到多个词根。如果问题的词根包含在句子的词根中,那么这个问题被那个句子回答的可能性就更大。考虑到这一点,我为每个值为 1 或 0 的句子创建了一个特征。这里,1 表示问句的词根包含在句根中,否则为 0。
注意:在比较句子的词根和问题词根之前,做词干分析是很重要的。在前面的例子中,问句的词根是出现,而句子中的词根是出现。如果不把出现的 & 出现的归结为一个共同术语,它们就不可能匹配。
下面的示例是转置的数据,带有来自已处理训练数据的 2 个观察值。因此,对于段落中的 10 个句子,结合余弦距离和根匹配,我们总共有 20 个特征。目标变量的范围从 0 到 9。
注意:对于逻辑回归,标准化数据中的所有列是非常重要的。
一旦创建了训练数据,我就使用了多项式逻辑回归、随机森林&梯度推进技术。
多项逻辑回归对验证集的准确率为 65% 。考虑到原始模型有很多特征,准确率为 79% ,这个模型非常简单。随机森林给出了 67% 的准确率,最后 XGBoost 在验证集上表现最好,准确率为 69% 。
我将增加更多的功能(NLP 相关)来改善这些模型。
非常欢迎与功能工程或其他改进相关的想法。这里提供了与上述概念相关的所有代码。
在下一部分中,我们将重点关注从这一部分中入围的句子中提取文本(正确跨度)。同时,看看我的其他博客这里!
关于我:https://alviraswalin.wixsite.com/alvira。有兴趣与跨职能部门合作,从数据中获得见解,并应用机器学习知识来解决复杂的数据科学问题。
领英:www.linkedin.com/in/alvira-swalin
参考
YOLO 的首个实时联邦快递/UPS/USPS 检测系统
您再也不会错过您的包裹了!
在这篇文章中,我将与你分享用 Yolo 建立一个实时物体检测系统来检测 FedEx/UPS/USPS 送货卡车的步骤。这是两部分项目的第一部分:
a.第 1 部分(本文):动机,Yolo 的快速介绍,以及如何训练和测试模型。
b.第二部分(即将推出):通过 Yolo 的 Darkflow 实现,连接 Raspberry Pi 和网络摄像头/摄像机进行真实生活检测。
你可以在我的 GitHub 回购这里找到代码,或者在我室友的 GitHub,Mladen 上找到代码,在这里。
Another attempt will be made!
动机
“再来一次尝试。”对于需要签名的包裹,您经常会收到这样一份丢失的送货单。只剩下最后一次尝试了,所以你决定改变你的计划,第二天呆在家里。然而,你的公寓在大楼的后面,无论你在订购产品时提供了多么详细的说明,大多数情况下,送货人只会试图在前门或中门送货,而不会在你的后门送货。你对此感到厌倦,所以下次你主动打电话给快递公司,要求将你的包裹放在他们的总部,只是要意识到卖家不允许他们这样做。你感到绝望,所以你开始在前门、中门、后门贴上各种说明,上面有你的电话号码,他们不允许打电话!!
是的,这些具有讽刺意味的情况一直发生在我和我的室友身上,也发生在很多我们认识的朋友和家人身上。上一次当我在等待我的新电脑时,我不得不花两个小时坐在大楼前,等待和阅读一本书来消磨时间。因此,我和我的室友决定开发我们自己的物体探测系统,当送货车辆在我们的大楼前停下或行驶时,它会通知我们。
为什么是 Yolo?
在深度学习对象检测的最先进方法(更快的 R-CNN、SSD、YOLO)中,Yolo 因其在速度和准确性之间的巨大平衡而脱颖而出。在 Yolo 中,每个输入图像都被分成一个 S x S 网格。对于网格中的每个单元,一些边界框预测与用于预测与该网格单元相关联的对象的分类概率/分数同时生成。每个分数反映了模型对盒子包含某一类对象的置信度。
你可以在这里找到一些关于 Yolo 解释的有用链接:
https://towards data science . com/yolo-v3-object-detection-53 FB 7d 3 bfe 6 b
【https://hackernoon.com/understanding-yolo-f5a74bbc7967
如果你有关于 Yolo 的问题,我强烈推荐这个谷歌团队。
总而言之:
-
Yolo 在做决定时对图像进行全局推理,它只扫描图像一次,因此节省了时间和计算,因此得名“你只看一次”。
-
Yolo 的检测速度使其成为实时检测的理想候选(在默认分辨率为 416 x 416 的情况下,GTX 1070 GPU 可以为 Yolov3 处理 32 FPS 的视频,为 Yolov2 处理 64 FPS 的视频)。
-具有“更深”层的 Yolov3 不仅能够探测大型和中型物体,还能以高精度探测小型物体。
- Yolo 学习对象的概化表示,因此它是高度概化的,因此,当应用于新的领域或意外输入时,它不太可能崩溃。
训练和测试模型
为了训练模型,我们使用了由 AlexeyAB(https://github.com/AlexeyAB/darknet)优化的 Yolo 实现。
1.收集训练数据
首先,我们从搜集数据进行训练开始。我们的目标数据集是 FedEx/ UPS/ USPS 送货卡车/标志。其他服务,如 DHL,以后很容易扩展。为了避免手动下载图片,我们使用了 https://github.com/hardikvasa/google-images-download 的谷歌图片抓取工具。我们为每个类别下载了大约 400 张图片,但最终每个类别只标记了大约 200 张图片,因为这对第一个试点实验来说应该足够了。为了更好地进行培训,我们还提供了负面示例,即不包含上述公司的卡车/标志的图片。理想情况下,这些图像应该包括不同种类的车辆、房屋、树木……类似于我们房屋周围的类似环境。请记住,在他的 GitHub repo 中,Alexey 建议使用尽可能多的负面例子图像,以便更好地检测。根据我在其他 Yolo 项目上的个人经验,也许 40%-50%的负面图像是一个好的范围,超过 50%可能会使模型变得不那么“敏感”。
2.标记图像
下一步是为训练标记数据。在我们的案例中,我们标记的对象是公司的徽标。我们决定排除整辆车,只在车标周围画一个边框。然而,在未来,我们将尝试对所有车辆进行检测,因为像 UPS 和 DHL 这样的卡车都非常独特/容易识别。我们使用 BBox-Label-Tool 来标记图像。由于 BBox-Label-Tool 不会自动调整图像大小以适合计算机屏幕,因此在标记之前应调整过大图像的大小,以防止图像中的部分徽标丢失。
3.训练模型
如果你的 GPU 不是很强,你可以在云上训练模型。我刚买了一台装有 GTX 1070 的新台式机,对培训来说绰绰有余。我试验了 Yolo 作者的原始 darknet 版本和 AlexeyAB 的 T2 改进版本。根据我对几个项目的经验,包括这个项目,我注意到 AlexeyAB 的版本在训练和检测阶段确实快得多(https://github . com/AlexeyAB/darknet/issues/529 # issue comment-377204382)。
为了训练模型,我们需要模型配置文件,其中包含几个用于训练的超参数。可以找找。我的 GitHub repo 上的 cfg 文件。
在这个项目中,我们对 Yolov2 和 Yolov3 都进行了实验。
*免责声明:*在模型开始检测物体之前,训练 Yolov2 比 Yolov3 更快,但可能需要更多的迭代(https://github.com/thtrieu/darkflow/issues/80)。
在我们的例子中,Yolov2 进行了超过 12,000 次迭代,Yolov3 进行了 3000 次迭代,才开始检测送货卡车。总的来说,Yolov3 在准确性方面会更好,它在检测小物体方面明显更好。然而,我们最终使用了 Yolov2,因为我们想在 Raspberry Pi web 服务器设置中使用 DarkFlow(下一篇文章),而 Yolov3 还没有在 DarkFlow 中可用。
该模型在 3 个类别上进行训练:联邦快递、UPS 和美国邮政,此外还有预训练的权重:http://pjreddie.com/media/files/darknet19_448.conv.23
关于如何使用 Yolov2 训练您的定制对象的详细说明可在此处找到:https://github . com/AlexeyAB/darknet/tree/47 c 7a f1 ce a5 bb dedf 1184963355 e 6418 CB 8 B1 b4f # how-to-train-detect-your-custom-objects
或 if
4.测试模型
知道什么时候停止训练,选择哪组砝码进行检测是相当重要的。一旦你对模型进行了足够多的训练和测试,你会获得更多的经验和直觉,但是当你第一次开始时,拥有这样的指导方针可能会非常有帮助:https://github . com/AlexeyAB/darknet # when-should-I-stop-training。
请记住,拥有一个小的训练数据集可能会很快导致过度拟合。在这种情况下,由于数据集很小,并且我已经从另一个项目中获得了足够的经验,我决定使用所有的数据进行训练,并通过对几个剪辑进行测试来选择最佳权重,而不是将我的数据分为训练集和测试集。一般来说,我们要把数据拆分成训练和测试,通过看 precision & recall,mAP (mean Average Precision),IoU (Intersection over Union)来检查模型的准确性。关于这些措施的一个很好的解释可以在这里找到。
以下是三个竞争对手在芝加哥市区的一次精彩交锋中的测试结果:
Let’s race!
您还可以在下面找到联邦快递/UPS/USPS 检测我们项目的演示剪辑:
Test on a clip
考虑到训练数据集的大小,以及它是在 416 x 416 分辨率下测试的事实,这是一个相当好的结果。您可以按照 Alexey 的说明,通过将分辨率从 416 x 416 更改为 608 x 608 或更高来改善检测。为了创建一个更加通用和健壮的检测系统,我们只需要创建更多的用于训练的标记数据。
结论
喜欢这个帖子就给我鼓掌(或者 50 :p)。请随意下载权重和配置文件并亲自测试。第二部分即将推出。我们基本上完成了实施,并且已经成功地对联邦快递、UPS 和 USPS 的送货卡车进行了现实生活中的检测。欢迎在下面留下任何评论和反馈!
用 Tensorflow 和 OpenCV 构建实时物体识别应用
在本文中,我将介绍如何使用 Python 3(具体来说是 3.5)中的tensor flow(TF)新的对象检测 API 和 OpenCV 轻松构建自己的实时对象识别应用程序。重点将放在我创建它时所面临的挑战上。你可以在我的回购上找到完整的代码。
这也是正在运行的应用程序:
Me trying to classify some random stuff on my desk:)
动机
谷歌刚刚发布了他们新的 TensorFlow 物体检测 API。第一个版本包含:
- 一些预训练的模型(特别关注轻量模型,以便它们可以在移动设备上运行)
- 一个 Jupyter 笔记本示例,其中包含一款已发布的型号
- 一些非常方便的脚本可用于模型的重新训练,例如,在您自己的数据集上。
我想把我的手放在这个新的很酷的东西上,并有一些时间来构建一个简单的实时对象识别演示。
物体检测演示
首先,我调出了 TensorFlow models repo ,然后又看了一下他们发布的笔记本。它基本上走过了使用预训练模型的所有步骤。在他们的例子中,他们使用了“带 Mobilenet 的 SSD”模型,但你也可以在他们所谓的“tensor flow 检测模型动物园”上下载其他几个预先训练好的模型。顺便说一下,这些模型是在 COCO 数据集上训练的,并根据模型速度(慢、中和快)和模型性能(mAP —平均精度)而变化。
我接下来做的是运行这个例子。这个例子实际上是有据可查的。本质上,它是这样做的:
- 导入所需的软件包,如 TensorFlow、PIL 等。
- 定义一些变量,如班级人数、模型名称等。
- 下载冻结模型(。pb — protobuf )并将其加载到内存中
- 加载一些辅助代码,例如标签转换器的索引
- 两幅测试图像上的检测代码本身
**注意:**在运行示例之前,请务必查看设置注意事项。特别是,protobuf 编译部分非常重要:
# From tensorflow/models/research/
protoc object_detection/protos/*.proto --python_out=.
如果不运行这个命令,这个示例将无法运行。
然后,我采用他们的代码,并对其进行了相应的修改:
- 删除模型下载部分
- PIL 是不需要的,因为 OpenCV 中的视频流已经在 numpy 数组中了(PIL 也是一个非常大的开销,特别是当使用它来读取图像或视频流时)
- TensorFlow 会话没有“with”语句,因为这是一个巨大的开销,特别是当每次会话都需要在每个流之后启动时
然后,我用 OpenCV 把它和我的网络摄像头连接起来。有很多例子向你解释如何去做,甚至是官方文档。所以,我就不深究了。更有趣的部分是我为提高应用程序的性能而做的优化。在我的例子中,我看到了良好的 fps——每秒帧数。
一般来说,许多 OpenCV 示例的普通/朴素实现并不是真正的最佳实现,例如 OpenCV 中的一些函数严重受限于 I/O。所以我不得不想出各种办法来解决这个问题:
- 从网络摄像头读取帧会导致大量的 I/O。我的想法是用多处理库将这部分完全转移到不同的 Python 进程。这不知何故没有奏效。在 Stackoverflow 上有一些解释为什么它不工作,但我没有深入探讨这个问题。幸运的是,我从的 Adrian Rosebrock 的网站“pyimagesearch”上找到了一个非常好的例子,使用线程代替它大大提高了我的 fps。顺便说一下,如果你想知道多处理和线程之间的区别,在 Stackoverflow 上有一个很好的解释。
- 每次应用程序启动时,将冻结的模型加载到内存中是一项很大的开销。我已经为每次运行使用了一个 TF 会话,但这仍然非常慢。那么我是怎么解决这个问题的呢?解决办法很简单。在这种情况下,我使用多处理库将对象检测部分的繁重工作转移到多个进程中。应用程序的初始启动会很慢,因为每个进程都需要将模型加载到内存中并启动 TF 会话,但在此之后,我们将受益于并行性😁
由 datitran 录制
asciinema.org](https://asciinema.org/a/125852)
- 减少视频流中帧的宽度和高度也大大提高了 fps。
**注意:**如果你像我一样使用 Mac OSX,并且使用 OpenCV 3.1,OpenCV 的 VideoCapture 可能会在一段时间后崩溃。已经有个问题提交。切换回 OpenCV 3.0 解决了这个问题。
结论与展望
给我一个❤️,如果你喜欢这个职位:)拉代码,并尝试自己。一定要看看 Tensorflow 对象检测 API。到目前为止,从第一眼看上去,它非常简洁明了。我想尝试的下一件事是用 API 训练我自己的数据集,并为我想到的其他应用程序使用预训练的模型。我对应用程序的性能也不完全满意。fps 速率仍然不是最佳的。OpenCV 中仍然有许多我无法影响的瓶颈,但是我可以尝试一些替代方法,比如使用 WebRTC 。然而,这是基于网络的。此外,我正在考虑使用异步方法调用(async)来提高我的 fps 率。敬请期待!
在推特上关注我: @datitran
为 iOS 构建实时对象识别器
使用 CoreML 和 Swift
Credits — Apple (https://developer.apple.com/documentation/coreml)
CoreML 是 WWDC 2017 上宣布的令人兴奋的功能之一。它是苹果的框架,可以用来将机器学习集成到你的应用程序中,全部离线😉。
Core ML 允许您将各种各样的机器学习模型集成到您的应用程序中。除了支持超过 30 种层类型的广泛深度学习,它还支持标准模型,如树集成、支持向量机和广义线性模型。因为它建立在 Metal 和 Accelerate 等底层技术之上,所以 Core ML 无缝地利用 CPU 和 GPU 来提供最大的性能和效率。你可以在设备上运行机器学习模型,这样数据就不需要离开设备进行分析。—苹果关于机器学习的文档(https://developer.apple.com/machine-learning/)
当你深入了解预测是如何发生的以及发生在哪里时,CoreML 的重要性就显现出来了。到目前为止,每个人都习惯于将机器学习集成到应用程序中,在托管服务器中进行预测。如果它是一个对象识别应用程序,您必须从设备中捕获帧,将这些数据发送到预测引擎,等到图像完全上传到服务器,并最终获得输出。这种方法主要有两个问题——网络延迟和用户隐私。现在,所有这些处理都可以在设备中进行,从而减少了这两个问题。
从头开始构建
我们可以尝试使用 CoreML 并为此实现一个简单的设备上解决方案。我将在不提及 iOS 或 Swift 基础知识的情况下讲述重要步骤。
我们要做的第一件事是获得一个 iOS 11 设备和 Xcode 9。
如果你不熟悉机器学习,看看这里的简介。或者你可以从这里得到一个很高层次的概述。
机器学习
这种技术赋予计算机学习的能力,而不需要明确地编码问题的解决方案。
这里基本上涉及两个过程——训练和预测。
训练是我们给模型不同组的输入(和相应的输出)来从模式中学习的过程。这个被训练的模型被给予一个它以前没有见过的输入,以便根据它早先的观察来预测。
选择模型
所以我们要做的第一件事就是为你的项目选择一个好的模型。有许多预先训练好的模型可用于图像识别。或者你甚至可以训练自己的模型来获得更好的体验。
从苹果的机器学习门户有好的模型可以作为 CoreML 模型。或者如果你有自己的模型,你可以使用苹果公司的 CoreML 工具将其转换成 CoreML 支持的模型。
我选择了苹果门户中可用的 Inception V3 库。
Inception v3 —从一组 1000 个类别中检测图像中存在的主要对象,例如树木、动物、食物、车辆、人等等。
创建 iOS 项目
您可以使用 swift 创建一个基本的 iOS 项目,并为此创建一个包含视频预览层和标签的视图控制器。
从视频预览中获取帧
像往常一样获取当前帧,这是我们已经知道的。这在这篇入侵代码文章中有解释。
使用 Inception v3 进行预测
当一个输入图像给你一个它知道的类别集合中的一个的概率时,考虑我们的初始模型作为黑盒。
从 Apple 的门户网站下载模型,将其拖放到您的项目中。您可以从 Xcode 中看到模型描述。
Inceptionv2.mlmodel in model viewer of Xcode
您可以看到,该模型将 299x299 像素的图像作为输入,并给出输出:
- 图像最可能属于的类别
- 每个类别的概率列表
我们可以利用这些参数中的任何一个来确定类别。我用的第一个是字符串,直接打印在屏幕上。
您还可以看到,Xcode 直接从 mlmodel 对象创建了一个 swift 模型(Inceptionv3.swift)。你不必为此做任何额外的改动。
使用
我们可以利用 Xcode 生成的预测 API,如下所示:
预测很简单:
但是它需要一个 CVPixelBuffer 的对象而不是 UIImage 来进行预测,hackingwithswift.com 的家伙在“机器学习和视觉”一节中对此做了很好的解释。
我已经创建了 UIImage 类别,该类别与 resize API 一起对此进行了抽象。
最终建筑
结果
该应用程序能够正确识别几乎所有提供的输入。
从回购中获取完整代码—https://github.com/m25lazi/inception。⭐️,如果你喜欢的话。
用 Flink 构建实时仪表板:后端
随着对“实时”低延迟数据需求的增长,更多的数据科学家可能必须熟悉流。一个很好的起点是 Apache Flink。Flink 是一个分布式流框架,专门为实时数据分析而构建。与 Spark 的批处理运行不同(甚至 Spark“流”在技术上也是微批处理),Flink 是建立在流模型上的(Spark vs. Flink 是一个很长的讨论,我在这里就不赘述了)。Apache Flink 可以处理极低延迟的高吞吐量数据。因此,Apache Flink 的一个有趣用例是创建一个实时数据分析仪表板。
为了简单起见,我选择使用 Twitter streaming API 作为我们的数据源,因为其他数据源通常需要反序列化模式,这使事情变得更加复杂。在第一篇文章中,我将向您展示如何用 Flink 预处理 Tweets,并执行一些基本的聚合/过滤。然后在第二篇文章中,我将向您展示如何用 D3.js 前端连接它,以制作一个实时仪表板。由于有大量代码,我决定只包括最相关的摘录,但您可以在文章底部找到包含所有代码的 GitHub 资源库。
为了进行设置,我们定义了基本的登录凭证(您需要一个 Twitter 开发人员帐户),然后设置执行环境。关于这个的更多细节你可以从 Flink 上看到官方示例代码。唯一的例外是,这里我们将使用 Filter.java 的类作为 FilterEndpoint。这将允许我们只获得包含特定单词的推文。你可以通过声明Filter.FilterEndPoint i = new Filter(wordsList,locationsList,null);
然后声明twitterSourceA.setCustomEndPointInitializer(i);
来实现。其中,wordsList 是要包含的单词列表,twitterSourceA 是用您的凭据声明的 TwitterSource 对象(如示例所示)。您还可以根据位置或用户 id(空值)进行筛选。
Apache Flink 遵循与 Spark 和 MapReduce 相同的函数式编程方法。您有三个主要功能来处理地图、平面地图和过滤器。首先,我们将执行一个平面图,以获取相关信息。这一部分与示例非常相似,除了我还包含了一个自定义标记器(即 tokenize ),以消除点号和数字。平面图的代码如下:
This is our flatMap function that performs a basic word count on the tweets. The function uses a second function getField to get the JSON data. Then it tokenizes it, removes the punctation, and collects the word with a count of one.
接下来我们要过滤掉停用词。为此定义一个过滤器既快速又简单;我们简单地定义一个新的过滤函数,然后做tweets.filter(aFilterFunction)
见下面的全部细节。接下来,我们定义一个 10 秒的时间窗口。时间窗口允许我们查看一个单词在十秒钟的窗口中出现了多少次。然后,这个流将被输入 sink,sink 将每十秒钟更新一次我们的图表(第二步)。
Filtering and windowing functions.
此外,我选择包含最后一个过滤器,以便过滤掉在窗口中出现不到十次的单词。现在我们有了一个合适的流,可以输入到我们的数据库中,并用来直接更新我们的图表。然而,关于数据的存储和显示,仍然有许多设计选择要做。
在本系列的第二部分中,我将讨论如何将新的数据流连接到我们的前端,以便创建实时更新的图表以及一些用于处理实时流和实现持久性的架构结构。
完整的代码可在以下链接。
Twitter stream——使用 Apache Flink 传输特定的推文
github.com](https://github.com/isaacmg/twitterStream)
我如何建立一个香水推荐系统
正如网飞首席执行官雷德·哈斯汀斯所说,*“你知道,想想看,当你看一个来自网飞的节目,你会上瘾,你会熬夜到很晚。我们在边缘上与睡眠竞争。”*在我看来,竞争的关键部分是如何在正确的时间向正确的人提供正确的内容,这是关于推荐系统的。
推荐系统一直吸引着我的注意力。不仅因为它们是机器学习应用的一大部分,还因为它们与理解人有关。每当我在潘多拉上听音乐,在亚马逊上买东西,在 Stitch Fix 上订购衣服,在网飞上看电影,在 StumbleUpon 上阅读新闻,在 Instagram 或 Pinterest 上浏览照片,我都强烈地感觉到推荐者已经充斥在我们数字生活的每个角落,他们将继续塑造我们未来的生活。
这就是为什么我选择了我的激励顶点项目来构建香水的推荐系统。人们说你身上散发的香味表明你是谁。一个人对香水的选择受到他/她的性别、年龄、国籍、气候、心情、一年中的季节、一天中的时间等的影响。因为我在过去做过几次错误的决定,所以几个月前决定买一台新的时,我非常谨慎。然后我想,作为一名数据科学家,我应该可以用我的数据科学和机器学习知识来回答这个问题。这就是我如何发展这个想法的——帮助人们找到他们的签名香水。
1.项目工作流程
我使用 6 个 AWS EC2 实例从中国最大的香水论坛搜集数据。将它们存储在 MongoDB 的第 7 个 EC2 实例中。数据准备好之后,我做了数据分析、特征工程、建模和交叉验证。然后我用 Flask 和 Bootstrap 搭建了一个 web app。终于在 AWS 上托管了 web app。
2.数据
数据是从中国最大的香水论坛刮来的(因为美国最大的论坛禁止我刮),它由两个表组成。一个表是香水数据,有品牌、性别、笔记、主题等特征。另一个表是用户评级数据,其中有用户 ID、香水 ID、用户评级得分(范围从 2 到 10)和用户评论。
3.模特们
在这个项目中,我针对不同的消费群体实施了不同的方法。对于冷启动问题(没有评级历史的新用户),我将使用基于内容的模型。在最终的 web 应用程序中,我会要求他们要么参加一个小测验,要么输入一种你喜欢的香水,根据香水的特性进行推荐。对于有评分历史的用户,我将实现项目-项目相似性协同过滤模型和矩阵分解模型,然后比较它们的性能。在最终的 web 应用程序中,我会要求他们登录他们的帐户,并根据相似的用户生成个性化的推荐。
(网络应用主页提供了三个选项来寻找你的下一款香水)
模型 1:基于内容的推荐系统
要建立一个基于内容的推荐器,我们唯一关心的是香水的特性。由于我收集的数据还没有告诉我人们闻到香水时的感觉的特征,我将 NMF(非负矩阵分解)和 LDA(潜在狄利克雷分配)应用于用户评论文本以进行主题建模。在比较了每种方法生成的话题后,我发现 LDA 生成的 12 个话题更具可解释性。因此,我人工标记了 12 个 LDA 主题,将它们与其他香水特征(如性别、音符和主题)结合在一起,构建了香水矩阵,然后基于 Jaccard 相似度建立了基于内容的相似度模型。
(基于内容的推荐器的快照)
模型 2:协同过滤推荐器
协同过滤的两个主要领域是邻域方法和潜在因素模型。邻域法的核心是计算项目之间的关系,或者说是使用之间的关系。面向项目的方法基于同一用户对“相邻”项目的评级来评估用户对项目的偏好。一个产品的邻居是当由同一用户评级时倾向于获得相似评级的其他产品。另一方面,潜在因素模型是一种替代方法,它试图通过根据评级模式推断的潜在因素来描述项目和用户来解释评级。对于香水,发现的因素可能测量明显或不太明确的维度,如花卉或柑橘主题,对不同性别的取向,或完全无法解释的维度。对于用户来说,每个因素都衡量用户有多喜欢在相应电影向量上得分高的香水。
在这个项目中,对于有评分历史的用户,我应用了来自邻域方法的项目-项目相似性模型和来自矩阵分解方法的 UV 分解模型。我用来评估我的建议的评估指标是 RMSE(均方根误差)。
基线模型是为用户随机推荐香水,这意味着预测评级是所有东西的平均评级(7.25,满分 10 分),它给了我 2.20 的 RMSE。
之后,我建立了项目-项目相似度模型。它表现很差(RMSE: 7.32),因为用户评级矩阵太稀疏(0.3%的单元填充有值),当矩阵太稀疏时,邻域方法不能很好地执行,因为进入每个预测的信息很少。
然后,我应用矩阵分解模型,通过随机梯度下降拟合。在调整了若干因素后,对线性项以及相互作用项进行正则化。矩阵分解模型给了我最好的表现(RMSE: 1.95)。因此,我使用矩阵分解模型作为我的协同过滤推荐器的最终模型。
(上图:矩阵 M 的紫外线分解)
协同过滤模型的性能如下图所示:
4.我如何进行训练测试分割和交叉验证
每当有人说“我建立了一个推荐者”,我脑海中浮现的第一个问题是:“你是如何验证你的推荐者的表现的?”因为这是所有推荐系统中最具挑战性的部分。在我的项目中,对于基于内容的推荐,由于它是无监督的,完全基于香水特征进行推荐,所以我通过让我的同学尝试并查看结果来验证它。总体来说,推荐是好的,尤其是主页上的“查找相似”功能,但最大的缺点是没有香水价格数据,一个喜欢香奈儿香水的顾客可能不会去一个大众市场品牌,即使它们有相似的成分。然而,对于主页上的“参加测验”功能,如果用户只选择了几个特征,结果将是 off,因为模型正在基于用户选择的所有特征构建向量,并计算用户向量和每种香水之间的 Jaccard 距离。
对于协同过滤推荐器,我尝试的第一个交叉验证方法是留一法交叉验证,但我花了 18 个小时来完成一个循环,计算量太大,因此我改用手动 K-fold 验证。首先,我手动删除了评分低于 3 的用户,将他们视为冷启动问题的一部分,并让他们使用基于内容的推荐器。然后,对于具有 3 个或更多评级的用户,我确保每个用户都存在于我的训练集、验证集和测试集中。因此,我可以使用用户在训练集中的评级来拟合模型,预测用户在验证集中的其他评级,并基于验证集上的 RMSE 来调整模型。最后,我使用用户在测试集中的评分来评估最终的模型。
5.局限性和未来工作
1)对数据的限制:
- 价格信息不可用:喜爱香奈儿香水的顾客可能不会去大众市场品牌,即使它们有相似的成分。
- 没有用户统计数据:人们对每种香水的感受在不同的文化和地域中是不同的,我的数据来自一个中国香水论坛,那里的人们对每种香水的感受并不反映其他国家的人们对每种香水的感受。
- 只有 20%的香水被评论过:好的推荐系统的一个优点是它的意外收获。但为了检索顾客对每款香水的感受,我只能查看已经有用户评论的香水。然而,80%的香水没有,要么是因为它们不流行,要么是因为它们是新的。因此,为了使我的推荐系统更好地工作,需要更多的香水领域知识和功能工程工作,这样我就不需要依赖用户评论来获得人们对每种香水的感觉。
2)顾客的口味会随着时间而改变
- 我现有的模型是静态的。但在现实中,顾客的偏好会发生变化,导致他们重新定义自己的品味。同样,随着新香水的出现,产品的认知和受欢迎程度也在不断变化。因此,系统应该考虑反映用户-项目交互的动态、时间漂移性质的时间效应。因此,对于未来的工作,我还应该收集用户评级数据,我的协同过滤模型应该考虑时间效应。
3)有更好的方法吗?
- 是的。永远不要停止学习。
6.最后的想法
在为期两周的项目体验中,我意识到我可以用推荐系统做很多事情,以及它们有多么强大。它们不仅仅是香水或任何消费品。它们可以是你可能在 MOOC 网站上喜欢的课程,或者你可能在约会应用程序上喜欢的人,不用说,新闻文章、书籍、音乐……背后的主要想法是关于你如何了解你的客户,你有多少领域知识,以及你如何利用数据科学和机器学习来帮助你实现你的理解。如果你碰巧也对这个领域感兴趣,我会非常乐意与你讨论。
代码可以在这里找到:【github.com/kellypeng/scentmate_rec】T2
参考资料:
- Anand Rajaraman 和 Jeffrey Ullman,挖掘海量数据集,第 9 章推荐系统
- Yehyda Koren,Robert Bell 和 Chris Volinsky,推荐系统的矩阵分解技术
- 耶胡达·科伦,因式分解与邻域相遇:多面协作过滤模型
- 斯蒂芬·高尔, Netflix 奖和 SVD
- Jiunn Haur Lim,用矩阵分解法预测用户评分
使用神经网络嵌入构建推荐系统
(Source)
如何利用深度学习和维基百科创建图书推荐系统
深度学习可以做一些不可思议的事情,但其用途往往在学术论文中被掩盖,或者需要只有大公司才能获得的计算资源。尽管如此,深度学习的应用可以在个人电脑上完成,不需要高级学位。在本文中,我们将看到如何使用神经网络嵌入来创建一个图书推荐系统,该系统使用所有关于图书的维基百科文章。
我们的推荐系统将建立在链接到相似维基百科页面的书籍彼此相似的理念之上。我们可以表现出这种相似性,从而通过使用神经网络学习书籍和维基百科链接的嵌入来提出建议。最终的结果是一个有效的推荐系统和深度学习的实际应用。
Most Similar Books to Stephen Hawking’s A Brief History of Time
这个项目的完整代码可以从 GitHub 上的 Jupyter 笔记本中获得。如果你没有 GPU,你也可以在 Kaggle 上找到笔记本,在那里你可以免费用一个 GPU 训练你的神经网络。本文将重点关注实现,神经网络嵌入的概念在之前的文章中有所涉及。(要了解如何检索我们将使用的数据——维基百科上的所有书籍文章——请看一下这篇文章。)
这个项目改编自深度学习食谱,这是一本关于应用深度学习的动手示例的优秀书籍。
神经网络嵌入
嵌入是一种将离散的分类变量表示为连续向量的方法。与一次性编码之类的编码方法相比,神经网络嵌入是低维的和学习过的,这意味着它们将相似的实体在嵌入空间中彼此放置得更近。
为了创建嵌入,我们需要一个神经网络嵌入模型和一个受监督的机器学习任务。我们网络的最终结果将是把每本书表示为 50 个连续数字的向量。
虽然嵌入本身并不有趣——它们只是向量——但它们可以用于三个主要目的:
- 在嵌入空间中寻找最近的邻居
- 作为机器学习模型的输入
- 低维可视化
该项目主要涵盖第一个用例,但我们也将看到如何从嵌入创建可视化。神经网络嵌入的实际应用包括机器翻译的单词嵌入和分类变量的实体嵌入。
数据:维基百科上的所有书籍
像通常的数据科学项目一样,我们需要从高质量的数据集开始。在这篇文章中,我们看到了如何下载和处理维基百科上的每一篇文章,搜索任何关于书籍的页面。我们保存了书名、基本信息、图书页面上指向其他维基百科页面的链接(wikilinks)以及外部站点的链接。为了创建推荐系统,我们需要的唯一信息是标题和 wikilinks。
**Book Title: 'The Better Angels of Our Nature'****Wikilinks:** **['Steven Pinker',
'Nation state',
'commerce',
'literacy',
'Influence of mass media',
'Rationality',
"Abraham Lincoln's first inaugural address",
'nature versus nurture',
'Leviathan']**
即使在处理神经网络时,探索和清理数据也很重要,在笔记本中,我对原始数据做了几处修改。例如,查看链接最多的页面:
Wikipedia pages most often linked to by books on Wikipedia.
我们看到前四页是一般性的,对推荐没有帮助。一本书的格式没有告诉我们关于内容的任何事情:知道一本书是paperback
或hardcover
并不允许我们——或神经网络——找出它相似的其他书。因此,我们可以去除这些链接,帮助神经网络区分书籍。
考虑最终目的可以在数据清理阶段有所帮助,并且这一行动本身就可以显著改进建议。
出于纯粹的好奇,我想在维基百科上找到与其他书链接最多的书。以下是 10 本“联系最紧密”的维基百科书籍:
Books on Wikipedia most often linked to by other books on Wikipedia.
这是参考书和经典书籍的混合体,很有意义。
经过数据清理,我们有了一套 41758 独有维基链接和 37020 独有书籍。希望每个人都有一本书!
一旦我们确信我们的数据是干净的,我们就需要开发一个有标签的训练样本的监督机器学习任务。
监督学习任务
为了*学习有意义的嵌入,*我们的神经网络必须被训练来完成一个目标。从项目的指导假设——相似的书籍链接到相似的维基百科页面——开始,我们可以将问题表述如下:给定一个(书名,维基链接)对,确定维基链接是否出现在该书的文章中。
我们实际上不需要把书的文章交给电视台。相反,我们将输入成千上万的训练样本,包括书名、维基链接和标签。我们给网络一些真实的例子——实际上存在于数据集中——和一些虚假的例子,最终它会学习嵌入来区分维基链接何时出现在一本书的页面上。
表达监督学习任务是这个项目最重要的部分。嵌入是为特定的任务学习的,并且只与那个问题相关。如果我们的任务是确定哪些书是简·奥斯汀写的,那么嵌入将反映这一目标,在嵌入空间中将奥斯汀写的书放在一起。我们希望通过训练来判断一本书的页面上是否有某个维基链接,网络可以学习嵌入,将内容相似的书彼此放得更近。
一旦我们概述了学习任务,我们需要用代码实现它。首先,因为神经网络只能接受整数输入,所以我们创建了从每个唯一书籍到整数的映射:
# Mapping of books to index and index to books
book_index = {book[0]: idx for idx, book in enumerate(books)}
book_index['Anna Karenina']**22494**
我们也对链接做同样的事情。在此之后,为了创建训练集,我们列出了数据中的所有(book,wikilink)对。这需要遍历每本书,并在页面上为每个 wikilink 记录一个例子:
pairs = []
*# Iterate through each book*
for book in books:
title = book[0]
book_links = book[2] # Iterate through wikilinks in book article
for link in book_links: # Add index of book and index of link to pairs
pairs.extend((book_index[title],
link_index[link]))
这给了我们总共 772798 个真实的例子,我们可以从中取样来训练模型。为了生成错误的示例(稍后完成),我们将简单地随机选取一个链接索引和图书索引,确保它不在pairs
中,然后将其用作负面观察。
关于训练/测试集的说明
虽然使用单独的验证和测试集是正常监督机器学习任务的必须,但在这种情况下,我们的主要目标是而不是制作最准确的模型,而是生成嵌入。预测任务只是我们为这些嵌入训练我们的网络的手段。在训练结束时,我们不会在新数据上测试我们的模型,所以我们不需要评估性能或使用验证集来防止过度拟合。为了获得最佳嵌入,我们将使用所有示例进行训练。
嵌入模型
虽然神经网络嵌入听起来在技术上很复杂,但使用 Keras 深度学习框架实现起来相对容易。(如果你是深度学习新手,我建议从 Keras 开始。TensorFlow 可能会给你更多的控制,但 Keras 在开发方面是无可匹敌的)。
嵌入模型有 5 层:
- **输入:**书本和链接的并行输入
- **嵌入:**平行长度 50 书籍和链接的嵌入
- **点:**通过计算点积合并嵌入
- **整形:**需要将点积整形为单个数字
- **密集:**一个具有乙状结肠激活的输出神经元
在嵌入神经网络中,嵌入是神经网络的参数——权重——在训练过程中调整,以最小化目标的损失。神经网络将一本书和一个链接作为整数,并输出一个介于 0 和 1 之间的预测,与真实值进行比较。该模型用[Adam](https://machinelearningmastery.com/adam-optimization-algorithm-for-deep-learning/)
优化器(随机梯度下降的变体)编译,该优化器在训练期间改变嵌入以最小化该二元分类问题的binary_crossentropy
。
下面是完整模型的代码:
这个框架可以用于许多嵌入模型。需要理解的重要一点是,嵌入是模型参数(权重),也是我们想要的最终结果。我们不在乎模型是否准确,我们想要的是相关的嵌入。
我们习惯于将模型中的权重作为做出准确预测的手段,但是在嵌入模型中,权重是目标,而预测是学习嵌入的手段。
如模型摘要所示,有将近 400 万个权重:
通过这种方法,我们不仅可以获得书籍的嵌入,还可以获得链接,这意味着我们可以比较所有书籍链接的维基百科页面。
生成训练样本
神经网络是批量学习器,因为它们是在一小组样本——观察值——上训练的,一次经过许多轮,称为历元。训练神经网络的常见方法是使用生成器。这是一个yields
(不是returns
)批量处理样本的功能,因此整个结果不会保存在内存中。虽然在这个问题中这不是问题,但是生成器的好处是大型训练集不需要全部加载到内存中。
我们的生成器接受训练pairs
,每批阳性样本数(n_positive
,每批阴性:阳性样本比率(negative_ratio
)。每次调用生成器时,它都会生成一批新的阳性和阴性样本。为了得到正面的例子,我们随机抽样真实的配对。对于反面的例子,我们随机抽取一本书和一个链接,确保这个配对不在正确的配对中,然后将它添加到批处理中。
下面的代码完整地展示了生成器。
每次我们调用生成器上的next
,我们都会得到一个新的训练批次。
next(generate_batch(pairs, n_positive = 2, negative_ratio = 2))**({'book': array([ 6895., 29814., 22162., 7206., 25757., 28410.]),
'link': array([ 260., 11452., 5588., 34924., 22920., 33217.])},
array([ 1., -1., 1., -1., -1., -1.]))**
有了监督任务、训练生成器和嵌入模型,我们就可以学习书籍嵌入了。
培训模式
有几个训练参数可供选择。第一个是每批的正例数。一般来说,我试着从小批量开始,然后逐渐增加,直到性能开始下降。此外,我们需要为每个正例选择训练的负例数量。我建议尝试几个选项,看看什么效果最好。因为我们没有使用验证集来实现提前停止,所以我选择了一些时期,超过这些时期,训练损失不会减少。
n_positive = 1024
gen = generate_batch(pairs, n_positive, negative_ratio = 2)
*# Train*
h = model.fit_generator(gen, epochs = 15,
steps_per_epoch = len(pairs) // n_positive)
(如果训练参数似乎是任意的,从某种意义上来说,它们是任意的,但基于 深度学习 中概述的最佳实践。像机器学习的大多数方面一样,训练神经网络在很大程度上是凭经验的。)
一旦网络完成训练,我们就可以提取嵌入。
*# Extract embeddings*
book_layer = model.get_layer('book_embedding')
book_weights = book_layer.get_weights()[0]
应用嵌入:提出建议
嵌入本身相当无趣:它们只是每本书和每个链接的 50 个数字的向量:
What War and Peace Looks Like as a Vector.
然而,我们可以将这些向量用于两个不同的目的,第一个目的是制作我们的图书推荐系统。为了在嵌入空间中找到与查询书最接近的书,我们获取该书的向量,并找到它与所有其他书的向量的点积。如果我们的嵌入是归一化的,那么向量之间的点积表示余弦相似度,范围从-1(最不相似)到+1(最相似)。
查询列夫·托尔斯泰的经典作品 《战争与和平》的嵌入结果:
**Books closest to War and Peace.
Book: Anna Karenina Similarity: 0.92
Book: The Master and Margarita Similarity: 0.92
Book: Demons (Dostoevsky novel) Similarity: 0.91
Book: The Idiot Similarity: 0.9
Book: Crime and Punishment Similarity: 0.9**
这些建议很有道理!这些都是经典的俄罗斯小说。当然,我们可以去 GoodReads 寻找这些相同的建议,但是为什么不自己建立这个系统呢?我鼓励你使用笔记本,自己探索嵌入的内容。
**Books closest to The Fellowship of the Ring.****Book: The Return of the King Similarity: 0.96
Book: The Silmarillion Similarity: 0.93
Book: Beren and Lúthien Similarity: 0.91
Book: The Two Towers Similarity: 0.91**
除了嵌入书籍,我们还嵌入了链接,这意味着我们可以找到与给定维基百科页面最相似的链接:
**Pages closest to steven pinker.
Page: the blank slate Similarity: 0.83
Page: evolutionary psychology Similarity: 0.83
Page: reductionism Similarity: 0.81
Page: how the mind works Similarity: 0.79**
目前,我正在读一本由史蒂芬·杰·古尔德写的精彩散文集,名为《雷龙的恶霸》。接下来该读什么?
Recommendations for my next book.
嵌入的可视化
嵌入最吸引人的一个方面是,它们可以用来可视化概念,比如相对于彼此的小说或非小说。这需要进一步的降维技术来使维数达到 2 或 3。最流行的约简技术是另一种嵌入方法: t 分布随机邻居嵌入(TSNE) 。
从维基百科上所有书籍的 37000 维空间开始,我们使用嵌入将其映射到 50 维,然后使用 TSNE 将其映射到 2 维。这会产生以下图像:
Embeddings of all books on Wikipedia.
这张图片本身并不那么有启发性,但是一旦我们开始根据书籍的特点给它着色,我们就开始看到集群的出现:
非小说和科幻小说有一些明确的分类(只有前 10 种类型被突出显示),它们有不同的部分。鉴于小说内容的多样性,这些小说似乎无处不在。
我们也可以嵌入国家:
我有点惊讶这些国家是如此的与众不同!显然,澳大利亚的书籍是非常独特的。
此外,我们可以在维基百科地图上突出显示某些书籍:
The corner of Wikipedia with books about the entire Universe
笔记本里有更多的视觉化图像,你可以自己制作。我再给你一个展示 10 本“联系最紧密”的书的例子:
Book embeddings with 10 most linked to books and genres.
关于 TSNE 需要注意的一点是,它试图保持原始空间中向量之间的距离,但因为它减少了维数,所以可能会扭曲原始分离。因此,在 50 维嵌入空间中彼此靠近的书籍在 2 维 TSNE 嵌入中可能不是最近的邻居。
交互式可视化
这些可视化非常有趣,但我们可以使用 TensorFlow 的投影仪工具制作令人惊叹的交互式图形,该工具专门为可视化神经网络嵌入而设计。我计划写一篇关于如何使用这个工具的文章,但是现在这里是一些结果:
Interactive visualizations made with projector
要以互动方式浏览书籍样本,请点击这里的。
潜在的其他项目
数据科学项目通常不是完全靠自己发明的。我从事的许多项目都是来自其他数据科学家的想法,我对这些想法进行了改编、改进和构建,以形成一个独特的项目。(这个项目的灵感来自于深度学习食谱中一个类似的电影推荐项目。)
带着这种态度,这里有一些方法来继续这项工作:
- 使用外部链接而不是 wikilinks 创建嵌入。这些是维基百科以外的网页,可能会产生不同的嵌入。
- 使用嵌入来训练受监督的机器学习模型,以预测包括流派、作者和国家的书籍特征。
- 在维基百科上选择一个主题类别,创建自己的推荐系统。你可以使用人物、地标,甚至历史事件。您可以使用这个笔记本获取数据,使用这个笔记本进行嵌入。
这绝不是一个家庭作业,只是一些项目想法,如果你想把你所读到的付诸实践。如果你决定接手一个项目,我很乐意听听你的想法。
结论
神经网络嵌入是一种将离散分类变量表示为连续向量的方法。作为一种学习的低维表示,它们对于寻找相似的类别、作为机器学习模型的输入或者可视化概念地图是有用的。在这个项目中,我们使用神经网络嵌入来创建一个有效的图书推荐系统,该系统基于链接到相似页面的图书彼此相似的思想。
创建神经网络嵌入的步骤如下:
- 收集数据。神经网络需要许多训练示例。
- 制定一个有监督的任务来学习反映问题的嵌入。
- 建立和训练嵌入式神经网络模型。
- 提取嵌入内容以进行推荐和可视化。
详细内容可以在笔记本中找到,我鼓励任何人参与这个项目。虽然深度学习可能因为技术复杂性或计算资源而看起来势不可挡,但这是通过有限的学习就可以在个人电脑上完成的许多应用之一。深度学习是一个不断发展的领域,这个项目是通过构建一个有用的系统来开始的好方法。而且,当你不在研究深度学习的时候,现在你知道你应该读什么了!
一如既往,我欢迎反馈和建设性的批评。可以通过 Twitter @koehrsen_will 或我的个人网站 willk.online 找到我。
使用 NVIDIA TensorRT 服务器和谷歌云为 Keras 模型构建可扩展的深度学习服务环境
在最近的一个项目中,我使用 Keras 和 Tensorflow 开发了一个用于图像分类的大规模深度学习应用程序。开发完模型后,我们需要将它部署到云环境中一个相当复杂的数据获取和准备例程管道中。我们决定在通过 API 公开模型的预测服务器上部署模型。因此,我们遇到了英伟达 TensorRT 服务器(TRT 服务器),一个不错的老 TF 服务的严肃替代品(顺便说一下,这是一个很棒的产品!).在权衡利弊之后,我们决定给 TRT 服务器一个机会。TRT 服务器有几个优于 TF 服务器的优点,如优化的推理速度、简单的模型管理和资源分配、版本控制和并行推理处理。此外,TensorRT 服务器并不“局限于”TensorFlow(和 Keras)模型。它可以服务于所有主要深度学习框架的模型,如 TensorFlow、MxNet、pytorch、theano、Caffe 和 CNTK。
尽管有很多很酷的功能,我发现设置 TRT 服务器有点麻烦。安装和文档分散在相当多的存储库、官方文档指南和博客文章中。这就是为什么我决定写这篇关于设置服务器的博文,让你的预言成真!
NVIDIA TensorRT 服务器
TensorRT 推理服务器是英伟达将深度学习模型投入生产的前沿服务器产品。它是 NVIDIA 的 TensorRT 推理平台的一部分,提供了一个可扩展的、生产就绪的解决方案,用于为来自所有主要框架的深度学习模型提供服务。它基于 NVIDIA Docker,包含从容器内部运行服务器所需的一切。此外,NVIDIA Docker 允许在 Docker 容器中使用 GPU,在大多数情况下,这大大加快了模型推理的速度。谈到速度——TRT 服务器可以比 TF 服务器快得多,并允许同时从多个模型进行多个推理,使用 CUDA 流来利用 GPU 调度和序列化(见下图)。
通过 TRT 服务器,您可以使用所谓的实例组来指定并发推理计算的数量,这些实例组可以在模型级别进行配置(参见“模型配置文件”一节)。例如,如果您为两个模型提供服务,并且其中一个模型获得了明显更多的推理请求,那么您可以为该模型分配更多的 GPU 资源,从而允许您并行计算更多的请求。此外,实例组允许您指定模型应该在 CPU 还是 GPU 上执行,这在更复杂的服务环境中是一个非常有趣的特性。总的来说,TRT 服务器有一大堆很棒的特性,这使得它非常适合生产使用。
上图展示了服务器的一般架构。人们可以看到 HTTP 和 gRPC 接口,它们允许您将模型集成到通过 LAN 或 WAN 连接到服务器的其他应用程序中。相当酷!此外,服务器公开了一些健全的特性,如健康状态检查等。,这在生产中也派上了用场。
设置服务器
如前所述,TensorRT 服务器位于 NVIDIA Docker 容器中。为了让事情进展顺利,您需要完成几个安装步骤(如果您是从一台空白的机器开始,就像这里一样)。整个过程相当漫长,需要一定的“一般云、网络、IT 知识”。我希望下面的步骤能让你清楚安装和设置的过程。
在谷歌云上启动深度学习虚拟机
对于我的项目,我使用了预装 CUDA 和 TensorFlow 库的 Google 深度学习虚拟机。你可以使用 Google Cloud SDK 或者在 GCP 控制台中启动云虚拟机(在我看来,这非常容易使用)。GCP SDK 的安装可以在这里找到。请注意,由于 CUDA 安装过程需要几分钟的时间,可能需要一段时间才能连接到服务器。您可以在云日志控制台中检查虚拟机的状态。
# Create project
gcloud projects create tensorrt-server# Start instance with deep learning image
gcloud compute instances create tensorrt-server-vm \
--project tensorrt-server \
--zone your-zone \
--machine-type n1-standard-4 \
--create-disk='size=50' \
--image-project=deeplearning-platform-release \
--image-family tf-latest-gpu \
--accelerator='type=nvidia-tesla-k80,count=1' \
--metadata='install-nvidia-driver=True' \
--maintenance-policy TERMINATE
成功设置实例后,您可以使用终端 SSH 到 VM。从这里,您可以执行所有必要的步骤来安装所需的组件。
# SSH into instance
gcloud compute ssh tensorrt-server-vm \
--project tensorrt-server \
--zone your-zone
注意:当然,您必须为您的项目和实例名修改脚本。
安装 Docker
在设置了 GCP 云虚拟机之后,你必须在你的机器上安装 Docker 服务。谷歌深度学习虚拟机使用 Debian 作为操作系统。您可以使用下面的代码在 VM 上安装 Docker。
# Install Docker
sudo apt-get update sudo
apt-get install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo apt-key add -
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install docker-ce
您可以通过运行以下命令来验证 Docker 是否已经成功安装。
sudo docker run --rm hello-world
你应该看到一个“你好,世界!”从 docker 容器中,您应该会看到这样的内容:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
d1725b59e92d: Already exists
Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
Status: Downloaded newer image for hello-world:latestHello from Docker!
This message shows that your installation appears to be working correctly.To generate this message, Docker took the following steps:
1\. The Docker client contacted the Docker daemon.
2\. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64)
3\. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading.
4\. The Docker daemon streamed that output to the Docker client, which sent it to your terminal.To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/ For more examples and ideas, visit: [https://docs.docker.com/get-started/](https://docs.docker.com/get-started/)
恭喜你,你已经成功安装了 Docker!
安装 NVIDIA Docker
不幸的是,Docker 对连接到主机系统的 GPU 没有“开箱即用”的支持。因此,需要安装 NVIDIA Docker 运行时,才能在容器化环境中使用 TensorRT Server 的 GPU 功能。NVIDIA Docker 也用于 TF 服,如果你想用你的 GPU 做模型推断的话。下图说明了 NVIDIA Docker 运行时的架构。
您可以看到,NVIDIA Docker 运行时围绕 Docker 引擎分层,允许您在系统上使用标准 Docker 和 NVIDIA Docker 容器。
由于 NVIDIA Docker 运行时是 NVIDIA 的专有产品,您必须在 NVIDIA GPU Cloud (NGC) 注册以获得 API 密钥,以便安装和下载它。要针对 NGC 进行身份验证,请在服务器命令行中执行以下命令:
# Login to NGC sudo docker login nvcr.io
将提示您输入用户名和 API 密钥。对于用户名,您必须输入$oauthtoken
,密码是生成的 API 密钥。成功登录后,您可以安装 NVIDIA Docker 组件。按照 NVIDIA Docker GitHub repo 上的说明,您可以通过执行以下脚本来安装 NVIDIA Docker(Ubuntu 14.04/16.04/18.04,Debian Jessie/Stretch)。
# If you have nvidia-docker 1.0 installed: we need to remove it and all existing GPU containers
docker volume ls -q -f driver=nvidia-docker | xargs -r -I{} -n1 docker ps -q -a -f volume={} | xargs -r docker rm -f
sudo apt-get purge -y nvidia-docker# Add the package repositories
curl -s -L [https://nvidia.github.io/nvidia-docker/gpgkey](https://nvidia.github.io/nvidia-docker/gpgkey) | \
sudo apt-key add -
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L [https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list](https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list) | \
sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update# Install nvidia-docker2 and reload the Docker daemon configuration
sudo apt-get install -y nvidia-docker2
sudo pkill -SIGHUP dockerd# Test nvidia-smi with the latest official CUDA image
sudo docker run --runtime=nvidia --rm nvidia/cuda:9.0-base nvidia-smi
安装 TensorRT 服务器
成功安装 NVIDIA Docker 后,下一步就是安装 TensorRT 服务器。它可以从 NVIDIA 容器注册表(NCR)中提取。同样,您需要通过 NGC 认证才能执行此操作。
# Pull TensorRT Server (make sure to check the current version) sudo docker pull nvcr.io/nvidia/tensorrtserver:18.09-py3
提取映像后,TRT 服务器就可以在您的云机器上启动了。下一步是创建一个将由 TRT 服务器提供服务的模型。
模型部署
在安装了所需的技术组件并拉出 TRT 服务器容器之后,您需要关注您的模型和部署。TensorRT Server 在服务器上的一个文件夹中管理其模型,即所谓的模型库。
设置模型库
模型库包含导出的 TensorFlow / Keras 等。特定文件夹结构中的模型图。对于模型库中的每个模型,需要定义一个具有相应模型名称的子文件夹。在这些模型子文件夹中,有模型模式文件(config.pbtxt
)、标签定义(labels.txt
)以及模型版本子文件夹。这些子文件夹允许您管理和提供不同的模型版本。文件labels.txt
包含适当顺序的目标标签的字符串,对应于模型的输出层。在 version 子文件夹中保存了一个名为model.graphdef
(导出的 protobuf 图)的文件。model.graphdef
实际上是一个冻结的 tensorflow 图,是导出 TensorFlow 模型后创建的,需要相应命名。
备注:由于一些变量初始化错误,我无法从 TRT 服务器的tensoflow.python.saved_model.simple_save()
或tensorflow.python.saved_model.builder.SavedModelBuilder()
导出中获得有效服务。因此,我们使用“冻结图”方法,将图中的所有 TensorFlow 变量转换为常量,并将所有内容输出到一个文件中(即model.graphdef
)。
/models
|- model_1/
|-- config.pbtxt
|-- labels.txt
|-- 1/
|--- model.graphdef
因为模型库只是一个文件夹,所以它可以位于 TRT 服务器主机有网络连接的任何地方。例如,您可以将导出的模型图存储在云存储库或您机器上的本地文件夹中。新模型可以导出并部署到那里,以便通过 TRT 服务器进行服务。
模型配置文件
在您的模型库中,模型配置文件(config.pbtxt
)为 TRT 服务器上的每个模型设置重要参数。它包含关于您的可服务模型的技术信息,并且是正确加载模型所必需的。这里有几件事你可以控制:
name: "model_1"
platform: "tensorflow_graphdef"
max_batch_size: 64
input [
{
name: "dense_1_input"
data_type: TYPE_FP32
dims: [ 5 ]
}
]
output [
{
name: "dense_2_output"
data_type: TYPE_FP32
dims: [ 2 ]
label_filename: "labels.txt"
}
]
instance_group [
{
kind: KIND_GPU
count: 4
}
]
首先,name
定义了模型下的标签在服务器上是可达的。这必须是模型库中模型文件夹的名称。platform
定义了框架,建立了模型。如果你用的是 TensorFlow 或者 Keras,有两个选项:(1) tensorflow_savedmodel
和tensorflow_graphdef
。如前所述,我用的是tensorflow_graphdef
(见上一节末尾我的备注)。batch_size
,顾名思义,控制你预测的批量大小。input
定义你的模型的输入层节点名称,比如输入层的name
(是的,你应该在 TensorFlow 或者 Keras 中命名你的层和节点)data_type
,目前只支持数值类型,比如TYPE_FP16, TYPE_FP32, TYPE_FP64
和输入dims
。相应的,output
定义了你的模型的输出层name
,分别是data_type
和dims
。您可以指定一个labels.txt
文件,以适当的顺序保存输出神经元的标签。因为这里只有两个输出类,所以文件看起来很简单,如下所示:
class_0 class_1
每行定义一个类标签。请注意,该文件不包含任何头。最后一部分instance_group
让您为您的模型定义特定的 GPU ( KIND_GPU
)或 CPU ( KIND_CPU
)资源。在示例文件中,有4
个并发 GPU 线程分配给模型,允许四个同时预测。
构建一个简单的服务模型
为了通过 TensorRT 服务器为模型提供服务,您首先需要一个模型。我准备了一个小脚本,在 Keras 中构建一个简单的 MLP 用于演示目的。我已经在生产中成功地将 TRT 服务器用于更大的模型,如 InceptionResNetV2 或 ResNet50,它工作得非常好。
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from keras.models import Sequential
from keras.layers import InputLayer, Dense
from keras.callbacks import EarlyStopping, ModelCheckpoint
from keras.utils import to_categorical# Make toy data
X, y = make_classification(n_samples=1000, n_features=5)# Make target categorical
y = to_categorical(y)# Train test split
X_train, X_test, y_train, y_test = train_test_split(X, y)# Scale inputs
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)# Model definition
model_1 = Sequential()
model_1.add(Dense(input_shape=(X_train.shape[1], ), units=16, activation='relu', name='dense_1'))
model_1.add(Dense(units=2, activation='softmax', name='dense_2'))
model_1.compile(optimizer='adam', loss='categorical_crossentropy')# Early stopping
early_stopping = EarlyStopping(patience=5)
model_checkpoint = ModelCheckpoint(filepath='model_checkpoint.h5', save_best_only=True, save_weights_only=True)
callbacks = [early_stopping, model_checkpoint]# Fit model and load best weights
model_1.fit(x=X_train, y=y_train, validation_data=(X_test, y_test), epochs=50, batch_size=32, callbacks=callbacks)# Load best weights after early stopping
model_1.load_weights('model_checkpoint.h5')# Export model
model_1.save('model_1.h5')
该脚本使用sklearn.datasets.make_classification
构建了一些玩具数据,并为这些数据拟合了一个单层 MLP。拟合后,模型被保存,以便在单独的导出脚本中进一步处理。
为上菜冻结图表
服务 Keras (TensorFlow)模型的工作方式是将模型图导出为一个单独的 protobuf 文件(.pb
-文件扩展名)。将模型导出到包含网络所有权重的单个文件中的一个简单方法是“冻结”图形并将其写入磁盘。由此,图形中的所有tf.Variables
被转换为tf.constant
,并与图形一起存储在一个文件中。为此,我修改了这个脚本。
import os
import shutil
import keras.backend as K
import tensorflow as tf
from keras.models import load_model
from tensorflow.python.framework import graph_util
from tensorflow.python.framework import graph_io def freeze_model(model, path):
""" Freezes the graph for serving as protobuf """
# Remove folder if present
if os.path.isdir(path):
shutil.rmtree(path)
os.mkdir(path)
shutil.copy('config.pbtxt', path)
shutil.copy('labels.txt', path)
# Disable Keras learning phase
K.set_learning_phase(0)
# Load model
model_export = load_model(model)
# Get Keras sessions
sess = K.get_session()
# Output node name
pred_node_names = ['dense_2_output']
# Dummy op to rename the output node
dummy = tf.identity(input=model_export.outputs[0], \
name=pred_node_names)
# Convert all variables to constants
graph_export = \
graph_util.convert_variables_to_constants(sess=sess,
input_graph_def=sess.graph.as_graph_def(),
output_node_names=pred_node_names)
graph_io.write_graph(graph_or_graph_def=graph_export,
logdir=path + '/1',
name='model.graphdef',
as_text=False)# Freeze Model
freeze_model(model='model_1.h5', path='model_1')# Upload to GCP
os.system('gcloud compute scp model_1 tensorrt-server-vm:~/models/ --project tensorrt-server --zone us-west1-b --recurse')
freeze_model()
函数获取保存的 Keras 模型文件model_1.h5
的路径以及要导出的图形的路径。此外,我还增强了该功能,以便构建所需的模型库文件夹结构,其中包含版本子文件夹config.pbtxt
和labels.txt
,两者都存储在我的项目文件夹中。该函数加载模型并将图形导出到定义的目的地。为了做到这一点,您需要定义输出节点的名称,然后使用graph_util.convert_variables_to_constants
将图中的所有变量转换为常量,这使用了相应的 Keras 后端会话,必须使用K.get_session()
获取这些会话。此外,在导出前使用K.set_learning_phase(0)
禁用 Keras 学习模式非常重要。最后,我添加了一个小的 CLI 命令,将我的模型文件夹上传到我的 GCP 实例的模型库/models
。
启动服务器
既然一切都已安装、设置和配置完毕,现在(终于)是时候启动我们的 TRT 预测服务器了。以下命令启动 NVIDIA Docker 容器,并将模型库映射到该容器。
sudo nvidia-docker run --rm --name trtserver -p 8000:8000 -p 8001:8001 \ -v ~/models:/models nvcr.io/nvidia/tensorrtserver:18.09-py3 trtserver \ --model-store=/models
--rm
删除由--name
给出的同名的现有容器。-p
显示主机上的端口 8000 (REST)和 8001 (gRPC ),并将它们映射到各自的容器端口。-v
将主机上的模型库文件夹(在我的例子中是/models
)安装到容器中的/models
,然后--model-store
将其引用为查找可服务模型图的位置。如果一切正常,您应该会看到类似如下的控制台输出。如果您不想看到服务器的输出,您可以在启动时使用-d
标志在分离的模型中启动容器。
===============================
== TensorRT Inference Server ==
===============================NVIDIA Release 18.09 (build 688039)Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
Copyright 2018 The TensorFlow Authors. All rights reserved.Various files include modifications (c) NVIDIA CORPORATION. All rights reserved.
NVIDIA modifications are covered by the license terms that apply to the underlying
project or file.NOTE: The SHMEM allocation limit is set to the default of 64MB. This may be
insufficient for the inference server. NVIDIA recommends the use of the following flags:
nvidia-docker run --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 ...I1014 10:38:55.951258 1 server.cc:631] Initializing TensorRT Inference Server
I1014 10:38:55.951339 1 server.cc:680] Reporting prometheus metrics on port 8002
I1014 10:38:56.524257 1 metrics.cc:129] found 1 GPUs supported power usage metric
I1014 10:38:57.141885 1 metrics.cc:139] GPU 0: Tesla K80
I1014 10:38:57.142555 1 server.cc:884] Starting server 'inference:0' listening on
I1014 10:38:57.142583 1 server.cc:888] localhost:8001 for gRPC requests
I1014 10:38:57.143381 1 server.cc:898] localhost:8000 for HTTP requests
[warn] getaddrinfo: address family for nodename not supported
[evhttp_server.cc : 235] RAW: Entering the event loop ...
I1014 10:38:57.880877 1 server_core.cc:465] Adding/updating models.
I1014 10:38:57.880908 1 server_core.cc:520] (Re-)adding model: model_1
I1014 10:38:57.981276 1 basic_manager.cc:739] Successfully reserved resources to load servable {name: model_1 version: 1}
I1014 10:38:57.981313 1 loader_harness.cc:66] Approving load for servable version {name: model_1 version: 1}
I1014 10:38:57.981326 1 loader_harness.cc:74] Loading servable version {name: model_1 version: 1}
I1014 10:38:57.982034 1 base_bundle.cc:180] Creating instance model_1_0_0_gpu0 on GPU 0 (3.7) using model.savedmodel
I1014 10:38:57.982108 1 bundle_shim.cc:360] Attempting to load native SavedModelBundle in bundle-shim from: /models/model_1/1/model.savedmodel
I1014 10:38:57.982138 1 reader.cc:31] Reading SavedModel from: /models/model_1/1/model.savedmodel
I1014 10:38:57.983817 1 reader.cc:54] Reading meta graph with tags { serve }
I1014 10:38:58.041695 1 cuda_gpu_executor.cc:890] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
I1014 10:38:58.042145 1 gpu_device.cc:1405] Found device 0 with properties:
name: Tesla K80 major: 3 minor: 7 memoryClockRate(GHz): 0.8235
pciBusID: 0000:00:04.0
totalMemory: 11.17GiB freeMemory: 11.10GiB
I1014 10:38:58.042177 1 gpu_device.cc:1455] Ignoring visible gpu device (device: 0, name: Tesla K80, pci bus id: 0000:00:04.0, compute capability: 3.7) with Cuda compute capability 3.7\. The minimum required Cuda capability is 5.2.
I1014 10:38:58.042192 1 gpu_device.cc:965] Device interconnect StreamExecutor with strength 1 edge matrix:
I1014 10:38:58.042200 1 gpu_device.cc:971] 0
I1014 10:38:58.042207 1 gpu_device.cc:984] 0: N
I1014 10:38:58.067349 1 loader.cc:113] Restoring SavedModel bundle.
I1014 10:38:58.074260 1 loader.cc:148] Running LegacyInitOp on SavedModel bundle.
I1014 10:38:58.074302 1 loader.cc:233] SavedModel load for tags { serve }; Status: success. Took 92161 microseconds.
I1014 10:38:58.075314 1 gpu_device.cc:1455] Ignoring visible gpu device (device: 0, name: Tesla K80, pci bus id: 0000:00:04.0, compute capability: 3.7) with Cuda compute capability 3.7\. The minimum required Cuda capability is 5.2.
I1014 10:38:58.075343 1 gpu_device.cc:965] Device interconnect StreamExecutor with strength 1 edge matrix:
I1014 10:38:58.075348 1 gpu_device.cc:971] 0
I1014 10:38:58.075353 1 gpu_device.cc:984] 0: N
I1014 10:38:58.083451 1 loader_harness.cc:86] Successfully loaded servable version {name: model_1 version: 1}
还有一个警告,显示您应该使用以下参数启动容器
--shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864
你当然可以这样做。然而,在这个例子中,我没有使用它们。
安装 Python 客户端
现在是时候测试我们的预测服务器了。TensorRT Server 附带了几个客户端库,允许您向服务器发送数据并获得预测。构建客户端库的推荐方法仍然是——Docker。要使用包含客户端库的 Docker 容器,您需要使用以下命令克隆各自的 GitHub repo :
git clone [https://github.com/NVIDIA/dl-inference-server.git](https://github.com/NVIDIA/dl-inference-server.git)
然后,cd
进入dl-inference-server
文件夹并运行
docker build -t inference_server_clients .
这将在您的机器上构建容器(需要一些时间)。要在主机上使用容器中的客户端库,需要将一个文件夹挂载到容器中。首先,在交互会话中启动容器(-it
标志)
docker run --name tensorrtclient --rm -it -v /tmp:/tmp/host inference_server_clients
然后,在容器的 shell 中运行以下命令(您可能必须首先创建/tmp/host
):
cp build/image_client /tmp/host/.
cp build/perf_client /tmp/host/.
cp build/dist/dist/tensorrtserver-*.whl /tmp/host/.
cd /tmp/host
上面的代码将预构建的image_client
和perf_client
库复制到挂载的文件夹中,并使其可以从主机系统访问。最后,您需要使用
pip install tensorrtserver-0.6.0-cp35-cp35m-linux_x86_64.whl
在集装箱系统上。终于!就这样,我们准备好了(听起来这是一个简单的方法)!
使用 Python 客户端进行推理
使用 Python,您可以使用客户端库轻松执行预测。为了向服务器发送数据,您需要一个来自inference_server.api
模块的InferContext()
,它获取 TRT 服务器的 IP 和端口以及所需的型号名称。如果您在云中使用 TRT 服务器,请确保您有适当的防火墙规则允许端口 8000 和 8001 上的流量。
from tensorrtserver.api import *
import numpy as np# Some parameters
outputs = 2
batch_size = 1# Init client
trt_host = '123.456.789.0:8000' # local or remote IP of TRT Server
model_name = 'model_1'
ctx = InferContext(trt_host, ProtocolType.HTTP, model_name)# Sample some random data
data = np.float32(np.random.normal(0, 1, [1, 5]))# Get prediction
# Layer names correspond to the names in config.pbtxt
response = ctx.run(
{'dense_1_input': data},
{'dense_2_output': (InferContext.ResultFormat.CLASS, outputs)},
batch_size)# Result
print(response)
{'output0': [[(0, 1.0, 'class_0'), (1, 0.0, 'class_1')]]}
注意:发送到服务器的数据必须与浮点精度相匹配,这一点很重要,浮点精度是先前在模型定义文件中为输入层定义的。此外,输入和输出层的名称必须与模型的名称完全匹配。如果一切顺利,ctx.run()
返回一个预测值的字典,您可以根据需要对其进行进一步的后处理。
结论和展望
哇,那真是一段旅程!然而,TensorRT Server 是一款非常棒的产品,可以将您的深度学习模型投入生产。它速度快、可扩展,并且具有适合生产使用的简洁特性。我没有详细讨论推理性能。如果你对更多感兴趣,一定要看看 NVIDIA 的博客文章。我必须承认,与 TRT 服务器相比,TF Serving 在安装、模型部署和使用方面要方便得多。然而,与 TRT 服务器相比,它缺少一些在生产中方便使用的功能。一句话:我和我的团队一定会将 TRT 服务器添加到我们深度学习模型的生产工具堆栈中。
如果你对我的故事有任何意见或问题,欢迎在下面评论!我将尝试回答这些问题。此外,请随意使用我的代码或在您选择的社交平台上与您的同行分享这个故事。
如果你对更多类似的内容感兴趣,请加入我们的邮件列表,不断为你带来新的数据科学、机器学习和人工智能阅读,并从我和我在 STATWORX 的团队直接发送到你的收件箱!
最后,如果你有兴趣与我联系,请在 LinkedIn 或 Twitter 关注我。
参考
- 所有图片均来自这个 NVIDIA 开发者博客
原载于 2018 年 11 月 19 日www.statworx.com。