TensorFlow相关

机器学习

1
机器学习是对能通过经验自动改进的计算机算法的研究。用数据或以往经验,以优化计算机程序的性能标准。
1
2
3
4
5
6
7
8
人工神经网络:一种运算模型(输入输出的映射),由大量节点(神经元)互相连接构成。
神经网络 = 输入层(不用于计算神经网络层数) + 隐藏层(若干) + 输出层
神经元 = {
权重:输入参数的权重,
偏置:针对差异做出的调节,
激活函数:用于添加一些非线性的变换
}
输入乘上权重+偏置 经过激活函数的到输出

image-20210725134218513

1
2
神经网络训练:给大量的输入(特征)输出(标签),算出神经网络所有神经元的权重,偏置,然后给定输入可以算出新的输出。
eg:给1000组相亲对象的数据(特征)以及满意程度(标签),训练完后,给定新的相亲对象数据就可以判定该数据的满意程度。

训练神经网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1. 初始化
随机生成一些权重和偏置。

2. 计算损失
给定特征,计算出标签,得到与真实标签的误差。(损失函数)

3. 优化
微调权重和和偏置,使损失降低。使用优化器(重复2,3使损失最小,**从最后一层开始微调**,梯度求导……)

- 前向传播
将训练数据的特征送入网络,得到标签。

- [反向传播](https://www.zhihu.com/question/24827633)
计算损失并优化。

- 计算损失(使用损失函数)

TensorFlowJS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//Tensor:张量——向量和矩阵向更高维度的推广。(类似于多维数组)
import * as tf from "@tensorflow/tfjs";
let t0 = tf.tensor([1,2]);
t0.print();
console.log(t0);
let t1 = tf.tensor([[1,2,3],[3,4,5]]);
t1.print();
console.log(t1);
let t2 = tf.tensor([[[1]]]);
t2.print();
console.log(t2);
// 传统for循环
let input = [1,2,3,4];
let w = [[1,2,3,4],[2,3,4,5],[3,4,5,6],[4,5,6,7]];
let output = [0,0,0,0];
// output = 所有指标的权重相乘之和
// 1*1+2*2+3*3+4*4,2*1+3*2+4*3+5*4……
w.forEach((i,index1)=>{
input.forEach((j,index2)=>{
output[index1] += i[index2]*j;
})
})
console.log(output);
// tensor 进行n层for循环 dot() 点积 矩阵之间的运算
// 会对dot内的矩阵进行转置
tf.tensor(w).dot(tf.tensor(input)).print();
tf.dot(w,input);

线性回归

1
线性回归:利用数理统计中回归分析,来确定两种或以上变量相互依赖的定量关系的一种统计分析。(身高体重预测,房价预测等)
1
2
3
1. 初始化神经网络模型
2. 为神经网络模型添加层
3. 设计层的神经元个数和inputShape(输入形状)
1
均方误差(MSE 损失函数之一)

image-20210725135533341

1
2
3
4
随机梯度下降法(SGD 优化器之一)
SGD通常每次迭代一个样本 但在样本集数量过多的情况下会非常杂乱无章,因此在样本集数量非常多的情况下,使用小批量SGD。小批量 SGD 通常包含 10-1000 个随机选择的样本,可以减少 SGD 中的杂乱样本数量,但仍然比全批量更高效。
我们随机选取选取初始值w,使用 梯度x学习效率 来确定到下一个点的位置,重复上述操作,逐渐接近最低点。但是要注意学习效率的选取秒如果学习效率太大他会在最低点的两侧反复横跳,反之速度太慢会耗费过长的时间。
[代码示例](https://developers.google.cn/machine-learning/crash-course/fitter/graph)

image-20210725143611993

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1. 将训练数据转为Tensor
let inputs = tf.tensor(xs);
let labels = tf.tensor(ys);
2. 训练模型
await model.fit(inputs,labels,{
batchSize:4,//
epochs:100,
callbacks:tfvis.show.fitCallbacks(
{name:'训练过程'},
['loss'],
)
})
3. 用tfvis可视化模型
callbacks:tfvis.show.fitCallbacks(
{name:'训练过程'},
['loss'],
)

归一化

1
2
3
4
5
6
7
8
9
10
11
//把大数量级特征转换成小数量级下,[0,1]或[-1,1]。
let inputMax = heights.sort((a,b)=> b-a)[0]
let inputMin = heights.sort((a,b)=> b-a)[heights.length-1]
let labelMax = weights.sort((a,b)=> b-a)[0]
let labelMin = weights.sort((a,b)=> b-a)[weights.length-1]

let inputs = tf.tensor(height).sub(inputMin).div(inputMax-inputMin);
let labels = tf.tensor(weight).sub(labelMin).div(labelMax-labelMin);
// sub 矩阵减 div 矩阵除 mul 矩阵乘 add 矩阵加
// 需要预测的输入数据也需要归一化
let output = model.predict(tf.tensor([175]).sub(inputMin).div(inputMax-inputMin));

逻辑回归

1
2
3
4
5
6
7
8
9
10
11
12
13
1. 加载二分类数据集
tfvis.render.scatterplot(
{ name: '逻辑回归训练集' },
{
values:[
data.filter(p=>p.label === 1),
data.filter(p=>p.label === 0)
]
},
);
显示两种点需要在values如上设置
2. 定义带有激活函数的单个神经元
3. 训练模型并预测

Box–Muller Transform

直角坐标下:
直角坐标

极坐标下:

极坐标

1
2
3
4
5
6
7
8
9
10
11
12
[Box-Muller](https://blog.csdn.net/weixin_41793877/article/details/84700875)
do{
// 生成-1~1之间的随机数 即极坐标下的某一点的坐标
v1 = 2*Math.random() - 1;
v2 = 2*Math.random() - 1;
s = v1 *v1 + v2*v2; //斜边的平方
} while(s > 1);
//如果s>1就一直循环 当跳出循环则说明s<1 确保s是在单位圆内
Box-Muller公式 均值为0,方差为1 mean = 0 variance = 1
let result = Math.sqrt(-2 * Math.log(s) / s)* v1;
// 转成mean = mean variance为1的正态分布
return mean + Math.sqrt(variance) * result;

多层神经网络

1
XOR模型 使用多层神经网络配合激活函数进行模型预测

image-20210726211555115

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
其实没啥 就是根据需要预测数据选择神经元的个数以及隐藏层的层数。选取合适的激活函数——relu/sigmoid……
let model = tf.sequential();
// 隐藏层
model.add(tf.layers.dense({
units:4,// 四个神经元
inputShape:[2],
activation:"relu" // 用于非线性的激活函数
}))
// 输出层
model.add(tf.layers.dense({
units:1,
activation:"sigmoid" // 输出0-1之间的概率
}))
model.compile({
loss:tf.losses.logLoss,
optimizer:tf.train.adam(0.1)
})

多分类任务

1
2
训练集(train)和验证集(test)
当我们使用神经网络进行模型构建的时候,我们使用训练集进行训练,而验证集则是从训练集中抽取的部分典型的样本,在我们训练模型时,如果对训练集训练得出的损失较小而验证集却损失非常大,则说明当前神经网络模型存在问题需要改进。
1
2
交叉熵损失函数
交叉熵能够衡量同一个随机变量中的两个不同概率分布的差异程度,在机器学习中就表示为真实概率分布与预测概率分布之间的差异。交叉熵的值越小,模型预测效果就越好。

image-20210727115411350

1
2
3
4
5
重排数据
tf.util.shuffle(data);
随机排列提供给训练算法的样本的顺序。数据重排很重要,因为在训练期间,数据集通常会被拆分成较小的子集(称为批次),以用于训练模型。借助重排,每个批次可从分布的所有数据中获取各种数据。通过这样做,我们可以帮助模型:
-不学习纯粹依赖于数据输入顺序的东西
-对子组中的结构不敏感(例如,如果模型在训练的前半部分仅看到高马力汽车,可能会学习一种不适用于数据集其余部分的关系)。

欠拟合/过拟合

img

1
2
3
4
5
6
7
8
9
10
11
12
应对过拟合:
1.L1正则化
我们经常听到这样的话:“L1正则化是为了产生稀疏矩阵”,L1正则化的实现是在loss函数后面添加一个权重绝对值之和的项,该和再乘以一个系数,这个系数就是L1正则化的系数。这样做可以使得一些权重的值为0,降低了模型的复杂度,从而控制过拟合。
2.L2正则化(权重衰减)
kernelRegularizer:tf.regularizers.l2({l2:1})
L2正则化跟L1正则化的区别在于loss函数后加的是权重^2之和,该和再乘以一个系数,这个系数就是L2正则化的系数。这样做可以使得一些高次方项的权重为0,从而也降低了模型复杂度,控制了过拟合。以下是L2正则化的简单实现
return (w**2).sum()/2
3.早停法
早停是在训练过程中所采用的方法,在训练模型的时候观察验证集上的表现,如果验证集上的loss开始上升的时候,停止训练模型,从而阻止了模型进一步变得复杂
4.丢弃法
model.add(tf.layers.dropout({ rate: 0.9 }));
丢弃法一般用于全连接神经网络,它指的是一些神经元不继续传递其值,从而活动的神经元的数量变少,减少了模型的复杂度。

卷积神经网络

1
2
3
与普通神经网络对比
普通的神经网络如果要提取一个图片的特征。以一个200x200的图片为例,需要提取200x200x3 = 12000 个特征。
卷积神经网络可以模拟人类的视觉处理流程,高效提取特征。
1
2
3
4
卷积层
卷积神经网络中每层卷积层由若干卷积单元组成,每个卷积单元的参数都是通过反向传播算法最佳化得到的。卷积运算的目的是提取输入的不同特征,第一层卷积层可能只能提取一些低级的特征如边缘、线条和角等层级,更多层的网路能从低级特征中迭代提取更复杂的特征。
- 卷积运算
通过卷积核(filter/kernel)对图片特征进行提取。

image-20210728121819215

image-20210728121908287

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
池化层
池化层夹在连续的卷积层中间, 用于压缩数据和参数的量,减小过拟合。简而言之,如果输入是图像的话,那么池化层的最主要作用就是压缩图像。
下采样层也叫池化层,其具体操作与卷积层的操作基本相同,只不过下采样的卷积核为只取对应位置的最大值、平均值等(最大池化、平均池化),即矩阵之间的运算规律不一样,并且不经过反向传播的修改。
池化层的作用:
1. invariance(不变性),这种不变性包括translation(平移),rotation(旋转),scale(尺度)
2. 保留主要的特征同时减少参数(降维,效果类似PCA)和计算量,防止过拟合,提高模型泛化能力

A: 特征不变性,也就是我们在图像处理中经常提到的特征的尺度不变性,池化操作就是图像的resize,平时一张狗的图像被缩小了一倍我们还能认出这是一张狗的照片,这说明这张图像中仍保留着狗最重要的特征,我们一看就能判断图像中画的是一只狗,图像压缩时去掉的信息只是一些无关紧要的信息,而留下的信息则是具有尺度不变性的特征,是最能表达图像的特征。

B. 特征降维,我们知道一幅图像含有的信息是很大的,特征也很多,但是有些信息对于我们做图像任务时没有太多用途或者有重复,我们可以把这类冗余信息去除,把最重要的特征抽取出来,这也是池化操作的一大作用

(1) translation invariance:
这里举一个直观的例子(数字识别),假设有一个16x16的图片,里面有个数字1,我们需要识别出来,这个数字1可能写的偏左一点(图1),这个数字1可能偏右一点(图2),图1到图2相当于向右平移了一个单位,但是图1和图2经过max pooling之后它们都变成了相同的8x8特征矩阵,主要的特征我们捕获到了,同时又将问题的规模从16x16降到了8x8,而且具有平移不变性的特点。图中的a(或b)表示,在原始图片中的这些a(或b)位置,最终都会映射到相同的位置。
(2) rotation invariance:
下图表示数字“0”的识别,第一张的“0”比较大,第二张的“0”进行了较小,相当于作了缩放,同样地,经过多次max pooling后具有相同的特征
池化层用的方法有Max pooling 和 average pooling,而实际用的较多的是Max pooling。这里就说一下Max pooling,其实思想非常简单。
对于每个2*2的窗口选出最大的数作为输出矩阵的相应元素的值,比如输入矩阵第一个2*2窗口中最大的数是6,那么输出矩阵的第一个元素就是6,如此类推。

img

img

img

1
全连接层 跟普通神经网络类似。
1
对训练好的模型进行评估
1
2
3
4
5
6
7
8
9
10
11
12
13
const classNames = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine'];
//做出预测
function doPrediction(model, data, testDataSize = 500) {
const IMAGE_WIDTH = 28;
const IMAGE_HEIGHT = 28;
const testData = data.nextTestBatch(testDataSize);
const testxs = testData.xs.reshape([testDataSize, IMAGE_WIDTH, IMAGE_HEIGHT, 1]);
const labels = testData.labels.argMax(-1);
const preds = model.predict(testxs).argMax(-1);
testxs.dispose();
//当使用WebGL后端时, tf.Tensor的内存必须以显式管理。这是因为WebGL不足以让tf.Tensor超出生命周期后内存被自动释放。您可以使用dispose方法或是tf.dispose方法用以释放tf.Tensor所占用的内存
return [preds, labels];
}
1
2
3
4
5
6
7
8
9
10
11
12
//显示每个类的准确率
async function showAccuracy(model) {
let data = new MnistData();
await data.load();
const [preds, labels] = doPrediction(model, data);
//在预测和标签之间计算每个类别精度。标签和预测中的每个值都应对应于某些输出类 返回每个对象数组,每个对象都有一个准确性和每个类的计数属性
const classAccuracy = await tfvis.metrics.perClassAccuracy(labels, preds);
const container = { name: 'Accuracy', tab: 'Evaluation' };
//为分类任务评估呈现每类精度表
tfvis.show.perClassAccuracy(container, classAccuracy, classNames);
labels.dispose();
}
1
2
3
4
5
6
7
8
9
10
11
//显示混淆矩阵
async function showConfusion(model) {
let data = new MnistData();
await data.load();
const [preds, labels] = doPrediction(model, data);
const confusionMatrix = await tfvis.metrics.confusionMatrix(labels, preds);
const container = { name: 'Confusion Matrix', tab: 'Evaluation' };
tfvis.render.confusionMatrix(container, { values: confusionMatrix, tickLabels: classNames });
labels.dispose();
}
//混淆矩阵与每个类的准确率相似,但会进一步细分以显示错误分类的模式。借助混淆矩阵,您可以了解模型是否对任何特定的类对感到困惑。

加载预训练模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
在浏览器加载模型文件
let bin = document.getElementById('bin');
let json = document.getElementById('json');
const model = await tf.loadLayersModel(tf.io.browserFiles(
[json.files[0], bin.files[0]]
));

后端直接加载模型
console.log('Loading mobilenet..');
// Load the model.
let net = await mobilenet.load();
console.log('Successfully loaded model');
// Make a prediction through the model on our image.
const imgEl = document.getElementById('img');
const result = await net.classify(imgEl);
text.innerHTML = result[0].className;

let imgFile = document.getElementById('imgFile');
let img = await file2img(imgFile.files[0]);
const input = tf.tidy(() => {
return tf.browser.fromPixels(img)
.toFloat()
.sub(255 / 2)
.div(255 / 2)
.reshape([1, 224, 224, 3]);
});
let pred = (await model).predict(input)
const index = pred.argMax(1).dataSync()[0];
let text = document.getElementById('text');
text.innerHTML = IMAGENET_CLASSES[index];

迁移学习

1
2
3
4
5
1.加载模型并截断
2.构建神经网络
3,截断模型作为输入,神经网络作为输出

**在迁移学习完成之后,对模型进行使用(预测)时,其输入数据也是要经过截断模型进行处理,然后在将截断模型的输出作为输入传给迁移的模型。**

TensorFlow相关
https://jing-jiu.github.io/jing-jiu/2021/12/05/其他/TensorFlow/
作者
Jing-Jiu
发布于
2021年12月5日
许可协议