MindSpore是一款由华为开发的新的机器学习框架。我们搞机器学习的都知道,目前国内三大主流框架为Pytorch、TensorFlow、百度开发的Paddle。其中Pytorch和TensorFlow均是由国外开发,MindSpore汲取了前两者视图处理的经验和不足,又开发新的框架,不知道使用效果如何,在之前,本博主是pytorch忠实爱好者,因为其简单的书写方式和调用方式上手比较好,希望这篇博客更新完,我能喜欢上华为开发的MindSpore,毕竟是国产的,咱们要科技文化自信。
我以前用python手撕神经网络编写,发现从零开始写一个比较好的神经网络很难,毕竟开发一个优秀的神经网络需要的是研究团队几年甚至几十年的尝试,单枪匹马只能望而兴叹,因此我开始使用框架,因为这些框架封装了很多常用的比较好的神经网络。唯一难受的可能就是我不能直接去改库,不然给别人也用不了,甚至可能把库给改崩了。
省流:Mindspore胜过其它框架的点是提供的分布式框架和下沉运算。而且它集成了pytorch的动态图和TensorFlow的静态图,代码风格偏向于pytorch,但是api和可读性没pytorch那么好。性能方面无法评价,因为没用那个昇腾系统测试过,也没有马内去买。目前Mindspore在Windows系统上只支持CPU版本,对于仅有Windows系统的普通用户而言不太友好。
机器学习前需知识:
在此之前先引入一个概念:模型。模型是什么?我之前在进行工程开发的时候,队员总以为模型就是一个实实在在的某个文件,例如yolo的pt文件,语言模型的pkt文件等文件。其实不一定,模型的定义很广泛,你可以将这些文件看作是模型,你甚至可以将一个个物理数学公式看作是一个模型,又或者是某一段概念看作是一个模型,例如软件开发经常涉及到的概念模型。所以模型无法具体到给出它是什么,但是能确定的是,它是为了解决某一个问题所创造出来的一种“方法集合”。我为了将一个问题解决,我定义了很多方法,但为了让这些方法统一起来,于是我便给这些方法集合取名为某种模型,所以模型里面包含了很多信息。例如机器学习的某个模型就包含了网络、输入形式、计算公式、参数等信息,这些信息又可以称做它们为解决某一个问题所采取的方法集合。
我再举一个更通俗的例子来解释模型。我最近遇到一个问题:如何蒸馒头?那么我就需要和面、发面、捏面团、放入蒸锅这四个步骤。那么模型就诞生了。这个模型叫蒸馒头模型,那么别人用我的这个蒸馒头模型,就知道用这四个方法去蒸馒头。之后我觉得这个蒸馒头模型太复杂了,我还可以发明一个蒸馒头机器,我称这个机器为蒸馒头模型,那么别人再用这个模型,只需要放入面粉就可以了,因为里面做馒头的四个步骤全交给机器自动完成。蒸馒头机器里面的构造就像模型中的神经网络一样帮你做事。
(该图片来源于百度图片)
激活函数详解:绿色部分为一个神经元,左边是输入向量x和权重矩阵w,输入到神经元的值是神经元里面那个公式求和得到的,因为神经元不能光输入,也得有输出。其输出值就是神经元的输入值经过激活函数运算后得到的值,当然激活函数有很多,例如常见的sigmoid函数。这一部分告诉我们,在利用代码构建网络时,层与层之间需要利用激活函数。
激活函数一般用于正向传播以此来产生输出。
那么问题来了?机器学习的“学习”究竟是学习的什么东西?观察激活函数部分就知道,输入向量x是由我们自己输入的,同时输出的值也可以通过公式计算得到。但唯独这个权重矩阵w不知道怎么来的,也不知道它有个啥作用,所以我们该怎么规定w呢?
解:一般来讲权重矩阵一般是随机产生的矩阵,那么意味着它是不具有可靠性的。如何将它变得可靠?这便是机器学习的目的:没错!就是不断训练,更新权重矩阵w。
更新权重矩阵的方法,利用损失函数将输出层的误差反向传给神经元,然后通过某种公式来更新权重矩阵w。
当机器学习结束后,权重矩阵w也就更新好了。产生的模型就可以使用了。
回归问题的损失函数为均方误差损失函数,分类问题的损失函数会选择交叉熵损失函数。
1:Tensor张量:一种特殊的数据结构,但是有特征。特征一:张量具有形状——张量的每个轴的长度,即元素数量。特征二:张量具有秩——张量的轴数,标量秩为0,向量秩为1,矩阵秩为2。特征三:张量具有轴——张量的一个特殊维度。特征四:张量的总项数,即乘积形状向量。
创建张量的方式:
可以通过创建数组生成Tensor,也可以通过numpy向量生成Tensor
import numpy as np
import mindspore
from mindspore import ops
from mindspore import Tensor, CSRTensor, COOTensor
data = [1, 0, 1, 0]
x_data = Tensor(data)
print(x_data, x_data.shape, x_data.dtype)
np_array = np.array(data)
x_np = Tensor(np_array)
print(x_np, x_np.shape, x_np.dtype)
结果为:后面(4,)相当于data向量的维度为4,因为里面包含了四个元素。
2:MindSpore训练模型的流程:
①:准备数据集:机器学习领域的数据集一般包括三项——训练集、验证集、测试集。这三个集合可以是各种各样的数据形式,例如图片(在计算机上就属于像素点构成的数据集合)、一串特殊数字或字符等。
标签值:机器学习领域中的含义是正确的值。
训练集:给机器进行学习时需要的数据(上面已解释,一大堆图片或者一大堆字符或数字),然后这一组数据集合会通过你写的代码转换为合适的输入形式后引入到你编写好的网络中。当每一轮训练结束后,将使用损失函数计算标签值与输出值之间的损失值,再根据损失值反向更新网络参数。标签值相当于题的答案,而网络相当于做题的人,如果答案不对就要看看脑子哪里想错了然后去更新想法。
验证集:验证集一般从训练集抽出来。不参与训练,用于在训练过程中检验模型的状态,收敛情况。通常用于调整超参数,根据几组模型验证集上的表现决定哪组超参数拥有最好的性能,避免模型过拟合或欠拟合,决定要不要停止训练。
测试集:当一个模型训练完成后。将使用这个模型在测试集上面进行检验,但此时只是返回检验效果,并不对模型做任何处理。因为在训练过程中,训练集和验证集是机器一直需要用到的。而测试集是机器学习时候接触不到的,因此可以用测试集反映这个模型的最终训练效果到底好不好,以此来判断你是否愿意接受这个模型。
②:设置训练时需要用到的几个重要参数(重点):
1:batch-size:设置训练轮次每次迭代的大小。举个例子:假如你的训练集里面有1024张图片,如果设置batch-size为64,则在每个轮次训练中就会以64为一个最小单位,1024/64=16,那么每一轮训练会有16次迭代才能训练完训练集。其大小设置会影响模型最终效果,不过具体设置为多少不能确定,需要自己尝试,唯一确定的是设置为2的指数倍是效果比较好的,如8,16,32,64...
2:resize:将图片设置为哪种大小格式,如果训练集是图片,那么机器学习一般会将所有图片变形为同一大小以便形成同一输入形式放入网络中进行训练。
3:network:设置你训练需要选择的网络,MindSpore提供了Lenet、CNN、RNN等网络。具体选择什么样的网络需要分析自己需要解决的问题属于哪种类型的问题,例如:时间序列问题还是分类问题,然后再看什么样的网络可以解决这种问题。例如RNN可以解决时间序列问题(如:通过时间预测股市变化。),CNN可以解决分类问题(如:著名的Yolo)。
4:net_loss:定义损失函数。损失函数的作用是计算你的训练数据通过网络之后得到的结果与真实标签值的差距有多大,常见的损失函数包含:交叉熵、直接作差等。具体选择哪种损失函数,需要自己尝试和通过看文章和问题进行分析。
5:net_opt:优化器。优化器通过计算损失的部分反向更新网络参数使其更准确,以此来达到对网络的训练目的,俗称:机器学习。因为一开始你的模型参数一定是不可靠或随机的(例如初始化时随机产生一个矩阵作为参数矩阵),所以需要进行优化来更新参数。优化器具有的种类比较多,可自主选择。重点:在优化器中需要设置学习率(learning_rate)这个参数,以此让训练效果最终收敛,控制更新幅度,一般学习率设置为0.01。所谓收敛就是学习效果逐渐稳定,若不设置,就相当于成绩不稳定,忽高忽低像坐过山车一样。
6:CheckpointConfig:设置训练过程中是否需要保存模型。意义是:一般机器学习可能是将所有轮次训练完之后再保存模型,MindSpore在训练过程中保存模型,一定程度上避免了程序意外终止,中间结果丢失,使得不得不重新开始。
7:ModelCheckpoint:保存CheckpointConfig配置的参数,并设置中间结果保存的路径。
8:Model:定义一个模型。用法举例Model(net_work,loss_fn=net_loss,optimizer=net_opt,metrics={'acc'})。这里将前面所有定义的参数方法都封装到一个模型之中,其中net_work代表你选择的网络,loss_fn代表你选择的损失函数,optimizer代表你选择的优化器,metrics代表你选择的性能指标(这里选取了accuracy来展示模型训练的好坏程度)。到此时,你已经创建了一个属于自己的模型。
补充:在机器学习中metrics(性能指标)包括精确率(Precision)、召回率(Recall)、准确率(Accuracy)。其中Precision代表预测的结果全不全,是不是都预测出来了。Recall代表预测地准不准,预测的对象是不是正确的。Accuracy代表预测正确的对象占所有对象的比率,一般Accuracy越高,说明模型越好。
9:model.train:开始训练模型。使用举例:model.train(1,dataset_train,callbacks=(ckpoint,LossMonitor(0.01)))。
到此就可以使用MindSpore简单训练一个模型了。
10:acc=model.eval(dataset_eval)这一行代码意味着在测试集用你的模型,看看Accuracy的值如何。
3:MindSpore进行数据处理
为了结合第一点和第二点的内容,接下来我将以cifar10数据集为例,利用MindSpore框架写机器学习。
该部分涉及了加载数据集。cifar10数据集建议自己从其它地方下载,MindSpore源码是从官网下载,下载要下一天。
from mindspore import Tensor
from mindspore import set_seed
from mindspore import dtype as mstype
from mindspore.common import initializer as init
from mindvision.dataset import Cifar10
import mindspore.dataset.vision.c_transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
trans=[transforms.HWC2CHW]
data_dir="./datasets"
#shuffle将数据集打乱,避免每次输入相同的一批图片,导致训练过拟合。
dataset=Cifar10(path=data_dir,split='train',batch_size=6,resize=32,shuffle=True,repeat_num=1)
dataset1=dataset.run()
data=next(dataset1.create_dict_iterator()) #将数据以迭代器的形式加载到内存
images=data["image"].asnumpy()
labels=data["label"].asnumpy()
plt.figure()
for i in range(1,7):
plt.subplot(2,3,i)
image_trans=np.transpose(images[i-1],(1,2,0))
plt.title(dataset.index2label[labels[i-1]])
plt.imshow(image_trans,interpolation="None")
plt.show()
效果如图:
4:利用MindSpore创建神经网络
LeNet-5 网络模型
分析这一张图:该网络模型总共有7层。C1、C3都代表着卷积层,S2、S4代表着下采样层(可通过池化操作完成图片缩小)、C5、S6代表着全连接层、output代表着输出层。可以看到,INPUT输入层是一张32×32的图片,然后通过卷积到了C1(C1特征是通道数为6,尺寸为28×28),然后通过下采样到了S2(S2的特征是通道数为6,尺寸为14×14,张量表示为6×14×14),然后通过卷积到了C3(C3特征是通道数为16,尺寸为10×10),然后通过下采样到了S4(S4特征是通道数为16,尺寸为5×5),然后到了全连接层C5(共120层),然后又到全连接层F6(共84层),最后到高斯连接层(共10层)。
LeNet-5网络建立代码:
import mindspore.nn as nn
class LeNet5(nn.Cell):
def __init__(self,num_class=10,num_channel=1):
super(LetNet5,self).__init__()
# 卷积层C1,输入的通道数为num channel,输出的通道数为6,卷积核大小为5*5
self.conv1 = nn.Conv2d(num_channel,6,5,pad_mode='vaild')
# 卷积层C3,输入的通道数为6,输出的通道数为16,卷积核大小为5*5
self.conv2 = nn.Conv2d(6,16,5,pad_mode='vaild')
# 全连接层C5,输入个数为16*5*5,输出个数为120
self.fc1=nn.Dense(16*5*5,120)
# 全连接层F6,输入个数为120,输出个数为84
self.fc2=nn.Dense(120,84)
# 输出层OUTPUT,输入个数为84,分类的个数为num_class
self.fc3=nn.Dense(84,num_class)
#ReLu激活函数
self.relu=nn.ReLU()
#池化层,stride代表着卷积核移动的步长
self.max_pool2d=nn.MaxPool2d(kernel_size=2,stride=2)
#多维数组展平为一维数组
self.flatten = nn.Flatten()
def construct(self,x):
# 使用定义好的运算构建前向网络
x=self.conv1(x)
x=self.relu(x)
x=self.max_pool2d(x)
x=self.conv2(x)
x=self.relu(x)
x=self.max_pool2d(x)
x=self.flatten(x)
x=self.fc1(x)
x=self.relu(x)
x=self.fc2(x)
x=self.relu(x)
x=self.fc3(x)
model = LeNet5()
此处有疑惑:如何将32*32尺寸的图片下采样成28*28的图片,关键在于conv1的卷积核移动,mindspore默认卷积核移动步长为1。解释如图:
(计算公式中如果用宽,公式就都用宽;用高,公式就都用高)
由上图公式可知:如果输入的图片的尺寸为32×32,卷积核为5×5,则有:
1 × n + 5 -1 ≤ 32 ,可得n=28,所以新的图片尺寸为28*28
此处以代码验证:
conv2d=nn.Conv2d(1,6,5,has_bias=False,weight_init='normal',pad_mode="valid")
input_x=Tensor(np.ones([1,1,32,32]),mstype.float32)
print(conv2d(input_x).shape)
输出结果为:
可见新图片的尺寸为28*28
5:配置信息(包括执行模式和硬件管理)
执行模式包括静态图模式(Graph):将神经网络模型编译成一整张图,然后下发到硬件执行。该模式利用图优化等技术提高运行性能,同时有助于规模部署和跨平台运行。
和动态图(PyNative)模式:将神经网络中的各个算子逐一下发到硬件中执行,该模式方便用户编写代码和调试神经网络模型。
# 设置动静态图
context.set_context(mode=context.PYNATIVE_MODE)
# 硬件管理,设置运行环境为GPU还是CPU还是昇腾
context.set_context(device_target="CPU",device_id=0)
二、MindSpore—API实践(按住ctrl再点击函数就有函数的英文解释)
1:dataset
import mindspore.dataset as ds
DATA_DIR='./datasets/MNIST_DATA/train'
mnist_dataset=ds.MnistDataset(DATA_DIR,num_samples=6,shuffle=False)
import matplotlib.pyplot as plt
mnist_it = mnist_dataset.create_dict_iterator()
data=next(mnist_it)
plt.imshow(data['image'].asnumpy().squeeze(),cmap=plt.cm.gray)
plt.title(data['label'].asnumpy(),fontsize=20)
plt.show()
效果:
2:ops
from mindspore import Tensor
import numpy as np
from mindspore import ops
from mindspore.common import dtype as mindspore
input_x=Tensor(np.ones([2,2]),mindspore.float32)
scale=Tensor(np.ones([2]),mindspore.float32)
bias=Tensor(np.ones([2]),mindspore.float32)
mean=Tensor(np.ones([2]),mindspore.float32)
variance=Tensor(np.ones([2]),mindspore.float32)
batch_norm=ops.BatchNorm()
output=batch_norm(input_x,scale,bias,mean,variance)
print(output[0])
效果:
3:loss
import mindspore.nn as nn
import mindspore.ops as ops
class L1Loss(nn.Cell):
def __init__(self):
super(L1Loss,self).__init__()
self.abs=ops.Abs()
self.reduce_mean=ops.ReduceMean()
def construct(self,predict,target):
x=self.abs(predict-target)
return self.reduce_mean(x)
import numpy as np
from mindspore import Tensor
loss=L1Loss()
input_data=Tensor(np.array([0.1,0.2,0.3]).astype(np.float32))
target_data=Tensor(np.array([0.1,0.2,0.2]).astype(np.float32))
output=loss(input_data,target_data)
print(output)
效果:
这个就是通过reduce_mean降维后得出的损失值loss
4:GeneratorDataset
import numpy as np
from mindspore import dataset as ds
from mindspore import nn
import mindspore.ops as ops
class L1Loss(nn.Cell):
def __init__(self):
super(L1Loss,self).__init__()
self.abs = ops.Abs()
self.reduce_mean=ops.ReduceMean()
def construct(self, predict,target):
x=self.abs(predict-target)
return self.reduce_mean(x)
def get_data(num,w=2.0,b=3.0):
for _ in range(num):
x=np.random.uniform(-10.0,10.0)
noise=np.random.normal(0,1)
y=w*x+b+noise
yield np.array([x]).astype(np.float32),np.array([y]).astype(np.float32)
def create_dataset(num_data,batch_size=16):
dataset=ds.GeneratorDataset(list(get_data(num_data)),column_names=['data','label'])
dataset=dataset.batch(batch_size)
return dataset
from mindspore.common.initializer import Normal
class LinearNet(nn.Cell):
def __init__(self):
super(LinearNet,self).__init__()
self.fc=nn.Dense(1,1,Normal(0.02),Normal(0.02))
def construct(self,x):
return self.fc(x)
net=LinearNet()
loss=L1Loss()
opt=nn.Momentum(net.trainable_params(),learning_rate=0.005,momentum=0.9)
from mindspore import Model
model=Model(net,loss,opt)
from mindspore.train.callback import LossMonitor
ds_train=create_dataset(num_data=160)
model.train(epoch=1,train_dataset=ds_train,callbacks=[LossMonitor()],dataset_sink_mode=False)
自然语言处理:
词向量的构造方式:
以词嵌入的方式。词嵌入方法有很多种。传统的词表示法如独热编码(One-Hot)模型,它将每个单词表示为一个长为词汇表大小的向量,且向量中只有一个元素为1,其他元素都为0。但这种方法的缺点是向量为稀疏表示,词汇表的大小决定了向量的维度大小,而当词汇表里单词很多时,向量的维度也就会很大,存在维数灾难的问题。
而词的分布式表示法,如Word2Vec、GloVe和FastText等,则将每个单词表示为一个固定长度的实数向量。相比于One-Hot编码,词的分布式表示法有明显优势:它可以将语义相近的单词在向量空间中的距离拉近。