使用 Python 进行实际时间序列预测
建立 SARIMA 模型的 Box-Jenkins 建模策略
布莱恩·苏曼在 Unsplash 上的照片
时间序列分析是从按时间顺序排列的数据点中提取有意义的摘要和统计信息的努力。它们广泛用于应用科学和工程,涉及时间测量,如信号处理、模式识别、数学金融、天气预报、控制工程、医疗数字化、智能城市应用等。
随着我们不断监测和收集时间序列数据,应用时间序列分析和预测的机会正在增加。
在本文中,我将展示如何用 Python 开发一个带有季节成分的 ARIMA 时间序列预测模型。我们将遵循 Box-Jenkins 三阶段建模方法,以得出预测的最佳模型。
我鼓励任何人查看我的 GitHub 上的 Jupyter 笔记本以获得完整的分析。
在时间序列分析中, Box-Jenkins 方法以统计学家 George Box 和 Gwilym Jenkins 命名,他们应用 ARIMA 模型来寻找时间序列模型的最佳拟合。
该模型分为 3 个步骤:模型识别、参数估计和模型验证。
时间序列
作为数据,我们将使用每月牛奶产量数据集。它包括 1962 年至 1975 年间每头奶牛的月生产记录(磅)。
df = pd.read_csv('./monthly_milk_production.csv', sep=',', parse_dates=['Date'], index_col='Date')
时间序列数据检验
正如我们从上面的图中可以观察到的,我们的数据有增长的趋势和很强的季节性。
我们将使用 Python 的 statsmodels 库来执行时间序列分解。时间序列分解是将时间序列解构为其趋势、季节和残差分量的统计方法。
import statsmodels.api as sm
from statsmodels.tsa.seasonal import seasonal_decomposedecomposition = seasonal_decompose(df['Production'], freq=12)
decomposition.plot()
plt.show()
分解图表明,每月产奶量具有增长趋势和季节性模式。
如果我们想更精确地观察季节性成分,我们可以根据月份来绘制数据。
1.模型识别
在这一步,我们需要检测时间序列是否是平稳的,如果不是,我们需要了解需要什么样的变换才能使其平稳。
当时间序列的统计属性(如均值、方差和自相关)随时间保持不变时,该时间序列就是稳定的。换句话说,当时间序列不依赖于时间并且没有趋势或季节效应时,它就是平稳的。大多数统计预测方法都是基于时间序列是(近似)平稳的假设。
想象一下,我们有一个随着时间持续增长的时间序列,样本均值和方差将随着样本的大小而增长,他们总是会低估未来期间的均值和方差。这就是为什么,我们需要从一个平稳的时间序列开始,从它的时间相关的趋势和季节成分中去除。
我们可以使用不同的方法来检查平稳性:
- 我们可以从图中了解到,比如我们之前看到的分解图,我们已经观察到了趋势和季节性。
- 我们可以绘制自相关函数和偏自相关函数图,它们提供了关于时间序列值对其先前值的依赖性的信息。如果时间序列是平稳的,那么 ACF/PACF 图将在少量滞后之后显示出一个快速截止点。
from statsmodels.graphics.tsaplots import plot_acf, plot_pacfplot_acf(df, lags=50, ax=ax1)
plot_pacf(df, lags=50, ax=ax2)
这里我们看到 ACF 和 PACF 图都没有显示快速切入 95%置信区间区域(蓝色),这意味着时间序列不是静止的。
- 我们可以应用统计检验和扩大的 Dickey-Fuller 检验是广泛使用的一种。检验的零假设是时间序列有一个单位根,这意味着它是非平稳的。我们使用测试的 p 值来解释测试结果。如果 p 值低于阈值(5%或 1%),我们拒绝零假设,时间序列是平稳的。如果 p 值高于阈值,我们无法拒绝零假设,时间序列是非平稳的。
from statsmodels.tsa.stattools import adfullerdftest = adfuller(df['Production'])dfoutput = pd.Series(dftest[0:4], index=['Test Statistic','p-value','#Lags Used','Number of Observations Used'])
for key, value in dftest[4].items():
dfoutput['Critical Value (%s)'%key] = value
print(dfoutput)
Dickey-Fuller 检验的结果:
检验统计量-1.303812
p 值 0.627427
#Lags 使用 13.000000
观察次数使用 154.000000
临界值(1%) -3.473543
临界值(5%) -2.880498
临界值(10%) -2.
p 值大于阈值,我们无法拒绝零假设,时间序列是非平稳的,它具有时间相关的成分。
所有这些方法都表明我们拥有非平稳数据。现在,我们需要找到一种方法让它静止不动。
非平稳时间序列背后有两大原因;趋势和季节性。我们可以应用差分法,从当前观测值中减去以前的观测值,使时间序列平稳。这样我们将消除趋势和季节性,并稳定时间序列的平均值。由于趋势和季节性因素,我们应用了一个非季节性差异diff()
和一个季节性差异diff(12)
。
df_diff = df.diff().diff(12).dropna()
Dickey-Fuller 检验结果:
检验统计量-5.038002
p 值 0.000019
#Lags 使用 11.000000
观察次数使用 143.000000
临界值(1%) -3.476927
临界值(5%) -2.881973
临界值(10%) -2.
应用前面列出的平稳性检查,我们注意到差分时间序列图没有揭示任何特定的趋势或季节行为,ACF/PACF 图有一个快速截止点,ADF 测试结果返回 p 值几乎为 0.00。其低于阈值。所有这些检查表明差异数据是稳定的。
我们将应用季节性自回归综合移动平均(SARIMA 或季节性 ARIMA ),这是 ARIMA 的扩展,支持带有季节性成分的时间序列数据。ARIMA 代表自回归综合移动平均,是时间序列预测最常用的技术之一。
ARIMA 模型用 ARIMA(p,D,Q)的顺序表示,而萨里玛模型用萨里玛(P,D,q)(P,D,Q)m 的顺序表示
AR§ 是一个回归模型,它利用了一个观测值和一些滞后观测值之间的依赖关系。
I(d) 是使时间序列平稳的差分阶。
MA(q) 是一种利用观测值与应用于滞后观测值的移动平均模型的残差之间的相关性的模型。
(P,D,Q)m 是专门描述模型的季节性组件的附加参数集。p、D 和 Q 代表季节回归、差分和移动平均系数,m 代表每个季节周期中的数据点数。
2.模型参数估计
我们将使用 Python 的 pmdarima 库,为我们的季节性 arima 模型自动提取最佳参数。在 auto_arima 函数中,我们将指定d=1
和D=1
,因为我们一次区分趋势,一次区分季节性,m=12
因为我们有月度数据,trend='C'
包括常数,seasonal=True
适合季节性 arima。此外,我们指定trace=True
来打印配合的状态。这有助于我们通过比较 AIC 分数来确定最佳参数。
import pmdarima as pmmodel = pm.auto_arima(df['Production'], d=1, D=1,
m=12, trend='c', seasonal=True,
start_p=0, start_q=0, max_order=6, test='adf',
stepwise=True, trace=True)
AIC (Akaike 信息准则)是样本外预测误差和我们模型的相对质量的估计量。期望的结果是找到尽可能低的 AIC 分数。
具有各种(P,D,q)(P,D,Q)m 参数的 auto_arima 函数的结果表明,当参数等于(1,1,0)(0,1,1,12)时,获得最低的 AIC 分数。
我们将数据集分成训练集和测试集。在这里,我用 85%作为火车分裂的大小。我们用建议的参数在列车组上创建一个 SARIMA 模型。我们使用的是 statsmodel 库中的 SARIMAX 函数(X 描述的是外生参数,这里不加任何)。拟合模型后,我们还可以打印汇总统计数据。
from statsmodels.tsa.statespace.sarimax import SARIMAXmodel = SARIMAX(train['Production'],
order=(1,1,0),seasonal_order=(0,1,1,12))
results = model.fit()
results.summary()
3.模型验证
该模型主要关注的是确保残差正态分布,均值为零且不相关。
为了检查残差统计,我们可以打印模型诊断:
results.plot_diagnostics()
plt.show()
- 左上角的图显示了一段时间内的残差,它似乎是一个没有季节性成分的白噪声。
- 右上角的图显示 kde 线(红色)紧密跟随 N(0,1)线,N(0,1)线是均值为零、标准差为 1 的正态分布的标准符号,表明残差是正态分布的。
- 左下方的正态 QQ 图显示残差的有序分布(蓝色)紧密遵循从标准正态分布中提取的样本的线性趋势,表明残差呈正态分布。
- 右下角是相关图,表明残差与滞后版本的相关性较低。
所有这些结果都表明残差呈低相关性正态分布。
为了衡量预测的准确性,我们将测试集上的预测值与其真实值进行比较。
forecast_object = results.get_forecast(steps=len(test))
mean = forecast_object.predicted_mean
conf_int = forecast_object.conf_int()
dates = mean.index
从图中,我们看到模型预测几乎与测试集的真实值相匹配。
from sklearn.metrics import r2_scorer2_score(test['Production'], predictions)>>> 0.9240433686806808
模型的 R 的平方为 0.92,表明模型的决定系数为 92%。
mean_absolute_percentage_error = np.mean(np.abs(predictions - test['Production'])/np.abs(test['Production']))*100>>> 1.649905
平均绝对百分比误差 (MAPE)是最常用的精度指标之一,以误差的百分比来表示精度。模型的 MAPE 得分等于 1.64,表明预测误差 1.64%,准确率为 98.36%。
由于诊断测试和准确性指标都表明我们的模型近乎完美,我们可以继续进行未来预测。
这是未来 60 个月的预测。
results.get_forecast(steps=60)
我希望您喜欢学习本教程并在 Python 中构建时间序列预测。
如果你喜欢这篇文章,你可以 在这里阅读我的其他文章和 关注我上媒如果有任何问题或建议,请告诉我。✨
喜欢这篇文章吗? 成为会员求更!
动手变形金刚(Kaggle 谷歌 QUEST 问答标注)。
《变形金刚》第 3/3 部 vs 谷歌 QUEST 问答标注(Kaggle top 5%)。
作者图片
这是一个由 3 部分组成的系列,我们将经历变形金刚、伯特和动手 Kaggle 挑战—Google QUEST Q&A Labeling来观看变形金刚的行动(在排行榜上排名第 4.4%)。在这部分(3/3 ),我们将会看到谷歌在 Kaggle 上的一个实践项目。由于这是一个 NLP 挑战,我在这个项目中使用了变形金刚。我在这一部分没有详细介绍变形金刚,但是如果你愿意的话,你可以看看这个系列的 1/3 部分,在那里我详细讨论了变形金刚。
博客的鸟瞰图:
为了方便阅读,我把博客分成了不同的子主题-
- 问题陈述和评估标准。
- 关于数据。
- 探索性数据分析(EDA)。
- 建模(包括数据预处理)。
- 建模后分析。
问题陈述和评估指标:
计算机非常擅长用单一的、可验证的答案来回答问题。但是,人类通常更善于回答关于观点、建议或个人经历的问题。
人类更擅长解决需要对背景有更深层次、多维度理解的主观问题。问题可以有多种形式——有些是多句子的阐述,有些可能是简单的好奇或者是一个完全成熟的问题。他们可以有多种意图,或者寻求建议和意见。有些可能是有帮助的,有些可能是有趣的。有些是简单的对错。
不幸的是,由于缺乏数据和预测模型,很难建立更好的主观问答算法。这就是为什么谷歌研究的众包团队,一个致力于通过众包推进 NLP 和其他类型的 ML 科学的团队,已经收集了许多这些质量评分方面的数据。
在这场比赛中,我们面临的挑战是使用这个新的数据集为问答的不同主观方面构建预测算法。这些问答配对是从近 70 个不同的网站上以“常识”的方式收集来的。评分者接受的指导和培训很少,很大程度上依赖于他们对提示的主观理解。因此,每个提示都是以最直观的方式制作的,这样评分者可以简单地利用他们的常识来完成任务。
证明这些主观标签可以被可靠地预测,可以为这一研究领域带来新的曙光。这次比赛的结果将告知未来智能问答系统的构建方式,希望有助于它们变得更像人类。
**评估指标:**根据平均列相关系数 Spearman 相关系数对提交的内容进行评估。为每个目标列计算 Spearman 等级相关性,并为提交分数计算这些值的平均值。
关于数据:
此竞赛的数据包括来自各种 StackExchange 属性的问题和答案。我们的任务是预测每个问答对的 30 个标签的目标值。
30 个目标标签的列表与sample_submission.csv
文件中的列名相同。前缀为question_
的目标标签与数据中的question_title
和/或question_body
特征相关。带有前缀answer_
的目标标签与answer
特征相关。
每一行都包含一个问题和该问题的一个答案,以及附加功能。训练数据包含带有一些重复问题(但答案不同)的行。测试数据不包含任何重复的问题。
目标标签可以有范围[0,1]
内的连续值。因此,预测也必须在该范围内。
提供的文件有:
- train.csv —训练数据(目标标签是最后 30 列)
- test.csv —测试集(您必须为每个测试集行预测 30 个标签)
- sample_submission.csv —格式正确的示例提交文件;列名是 30 个目标标签
您可以使用这个链接来检查数据集。
探索性数据分析(EDA)
Check-out 深入 EDA +数据抓取的笔记本( Kaggle 链接 )。
训练数据包含 6079 个列表,每个列表有 41 列。在这 41 列中,前 11 列/特征必须用作输入,后 30 列/特征是目标预测。
让我们来看看输入和目标标签:
作者图片
输出特征都是介于 0 和 1 之间的浮点类型。
让我们一个一个地探索输入标签。
qa_id
问题答案 id 表示给定数据集中特定数据点的 ID。每个数据点都有一个唯一的 qa_id。此功能不用于培训,稍后将在向 Kaggle 提交输出时使用。
问题 _ 标题
这是一个字符串数据类型功能,保存所提问题的标题。
为了分析 question_title,我将绘制这个特性的字数直方图。
从分析来看,很明显:
——大部分 question_title 特征的词长在 9 左右。
-最小问题长度为 2。
-最大问题长度为 28 个。
- 50%的问题标题的长度在 6 到 11 之间。
- 25%的问题标题长度在 2 到 6 之间。
- 25%的问题标题长度在 11 到 28 之间。
问题 _ 正文
这也是一个字符串数据类型特性,保存所提问题的详细文本。
为了分析 question_body,我将绘制这个特性的字数直方图。
从分析来看,很明显:
——大部分 question_body 特征的词长在 93 左右。
-最小问题长度为 1。
-最大问题长度为 4666。
- 50%的问题标题长度在 55 到 165 之间。
- 25%的问题标题长度在 1 到 55 之间。
- 25%的问题标题长度在 165 到 4666 之间。
该分布看起来像幂律分布,可以使用 log 将其转换为高斯分布,然后用作工程特征。
问题 _ 用户名
这是一个字符串数据类型功能,表示提出问题的用户的姓名。
为了分析 question_answer,我将绘制这个特性中单词数量的直方图。
我没有发现这个特性有多大用处,所以我不会用它来建模。
问题 _ 用户 _ 页面
这是一个字符串数据类型特性,它保存了提问用户的个人资料页面的 URL。
在个人资料页面上,我注意到了 4 个有用的特性,它们可能会有助于做出正确的预测。特征有:
-信誉:表示用户的信誉。
- gold_score:颁发的金牌数。
- silver_score:颁发的银牌数。
- bronze_score:颁发的铜牌数量。
回答
这也是一个字符串数据类型特性,它保存问题答案的详细文本。
为了对答案进行分析,我将绘制这个特征的字数直方图。
从分析来看,很明显:
-大部分的 question_body 特征的词长在 143 左右。
-最小问题长度为 2。
-最大问题长度为 8158。
- 50%的问题标题长度在 48 到 170 之间。
- 25%的问题标题长度在 2 到 48 之间。
- 25%的问题标题长度在 170 到 8158 之间。
这种分布看起来也像幂律分布,也可以使用对数转换为高斯分布,然后用作工程特征。
答案用户名
这是一个字符串数据类型功能,表示回答问题的用户的姓名。
我没有发现这个特性有多大用处,所以我不会用它来建模。
答案 _ 用户 _ 页面
这是一个字符串数据类型特性,类似于特性“question_user_page ”,它保存了提问用户的个人资料页面的 URL。
我还使用这个特性中的 URL 从用户的个人资料页面中抓取外部数据,类似于我对特性‘question _ user _ page’所做的。
全球资源定位器(Uniform Resource Locator)
此功能保存 StackExchange 或 StackOverflow 上的问答页面的 URL。下面我打印了来自 train.csv 的前 10 个 url 数据点
需要注意的一点是,这个功能会把我们带到问答页面,而这个页面通常会包含更多的数据,比如评论、投票、其他答案等等。如果模型由于训练中的数据较少而表现不佳,则可以使用它来生成更多的特征。csv
让我们看看数据是否存在,以及可以从问答页面中获取哪些附加数据。
在上面附加的快照中,帖子 1 和帖子 2 包含对所提问题的回答、支持投票和评论,按支持投票的降序排列。带有绿色勾号的帖子包含 train.csv 文件中提供的答案。
每个问题可能有不止一个答案。我们可以收集这些答案,并将其用作额外的数据。
上面的快照定义了一篇文章的结构。我们可以收集有用的特征,如 upvotes 和 comments 并将其用作附加数据。
下面是从 URL 页面抓取数据的代码。
我刮到了 8 个新功能-
- upvotes:所提供答案的 upvotes 数。
- comments_0:对所提供答案的评论。
-答案 1:除了提供的答案之外,投票最多的答案。
-评论 _1:回答 _1 的置顶评论。
-答案 2:第二多的投票答案。
-评论 _2:回答 _2 的置顶评论。
-答案 _3:票数第三的答案。
-评论 _3:回答 _3 的置顶评论。
种类
这是一个分类特征,它告诉问题和回答对的类别。下面我打印了来自 train.csv 的前 10 个类别数据点
下面是绘制类别饼图的代码。
图表告诉我们,大部分分属于技术类,最少属于生活 _ 艺术类(6079 分中的 709 分)。
宿主
此功能保存 StackExchange 或 StackOverflow 上的问答页面的主机或域。下面我打印了来自 train.csv 的前 10 个主机数据点
下面是绘制唯一主机条形图的代码。
看起来在训练数据中并不多,只有 63 个不同的子域。大多数数据点来自 StackOverflow.com,而最少来自 meta.math.stackexchange.com
目标值
我们来分析一下需要预测的目标值。但是首先,为了更好的解释,请使用链接查看 kaggle 上的完整数据集。
下面是显示目标值统计描述的代码块。这些只是全部 30 个特性中的前 6 个特性。
所有特性的值都是浮点型的,并且在 0 和 1 之间。
请注意第二个代码块,它显示了数据集中存在的唯一值。0 和 1 之间只有 25 个唯一值。这在以后微调代码时会很有用。
最后,让我们检查目标特性的分布及其相关性。
目标特征的直方图。
目标特征之间相关性的热图。
建模
作者图片
现在我们通过 EDA 更好地了解了我们的数据,让我们从建模开始。下面是我们将在这一部分讨论的副主题-
- **架构概述:**总体架构及其不同组件的快速概述。
- **基础学习者:**集合中使用的基础学习者的概述。
- **准备数据:**数据清理和建模准备。
- **集合:**创建训练模型,并进行预测。将数据准备、模型训练和模型预测步骤流水线化。
- **从 Kaggle 获取分数:**在 Kaggle 上提交测试数据的预测目标值,并生成排行榜分数,以查看整体表现如何。
我尝试了各种深度神经网络架构,包括 GRU、Conv1D、密集层,以及竞争对手的不同功能,但 8 个变压器的组合(如上所示)似乎效果最佳。
在这一部分中,我们将关注所使用的整体的最终架构,对于我实验的其他基线模型,你可以查看我的 github repo。
架构概述:
记住我们的任务是给定 问题标题、问题正文和答案 、,我们必须预测 30 个目标标签。
现在出来的这 30 个目标标签中,前 21 个都与 问题 _ 标题 和问题 _ 正文 和 答案 没有关系,而最后 9 个目标标签都只与 答案 有关,但在这 9 个当中,有些还带 问题 _ 标题
例如,像答案 _ 相关性和答案 _ 满意度这样的特征只能通过看问题和答案来评级。**
通过一些实验,我发现基础学习者(BERT_base)在预测前 21 个目标特征(仅与问题相关)方面表现得非常好,但是在预测后 9 个目标特征方面表现得不太好。注意到这一点,我构建了 3 个专门的基础学习者和 2 个不同的数据集来训练他们。
- 第一个基础学习者只致力于预测与问题相关的特征(前 21 个)。用于训练该模型的数据集仅由特征 问题 _ 标题 和 问题 _ 正文 组成。
- 第二个基础学习者只致力于预测与答案相关的特征(最后 9 个)。用于训练该模型的数据集由特征 问题 _ 标题 、 问题 _ 正文、 和 答案 组成。
- 第三个基础学习者致力于预测所有 30 个特征。用于训练该模型的数据集再次由特征 问题 _ 标题 、 问题 _ 正文、 和 答案 组成。
为了使架构更加健壮,我使用了 3 种不同类型的基础学习者——BERT、RoBERTa 和 XLNet。
我们将在本博客稍后讨论这些不同的变压器模型。
在上面的系综图中,我们可以看到—
- 由**【问题标题+问题正文】和【问题标题+问题正文+答案】**组成的 2 个数据集分别用于训练不同的基础学习者。
- 然后,我们可以看到 3 个不同的基础学习者 (BERT、RoBERTa 和 XLNet) 致力于使用数据集**[question _ title+question _ body]预测仅问题相关特征的蓝色**(前 21 个)
- 接下来,我们可以看到 3 个不同的基础学习者 (BERT、RoBERTa 和 XLNet) 专门使用数据集**[question _ title+question _ body+answer]预测仅答案相关的特征(最后 9 个)以绿色显示。**
- 最后,我们可以看到 2 个不同的基础学习器 (BERT 和 RoBERTa) 致力于使用数据集**[question _ title+question _ body+answer]预测所有 30 个红色特征。**
在下一个步骤中,来自仅专用于预测问题相关特征的模型的预测数据(表示为 bert_pred_q,roberta_pred_q,xlnet _ pred _ q)和来自仅专用于预测答案相关特征的模型的预测数据**(表示为 bert_pred_a,Roberta _ a 这些连接的特征被表示为 xlnet_concat、roberta_concat、 和 bert_concat。**
类似地,收集来自专用于预测所有 30 个特征模型的预测数据(表示为 bert_qa,roberta_qa )。请注意,我在这里没有使用 XLNet 模型来预测所有 30 个特性,因为分数没有达到标准。
最后,在收集了所有不同的预测数据—【xlnet _ concat,roberta_concat,bert_concat,bert_qa,和 roberta_qa】,** 之后,通过取所有不同预测值的平均值来计算最终值。**
基础学习者
现在,我们将看看作为基础学员使用的 3 种不同的变压器模型。
- bert_base_uncased:
Bert 是由谷歌人工智能在 2018 年底提出的,从那时起,它已经成为广泛的自然语言处理任务的最先进技术。
它使用一种源自 transformers 的架构,对大量未标记的文本数据进行预训练,以学习一种语言表示,可用于微调特定的机器学习任务。BERT 在几个具有挑战性的任务上超过了 NLP 的最新水平。BERT 的这种性能可以归因于 transformer 的编码器架构、非常规的训练方法,如掩蔽语言模型(MLM)、下一句预测(NSP)以及它所训练的海量文本数据(所有维基百科和书籍语料库)。BERT 有不同的尺寸,但在这个挑战中,我使用了 bert_base_uncased。
作者图片
bert_base_uncased 的架构由 12 个编码器单元组成,每个编码器单元中有 8 个注意头。默认情况下,它接受大小为 512 的输入并返回 2 个值,输出对应于第一个输入令牌[CLS],其维度为 786,另一个输出对应于所有 512 个输入令牌,其维度为(512,768) aka pooled_output。
但除此之外,我们还可以通过将output _ hidden _ States = True作为参数之一来访问 12 个编码器单元中的每一个返回的隐藏状态。
BERT 接受几组输入,在这次挑战中,我将使用三种类型的输入:
- input _ ids:标记嵌入是输入句子中单词的数字表示。还有一种叫做子词标记化的东西,BERT 使用它首先将较大或复杂的单词分解为简单的单词,然后将它们转换为标记。例如,在上图中,在生成令牌嵌入之前,看看单词“playing”是如何被分解为“play”和“##ing”的。标记化中的这一调整创造了奇迹,因为它利用了一个复杂单词的子单词上下文,而不是像对待一个新单词一样对待它。
- attention _ mask:片段嵌入用于帮助 BERT 区分单个输入中的不同句子。对于来自同一个句子的单词,这个嵌入向量的元素都是相同的,并且如果句子不同,该值也会改变。
让我们考虑一个例子:假设我们要将两个句子“我有一支笔”和“笔是红色的”传递给 BERT。分词器首先将这些句子分词为:
[‘[CLS]’,‘我’,’ have ‘,’ a ‘,’ pen ‘,’[SEP]‘,’ The ‘,’ pen ‘,’ is ‘,’ red ‘,’[SEP]'] 并且这些句子的段嵌入将看起来像:
【0,0,0,0,0,0,0,1,1,1,1,1,1] 注意对应于第一个句子中的单词的所有元素如何具有 - 由于 BERT 采用 512 维输入,并且假设我们只有 10 个单词的输入。为了使标记化的单词与输入大小兼容,我们将在末尾添加大小为 512–10 = 502 的填充。连同填充符一起,我们将生成大小为 512 的屏蔽令牌,其中对应于相关单词的索引将具有 1 s,对应于填充符的索引将具有 0 s。
2。XLNet_base_cased:
XLNet 由谷歌人工智能大脑团队和 CMU 的研究人员在 2019 年年中提出。它的架构比 BERT 更大,并使用改进的方法进行训练。它在更大的数据上进行训练,在许多语言任务中表现出比 BERT 更好的性能。 BERT 和 XLNet 之间的概念差异在于,在训练 BERT 时,按照前一个预测单词有助于下一个单词预测的顺序预测单词,而 XLNet 学习按照任意顺序但以自回归方式(不一定是从左到右)预测单词。
传统语言模型的预测方案。阴影单词作为输入提供给模型,而非阴影单词被屏蔽掉。
一个置换语言模型如何预测某个置换的记号的例子。阴影单词作为输入提供给模型,而非阴影单词被屏蔽掉。
这有助于模型学习双向关系,从而更好地处理单词之间的依赖性和关系。
除了训练方法之外,XLNet 使用基于 Transformer XL 的架构和两个主要的关键思想:相对位置嵌入和递归机制,即使在没有基于排列的训练的情况下也表现出良好的性能。
XLNet 用超过 130 GB 的文本数据和运行 2.5 天的 512 个 TPU 芯片进行训练,这两个数据都比 BERT 大得多。
对于 XLNet,我将只使用 input_ids 和 attention_mask 作为输入。
3。罗伯塔 _ 基地:
罗伯塔是脸书在 2019 年年中提出的。这是一种鲁棒优化的方法,用于预处理自然语言处理(NLP)系统,改进了 BERT 的自监督方法。
RoBERTa 基于 BERT 的语言掩蔽策略,其中系统学习预测未标注语言示例中有意隐藏的文本部分。RoBERTa 修改了 BERT 中的关键超参数,包括删除 BERT 的下一句预测(NSP)目标,以及使用更大的小批量和学习率进行训练。与 BERT 相比,这允许 RoBERTa 改进屏蔽语言建模目标,并导致更好的下游任务性能。罗伯塔也比伯特接受了更多的数据和更长的时间的训练。使用的数据集来自现有的未标注的 NLP 数据集以及 CC-News,这是一个从公共新闻文章中提取的新数据集。
对于 RoBERTa_base,我将只使用 input_ids 和 attention_mask 作为输入。
最后这里是伯特、XLNet、罗伯塔的对比:
****
准备数据
现在我们已经对架构有了一些了解,让我们看看如何为基础学习者准备数据。
-
作为预处理步骤,我刚刚处理了特性中的 HTML 语法。我使用 html.unescape()从 HTML DOM 元素中提取文本。
在下面的代码片段中,函数 get_data() 读取训练和测试数据,并对特征 question_title、 和 答案进行预处理。 -
下一步是从输入句子中创建 输入 _ 标识、注意 _ 掩码、 和 令牌 _ 类型 _ 标识 。
在下面的代码片段中,函数 get_tokenizer() 为不同的 base_learners 收集预训练的 tokenizer。
第二个函数 fix_length() 检查生成的问题令牌和答案令牌,并确保令牌的最大数量为 512。确定记号数量的步骤如下:
-如果输入句子的记号数量为> 512,则将句子削减至 512。
-为了削减令牌的数量,从开始的 256 个令牌和从结束的 256 个令牌被保留,其余的令牌被丢弃。
-例如,假设一个答案有 700 个记号,为了将其减少到 512 个,从开头取 256 个记号,从结尾取 256 个记号,并连接起来得到 512 个记号。位于答案中间的其余[700-(256+256) = 288]个标记将被丢弃。
-这种逻辑是有道理的,因为在一篇大文章中,开头部分通常描述文章的全部内容,结尾部分描述文章的结论。
接下来是用于生成输入 _ 标识、注意 _ 屏蔽、和令牌 _ 类型 _ 标识的代码块。我使用了一个条件来检查函数是否需要返回为依赖数据集【问题标题+问题正文】或数据集【问题标题+问题正文+答案】的基础学习者生成的数据。****
**最后,这个函数利用上面初始化的函数,为所提供数据中的每个实例生成 **input_ids、attention_masks、和 token_type_ids 。
为了使模型训练变得容易,我还创建了一个类,它在使用 KFlod CV 时,在上面指定的函数的帮助下,基于折叠生成训练和交叉验证数据。
组装
在数据预处理之后,让我们从基础学习者开始创建模型架构。
下面的代码将模型名称作为输入,根据输入名称收集预训练模型及其配置信息,并创建基本学习者模型。注意output _ hidden _ States = True在添加配置数据后通过。
下一个代码块是创建整体架构。该函数接受两个参数 name 和 model_type,name 表示我们要训练的模型的名称,model _ type 表示我们要训练的模型的类型。模型类型可以是 bert-base-uncased、roberta-base 或 xlnet-base-cased ,而模型类型可以是问题、答案、或问题 _ 答案。** 函数 create_model() 采用 model_name 和 model_type,生成一个可以根据指定数据进行训练的模型。**
现在让我们创建一个用于计算评估指标 Spearman 相关系数的函数。
现在,我们需要一个功能,可以收集基础学习者模型,根据基础学习者模型的数据,并训练模型。
我用了 5 折的 K 折交叉验证进行训练。
*现在,一旦我们训练了模型并生成了预测值,我们就需要一个函数来计算加权平均值。这是代码。
加权平均值中的权重均为 1。
在把所有东西放在一起之前,我还使用了一个函数来处理最终的预测值。记得在 EDA 部分有一个目标值分析,我们注意到目标值只有 25 个介于 0 和 1 之间的唯一浮点数。为了利用这些信息,我计算了 61 个(超参数)均匀分布的百分位值,并将它们映射到 25 个唯一值。这在目标值的上限和下限之间创建了 61 个均匀间隔的箱。现在,为了处理预测的数据,我使用这些容器来收集预测值,并将它们放在正确的位置/顺序。这一招在一定程度上帮助提高了最终提交排行榜的分数。
最后,为了将数据预处理、模型训练和后处理结合在一起,我创建了收集数据的 get_predictions() 函数。
-创建 8 个基础学习者。
-为基础学员准备数据。
-训练基础学习者并从他们那里收集预测值。
-计算预测值的加权平均值。
-处理加权平均预测。
-将最终预测值转换成 Kaggle 要求的数据帧格式提交并返回。
从 Kaggle 获取分数
一旦代码编译运行成功,就会生成一个输出文件,可以提交给 Kaggle 进行分数计算。排行榜上代码的排名是使用分数生成的。** 合奏模特获得 0.43658 的公众评分,在排行榜上位列前 4.4%。**
建模后分析
检出完成后建模分析的笔记本( Kaggle 链接 )。
是时候进行一些后建模分析了!
在这一节中,我们将分析训练数据,以找出模型在数据的哪些部分表现良好,在数据的哪些部分表现不佳。
这一步背后的主要思想是了解训练模型的能力,如果应用得当,它可以很好地微调模型和数据。
但在本节中,我们不会进入微调部分,我们将使用列车数据的预测目标值对列车数据执行一些基本的 EDA。我将逐一介绍这些数据。以下是我们将要进行分析的主要功能-
- 问题标题、问题正文和答案。
- 问题标题、问题正文和答案的单词长度。
- 主持
- 种类
首先,我们必须将数据分为好数据和坏数据。好数据将是模型获得好分数的数据点,而坏数据将是模型获得坏分数的数据点。
现在为了评分,我们将比较训练数据的实际目标值和训练数据的模型预测目标值。我使用均方差(MSE)** 作为评分标准,因为它关注的是实际值和目标值的接近程度。记住 MSE 分数越大,数据点就越差。计算 MSE 分数相当简单。代码如下:**
# Generating the MSE-score for each data point in train data.
from sklearn.metrics import mean_squared_errortrain_score = [mean_squared_error(i,j) for i,j in zip(y_pred, y_true)]# sorting the losses from minimum to maximum index wise.
train_score_args = np.argsort(train_score)
问题 _ 标题、问题 _ 正文和答案
从第一组特征开始,它们都是文本类型的特征,我将用它们来绘制单词云。计划是从得分最低的 5 个数据点和得分最高的另外 5 个数据点中分割出这些特征。
绘制单词云的功能
让我们运行代码,看看结果是什么样的。
****
问题标题、问题正文和答案的单词长度
接下来的分析是关于问题标题、问题正文和答案的单词长度。为此,我将为问题标题、问题正文和答案这三个特征中的每一个选择 30 个 MSE 分数最低的数据点和 30 个 MSE 分数最高的数据点。接下来,我将计算所有 3 个特征的这 30 个数据点的单词长度,并绘制它们以观察趋势。
****
如果我们查看 question_title、question_body 和 answer 中的字数,我们可以观察到,产生高损失的数据具有高字数,这意味着问题和答案有点彻底。所以,当问题和答案简洁时,模型做得很好。
主持人
下一个分析是关于特性主机的。对于这个特性,我将挑选 100 个具有最低 MSE 分数的数据点和 100 个具有最高 MSE 分数的数据点,并选择特性主机中的值。然后,我将绘制这一分类特征的直方图,以查看分布情况。
****
我们可以看到,来自英语、生物、科幻、物理领域的大量数据点造成了较小的损失值,而来自 drupal、程序员、tex 的大量数据点造成了较大的损失。
让我们也来看看造成低分和高分的独特主机值的词云。使用顶部和底部的 100 个数据点再次进行该分析。
类别
最后分析的是特征类别。对于这个特性,我将挑选 100 个具有最低 MSE 分数的数据点和 100 个具有最高 MSE 分数的数据点,并选择特性类别中的值。然后我会绘制一个这种分类特征的饼状图来查看比例。
我们可以注意到,类别为技术的数据点占了模型不能很好预测的数据的 50%,而像生活艺术、科学和文化这样的类别对坏预测的贡献要小得多。
对于良好的预测,所有 5 个类别的贡献几乎相同,因为在比例上没有重大差异,但我们仍然可以说,以 StackOverflow 作为类别的数据点贡献最小。
至此,我们已经结束了这个博客和 3 部分系列。希望阅读愉快。
你可以使用 这个链接 在 Kaggle 上查看完整的笔记本,如果发现我的工作有用,可以留下一个向上的投票。我要感谢所有的创作者,感谢他们创作了我写这篇博客时提到的精彩内容。
参考链接:
- 应用人工智能课程:【https://www.appliedaicourse.com/】
- https://www.kaggle.com/c/google-quest-challenge/notebooks
- http://jalammar.github.io/illustrated-transformer/
- https://medium . com/inside-machine-learning/what-a-transformer-d 07 DD 1 fbec 04
- https://towards data science . com/Bert-Roberta-distil Bert-xlnet-one-to-use-3d 5 ab 82 ba 5 f 8
最终注释
感谢您阅读博客。我希望它对那些渴望做项目或学习 NLP 新概念的人有用。
在第 1/3 部分中,我们报道了变形金刚如何在各种现代自然语言处理任务及其工作中成为最先进的。
在第 2/3 部分中,我们讨论了 BERT(变压器的双向编码器表示)。
Kaggle 深入 EDA 笔记本链接:https://www . ka ggle . com/sarthakvajpayee/top-4-4-深入 eda-feature-scraping?scriptVersionId=40263047
Kaggle 建模笔记本链接:https://www . ka ggle . com/sarthakvajpayee/top-4-4-Bert-Roberta-xlnet
Kaggle 后建模笔记本链接:https://www . ka ggle . com/sarthakvajpayee/top-4-4-后建模-分析?scriptVersionId=40262842
在 LinkedIn 上找到我:www.linkedin.com/in/sarthak-vajpayee
在 Github 上找到这个项目:https://github.com/SarthakV7/Kaggle_google_quest_challenge
和平!☮
动手抓取网页:用 python 和 scrapy 构建你的 Twitter 数据集
我明白了——你厌倦了为你的机器学习项目在网上搜索数据集,或者可能是为了分析流行的 Twitter 趋势。
今天我们将学习如何使用 hashtag 搜索从 Twitter 生成您自己的自定义数据集。据 internetlivestats.com 的报道,平均每秒钟,大约有 6000 条推文被发布,相当于每分钟超过 35 万条推文,每天有 5 亿条推文。这使得 Twitter 成为一个为您的项目获取数据的绝佳场所,因为 Tweets 是当今社交媒体上自然语言的准确表达。
想跳过帖子直接看好东西?这是给你的 Github 回购
了解机器人的 Twitter 政策
首先,让我们确保遵守 twitter.com 为机器人爬行制定的政策,这样我们就不会遇到任何法律问题。这可以在https://twitter.com/robots.txt找到
面向所有用户代理的 robots.txt for twitter
我们可以从 User-agent 和 Allow 部分(第 1-4 行)看到,Twitter 允许所有的机器人访问标签和搜索 URL,在抓取请求之间有 1 秒钟的延迟。
User-agent: *
Allow: /hashtag/*?src=
Crawl-delay: 1
我们如何刮推特?让我们看一下代码。
这里假设你有一些 python 和 scrapy 的基础知识。如果您只对生成数据集感兴趣,请跳过这一部分,转到GitHub repo上的样本抓取部分。
通过搜索标签收集 tweets URL
为了搜索推文,我们将使用传统的 Twitter 网站。让我们试着搜索#猫https://mobile.twitter.com/hashtag/cats。我们希望使用这个遗留版本来收集 tweets URLs,因为它加载数据而不使用 javascript,这使得我们的工作非常容易。
我选择一个懒惰的人去做艰苦的工作。因为一个懒惰的人会找到一个简单的方法去做。
―比尔·盖茨
twitter 的传统版本
**查找我们标签搜索的所有 tweet URL—**find _ tweets()函数
scrapy 爬虫的 find_tweets 功能
解释:我们将使用XPath找到当前页面中的所有 tweets URL,并抓取这些 URL,然后将响应发送给我们的第二个函数parse_tweet ()
tweets = response.xpath('//table[@class="tweet "]/@href').getall() logging.info(f'{len(tweets)} tweets found')
for tweet_id in tweets:
tweet_id = re.findall("\d+", tweet_id)[-1]
tweet_url = 'https://twitter.com/anyuser/status/'+str(tweet_id)
yield scrapy.Request(tweet_url, callback=self.parse_tweet)
现在我们将找到**Load older Tweets**
按钮的下一个 URL,抓取它并将响应发送给我们当前的find_tweets()
函数。这样,我们的爬虫将不断递归地点击**Load older Tweet**
按钮,如果它在每次爬行时可用的话。这意味着我们将逐一访问所有结果页面。
加载旧推文
next_page = response.xpath(
'//*[@class="w-button-more"]/a/@href').get(default='')
logging.info('Next page found:')
if next_page != '':
next_page = 'https://mobile.twitter.com' + next_page
yield scrapy.Request(next_page, callback=self.find_tweets)
从单个 tweet 中查找所有数据— parse_tweet()函数
解释:在这里,我们将使用 Twitter 的当前版本(示例:https://Twitter . com/catsfootprint/status/1213603795663491075),因为当前版本加载了所有数据,如赞数、转发数、评论数和其他元数据。下面是在旧版和当前版本的 twitter 中加载上述 tweet 的比较。
parse tweet()函数
让我们来看看数据集。
我们的数据集将由 14 列组成,其中包含了几乎所有可以从 tweet 中抓取的数据。目前爬虫不收集评论。数据集还将包括列hashtags
和mentions
,这是通过解析 tweet 文本并搜索开头带有#的单词(用于查找标签)和开头带有@的单词(用于查找提及)获得的。
示例数据截图 CSV
示例 twitter 数据 JSON
您可以在哪里使用这些数据?
您可能希望通过以下几种方式使用该数据集:
- 流行 twitter 趋势的情感分析
- 竞争对手分析(例如,人们可以找到并分析竞争对手的推文)
- 通过标签建立你的推文数据库
- 分析一个流行趋势。例如,人们可以使用#globalwarmingto 查找和分析推文,以了解人们的意见。
结论
目前,我们可以看到 Twitter 允许抓取其内容搜索结果,即推文和标签,并且对我们可以抓取的页面数量没有限制。因此,我们的爬行器不受任何速率限制的影响,我们能够使用这个简单的脚本,使用标签和 tweet 搜索方法来爬行数百万条 tweet。为了收集大量的推文,我建议你使用数百个标签,并在 VPS 服务器或垃圾云上运行爬虫,以避免任何形式的中断。
如果你有建议或发现一些问题。欢迎您在 GitHub 上开问题或拉请求。
爬行愉快。
你真棒
Handtrack.js —让火焰在你手中舞动
从网络摄像头流中检测手,并将您的手掌放在火上
魔术师走上舞台,伸出左手——嗖——一个火球出现了!这是一个你通常在舞台上看到的把戏,但今天我将在你的网络摄像头中向你展示这个魔法,通过手跟踪技术来实现。
让我们建立一个网络应用程序,可以从网络摄像头检测手,并把你的手掌着火!打开你的网络摄像头,举起你的手,让火焰在你手中舞动。
点击下面的链接亲自尝试一下:
[## handtrack.js - Benson 技术让魔法之火在你手中
魔术师走上舞台,伸出左手,嗖的一声,一个火球出现了!那是一个你通常…
bensonruan.com](https://bensonruan.com/magic-fire-in-your-hand-with-handtrack-js/)
太酷了,对吧?你玩得开心吗,把这神奇的火给你的朋友看?我希望你喜欢它,这个演示利用了一种叫做手跟踪的先进技术,它可以在图像或视频流中识别人手。
感谢 handtrack.js ,这是一个很棒的 javascript 库,它使用 TensorFlow.js 对象检测 API 来支持浏览器中的手部跟踪。如果你有兴趣构建自己的手部跟踪应用,请在下面跟随我,了解我是如何实现它的。
履行
#步骤 1:包含 handtrack.js
首先,简单的在 html 文件的<头>部分包含脚本handtrack.js
。
<script src="[https://cdn.jsdelivr.net/npm/handtrackjs/dist/handtrack.min.js](https://cdn.jsdelivr.net/npm/handtrackjs/dist/handtrack.min.js)"> </script>
或者您可以通过 npm 安装它,以便在 TypeScript / ES6 项目中使用
npm install --save handtrackjs
#步骤 2:将网络摄像头传输到浏览器
为了将您的网络摄像头传输到浏览器中,我使用了 npm JavaScript 模块webcam-easy.js
,它提供了一个易于使用的模块,可以访问网络摄像头并拍照。要了解更多细节,请参考我之前的博客:
[## 如何使用 JavaScript 访问网络摄像头并拍照
介绍网络摄像头-简易 npm 模块
medium.com](https://medium.com/swlh/how-to-access-webcam-and-take-picture-with-javascript-b9116a983d78)
#第 3 步:加载扶手模型
为了执行手部跟踪,我们首先需要通过调用handTrack.load(modelParams)
的 API 来加载预先训练好的手部跟踪模型。HandTrack 带有几个可选的模型参数:
- flipHorizontal —翻转,例如对于视频,默认值:True
- imageScaleFactor 减小输入图像大小以提高速度,默认值:0.7
- maxNumBoxes —要检测的最大盒子数,默认值:20
- iouThreshold —非最大抑制的 ioU 阈值,默认值:0.5
- scoreThreshold 预测的置信度阈值,默认值为 0.99
const modelParams = {
flipHorizontal: true,
maxNumBoxes: 20,
iouThreshold: 0.5,
scoreThreshold: 0.8
}**handTrack.load(modelParams)**.then(mdl => {
model = mdl;
console.log("model loaded");
});
#步骤 4:手部检测
接下来,我们通过调用model.detect(video)
的 API,开始通过 HandTrack 模型馈送网络摄像头流来执行手检测。它接受一个输入图像元素(可以是一个img
、video
、canvas
标签),并返回一个包含类名和可信度的边界框数组。
**model.detect(webcamElement)**.then(predictions => {
console.log("Predictions: ", predictions);
showFire(predictions);
});
预测的返回看起来像:
[{
bbox: [x, y, width, height],
class: "hand",
score: 0.8380282521247864
}, {
bbox: [x, y, width, height],
class: "hand",
score: 0.74644153267145157
}]
#第五步:展示魔法之火
在上面的函数中,我们得到了手的位置的包围盒,现在我们可以用它来显示你手中的火焰 GIF 图像。
HTML
在webcam
元素上叠加canvas
层
<video id="webcam" autoplay playsinline width="640" height="480"></video><div id="canvas" width="640" height="480"></div>
JavaScript
设置fireElement
的大小和位置,并将其添加到canvas
层。
function showFire(predictions){
if(handCount != predictions.length){
$("#canvas").empty();
fireElements = [];
}
handCount = predictions.length;
for (let i = 0; i < predictions.length; i++) {
if (fireElements.length > i) {
fireElement = fireElements[i];
}else{
**fireElement = $("<div class='fire_in_hand'></div>");
fireElements.push(fireElement);
fireElement.appendTo($("#canvas"));**
}
var fireSizeWidth = fireElement.css("width").replace("px","");
var fireSizeHeight = fireElement.css("height").replace("px","");
var firePositionTop = hand_center_point[0]- fireSizeHeight;
var firePositionLeft = hand_center_point[1] - fireSizeWidth/2;
**fireElement.css({top: firePositionTop, left: firePositionLeft, position:'absolute'});**
}
}
CSS
将background-image
设置为fire.gif
图像
.fire_in_hand {
width: 300px;
height: 300px;
**background-image: url(../images/fire.gif);**
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
}
代码就这么多了!现在你应该可以开始展示你手中的魔法之火了!
GitHub 知识库
您可以通过下面的链接下载上述演示的完整代码:
在浏览器中使用 javascript 库 handtrack.js 流网络摄像头在台式计算机上从网络摄像头进行手部跟踪,或者…
github.com](https://github.com/bensonruan/Hand-Track)
感谢您的阅读。如果你喜欢这篇文章,请在脸书或推特上分享。如果你有任何问题,请在评论中告诉我。在 GitHub 和 Linkedin 上关注我。
使用具有 CTC 丢失功能的时间分布 CNN 和 LSTM 的手写到文本转换
一种使用深度学习框架 Keras 进行手写字符到文本转换的光学字符识别(OCR)方法。
尼克·莫里森在 Unsplash 上拍摄的照片
这篇文章是我们为印度商学院的商业分析项目Co’2019 年夏季所做的项目的一部分,由 Jan Elaaj 赞助。
简介:
T 该项目的动机来自于这样一个事实,即许多人仍然喜欢用笔在纸上书写,如果数字化,它会增加许多要长期存储的价值,并用于下游系统进行分析。一个这样的例子是在医学领域,在印度,大多数医生仍然将患者处方写在纸上,我们的赞助商需要将其数字化,以便可以将其输入到患者健康记录系统中。通过这个项目,我们演示了如何用标记数据训练一个手写识别系统。具体而言,我们在来自特定医生处方数据集的手动标记的文本行数据上训练深度卷积递归神经网络(CRNN)系统,并提出一种增量训练程序,该程序在可用时覆盖其余数据,以便以最高精度执行模型,从而能够适应单个医生的笔迹。
项目的各个部分:
该项目由下述多个部分组成:
- 使用影像注记工具标注数据
- 原始图像转换为线分割图像
- 将图像分割成不同的帧
- 时间分布卷积回归神经网络
- 连接主义时间分类(CTC)损失函数的实现
- 使用 Levenshtein 距离(也称为编辑距离)的最近单词预测
第 1 部分:使用图像标注工具标注数据
这是项目的第一步,也是最耗时的一步。为此,我们尝试并评估了多种工具,发现牛津的 VGG 图像注释器最适合我们的目的。该工具允许我们通过为单词绘制边界框,并通过输入对应于所选区域的注释来选择处方中的区域。注释完成后,我们将能够下载 CSV 格式的注释,以便用 Python 进行进一步处理。下载的文件提供了 x 和 y 坐标中的注释区域、文件名和区域属性等信息,这是单词本身的实际注释。下面是一个示例注释的样子:
第 2 部分:原始图像到线分割图像
这是在 python 中 OpenCV 库的帮助下完成的。该过程以原始分辨率读取原始图像。然后它被转换成灰度值。因为对于手写识别系统的输入,如果我们以灰度值或三个通道给出图像,这并不重要。一旦完成,图像中的轮廓就被识别。轮廓有助于识别具有相同颜色或强度的像素的边界。在画出边界框后,我们裁剪图像以取出单独的线分割图像。
在获取适当的线分割数据的过程中,我们忽略所有具有低于某个阈值限制的高度包围盒的图像。通过实验,我们发现使用 64 的值可以得到我们想要的输出。
此外,保持所获取图像的顺序也非常重要。为此,我们确保根据图像的起始像素值从轮廓读取图像。因此,将从左上角到右下角读取图像。
第 3 部分:将图像分割成不同的帧
我们使用 Keras wrappertime distributed来为 CNN 各层提供信息。这样做的目的是获取不同的输入帧,并逐帧进行处理。然后,从输入图像中获得 64×64 像素图像的帧,对于每个后续帧,步长为 4。滑动窗口应用于从左到右的书写方向。Keras 时间分布式包装器用于将输入帧传递到 CNN 层。时间分布式包装器有助于保存我们从输入中获得的图像帧的时间序列。本质上,输入和输出将如下所示:
第四部分:时间分布卷积回归神经网络(CRNN)
这个项目的核心是一个受 VGG16 架构启发的深度 CRNN 。我们使用堆叠的 13 个卷积层,然后是三个双向 LSTMs,每个方向由 256 个单元组成。在一些卷积层之后应用最大池,总共应用 5 个最大池层。为了在卷积层中引入非线性,使用激活函数 ReLU。卷积层的权重被初始化为 he-normal,并且在所有卷积层之后使用批量归一化层来归一化权重,这将有助于网络的更快收敛。在每个 LSTM 层中,训练的辍学率被设置为 0.25。在 CNN 的输出和 LSTMs 的输入之间有一个密集层,这对于减少用于我们训练的参数数量非常有效。
我们将适用于我们问题的输出类的总数设置为 91 (A-Z,A-Z,0–9,英语键盘上的所有标准特殊字符,外加一个未知的附加类)。因此,神经网络的最后一层对于 LSTM 框架的每个输出有 91 个权重。在最终层中使用 softmax 层来获得在一帧中具有最高概率的输出类。
第 5 节:连接主义者时间分类(CTC)损失函数的实现
用于最小化损失的目标函数是连接主义者时间分类 (CTC)损失函数。当其他损失函数优化单个目标函数时,CTC 损失被特别设计来优化预测序列的长度和预测序列的类别,因为输入图像本质上是变化的。卷积滤波器和 LSTM 权重在反向传播过程中被共同学习。Adam optimizer 用于初始学习率为 0.001 的训练。
这些模型是在取自谷歌云服务的 GPU 服务器上训练的。系统配置为 32GB RAM、8 核处理器和 4 个 Tesla K80 GPUs。
完整建筑:
第 6 部分:使用 Levenshtein 距离的最近单词预测
用于跟踪网络性能的度量是 Levenshtein distance (也称为编辑距离),这是一种常用的度量,通过测量观察序列和预测序列之间的差异来测量字符串度量。
为此,我们构建了输入中所有单词的词汇表。CTC 丢失函数实际上并没有正确给出整个字,因为在使用时间分布包装器馈送的帧之间存在重叠。由于单个字母表可能出现在多个帧中,因此输出将包含单词的重复。因此,我们必须使用编辑距离作为度量,以找出在我们的整个语料库中与作为输出给定的单词最匹配的最近的单词。
结论和进一步的工作:
将所有的部分放在一起,模型的输出以 0.75 的精度预测测试数据集,并以大约 0.80 的精度预测训练数据集。预测将被逐行馈送到 CRNN,并且 CTC 层的输出将与我们的语料库中最接近的单词相匹配,以便使用编辑距离度量用实际单词来检查预测。
此外,我们希望在 CNN 层试验 RESNET 架构,而不是最初为该项目选择的 VGG-16 架构,但是从头开始重新培训将非常耗时耗力,因为顶点计划即将结束,我们没有机会这样做。
至于该项目的实际应用,我们希望将它与一个题字板集成,您可以在上面放置一张纸并进行书写,在后端,我们的系统会自动将整个页面分成行,行分成词,并输入到 CRNN 架构,并自动给出与手写字母相对应的词。
GitHub 回购链接:【https://github.com/GireeshS22/Handwriting-CNN-LSTM
作者:吉瑞什·孙达拉姆、巴拉吉·文克泰什、苏米亚·帕尼格拉希、维尼特·卡普尔
手写数字分类器——5 分钟内您的第一个端到端 CNN
乔恩·泰森在 Unsplash 上的照片
数字分类器显然是新的 Hello World!
没错,这是最基本的卷积神经网络例子。因此,理解这一点至关重要。因此,每当我们开始一些新的东西,运行我们的第一个 hello world 程序可能看起来很容易,但有很多摩擦。成功运行它有时可能需要几天时间——主要是因为涉及到的设置,陷入在我们看来像陌生文本的错误中,等等。
所以,我写这篇文章背后的主要动机是帮助你消除这种摩擦——这样你就可以在短短 5 分钟内在 CNN 上运行你的第一个节目。
先决条件
- 应该知道 CNN 的基本知识。我强烈建议做一个谷歌搜索,了解卷积层和最大池层。
- 你应该对它背后的数学有很好的理解。为此,我强烈推荐阅读我以前的文章。
- 通过 Google Colab 运行该程序。
有人刚刚运行了他们的第一个 CNN 节目吗?!恭喜🎉
感觉如何?!如果您还没有运行它,我强烈建议您这样做。Google Colab 是一个非常简单易用的软件,它消除了在我们的机器上进行设置的所有不必要的需求。如果你还在学习深度学习,强烈推荐。现在,让我们分解它,了解每一个组件。
从 MNIST 数据集中加载数据
作者图片
修改的国家标准和技术研究所(MNIST)数据集包括 70,000 幅 28 * 28 像素的灰度图像,包含一个 0 到 9 之间的手写数字。
作为一个经验法则,总是在训练和测试集中划分你的数据。 为什么会这样?因此,您可以在训练集上训练您的模型,并在测试集上测试它。如果它在训练集上表现良好,但在测试数据上表现不佳,我们就可以知道我们的模型过度拟合了。因此,在测试数据上测试它有助于您独立于训练数据来测试您的模型,并更加确定您的模型。
如您所见,训练数据有 60000 个条目,测试数据有 10000 个条目。此外,为了便于理解,我绘制了一张 28*28 像素的图像。
预处理
清理数据是所有模型中的主要步骤之一。有各种各样的技术可以做到这一点。但是,对于这一个,我们将只关注其中的 3 个。
作者图片
- 重塑 :我们的模型由[2828]矩阵的 60k 个条目组成对吗?但是,如果你记得 CNN 在频道上工作。如果是灰度图像,有一个通道,如果是彩色图像,有 3 个通道。因为它是灰度图像,我们可以把它重写为单通道矩阵。于是,[60k,28,28] - > [60k,28,28,1]。*
- 一热编码: 现在,我们的 NN 会是什么样子?因为它是一个多类分类器,类从 0 到 9,我们可以有把握地假设在最后一步之后将有 10 个输出。还记得 softmax 吗?这种系统将有 10 个输出,其预测的数字的概率最高。所以,我们应该把我们的输出从[60k,1]->,[60k,10]。这就是通过一键编码完成的。因此,假设输出值为 1,将被重写为[0,1,0,0,0,0,0,0,0]。无论哪一个是将由 1 表示的输出,其他的由 0 表示。简单,嗯?!
- 归一化 :每个矩阵都会有[0–255]之间的值对吗?因此,为了便于计算,所有值都从[0–255]移动到[0–1]范围。这可以简单地通过将所有值除以 255 来实现。
定义模型
作者图片
是时候修改 CNN 了!随机创建一个你认为性能足够好的模型。例如,这里我们创建了上面的模型,它具有:
- 卷积层 有 32 个大小为[33]的内核,输入大小为[28281]。*
- 最大汇集层 同【2 * 2】
- 展平 输出,并将其传递给一个全连接层。
- 全连通层 有 100 个隐藏节点和 10 个输出节点。
- 优化功能 —该功能定义如何重新计算权重。
- 损失函数 —这是我们用来定义实际值和预测值之间差异的误差函数。
- 激活功能 —在每个隐藏节点后,该功能用于将非线性成分添加到我们的输出中。
关于这些函数的可能值,我会有一篇单独的文章。现在,让我们只是使用它们,因为不知何故已经告诉我们这样做。
模型评估
作者图片
现在,这是最棘手的部分。因此,*我们只在训练集上训练我们的模型。我们根本不会使用我们的测试集。*这里我们需要理解两个概念:
- 验证数据: 我们将我们的训练集进一步分为 2 个集——训练集和测试集。所以基本上我们是在第一组上训练我们的模型,并在测试组上测试它。这有助于我们在不涉及原始测试集的情况下检查模型的准确性。
- K-Fold: 基本上,这里我们将训练集分成 K 个集合,而不仅仅是 2 个。从中我们随机选择一组作为训练组,另一组作为测试组。更多可以在这里阅读。
***Example: 3-Fold****Array:** [1,2,3,4,5,6]
**Fold 1:** [1, 2]
**Fold 2:** [3, 4]
**Fold 3:** [5, 6]So,
**1st Iteration:** Training on Fold 1 + Testing on Fold 2
**2nd Iteration:** Training on Fold 2 + Testing on Fold 3
**3rd Iteration:** Training on Fold 3 + Testing on Fold 1*
我将在下一篇文章中讨论 Keras 的批量大小。
绘制学习曲线
作者图片
在这里,我们正在绘制我们的损失和准确性。 首先,我们为什么要在 al l 处绘制它们?这让我们可以直观地了解我们的模型在不同配置下的表现。在这种情况下,只是不同的训练数据和测试数据。但是,我们可以为一个模型配置不同的隐藏节点,或者卷积层中的不同层,等等。这通常有助于我们从视觉上看出我们的模型是过度拟合、欠拟合还是非常拟合。
上面的图像表明,它在两个训练集上表现相同,因此非常适合。
作者图片
这只是另一种方式来看看我们的分数表现如何。
最终测试
作者图片
这是最后一步,我们将使用我们定义的模型对测试数据进行测试,并计算最终的准确性。
这篇文章的主要焦点是消除摩擦,让你们开始在 CNN 的美丽旅程。这篇博客深受这篇文章的启发🙏。
Tensorflow.js 手写数字识别
机器如何预测你画的数字?
手写识别使我们能够将手写文档转换成数字形式。这项技术现在被广泛应用:读取邮政地址、银行支票金额、数字化历史文献。
感谢 tensorflow.js,它将这项强大的技术带入了浏览器。在本文中,我们将构建一个 web 应用程序,它可以预测您在画布上绘制的数字。
下面是 web 应用程序的最终结果,正如你所看到的,我们得到了一个很好的结果,它预测我已经抽取了数字 5 有 99% 的置信度
手写数字识别演示
点击下面的链接亲自尝试一下:
[## 使用 Tensorflow.js 进行手写数字识别-技术游乐场
手写识别使我们能够将手写内容转换成数字形式,在本文中,我们将构建一个 web 应用程序…
bensonruan.com](https://bensonruan.com/handwritten-digit-recognition-with-tensorflow-js/)
履行
这个应用首先使用 Python 脚本训练并保存模型,然后使用 JavaScript 库 tensorflow.js 将模型加载到浏览器中,并预测手绘数字是什么数字。请在下面关注我,进一步探索它是如何构建的。
文件夹结构
让我们从用适当的文件夹结构设置项目开始
手写数字识别项目
- js —包含 JavaScript 文件
- chart . min . js-图表显示 JavaScript 库
- digit-recognition.js —主应用程序 JavaScript
- 模型(models )-包含保存的模型和权重
- 样式—包含 css 样式文件
- index.html—主 html 文件
- MNIST.py 用于训练和保存模型的 Python 脚本
#步骤 1:训练并保存模型
为了开始我们的旅程,我们将编写一个 Python 脚本,在著名的 MNIST 数据集上训练 CNN(卷积神经网络)模型。
MNIST 是一个由手写数字组成的计算机视觉数据库,带有识别数字的标签。每个 MNIST 数据点都有两个部分:一个手写数字的图像和一个相应的标签。
导入库
加载 MNIST 数据集
MNIST 数据集由 60,000 个例子组成,我们将它们分成训练和测试数据集。之后,它需要一些预处理才能进入 CNN。
首先,你将输入向量整形为一个 4 维数组
接下来,我通过将 RGB 代码划分为 255 来标准化输入
定义 CNN 模型
下面我用一个常见的模式定义卷积基:Conv2D 和 MaxPooling2D 层的堆栈。
- 第一层将有 32–5x 5 个过滤器,
- 第二层将有 64–5x 5 个过滤器
此外,还有两个最大池层,每个大小为 2 x 2。
训练模型
It 培训可能需要几分钟,我们可以在测试集上获得 98.5%的正确率。
将模型保存为 tfjs 格式
现在我们有了一个模型,我们需要将它保存为某种 tensorflowjs 可以加载到浏览器中的格式。
tfjs.converters.save_keras_model(model, 'models')
该模型将被保存到“models”文件夹中,其中包含一个 model.json 文件和一些其他权重文件。
#第二步:包含 tensorflow.js
简单地将tfjs
的脚本包含在 html 文件的<头>部分中。我还包括 jquery 库和图表库。
#步骤 3:设置画布
为了让用户在桌面上使用鼠标或在移动设备上使用手指来绘制数字,我们需要创建一个名为 canvas 的 HTML5 元素。在画布内部,用户将绘制数字。我们会将用户绘制的数字输入到我们创建的深度神经网络中进行预测。
HTML—index.html
添加一个占位符
来包含您可以在其上绘制数字的画布
<div id="canvas_box" class="canvas-box"></div>
添加“预测”按钮以获得手写数字预测的结果,“清除”按钮擦除画布并再次开始绘制
<button id="clear-button" class="btn btn-dark">Clear</button><button id="predict-button" class="btn btn-dark">Predict</button>
在的末尾,包含主 JavaScript 文件 digit-recognition.js
<script src="js/digit-recognition.js"></script> </body></html>
JavaScript—digit-recognition . js
初始化变量
创建画布并将其附加到占位符上以进行显示
在移动和桌面环境中,在画布内部绘图有点棘手。我们需要了解所有适用于鼠标和触摸的 jQuery 处理程序。下面是我们将使用的 jQuery 事件处理程序。
适用于台式机和笔记本电脑
- 鼠标按下
- 鼠标移动
- 鼠标抬起
- 老鼠离开
对于移动设备
- 触摸开始
- 触摸移动
- 触摸端
- 触球
#步骤 4: Tensorflow.js 负载建模和预测
现在,我们需要使用 TensorFlow.js 来加载我们之前在 Python 中训练的模型,并使用它来预测我们在画布上绘制的数字。
负荷模型
函数 loadModel()来调用 tensorflow.js API tf.loadLayersModel
预处理画布
函数 preprocessCanvas 在将用户绘制的画布馈送到 CNN 模型之前对其进行预处理
预测
当点击“预测”按钮时,我们从画布中获取图像数据,将其作为张量进行预处理,然后将其送入 API model.predict
以获得预测结果。
显示结果
函数 loadChart 利用 Chart.js 库将预测结果显示为可视化条形图。
GitHub 知识库
您可以通过下面的链接下载上述演示的完整代码:
使用 javascript 库 tensorflowjs 的手写数字识别…
github.com](https://github.com/bensonruan/Hand-Written-Digit-Recognition)
结论
毫无疑问,在手写识别领域已经进行了大量的研究。和 tensorflow.js 使这些预先训练的深度模型可以在浏览器中访问。我希望你能从这篇文章中得到乐趣,并鼓励你更多地了解这个库。我迫不及待地想看到更多创造性的想法出现,将这项尖端技术投入实际应用。
感谢您的阅读。如果你喜欢这篇文章,请在脸书或推特上分享。如果你有任何问题,请在评论中告诉我。在 GitHub 和 Linkedin 上关注我。
手写高棉数字识别
线性判别分析方法
手写识别
手写识别是将给定的手写模式分类成组(类别)的任务。有许多方法可以实现这项任务,从传统的机器学习方法到深度学习,如卷积神经网络(CNN)。
图 1 手写数字识别示例
本文介绍一种简单的模式识别方法——高斯极大似然估计线性判别分析。我们以手写高棉数字识别任务为例。
高棉数字
高棉语是柬埔寨的官方语言。大约有 1600 万人说这种语言,主要分布在柬埔寨以及越南和泰国的部分地区。在高棉语中,图 2 所示的一组数字用于数字系统。
图 2 高棉数字
图 3 手写高棉数字
线性判别分析(LDA)
假设我们有模式 x 及其类别 y 的样本,{(x_i,y_i)} (i=1,…,n),其中 n 是训练样本的数量。设 n_y 是 y 范畴中模式的个数。
为了定义给定模式 x 的相应类别,我们选择具有最大后验概率值 p(y|x)的 y。这里 p(y|x)是给定 x 的 y 的一个条件概率,这个判定规则叫做最大后验概率规则。
根据贝叶斯理论,后验概率可以通过下式计算
在上面的表达式中,p(x)不依赖于 y。因此,我们可以忽略分母。
现在我们需要估计 p(y)和 p(x|y)。对于 y,由于它是一个离散的概率变量(y 是一个类别),我们可以简单地用 y 类别中模式的个数与样本总数的比值来估计它的概率。
x 是一个连续的概率变量,我们不能直接应用于 y 的方法。这里,我们将介绍一种参数化方法。
假设模式 x 由高斯分布独立且同分布,我们将对高斯模型应用最大似然估计来估计其参数并计算条件概率 p(x|y)。
通常,具有 d 维高斯分布的 x 的高斯模型由下式给出
使用最大似然估计,高斯模型参数的估计为
现在,p(x|y)的估计值可以通过下式计算
将估计的 p(x|y)和 p(y)代入 p(y|x)表达式,我们可以得到给定模式 x 的类别 y 的后验概率,然而,为了使它更简单,我们在下面的计算中使用了对数函数。因为对数函数是单调递增的,所以与 p(y|x)的关系没有区别。
为了简化这个对数后验概率,在线性判别分析中,我们做如下假设:每一类的方差-协方差矩阵相等。在这种情况下,常见的方差-协方差矩阵为
log p(y|x)是
因此,给定模式 x 的类别 y 可以通过选择其 log p(y|x)在所有类别中保持最大值的 y 来预测。
资料组
现在让我们将这种 LDA 方法应用于手写高棉数字识别。
在这种情况下,手写数字的数字图像是模式 x,而
0–9 是类别 y。我们使用 1500 个 64x64 灰度图像作为数据集。我们将这个数据集分成 1200 个用于训练数据,300 个用于测试数据。
对于模式 x,我们简单地将 64x64 灰度图像整形为 4096 维向量。因此,我们将 LDA 应用于具有 4096 维高斯分布的高斯模型。
用 Python 实现
o 将数据集分成训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
o 估计模型的参数(训练)
means = np.zeros((10, nD*nD))
cov = np.zeros((nD*nD, nD*nD))
count = [sum(map(lambda x : x == i, y_train)) for i in range(0,10)]for i in range(10):
cov_array = []
for j in range(len(y_train)):
if int(y_train[j]) == i:
means[i] = means[i] + X_train[j]
cov_array.append(X_train[j])
cov = cov + np.cov(np.array(cov_array).T) * (count[i]/len(y_train))means = means / len(y_train)
inv_cov = np.linalg.inv(cov + 0.00001*np.eye(nD*nD))
o 评估
ans = np.zeros((10, 10),dtype='int')
total = 0
errors = []for i in range(10):
for j in range(len(y_test)):
if int(y_test[j]) == i:
p = np.zeros(10)
for k in range(len(p)):
p[k] = np.dot(np.dot(means[k].T, inv_cov), X_test[j]) - (np.dot(np.dot(means[k].T, inv_cov), means[k])) / 2 + np.log(count[i])m = p.argmax()
if m!=y_test[j]:
errors.append((j,m))
ans[m][int(y_test[j])] = int(ans[m][int(y_test[j])] + 1)
密码
https://github.com/loem-ms/PatternRecognition.git
结果和讨论
o 混淆矩阵
**0 1 2 3 4 5 6 7 8 9**
**0** 28 0 0 0 0 0 0 0 0 0
**1** 2 27 0 0 0 0 0 0 0 0
**2** 0 0 37 0 0 0 0 0 0 0
**3** 0 0 0 29 0 0 0 0 0 0
**4** 0 0 0 0 33 0 0 0 1 0
**5** 0 0 0 0 0 25 0 0 0 0
**6** 0 0 0 0 0 0 26 0 0 0
**7** 0 0 0 0 0 0 0 28 0 0
**8** 0 0 0 0 0 0 0 0 38 0
**9** 0 0 0 0 0 0 0 0 0 26
o 测试准确度
0.99
有用!
o 错误案例
类别:១;预测:០
类别:៤;预测:៨
与其他基于深度学习的方法中的黑盒概念不同,使用 LDA,我们可以简单地理解使用数据的统计特征的学习过程。
然而,由于它是基于线性的模型,在某些情况下,它不能被线性判别函数识别,性能将明显下降。
另一个问题是输入模式 x 的维数。在本例中,我们使用 4096 维向量(64x64)作为输入,估计模型参数需要 7.17 秒(注意 PC)。当将输入图像增加到 128x128 时,这意味着使用 16384 维向量作为输入,这需要 470.80 秒。增加输入向量的维数导致在计算方差-协方差矩阵的逆矩阵时的巨大时间复杂度。
用于客户端 ML 开发的便捷 TensorFlow.js API
TensorFlow.js 提供了一组丰富的 API 方法来处理训练数据,在客户端执行训练和推理。这篇文章通过一个简单实用的例子解释了如何使用这个 API。
来源:Pixabay
让我们研究一下 TensorFlow.js API,用于训练数据处理、训练执行和推理。TensorFlow.js 非常棒,因为它将机器学习带到了 Web 开发人员的手中,这提供了互利。机器学习领域得到了更多的开发者和支持者,而 Web 开发在机器学习的支持下变得更加强大。
我过去有一个关于 TensorFlow.js 的帖子— 综合 TensorFlow.js 举例。本文旨在展示如何使用 TensorFlow.js 构建实际用例。今天,我将重点介绍 TensorFlow.js API,并展示如何使用开箱即用的 API 获取数据、训练模型和运行预测。
我想让示例应用程序尽可能简单,不使用任何特定的 JavaScript 框架/工具包。这将允许您下载示例代码并快速运行它。示例应用程序( GitHub repo)作为 HTML 页面运行,要在本地测试它,您需要使用 HTTP 服务器。我会推荐使用 NPM 的 HTTP-Server 。转到包含 HTML 文件的文件夹,运行 http-server 命令。在浏览器中导航到端口 8080 并单击 HTML 文件,这将加载示例应用程序。
示例应用程序基于皮马印第安人糖尿病数据集(可在 Kaggle 上获得)。数据被分成训练集和测试集。
第一步是加载数据。通常,数据来自 CSV,有一个帮助器 TensorFlow.js 函数从 CSV 文件中读取数据— tf.data.csv :
const trainingUrl = 'diabetes-train.csv';
const trainingData = tf.data.csv(trainingUrl, {
columnConfigs: {
Outcome: {
isLabel: true
}
}
});
我们可以指定属性名,哪个将被用作训练目标。
接下来,我们转换数据集,为训练做好准备(这里我们可以对分类数据进行一次性编码)。将数据转换为数组形式:
const convertedTrainingData =
trainingData.map(({ xs, ys }) => {
return { xs: Object.values(xs), ys: Object.values(ys) };
}).batch(30);
这就是模型的构建方式。有三层,分别有 12、8 和 1 个神经元。最后一层包含 1 个神经元,并通过 sigmoid 激活设置为返回 0/1。模型被编译以支持二进制分类任务:
const numOfFeatures = (await trainingData.columnNames()).length - 1;const model = tf.sequential();
model.add(tf.layers.dense({
inputShape: [numOfFeatures],
units: 12,
activation: 'relu'
}));
model.add(tf.layers.dense({
units: 8,
activation: 'relu'
));
model.add(tf.layers.dense({
units: 1,
activation: 'sigmoid'
}));model.compile({ optimizer: tf.train.adam(0.001), loss: 'binaryCrossentropy', metrics: 'accuracy' });
我使用的是 fitDataset 函数,它直接根据之前准备的数据进行训练。这非常方便,省去了繁琐的数据操作过程:
const metrics = ['loss', 'val_loss', 'acc', 'val_acc'];
const container = { name: 'Model Training', styles: { width: '1200px' } };
const fitCallbacks = tfvis.show.fitCallbacks(container, metrics, {callbacks: ['onEpochEnd'], width: 1200});await model.fitDataset(convertedTrainingData,
{
epochs: 100,
validationData: convertedTestingData,
callbacks: fitCallbacks
});
我正在使用 tfjs-vis 回调来可视化培训过程:
最后,我们执行预测函数来计算标签(训练目标)。TensorFlow.js 预测函数期望 tf.tensor2d 变量。[1,8]表示 1 维,8 个输入,用于描述我们传递给预测函数的数据的形状:
const testVal = tf.tensor2d([2, 108, 64, 0, 0, 30.8, 0.158, 21], [1, 8]);
const prediction = model.predict(testVal);
outcome = prediction.dataSync()[0];
prediction.dispose();
样本代码可以在我的 GitHub repo 上找到。
机库教程(1/2):将数据添加到机库
来源:https://www.tech101.in/how-version-control-works/
一步一步的指南,以建立一个机库回购和添加您的数据。
更新: 机库 0.5 版本发布不久,它已经将 arraysets 重命名为 columns,现在支持在这些列中存储字符串,不仅支持 numpy 数组,还移除了元数据。这将是今后使用的惯例,博客已经更新以应对这些变化。这些变化已被注意到了。
数据可以说是机器学习中最重要的一块。每时每刻收集的大量数据是在巨大的计算资源上运行的深度学习算法的燃料,以模仿任何形式的智能。因此,收集、存储和维护这些不断增长的数据对于训练更好的深度学习算法非常重要。
这就是机库的用武之地。
hanger 是对张量数据的版本控制。它就像 git,但是对于你的 tensors 来说就是数字数据。使用 hangar,您可以在数据集的演变过程中进行时间旅行,创建不同的分支进行实验而无需任何开销,与组织中的多人合作创建数据集,创建仅包含数据子集的本地副本等等。Hangar 使处理数据集变得轻而易举。
冷静的人…机库让你回来了!(来源:quickmeme.com)
核心概念
- **数据集:**这是用于训练的数据样本的集合。数据集中的每个样本都是最小的个体实体,当单独考虑时仍然有意义。这个样本可以进一步分解成更小的属性,这些属性中的每一个都可以单独存储。例如,在 MNIST 数据集的情况下,单个 28x28 图像是样本,而在类似 Kaggle 中的 Titanic 数据集的表格数据集的情况下,每一行是样本,每一列是属性。
- **列:**一个数据集可以被分解成列,这就是它们在 Hangar 中的存储方式。这类似于表中的列。数据集中的每个样本都细分为代表该样本不同属性的列。只有当所有的属性(列)结合在一起时,才能完整地描述样本。
解释机库柱子的插图
机库提供两种类型的立柱
- 数值列(ndarray):这些列用于在类似 numpy 的数组中存储数值数据。
- 字符串列(str):这些列存储字符串类型的数据。
注意:在*机库 0.5 之前的版本中,*列曾被称为 arraysets,仅用于存储数值数据,而元数据现在被称为 String 列,用于存储字符串。
3.**存储库:**这是数据存储的地方,作为提交的时间列表,由不同的贡献者在不同的分支中处理。它类似于 git 存储库。我们通常为每个数据集使用一个存储库。每个数据样本都存储在列中。也可以有远程存储库
如果你想了解更多的概念,我强烈推荐你查阅文档。
设置存储库和列
对于本教程,我们将在 MNIST 数据集上设置 Hangar(下载数据集并保存在您的工作目录中)。我们将把数据集添加到机库,并在这个过程中学习机库的基本功能和用法。需要注意的一点是,与 git 不同,git 与版本控制系统的主要交互是 CLI,这里最常用的访问点是通过 python APIs。这使得数据集版本化可以与大型数据集管道无缝协作。
安装很简单,如果使用 anaconda,只需运行pip install hangar
或conda install -c conda-forge hangar
即可。对于从源代码安装,只需运行
$ git clone [https://github.com/tensorwerk/hangar-py.git](https://github.com/tensorwerk/hangar-py.git)
$ cd hangar-py
$ python setup.py install
初始化储存库
每次使用新的数据集时,我们都必须创建一个新的存储库。我们使用存储库init()
方法来初始化一个存储库。您可以在这里向 Hangar 提供您的姓名和电子邮件地址,这些信息将在提交日志中使用。
这将生成存储库,并在. hangar 文件夹中创建下划线文件。
Hangar Repo initialized at: /home/jjmachan/jjmachan/hangar_examples/mnist/.hangarOut []:Hangar Repository
Repository Path : /home/jjmachan/jjmachan/hangar_examples/mnist
Writer-Lock Free : True
现在,我们必须创建回购的签出,以访问其中的数据。有两种结账模式。
- 可写签出:在这种模式下,所有操作都在临时区域的当前状态下执行。在给定时间内,只能有一个可写签出处于活动状态,并且必须在退出前正确关闭。
- 只读签出:此模式用于仅从回购中读取。在给定时间内,可以有多个签出处于活动状态。
现在,我们必须创建回购的签出,以访问其中的数据。有两种结账模式。
可写签出:在此模式下,所有操作都在暂存区的当前状态下执行。在给定时间内,只能有一个可写签出处于活动状态,并且必须在退出前正确关闭。
只读签出:该模式用于只从回购中读取。在给定时间内,可以有多个签出处于活动状态。
这将创建一个可写的签出。
Out[]:Hangar WriterCheckout
Writer : True
Base Branch : master
Num Columns : 0
如前所述,数据集存储为包含描述样本的不同属性的列。要初始化一个列,我们需要名称、dtype(数据类型)和形状。我们还可以向它展示一个原型,这是一个示例数组,具有与示例数据相同的正确的 dtype 和 shape ,并且 hangar 将从中推断出列的 shape 和 dtype 。
一个数字列由add_ndarray_column()
方法初始化,而一个新的字符串列由add_str_column( )
初始化。它接受样本的名称和原型。
在本例中,我们正在对 MNIST 数据集进行版本控制。要继续本教程,请下载数据集并将其放在工作目录中。然后运行以下代码来加载数据集,并为初始化列创建一个原型。
现在我们有了样本图像(sample_trimg)和标签(sample_trlabel)来初始化列。
Out []:Hangar FlatSampleWriter
Column Name : mnist_training_images
Writeable : True
Column Type : ndarray
Column Layout : flat
Schema Type : fixed_shape
DType : float32
Shape : (784,)
Number of Samples : 50000
Partial Remote Data Refs : False
添加数据
当初始化一个列时,将返回一个列访问器对象,我们可以使用该对象访问列中的示例。我们还可以使用 repo 的 checkout 实例来访问 Column 访问器对象。
checkout[*column_name*]
→专栏
样本数据以类似字典的方式存储,带有一个键和值。可以像在 python 字典中添加数据一样添加新样本
column_1[key] = value
密钥可以是 int 或 str 。这些关键字充当检索相应样本的索引。如果没有找到数据,可以对返回 None 的列使用 get()函数,而不是抛出一个 KeyExeption。
column_1[key]
column_1.get(key)
checkout[*column_name, key]*
关于机库中的上下文管理器。从第一天起,安全就是机库的一大焦点。hanger 锁定并检查用于访问存储数据的方法,这样无论 hanger 以何种规模运行,数据都不会被破坏。每次我们试图检索或设置数据时,Hangar 都会执行一些检查,然后才执行操作。当执行大量读写任务时,这可能是一个很大的开销。因此,为了克服这个问题,建议使用 python 上下文管理器,使用*with*
关键字。使用它可以确保对每个读/写会话执行检查,而不是单独对每个操作执行检查。
现在我们有了将 MNIST 数据添加到列中的最终代码。
作为一个练习,尝试在没有上下文管理器的情况下执行代码( 带 img_col ,label_col : 行),并查看性能差异。
提交更改
添加完数据后,您可以使用commit()
方法将添加内容提交给回购。它将提交消息作为参数。提交所有更改后,请确保关闭连接,尤其是在执行可写签出时。
Out []:'a=3f4c4497c93982d0d182110277841ed37d1bbf8e'
与此同时,我们的数据被散列和安全地存储在机库。
要查看存储库的日志和详细信息,我们可以使用存储库对象。使用repo.log()
获取回购提交消息的日志。repo.summary()
返回关于存储库的最重要的信息。
Out []:Summary of Contents Contained in Data Repository
==================
| Repository Info
|-----------------
| Base Directory: /home/jjmachan/jjmachan/hangar_examples/mnist
| Disk Usage: 105.90 MB
===================
| Commit Details
-------------------
| Commit: a=41f88d0fe944f431339d3c0b3935e787328d01cd
| Created: Mon Apr 13 13:12:04 2020
| By: jjmachan
| Email: jjmachan@g.com
| Message: added all the mnist datasets
==================
| DataSets
|-----------------
| Number of Named Columns: 6
|
| * Column Name: ColumnSchemaKey(column="mnist_test_images", layout="flat")
| Num Data Pieces: 10000
| Details:
| - column_layout: flat
| - column_type: ndarray
| - schema_hasher_tcode: 1
| - data_hasher_tcode: 0
| - schema_type: fixed_shape
| - shape: (784,)
| - dtype: float32
| - backend: 00
| - backend_options: {'complib': 'blosc:lz4hc', 'complevel': 5, 'shuffle': 'byte'}
|
| * Column Name: ColumnSchemaKey(column="mnist_test_labels", layout="flat")
| Num Data Pieces: 10000
| Details:
| - column_layout: flat
| - column_type: ndarray
| - schema_hasher_tcode: 1
| - data_hasher_tcode: 0
| - schema_type: fixed_shape
| - shape: (1,)
| - dtype: int64
| - backend: 10
| - backend_options: {}
|
| * Column Name: ColumnSchemaKey(column="mnist_training_images", layout="flat")
| Num Data Pieces: 50000
| Details:
| - column_layout: flat
| - column_type: ndarray
| - schema_hasher_tcode: 1
| - data_hasher_tcode: 0
| - schema_type: fixed_shape
| - shape: (784,)
| - dtype: float32
| - backend: 00
| - backend_options: {'complib': 'blosc:lz4hc', 'complevel': 5, 'shuffle': 'byte'}
|
| * Column Name: ColumnSchemaKey(column="mnist_training_labels", layout="flat")
| Num Data Pieces: 50000
| Details:
| - column_layout: flat
| - column_type: ndarray
| - schema_hasher_tcode: 1
| - data_hasher_tcode: 0
| - schema_type: fixed_shape
| - shape: (1,)
| - dtype: int64
| - backend: 10
| - backend_options: {}
|
| * Column Name: ColumnSchemaKey(column="mnist_validation_images", layout="flat")
| Num Data Pieces: 10000
| Details:
| - column_layout: flat
| - column_type: ndarray
| - schema_hasher_tcode: 1
| - data_hasher_tcode: 0
| - schema_type: fixed_shape
| - shape: (784,)
| - dtype: float32
| - backend: 00
| - backend_options: {'complib': 'blosc:lz4hc', 'complevel': 5, 'shuffle': 'byte'}
|
| * Column Name: ColumnSchemaKey(column="mnist_validation_labels", layout="flat")
| Num Data Pieces: 10000
| Details:
| - column_layout: flat
| - column_type: ndarray
| - schema_hasher_tcode: 1
| - data_hasher_tcode: 0
| - schema_type: fixed_shape
| - shape: (1,)
| - dtype: int64
| - backend: 10
| - backend_options: {}
结论
就像我之前提到的,python APIs 是 hanger 最常用的接口,但是 hanger 也提供了一个直观的 CLI。你可以创建,删除列,管理各种分支和远程数据源,从远程推送和获取数据,管理机库服务器等。有关更多详情,请查看官方文件。
现在,我们已经成功地将 MNIST 数据集添加到了机库,在下一部分中,我们将创建一个数据加载器来从机库获取数据,并使用它来训练一个模型。
机库教程(2/2):使用版本化数据训练模型
如何从机库获取数据并训练 PyTorch 模型?
在上一篇教程中,我们介绍了hanger,这是一个 python 库,可以帮助你对数据进行版本控制。数据是新的石油,正确管理和传输数据流对您的公司至关重要。Hangar 是您工具箱中的一个很好的工具,它带来了从软件开发到 ML 工程的版本控制等最佳实践,从而帮助您为客户提供强大的 ML 服务。如果你还没看过之前的博客,那就去看看吧。我讨论了机库的核心概念,以及如何在那里创建自己的机库回购。
ML 工作流程变得越来越复杂,我认为 Hangar 是你工具箱中非常重要的工具(via Giphy )
注: 随附的 GitHub repo 中有一些详细的笔记本,解释了所有讨论的概念。您可以在本教程中尝试一下。【 链接】
我们说到哪了…?
因此,我们已经学习了机库的核心概念,我们将玩具数据集(MNIST)添加到了机库中。今天,我们将从我们停止的地方继续,并根据我们添加到 Hangar 的数据训练一个模型。首先我们来连线机库回购。
将所有制表符转换为空格 vim
让我们快速浏览一下摘要,了解一下我们回购中的数据。
Summary of Contents Contained in Data Repository
==================
| Repository Info
|-----------------
| Base Directory: /home/jjmachan/jjmachan/hangar_tutorial
| Disk Usage: 105.88 MB
===================
| Commit Details
-------------------
| Commit: a=39a36c4fa931e82172f03edd8ccae56bf086129b
| Created: Fri May 1 18:23:19 2020
| By: jjmachan
| Email: jjmachan@g.com
| Message: added all the mnist datasets
==================
| DataSets
|-----------------
| Number of Named Columns: 6
|
| * Column Name: ColumnSchemaKey(column="mnist_test_images", layout="flat")
| Num Data Pieces: 10000
| Details:
| - column_layout: flat
| - column_type: ndarray
| - schema_hasher_tcode: 1
| - data_hasher_tcode: 0
| - schema_type: fixed_shape
| - shape: (784,)
| - dtype: float32
| - backend: 00
| - backend_options: {'complib': 'blosc:lz4hc', 'complevel': 5, 'shuffle': 'byte'}
|
| * Column Name: ColumnSchemaKey(column="mnist_test_labels", layout="flat")
| Num Data Pieces: 10000
| Details:
| - column_layout: flat
| - column_type: ndarray
| - schema_hasher_tcode: 1
| - data_hasher_tcode: 0
| - schema_type: fixed_shape
| - shape: (1,)
| - dtype: int64
| - backend: 10
| - backend_options: {}
|
| * Column Name: ColumnSchemaKey(column="mnist_training_images", layout="flat")
| Num Data Pieces: 50000
| Details:
| - column_layout: flat
| - column_type: ndarray
| - schema_hasher_tcode: 1
| - data_hasher_tcode: 0
| - schema_type: fixed_shape
| - shape: (784,)
| - dtype: float32
| - backend: 00
| - backend_options: {'complib': 'blosc:lz4hc', 'complevel': 5, 'shuffle': 'byte'}
|
| * Column Name: ColumnSchemaKey(column="mnist_training_labels", layout="flat")
| Num Data Pieces: 50000
| Details:
| - column_layout: flat
| - column_type: ndarray
| - schema_hasher_tcode: 1
| - data_hasher_tcode: 0
| - schema_type: fixed_shape
| - shape: (1,)
| - dtype: int64
| - backend: 10
| - backend_options: {}
|
| * Column Name: ColumnSchemaKey(column="mnist_validation_images", layout="flat")
| Num Data Pieces: 10000
| Details:
| - column_layout: flat
| - column_type: ndarray
| - schema_hasher_tcode: 1
| - data_hasher_tcode: 0
| - schema_type: fixed_shape
| - shape: (784,)
| - dtype: float32
| - backend: 00
| - backend_options: {'complib': 'blosc:lz4hc', 'complevel': 5, 'shuffle': 'byte'}
|
| * Column Name: ColumnSchemaKey(column="mnist_validation_labels", layout="flat")
| Num Data Pieces: 10000
| Details:
| - column_layout: flat
| - column_type: ndarray
| - schema_hasher_tcode: 1
| - data_hasher_tcode: 0
| - schema_type: fixed_shape
| - shape: (1,)
| - dtype: int64
| - backend: 10
| - backend_options: {}
如您所见,数据存储在 6 个 ndarray 列中。2 列各存储 3 个数据分割、训练、测试和验证的图像和目标对。现在让我们从主分支创建一个只读签出来访问列。
* Checking out BRANCH: master with current HEAD: a=39a36c4fa931e82172f03edd8ccae56bf086129b
数据加载器
现在,让我们从 Hangar 加载数据,将其输入我们的神经网络进行训练。尽管您可以使用 checkout 直接访问数据,但推荐的方法是使用 make_torch_dataset。它提供了更多可配置的选项,并使其更有效、更容易地加载到 PyTorch 中。
make _ torch _ dataset(列,key:Sequence[str]= None, index_range: slice = None ,field _ names:Sequence[str]= None**)**
make_torch_dataset 创建一个 PyTorch 数据集,将列传递给它。在我们的示例中,如果我们要传递“mnist_training_images”列和“mnist_training_labels”列。现在,make_torch_dataset 将为每个索引返回一个元组,其中包含与该索引对应的图像和标签。
因为它返回一个 PyTorch 数据集,所以我们可以将它传递给 PyTorch 数据加载器来执行混洗、批处理等操作。
现在,遍历 trainDataloader 将为我们提供成批的数据,并作为张量准备用于训练。
培养
我们不会深入模型是如何定义和训练的细节。如果你觉得下面的代码很难跟上,我强烈建议你看看 Pytorch 教程
我们将为 MNIST 定义一个简单的 3 层全连接神经网络。
现在,使用 dataloader,我们通过批处理进行迭代,进行正向传播,计算损耗,通过反向传播找到梯度,并调整模型参数,这就是您所熟悉的标准神经网络训练。
[EPOCH 0/10] Train Loss: 1.2083537247954312
Test Loss: 0.47797256113050846 Accuracy: 0.865814696485623
[EPOCH 1/10] Train Loss: 0.3944594695549208
Test Loss: 0.3467818654228609 Accuracy: 0.897064696485623
[EPOCH 2/10] Train Loss: 0.31767420198765994
Test Loss: 0.2960374093682954 Accuracy: 0.9107428115015974
[EPOCH 3/10] Train Loss: 0.27709063706813486
Test Loss: 0.2613061714274719 Accuracy: 0.9223242811501597
[EPOCH 4/10] Train Loss: 0.24662601495887404
Test Loss: 0.234231408689909 Accuracy: 0.9306110223642172
[EPOCH 5/10] Train Loss: 0.22161395768786918
Test Loss: 0.21181030162928488 Accuracy: 0.9365015974440895
[EPOCH 6/10] Train Loss: 0.20021527176466666
Test Loss: 0.19286749035137862 Accuracy: 0.9421924920127795
[EPOCH 7/10] Train Loss: 0.18172580767267993
Test Loss: 0.1767021114335428 Accuracy: 0.946685303514377
[EPOCH 8/10] Train Loss: 0.16573666792806246
Test Loss: 0.16299303889887545 Accuracy: 0.9507787539936102
[EPOCH 9/10] Train Loss: 0.1518441192202165
Test Loss: 0.15127997121999795 Accuracy: 0.954073482428115
瞧!
我们已经成功地使用来自机库的数据训练了我们的模型。
结论
到目前为止,您应该已经学习了 Hangar 的基础知识,如何根据您的数据集定义列,向这些列添加数据,并从中加载数据以在 PyTorch 中训练模型。hangle 还有其他一些很酷的功能,比如远程仓库、从这些远程仓库部分下载数据、hangle 外部是 hangle 的一个高级接口,当你将 hangle 与现有数据流集成时可以使用。我已经链接了相关的文档,并且我建议你去看看这些文档,了解一下 Hangar 提供的所有功能。
随着越来越多的公司正在寻找新的方法将机器学习的力量融入他们的产品,我们将一些最佳实践融入其中以避免积累技术债务是很重要的。这仍然是一个不断发展的领域,每个人都仍然在弄清楚这些“最佳实践”是什么,以及哪些工作流可能最适合他们。对您的数据进行版本控制就是这样一种实践,可以添加它来确保可再现性、协作和更好的管理。像 Hangar 和 DVC 这样的工具是最好的选择,尽管还有很多工作要做。Hangar 非常容易添加到你现有的 ML 工作流程中,希望这篇教程能让你更好的理解它。
干杯
辅助
这是一个比较两方面机库的辅助部分
- 存储在机库之前和之后数据集的大小
- 使用 Hangar 和一个简单数据加载器的数据加载器的速度,该数据加载器采用我们定义的 mnist_dataset。
现在,这些是我个人的实验和基准,所以你可以自己尝试一下,看看我下面讨论的结果是否相同。如果现在你能把它分享给每个人,我会很高兴的!该代码可在 GitHub repo 上获得。
首先,让我们谈谈数据集的大小。现在,内部机库在将数据保存到磁盘时,在处理数据方面做得非常好。在我们的例子中,原来的 MNIST 文件是 210 兆,但在机库它只占 105 兆。但我说的是未压缩版的 MNIST。压缩版只有 17Mb,这是一个很大的容量。这是我见过的 Hangar 最大的问题之一。它在压缩数字数据方面做得很好,但最有可能的是,有其他格式的数据可能会提供显著的大小缩减。这在把 jpeg 文件保存在机库时变得非常明显,它们占据了大量的空间。
现在有办法通过手动调整库选择保存数据的后端来提高效率。但是这是以更高的读取时间为代价的。据开发商称,这是出于好意。存储很便宜,但时间不便宜。
第二是从磁盘加载用于训练。差不多要多花 7 倍的时间,很多!同样在这个例子中,我在将数据加载到内存之前执行解压缩,仍然只需要大约 1 秒。
这是我系统的输出。
显示结果的屏幕截图
这又回到了后端选择。所有这些结果都是默认设置,但可以调整。所以问题是我们如何调整这些。一份官方指南正在制作中。当它出来的时候,我会更多的评论后端选择。
同时,你检查一下,告诉我你的结果。很想听听!
幸福和生活满意度
世界幸福报告的探索性数据分析。
人生的目的是什么?那是幸福吗?为什么人们要经历所有的痛苦和艰难?是为了通过某种方式达到快乐吗?
我不是唯一相信生活的目的是幸福的人。环顾四周,大多数人的生活都在追求幸福。
3 月 20 日,全世界庆祝国际幸福日。2020 年的报告根据公民对自己生活的评价,对 156 个国家的幸福程度进行了排名。国民幸福排名是基于坎特里尔阶梯调查。全国范围内有代表性的受访者被要求想出一个阶梯,对他们来说最好的生活可能是 10,最差的可能是 0。然后他们被要求在 0 到 10 的范围内给自己目前的生活打分。该报告将结果与各种生活因素联系起来。在这些报告中,经济学、心理学、调查分析和国家统计方面的专家描述了如何有效地使用福祉测量来评估国家的进步和其他主题。
那么,今天的人们有多幸福呢?以前的人是不是更舒服?不同社会的人对自己的生活有多满意?我们的生活条件是如何影响这一切的?
分析的特征
- GDP :人均 GDP 是衡量一个国家经济产出占其人口数量的指标。
- 支持:社会支持意味着有朋友和其他人,包括家人,在需要或危机的时候向你求助,给你更广泛的关注和积极的自我形象。社会支持提高了生活质量,并为不良生活事件提供了缓冲。
- 健康:健康预期寿命是指新生儿可以预期生活在“完全健康”状态下的平均年数——换句话说,不受致残疾病或伤害的影响。
- **自由:**选择的自由描述了个人从至少两个可用选项中选择行动的机会和自主性,不受外部各方的约束。
- 慷慨度:被定义为对“你在过去几个月里是否向慈善机构捐过钱?”这一问题的全国平均回答进行回归后的残差论人均 GDP。
- 腐败:清廉指数(CPI)是透明国际自 1995 年以来每年发布的一项指数,它“根据专家评估和民意调查确定的公共部门腐败程度”对各国进行排名
大纲:
- 导入模块,读取数据集并定义评估表
- 定义一个函数来计算调整后的 R
- 幸福分数是如何分布的
- 不同特征与幸福得分的关系。
- 可视化和检查数据
- 多元线性回归
- 结论
给自己拿杯咖啡,和我一起踏上预测幸福的旅程吧!
1.导入模块,读取数据集并定义评估表
为了进行一些分析,我们需要设置我们的环境。首先,我们介绍一些模块并读取数据。下面的输出是数据的头,但是如果你想看到更多的细节,你可以去掉df_15.describe()
和df_15.info()
前面的#号
# FOR NUMERICAL ANALYTICS
import numpy as np# TO STORE AND PROCESS DATA IN DATAFRAME
import pandas as pd
import os# BASIC VISUALIZATION PACKAGE
import matplotlib.pyplot as plt# ADVANCED PLOTING
import seaborn as seabornInstance# TRAIN TEST SPLIT
from sklearn.model_selection import train_test_split# INTERACTIVE VISUALIZATION
import chart_studio.plotly as py
import plotly.graph_objs as go
import plotly.express as px
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
init_notebook_mode(connected=True)import statsmodels.formula.api as stats
from statsmodels.formula.api import ols
from sklearn import datasets
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from discover_feature_relationships import discover#2015 data
df_15 = pd.read_csv('2015.csv')
#df_15.describe()
#df_15.info()
usecols = ['Rank','Country','Score','GDP','Support',
'Health','Freedom','Generosity','Corruption']
df_15.drop(['Region','Standard Error', 'Dystopia Residual'],axis=1,inplace=True)
df_15.columns = ['Country','Rank','Score','Support',
'GDP','Health',
'Freedom','Generosity','Corruption']
df_15['Year'] = 2015 #add year column
df_15.head()
输出
我只呈现 2015 年的数据代码作为例子;你可以在其他年份做类似的事情。
以Happiness
、Whisker
、Dystopia
开头的零件。Residual
目标不同。将每个国家的分数与世界上最不幸福的国家进行比较。由于这些年的数据有一点不同的命名约定,我们将把它们抽象成一个通用名称。
target = ['Top','Top-Mid', 'Low-Mid', 'Low' ]
target_n = [4, 3, 2, 1]df_15["target"] = pd.qcut(df_15['Rank'], len(target), labels=target)
df_15["target_n"] = pd.qcut(df_15['Rank'], len(target), labels=target_n)
然后我们将所有数据文件合并到finaldf
# APPENDING ALL TOGUETHER
finaldf = df_15.append([df_16,df_17,df_18,df_19])
# finaldf.dropna(inplace = True)#CHECKING FOR MISSING DATA
finaldf.isnull().any()# FILLING MISSING VALUES OF CORRUPTION PERCEPTION WITH ITS MEAN
finaldf.Corruption.fillna((finaldf.Corruption.mean()), inplace = True)
finaldf.head(10)
通过使用 describe()函数,我们可以看到数据集的统计细节:
进一步,我们定义一个空的dataframe
。这个dataframe
包括Root Mean Squared Error (RMSE)
、R-squared
、Adjusted R-squared
、mean of the R-squared values obtained by the k-Fold Cross-Validation
,是比较不同车型的必备指标。R 平方值越接近 1,RMSE 越小,表示拟合越好。在接下来的部分中,我们将用结果填充这个dataframe
。
evaluation = pd.DataFrame({'Model':[],
'Details':[],
'Root Mean Squared Error (RMSE)': [],
'R-squared (training)': [],
'Adjusted R-squared (training)': [],
'R-squared (test)':[],
'Adjusted R-squared(test)':[],
'5-Fold Cross Validation':[]
})
2.定义一个函数来计算调整后的 R
当特征数量增加时,R 平方增加。有时,更强大的评估工具更适合用来比较不同模型之间的性能。该评估器称为调整后的 R 平方,只有当变量的增加降低了 MSE 时,它才会增加。调整后的 R 的定义是:
3.幸福分数是如何分布的
正如我们在下面看到的,幸福分数的值在 2.85 以上和 7.76 以下。所以没有一个国家的幸福指数高于 8。
4.不同特征与幸福得分的关系
我们要预测幸福得分,所以我们这里的因变量是Score
其他特征如GPD
Support
Health
等。,是我们的自变量。
人均国内生产总值
我们首先使用散点图来观察变量之间的关系。
作者 Gif
'''Happiness score vs gdp per capital'''
px.scatter(finaldf, x="GDP", y="Score", animation_frame="Year",
animation_group="Country",
size="Rank", color="Country", hover_name="Country",
trendline= "ols")train_data, test_data = train_test_split(finaldf, train_size = 0.8, random_state = 3)
lr = LinearRegression()
X_train = np.array(train_data['GDP'],
dtype = pd.Series).reshape(-1,1)
y_train = np.array(train_data['Score'], dtype = pd.Series)
lr.fit(X_train, y_train)X_test = np.array(test_data['GDP'],
dtype = pd.Series).reshape(-1,1)
y_test = np.array(test_data['Score'], dtype = pd.Series)pred = lr.predict(X_test)#ROOT MEAN SQUARED ERROR
rmsesm = float(format(np.sqrt(metrics.mean_squared_error(y_test,pred)),'.3f'))#R-SQUARED (TRAINING)
rtrsm = float(format(lr.score(X_train, y_train),'.3f'))#R-SQUARED (TEST)
rtesm = float(format(lr.score(X_test, y_test),'.3f'))cv = float(format(cross_val_score(lr,finaldf[['GDP']],finaldf['Score'],cv=5).mean(),'.3f'))print ("Average Score for Test Data: {:.3f}".format(y_test.mean()))
print('Intercept: {}'.format(lr.intercept_))
print('Coefficient: {}'.format(lr.coef_))r = evaluation.shape[0]
evaluation.loc[r] = ['Simple Linear Regression','-',rmsesm,rtrsm,'-',rtesm,'-',cv]
evaluation
通过使用这些值和下面的定义,我们可以手动估计幸福分数。我们用于估计的方程称为假设函数,定义如下
我们还打印了简单线性回归的截距和系数。
让我们展示结果,好吗?
因为在简单回归中我们只有两个维度,所以很容易画出来。下图确定了简单回归的结果。它看起来不像是完美的拟合,但当我们处理真实世界的数据集时,获得理想的拟合并不容易。
seabornInstance.set_style(style='whitegrid')plt.figure(figsize=(12,6))
plt.scatter(X_test,y_test,color='blue',label="Data", s = 12)
plt.plot(X_test,lr.predict(X_test),color="red",label="Predicted Regression Line")
plt.xlabel("GDP per Captita", fontsize=15)
plt.ylabel("Happiness Score", fontsize=15)
plt.xticks(fontsize=13)
plt.yticks(fontsize=13)
plt.legend()plt.gca().spines['right'].set_visible(False)
plt.gca().spines['top'].set_visible(False)
人均 GDP(国家的经济)的关系与幸福得分有很强的正相关关系,即如果一个国家的人均 GDP 高于该国的幸福得分,也更有可能高。
支持
为了保持文章简短,我不会在这部分包含代码。代码类似于上面的 GDP 特性。我建议你自己努力实现。我会在本文末尾附上链接,以供参考。
国家的社会支持与幸福得分也有很强的正相关关系。所以,我们需要社会支持来获得幸福是有道理的。人们也有情绪,我们在社会环境中体验这些情绪。
健康预期寿命
健康的预期寿命与幸福指数有很强的正相关关系,也就是说,如果一个国家的预期寿命很高,它也可以有很高的幸福指数。快乐不仅仅是提高一个人的生活质量。它也可以增加我们生活的质量。如果我健康长寿,我也会很开心。你呢。
做出人生选择的自由
做出生活选择的自由与幸福指数成正相关。与拥有大量金钱相比,选择和自主与幸福更直接相关。它给了我们选择去追求生活的意义,去寻找刺激我们的活动。这是感到快乐的一个重要方面。
慷慨
慷慨与幸福分数之间存在脆弱的线性关系。为什么慈善和幸福得分没有直接关系?慷慨度得分是根据世界上向非营利组织捐赠最多的国家来计算的。不慷慨的国家并不意味着他们不快乐。
对腐败的看法
对腐败看法的分布是正确的,这意味着对腐败有高度看法的国家很少。这意味着该国大部分地区都存在腐败问题。
腐败特征如何影响幸福得分?
对腐败数据的看法高度扭曲,难怪这些数据的线性关系很弱。尽管如此,正如我们在散点图中看到的,大多数数据点都在左侧,大多数对腐败看法较低的国家的幸福指数都在 4 到 6 之间。感知分高的国家,幸福分也高,在 7 分以上。
5.可视化和检查数据
我们没有包含太多功能的大数据。因此,我们有机会绘制出它们中的大部分,并得出一些有用的分析结果。在应用模型之前绘制图表并检查数据是一个很好的做法,因为我们可能会检测到一些可能的异常值或决定进行归一化。这一步不是必须的,但是要知道数据总是有用的。我们从dataframe
的直方图开始。
# DISTRIBUTION OF ALL NUMERIC DATA
plt.rcParams['figure.figsize'] = (15, 15)
df1 = finaldf[['GDP', 'Health', 'Freedom',
'Generosity','Corruption']]h = df1.hist(bins = 25, figsize = (16,16),
xlabelsize = '10', ylabelsize = '10')
seabornInstance.despine(left = True, bottom = True)
[x.title.set_size(12) for x in h.ravel()];
[x.yaxis.tick_left() for x in h.ravel()]
接下来,为了让我们更直观地了解每个国家在世界排名报告中的排名,我们使用深蓝色代表在报告中排名最高的国家(即“最幸福”),而浅蓝色代表排名较低的国家。我们可以看到,欧洲和美洲地区的国家比亚洲和非洲地区的国家排名要高。
'''World Map
Happiness Rank Accross the World'''happiness_rank = dict(type = 'choropleth',
locations = finaldf['Country'],
locationmode = 'country names',
z = finaldf['Rank'],
text = finaldf['Country'],
colorscale = 'Blues_',
autocolorscale=False,
reversescale=True,
marker_line_color='darkgray',
marker_line_width=0.5)
layout = dict(title = 'Happiness Rank Across the World',
geo = dict(showframe = False,
projection = {'type': 'equirectangular'}))
world_map_1 = go.Figure(data = [happiness_rank], layout=layout)
iplot(world_map_1)
让我们来看看哪些国家在所分析的每个方面都有更好的定位。
fig, axes = plt.subplots(nrows=3, ncols=2,constrained_layout=True,figsize=(10,10))seabornInstance.barplot(x='GDP',y='Country',
data=finaldf.nlargest(10,'GDP'),
ax=axes[0,0],palette="Blues_r")
seabornInstance.barplot(x='Health' ,y='Country',
data=finaldf.nlargest(10,'Health'),
ax=axes[0,1],palette='Blues_r')
seabornInstance.barplot(x='Score' ,y='Country',
data=finaldf.nlargest(10,'Score'),
ax=axes[1,0],palette='Blues_r')
seabornInstance.barplot(x='Generosity' ,y='Country',
data=finaldf.nlargest(10,'Generosity'),
ax=axes[1,1],palette='Blues_r')
seabornInstance.barplot(x='Freedom' ,y='Country',
data=finaldf.nlargest(10,'Freedom'),
ax=axes[2,0],palette='Blues_r')
seabornInstance.barplot(x='Corruption' ,y='Country',
data=finaldf.nlargest(10,'Corruption'),
ax=axes[2,1],palette='Blues_r')
检验解释变量之间的相关性
mask = np.zeros_like(finaldf[usecols].corr(), dtype=np.bool)
mask[np.triu_indices_from(mask)] = Truef, ax = plt.subplots(figsize=(16, 12))
plt.title('Pearson Correlation Matrix',fontsize=25)seabornInstance.heatmap(finaldf[usecols].corr(),
linewidths=0.25,vmax=0.7,square=True,cmap="Blues",
linecolor='w',annot=True,annot_kws={"size":8},mask=mask,cbar_kws={"shrink": .9});
看起来GDP
、Health
和Support
与幸福指数密切相关。Freedom
与幸福指数有很好的相关性;然而,Freedom
与所有数据连接得相当好。Corruption
与幸福得分的相关性仍然一般。
超越简单的关联
在散点图中,我们可以看到GDP
、Health
和Support
与某些噪声呈线性相关。我们发现Corruption
的自相关在这里很迷人,这里一切都很可怕,但如果腐败程度很高,分布到处都是。它似乎只是一个阈值的负面指标。
我发现了一个令人兴奋的由 Ian Ozsvald 设计的软件包。它训练随机森林来预测彼此的特征,超越了简单的相关性。
# visualize hidden relationships in data
classifier_overrides = set()
df_results = discover.discover(finaldf.drop(['target', 'target_n'],axis=1).sample(frac=1),
classifier_overrides)
我们在这里使用热图来可视化我们的要素是如何在空间上聚集或变化的。
fig, ax = plt.subplots(ncols=2,figsize=(24, 8))
seabornInstance.heatmap(df_results.pivot(index = 'target',
columns = 'feature',
values = 'score').fillna(1).loc[finaldf.drop(
['target', 'target_n'],axis = 1).columns,finaldf.drop(
['target', 'target_n'],axis = 1).columns],
annot=True, center = 0, ax = ax[0], vmin = -1, vmax = 1, cmap = "Blues")
seabornInstance.heatmap(df_results.pivot(index = 'target',
columns = 'feature',
values = 'score').fillna(1).loc[finaldf.drop(
['target', 'target_n'],axis=1).columns,finaldf.drop(
['target', 'target_n'],axis=1).columns],
annot=True, center=0, ax=ax[1], vmin=-0.25, vmax=1, cmap="Blues_r")
plt.plot()
这变得更有趣了。腐败比支持更能预测幸福指数。可能是因为我们之前发现的“门槛”?
此外,虽然社会支持有很好的相关性,但它没有实质性的预测价值。我猜这是因为散点图中所有四分位数的分布都非常接近。
6.多元线性回归
在本文的“渴望”一节中,我们使用了一个简单的线性回归来检验幸福指数和其他特征之间的关系。我们发现不适合。为了改进这个模型,我们想增加更多的功能。现在,是时候创建一些复杂的模型了。
通过查看前面的部分,我们初步确定了特征,并在第一次多元线性回归中使用了它们。与简单回归一样,我们打印了模型用于预测的系数。然而,如果我们想手工计算,这次我们必须使用下面的预测定义。
我们创建一个具有所有特征的模型。
# MULTIPLE LINEAR REGRESSION 1
train_data_dm,test_data_dm = train_test_split(finaldf,train_size = 0.8,random_state=3)independent_var = ['GDP','Health','Freedom','Support','Generosity','Corruption']
complex_model_1 = LinearRegression()
complex_model_1.fit(train_data_dm[independent_var],train_data_dm['Score'])print('Intercept: {}'.format(complex_model_1.intercept_))
print('Coefficients: {}'.format(complex_model_1.coef_))
print('Happiness score = ',np.round(complex_model_1.intercept_,4),
'+',np.round(complex_model_1.coef_[0],4),'∗ Support',
'+',np.round(complex_model_1.coef_[1],4),'* GDP',
'+',np.round(complex_model_1.coef_[2],4),'* Health',
'+',np.round(complex_model_1.coef_[3],4),'* Freedom',
'+',np.round(complex_model_1.coef_[4],4),'* Generosity',
'+',np.round(complex_model_1.coef_[5],4),'* Corrption')pred = complex_model_1.predict(test_data_dm[independent_var])
rmsecm = float(format(np.sqrt(metrics.mean_squared_error(
test_data_dm['Score'],pred)),'.3f'))
rtrcm = float(format(complex_model_1.score(
train_data_dm[independent_var],
train_data_dm['Score']),'.3f'))
artrcm = float(format(adjustedR2(complex_model_1.score(
train_data_dm[independent_var],
train_data_dm['Score']),
train_data_dm.shape[0],
len(independent_var)),'.3f'))
rtecm = float(format(complex_model_1.score(
test_data_dm[independent_var],
test_data_dm['Score']),'.3f'))
artecm = float(format(adjustedR2(complex_model_1.score(
test_data_dm[independent_var],test_data['Score']),
test_data_dm.shape[0],
len(independent_var)),'.3f'))
cv = float(format(cross_val_score(complex_model_1,
finaldf[independent_var],
finaldf['Score'],cv=5).mean(),'.3f'))r = evaluation.shape[0]
evaluation.loc[r] = ['Multiple Linear Regression-1','selected features',rmsecm,rtrcm,artrcm,rtecm,artecm,cv]
evaluation.sort_values(by = '5-Fold Cross Validation', ascending=False)
我们知道GDP
、Support
和Health
是线性相关的。这一次,我们创建一个具有这三个特征的模型。
# MULTIPLE LINEAR REGRESSION 2
train_data_dm,test_data_dm = train_test_split(finaldf,train_size = 0.8,random_state=3)independent_var = ['GDP','Health','Support']
complex_model_2 = LinearRegression()
complex_model_2.fit(train_data_dm[independent_var],train_data_dm['Score'])print('Intercept: {}'.format(complex_model_2.intercept_))
print('Coefficients: {}'.format(complex_model_2.coef_))
print('Happiness score = ',np.round(complex_model_2.intercept_,4),
'+',np.round(complex_model_2.coef_[0],4),'∗ Support',
'+',np.round(complex_model_2.coef_[1],4),'* GDP',
'+',np.round(complex_model_2.coef_[2],4),'* Health')pred = complex_model_2.predict(test_data_dm[independent_var])
rmsecm = float(format(np.sqrt(metrics.mean_squared_error(
test_data_dm['Score'],pred)),'.3f'))
rtrcm = float(format(complex_model_2.score(
train_data_dm[independent_var],
train_data_dm['Score']),'.3f'))
artrcm = float(format(adjustedR2(complex_model_2.score(
train_data_dm[independent_var],
train_data_dm['Score']),
train_data_dm.shape[0],
len(independent_var)),'.3f'))
rtecm = float(format(complex_model_2.score(
test_data_dm[independent_var],
test_data_dm['Score']),'.3f'))
artecm = float(format(adjustedR2(complex_model_2.score(
test_data_dm[independent_var],test_data['Score']),
test_data_dm.shape[0],
len(independent_var)),'.3f'))
cv = float(format(cross_val_score(complex_model_2,
finaldf[independent_var],
finaldf['Score'],cv=5).mean(),'.3f'))r = evaluation.shape[0]
evaluation.loc[r] = ['Multiple Linear Regression-2','selected features',rmsecm,rtrcm,artrcm,rtecm,artecm,cv]
evaluation.sort_values(by = '5-Fold Cross Validation', ascending=False)
当我们看评估表时,多元线性回归-2(选定特征)是最好的。但是,我怀疑它的可靠性,我更喜欢所有元素的多元线性回归。
X = finaldf[[ 'GDP', 'Health', 'Support','Freedom','Generosity','Corruption']]
y = finaldf['Score']'''
This function takes the features as input and
returns the normalized values, the mean, as well
as the standard deviation for each feature.
'''
def featureNormalize(X):
mu = np.mean(X) ## Define the mean
sigma = np.std(X) ## Define the standard deviation.
X_norm = (X - mu)/sigma ## Scaling function.
return X_norm, mu, sigmam = len(y) ## length of the training data
X = np.hstack((np.ones([m,1]), X)) ## Append the bias term (field containing all ones) to X.
y = np.array(y).reshape(-1,1) ## reshape y to mx1 array
theta = np.zeros([7,1]) ## Initialize theta (the coefficient) to a 3x1 zero vector.'''
This function takes in the values for
the training set as well as initial values
of theta and returns the cost(J).
'''def cost_function(X,y, theta):
h = X.dot(theta) ## The hypothesis
J = 1/(2*m)*(np.sum((h-y)**2)) ## Implementing the cost function
return J'''
This function takes in the values of the set,
as well the intial theta values(coefficients), the
learning rate, and the number of iterations. The output
will be the a new set of coefficeients(theta), optimized
for making predictions, as well as the array of the cost
as it depreciates on each iteration.
'''num_iters = 2000 ## Initialize the iteration parameter.
alpha = 0.01 ## Initialize the learning rate.
def gradientDescentMulti(X, y, theta, alpha, iterations):
m = len(y)
J_history = []
for _ in range(iterations):
temp = np.dot(X, theta) - y
temp = np.dot(X.T, temp)
theta = theta - (alpha/m) * temp
J_history.append(cost_function(X, y, theta)) ## Append the cost to the J_history array
return theta, J_historyprint('Happiness score = ',np.round(theta[0],4),
'+',np.round(theta[1],4),'∗ Support',
'+',np.round(theta[2],4),'* GDP',
'+',np.round(theta[3],4),'* Health',
'+',np.round(theta[4],4),'* Freedom',
'+',np.round(theta[5],4),'* Generosity',
'+',np.round(theta[6],4),'* Corrption')
打印出 J _ history 大约是 0.147。我们可以得出结论,使用梯度下降给了我们一个预测幸福分数的最佳模型。
我们也可以用statsmodels
来预测幸福得分。
# MULTIPLE LR
import statsmodels.api as smX_sm = X = sm.add_constant(X)
model = sm.OLS(y,X_sm)
model.fit().summary()
我们的系数非常接近我们在这里得到的。
这篇笔记中的代码可以在 Github 上找到。
7.结论
似乎对“世界幸福报告”的普遍批评是很有道理的。高度关注国内生产总值和密切相关的特征,如家庭和预期寿命。
高 GDP 让你幸福吗?最终,我们就是我们。我用我的七月来分析为什么人们不满足或者没有过上充实的生活。我关心的是“为什么”在分析完数据之后,就提出了一个问题:如何才能追逐幸福?
来源:迷因
就在几个月前,自从锁定以来,我做了一切去追逐幸福。
- 我把自己定义为极简主义者。但是我开始购买很多东西,我认为这让我很开心。
- 我在社交媒体上花了很多时间,与他人保持联系,我希望这能让我开心。
- 我开始在媒介上写作;我找到了与我的技能和兴趣相匹配的志愿者工作,我相信金钱和工作让我快乐。
- 我试着吃素,我希望这能让我开心。
一天结束的时候,我独自躺在床上,想着,“幸福可以实现吗?这种对幸福无止境的追求下一步是什么?”
嗯,我意识到我在追逐一些我自己认为能让我快乐的东西。
在写这篇文章的时候,我看到了拉尔夫·沃尔多·爱默生的一句名言:
“生活的目的不是快乐。它是有用的,是可敬的,是富有同情心的,是让你活着并过得好的一些不同之处。”
圆点终于连上了。幸福本身不能成为目标。它仅仅是有用的副产品。
让我开心的是我有用的时候。好吧,但是怎么做?有一天我醒来,心想:“我在为这个世界做什么?”答案是什么都没有。同一天,我开始在媒体上写作。我把我的学校笔记变成了文章,希望人们在阅读后能学到一两件事。对你来说,它可以是任何事情,比如绘画,创造一个产品,支持你的家人和朋友,任何你想做的事情。请不要太认真。还有最重要的,不要想多了。做点有用的事就好。任何事。
保持安全和健康!
资源:
- 【2020 年世界幸福报告。
- Kaggle 世界幸福报告。
- 2020 年世界幸福国家。
- 设计一个幸福预测模型。