在这个科技发展突飞猛进的时代,行业的宠儿与弃儿就如同手掌的两面,只需轻轻一翻,从业者的境遇便会有天翻地覆的改变。前端
人工智能做为近两年来业界公认的热门领域,不一样于以前火热的移动端开发或前端开发,其距离传统软件开发行业之远,入门门槛之高,都是以往未曾出现过的,这也让许多但愿可以终身学习,持续关注行业发展的软件工程师们望而却步。git
在咱们进一步讨论阻止传统软件工程师向人工智能领域转型的障碍以前,让咱们先来明确几个名词的定义:github
那么究竟是哪些障碍阻止了传统软件工程师进入人工智能领域呢?后端
为了解决上面提到的这两个障碍,笔者使用 TypeScript 以零依赖的方式初步完成了一个基于 JavaScript 的深度学习框架:deeplearning-js,但愿能够以 Web 开发者熟悉的语言与生态为各位提供一种门槛更低的深度学习的入门方式,并将以写给 Web 开发者的深度学习教程这一系列文章,帮助各位理解深度学习的基本思路以及其中涉及到的数学概念。数组
src/
├── activationFunction // 激活函数
│ ├── index.ts
│ ├── linear.spec.ts
│ ├── linear.ts
│ ├── linearBackward.spec.ts
│ ├── linearBackward.ts
│ ├── relu.spec.ts
│ ├── relu.ts
│ ├── reluBackward.spec.ts
│ ├── reluBackward.ts
│ ├── sigmoid.spec.ts
│ ├── sigmoid.ts
│ ├── sigmoidBackward.spec.ts
│ ├── sigmoidBackward.ts
│ ├── softmax.spec.ts
│ ├── softmax.ts
│ ├── softmaxBackward.spec.ts
│ └── softmaxBackward.ts
├── costFunction // 损失函数
│ ├── crossEntropyCost.spec.ts
│ ├── crossEntropyCost.ts
│ ├── crossEntropyCostBackward.spec.ts
│ ├── crossEntropyCostBackward.ts
│ ├── index.ts
│ ├── quadraticCost.spec.ts
│ ├── quadraticCost.ts
│ ├── quadraticCostBackward.spec.ts
│ └── quadraticCostBackward.ts
├── data // 数据结构:矩阵 & 标量
│ ├── Array2D.spec.ts
│ ├── Array2D.ts
│ ├── Scalar.spec.ts
│ ├── Scalar.ts
│ └── index.ts
├── index.ts
├── math // 计算:矩阵计算函数 & 生成随机数矩阵 & 生成零矩阵
│ ├── add.spec.ts
│ ├── add.ts
│ ├── divide.spec.ts
│ ├── divide.ts
│ ├── dot.spec.ts
│ ├── dot.ts
│ ├── index.ts
│ ├── multiply.spec.ts
│ ├── multiply.ts
│ ├── randn.spec.ts
│ ├── randn.ts
│ ├── subtract.spec.ts
│ ├── subtract.ts
│ ├── transpose.spec.ts
│ ├── transpose.ts
│ ├── zeros.spec.ts
│ └── zeros.ts
├── model // 模型:初始化参数 & 正向传播 & 反向传播 & 更新参数
│ ├── Cache.ts
│ ├── backPropagation.ts
│ ├── forwardPropagation.ts
│ ├── index.ts
│ ├── initializeParameters.spec.ts
│ ├── initializeParameters.ts
│ ├── train.ts
│ └── updateParameters.ts
├── preprocess // 数据预处理:数据标准化
│ ├── index.ts
│ └── normalization
│ ├── index.ts
│ ├── meanNormalization.spec.ts
│ ├── meanNormalization.ts
│ ├── rescaling.spec.ts
│ └── rescaling.ts
└── utils // 帮助函数:数据结构转换 & 矩阵广播
├── broadcasting.spec.ts
├── broadcasting.ts
├── convertArray1DToArray2D.ts
├── convertArray2DToArray1D.ts
└── index.ts
复制代码
做为一个专一于深度学习自己的框架,deeplearning-js 只负责构建及训练深度学习模型,使用者可使用提供的 API 在任意数据集的基础上搭建深度学习模型并得到训练后的结果,具体的例子各位能够参考 Logistic regression。bash
咱们将学习率,迭代次数,隐藏层神经元个数等这些超参数暴露给终端用户,deeplearning-js 会自动调整模型,给出不一样的输出。基于这些输出,咱们就能够自由地使用任意图表或可视化库来展示模型训练后的结果。数据结构
另外,你们在阅读本系列文章的同时,建议配合着 deeplearning-js 的源码一块儿阅读,相信这样的话,你将会对深度学习到底在作一件什么样的事情有一个更感性的认识。架构
不一样于其余的机器学习教程,咱们并不但愿在一开始就将大量拗口的数学名词及概念灌输给你们,相反,咱们将从训练深度学习模型的第一步数据处理讲起。框架
让咱们以学术界很是著名的 Iris 数据集为例。机器学习
如今咱们拥有了 150 个分别属于 3 个品种的鸢尾属植物的花萼长度,宽度及花瓣长度,宽度的样本数据,目的是训练一个输入任意一个鸢尾属植物的花萼长度,宽度及花瓣长度,宽度,判断它是不是这 3 个品种中的某一个品种,即逻辑回归。
虽然咱们的最终模型是输入任意一个样本数据获得结果,但咱们在训练时,并不但愿每次只可以输入一个样本数据,而是但愿一次性地输入全部样本数据,获得训练结果与实际结果的差值,而后使用反向传播来修正这些差别。
因而咱们就须要将多个样本数据组合成一个矩阵,以下图所示:
在将数据向量化后,咱们才有了处理大数据集的能力,即在整个数据集上而不是在某个数据样本上训练模型。这也是为何在深度学习领域,GPU 比 CPU 要快得多的缘由。在训练深度学习模型时,全部的计算都是基于矩阵的,因而并行计算架构(处理多任务时计算时间等于最复杂任务的完成时间)的 GPU 就要比串行计算架构(处理多任务时计算时间等于全部任务运行时间的总和)的 CPU 快得多。
细心的读者可能会观察到上图中的一个数据样本中的不一样维度的数据是竖排列的,这与传统数组中数据的横排列方式刚好相反,即咱们须要将
[5.1, 3.5, 1.4, 0.2]复制代码
转换为
[
[5.1],
[3.5],
[1.4],
[0.2],
]复制代码
细心的读者可能又会问了,如 Iris 数据集,为何必定要将初始数据转换为 4 行 150 列的矩阵,用方便处理的 150 行 4 列的矩阵不能够吗?
对于这个问题有如下两方面的考虑。在接下来输入数据与隐藏层作矩阵点乘时
隐藏层矩阵(W)的列数须要等于输入层(A)的行数,因此为了减小没必要要的计算量,咱们但愿输入层的行数尽量得小,因而咱们将数据样本的维度数与样本数量进行对比,不可贵出在绝大多数状况下,数据样本的维度数都远远小于样本数量这个结论。另外一方面,在点乘以后,结果矩阵的列数将等于输入层的列数,也就是说若是咱们但愿咱们的输出是一个 [X, 150] 的矩阵,输入层就须要是一个 [4, 150] 的矩阵。
那么如何快速地在原始数据集与使用数据集之间进行这样的转换呢?这就涉及到矩阵的一个经常使用运算,矩阵转置了。
提及矩阵,它的许多奇怪的特性,如转置,点乘等,想必是许多朋友大学时代的噩梦。在这里咱们不谈具体的数学概念,先尝试用几句话来描述一下矩阵及它的基础运算。
从最直观的角度来说,肯定一个矩阵须要哪些信息?一是矩阵的形状,即坐标系(空间),二是矩阵在这个坐标系下各个维度上的值(位置)。
在 deeplearning-js 中咱们使用二维数组的数据结构来表示矩阵,对于上述运算的具体代码实现各位能够参考 Array2D。
一个简单的数据转换的例子以下:
function formatDataSet(dataset: Array<any>) {
const datasetSize = dataset.length;
let inputValues: Array<number> = [];
map(dataset, (example: {
"sepalLength": number,
"sepalWidth": number,
"petalLength": number,
"petalWidth": number,
"species": string,
}) => {
const input: any = omit(example, 'species');
inputValues = inputValues.concat(values(input));
});
const input = new Array2D(
[datasetSize, inputValues.length / datasetSize],
inputValues,
).transpose();
return input;
}复制代码
在理解了数据向量化及矩阵的概念后,相信你们已经能够将大样本量,以数组形式存储的数据转换为适合进行深度学习模型训练的大型矩阵了,接下来让咱们从如何初始化参数开始,一步步搭建咱们的第一个深度学习模型。