• [其他] 浅谈摄像头感知算法
    自动驾驶技术所采用的传感器主要包括摄像机,激光雷达和毫米波雷达。摄像机用于采集可见光图像,对于物体的形状和类别的感知精度较高。由于深度学习技术的成功起源于计算机视觉任务,很多成功的算法也是基于对图像数据的处理,因此基于摄像机的感知技术目前已经相对成熟。图像数据的缺点在于缺少了场景和物体的距离信息,且受天气和环境的影响较大。激光雷达在一定程度上弥补了摄像机的缺点,可以精确的感知物体的距离,但是限制在于成本过高,难以大批量生产。毫米波雷达具有天线波束窄、分辨率高、频带宽、抗干扰能力强等点, 可以比较精确的测量物体的速度和距离,受天气和环境影响较小,而且成本较低,易于大规模生产,因为也成为了目前自动驾驶技术研究的一个热点方向。2003年,NASA发射勇气号和机遇号(Spirit and Opportunity)火星车,其目的是寻找火星上是否有水存在或曾经有水存在。2004年,美国主要的军事研究机构DARPA在Mojave沙漠组织了一场比赛,邀请全世界各地的企业或人建造来制造一辆能够横穿加州Mojave沙漠的自动驾驶汽车,要求无人驾驶车辆驾驶150英里的越野路线。这场比赛共有21支队伍获得参赛资格,但最终只有12辆车参赛;而由于机械故障和陷入沙坑等问题,所有参赛队伍都没有跑完全程。2009年,由国家自然科学基金委主办、西安交通大学承办的首届中国“智能车未来挑战”大赛在西安浐灞生态区拉开战幕。自动驾驶是为了实现一定的需求而研发的新技术。现主要有两类需求:满足园区、港口、工厂等特定场所的工作需求;实现物流,巡检,接驳等特定功能。自动驾驶在公共交通领域和特定场所的使用将早于在个人乘用车市场的普及。自动驾驶感知技术与多传感器数据融合技术模块中,自动驾驶算法总结一下大致包含一下方面:摄像头感知算法(1)摄像头目标检测机器学习算法(2)摄像头目标检测深度学习算法自动驾驶激光雷达感知算法(1)激光雷达地面检测机器学习算法(2)激光雷达目标检测机器学习算法(3)激光雷达目标检测深度学习算法自动驾驶多传感器融合算法(1)数据级融合算法(2)特征级融合算法(3)决策级融合算法 
  • [其他] 《深度学习入门》笔记 - 15
    ```python #定义sigmoid函数 def sigmoid(input): return 1.0/(1+np.exp(-input)) #通过随机梯度下降法估计参数 def logit_model(x,y,w,b,lr=0.1): for iter in range(60): loss=0 for i in range(len(x)): pred=sigmoid(np.dot(x[i:i+1],w)+b) loss+=-(y[i:i+1]*np.log(pred)+\ (1-y[i:i+1])*np.log(1-pred)) delta=pred-y[i:i+1] b-=lr*delta w-=lr*np.dot(x[i:i+1].T,delta) if (iter%10==9 or iter==59): print('Loss:'+str(loss)) return w,b #给定w,b时,计算模型预测值 def predict_logit_model(x,w,b): pred=[] for i in range(len(x)): tmp=sigmoid(np.dot(x[i:i+1],w)+b) if tmp>0.5: tmp=1 else: tmp=0 pred.append(tmp) return np.array(pred) #画x的散点图 def scatter_simple_data(x,y): plt.scatter(x[y==0,0],x[y==0,1],label='0',marker='o') plt.scatter(x[y==1,0],x[y==1,1],s=80,label='1',marker='s') plt.legend() plt.xlabel('x1',fontsize=16) plt.ylabel('x2',fontsize=16) plt.show() #画logistic模型的决策边界 def plot_decision_bound(x,y,w,b): x1=np.linspace(0,1,100) x2=(-b-x1*w[0])/w[1] plt.scatter(x[y==0,0],x[y==0,1],label='0',marker='o') plt.scatter(x[y==1,0],x[y==1,1],s=80,label='1',marker='s') plt.plot(x1,x2) plt.legend() plt.xlabel('x1',fontsize=16) plt.ylabel('x2',fontsize=16) ``` ```python #构造第1组数据 def createDataSet_1(): x=np.array([[0,0],[0,1],[1,0],[1,1]]) y=np.array([1,1,0,0]) return x,y x,y=createDataSet_1() scatter_simple_data(x,y) ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/6/1659794543891840709.png) ```python np.random.seed(4) w=np.random.normal(size=2) #从标准正态分布中随机生成初始权重 b,lr=0,0.1 w,b=logit_model(x,y,w,b,lr) pred=predict_logit_model(x,w,b) print('因变量的真实值:'+str(y)) print('因变量的预测值:'+str(pred)) ``` ```html Loss:[2.13470015] Loss:[1.67673736] Loss:[1.36190603] Loss:[1.13485563] Loss:[0.96600061] Loss:[0.83689858] 因变量的真实值:[1 1 0 0] 因变量的预测值:[1 1 0 0] ``` ```python plot_decision_bound(x,y,w,b) ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/6/1659794617251380299.png) logistic模型找到的这条线,预测都正确。 但是,如果不是这么“完美”的分布呢,比如这样的第2组数据,就不是一条线能泾渭分明的分开的: ```python #构造第2组数据 def createDataSet_2(): x=np.array([[0,0],[0,1],[1,0],[1,1]]) y=np.array([1,0,0,1]) return x,y x,y=createDataSet_2() scatter_simple_data(x,y) w=np.array([0,-0.3]) b,lr=0,0.1 w,b=logit_model(x,y,w,b,lr) pred=predict_logit_model(x,w,b) print('因变量的真实值:'+str(y)) print('因变量的预测值:'+str(pred)) plot_decision_bound(x,y,w,b) ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/6/1659794675489523014.png) ```html Loss:[2.88349771] Loss:[2.8812307] Loss:[2.8797981] Loss:[2.87874047] Loss:[2.87800035] Loss:[2.87749129] 因变量的真实值:[1 0 0 1] 因变量的预测值:[1 0 1 1] ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/6/1659794730122414008.png) 可以看到logistic模型找到的线,有一个右下角的原点预测错误。4个当中1个错误。这个是情理之中,别说机器,让你只画一条决策线,能进行正确的预测划分,你也画不出来。 但是如果可以让你画2条线,那就没什么问题。那么对机器来说,又应该怎么做呢?
  • [其他] 《深度学习入门》笔记 - 14
    在logistic模型中,损失函数可以定义为 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/6/1659777983871392224.png) 其中$p_{i}$表示第i个观测点的预测概率值,$y_i$表示第i个观测点的因变量的值(0或者1) 观察$y_i$等于零或者一的情况,可以做出下的变换 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/6/1659778218042666253.png) 接下来实在是看不下去了,还有求偏导数的主要技巧用到了链式法则,还有其他的太难看了。所以这一小部分跳过。 接下来的内容是深度神经网络。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/6/1659778927647116229.png) 观察箭头的方向,代表了处理的流程。通过线性回归模型和生物神经元的类比,可以将线性模型称作一个只包含一个神经元的神经网络。 同样的,logistic模型也可以用来进行类比,下图代表的就是预估y等于1的概率的处理过程: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/6/1659779156891635190.png) 他与线性回归模型的不同点在于:Logistic模型对输入值加权,并使用sigmoid函数做了非线性处理。 为什么需要深度神经网络呢?下面用代码和例子来说明。
  • [其他] 《深度学习入门》笔记 - 13
    这里有一个数据集,它的内容包含:信用卡余额、每月收入、是否违约。 ```python %config InlineBackend.figure_format='retina' import pandas as pd import matplotlib.pyplot as plt import numpy as np def loadDataSet(): x=[];y=[] f=open('./Default.txt') for line in f.readlines(): lineArr=line.strip().split() x.append([float(lineArr[0]),float(lineArr[1])]) y.append(lineArr[2]) return np.array(x),y x,y=loadDataSet() print(y) #Yes > 1, No > 0 y01=np.zeros(len(y)) for i in range(len(y)): if y[i] == 'Yes': y01[i]=1 y=y01 print(y) ``` ```html ['Yes', 'Yes',... 'No', 'No'] [1. 1. ... 0. 0.] ``` ```python plt.scatter(x[y==0,0],x[y==0,1],label='No') plt.scatter(x[y==1,0],x[y==1,1],s=80,label='Yes') plt.xlabel('Balance',fontsize=16) plt.ylabel('Income',fontsize=16) plt.legend() plt.show ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/6/1659775404176492371.png) 从上图中可以看到,信用卡余额相对于每月收入来说,对还款违约的影响更大。 一般模型不会直接预测某信用卡用户是否违约,而是预测其违约的概率,表示为`P(Default|Balance,Income)`,因为它的值在0和1之间,所以如果直接用类似线性回归模型的方式是不行的,需要对加权和进行变换。即: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/6/1659775552137365917.png) $f(x)=\frac{1}{1+e^{-x}}$函数在统计学文献中称为`logistic函数`,在机器学习文献中称为`sigmoid函数`。 ```python a=np.linspace(-10,10,100) plt.plot(a,1/(1+np.exp(-a)),linewidth=3) plt.axvline(x=0,linestyle='--',color='m',linewidth=2) plt.axhline(y=0.5,linestyle='--',color='m',linewidth=2) plt.show ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/6/1659775455898229675.png) 根据这个函数的图形,预测值和x值的关系看的就比较清楚。基本上就是x大于0时,预测值为1的概率大于0.5,而概率大于0.5,可以认为是分类值1。
  • [其他] 《深度学习入门》笔记 - 12
    学习步长$\alpha$是一个很重要的参数。 如果太小,算法会收敛的很慢。 如果太大,容易造成算法不收敛,甚至发散。 自变量的标准化,和因变量的中心化,是建立深度学习模型常用的数据预处理方法。 他们的好处,是不仅可以让梯度下降法的数值表现的更加稳定,还有助于我们找到合适的初始值和步长。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/4/1659621510931174824.png) 在梯度下降法中,`学习步长`和`batch size`需要事先给定,而不像`参数w`一样通过最小化损失函数得到,这类参数在机器学习中叫做`超参数`。 接下来是介绍线性分类模型,logistic模型。`回归模型`和`分类模型`的主要区别就是因变量y的数据类型不一样。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/4/1659621769682711777.png) 在处理实际问题的时候,根据这个区别,我们就可以选择建立回归模型还是分类模型: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/4/1659621848855146071.png)
  • [其他] 《深度学习入门》笔记 - 11
    继续随机梯度下降法, 回到广告数据,以TV,radio为自变量,以sales为因变量,没有截距,所有观测点作为训练数据。 先要对自变量进行`标准化`,对因变量进行`中心化`。 标准化后所有自变量的均值是0,方差是1。中心化后因变量的均值是0。 这样做可以让梯步下降法的数值更加稳定,更容易找到合适的初始值和学习步长。 一个标准化的方法就是让数据的每一列减去该列的均值,然后除以该列的样本标准差($sd(x)$): ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/4/1659569344728935204.png) 中心化,只要前面的一步即可: $centered\_y = y - \bar{y}$ ```python #标准化和中心化。然后随机梯度下降 x=ad.iloc[:,0:2].values y=ad.iloc[:,3].values scaled_x=(x-np.mean(x,axis=0,keepdims=True))/ \ np.std(x,axis=0,keepdims=True) centered_y=y-np.mean(y) lr=0.1 w=np.zeros(2) w_record=[w.copy()] for iter in range(5): total_loss=0 for i in range(len(scaled_x)): pred=np.sum(scaled_x[i]*w) total_loss+=((pred-centered_y[i])**2)/2 delta=(pred-centered_y[i]) w-=lr*(delta*scaled_x[i]) w_record.append(w.copy()) print('Loss: %0.5f'%(total_loss/(i+1))) print(w) ``` ```html Loss: 1.98663 [3.46941055 3.19188802] Loss: 1.62702 [3.46941055 3.19188802] Loss: 1.62702 [3.46941055 3.19188802] Loss: 1.62702 [3.46941055 3.19188802] Loss: 1.62702 [3.46941055 3.19188802] ``` 在`随机梯度下降法`中,每次迭代只使用一个观测点,计算的梯度随机性比较大,所以有时候参数的值不会朝着最小损失的方向移动。 `全数据梯度下降法`(`Full Gradient Dscent`)在计算梯度时会用到所有观测点,因此算出来的梯度会比较稳定。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/4/1659571169573914119.png) 全数据梯度下降法的代码例子就不看了。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/4/1659571583651887858.png) 这两种梯度下降法各自的特点是如此的鲜明,所以必定还会有一种中庸调和的方法,那就是`批量随机梯度下降法`(`Batch Stochastic Gradient Descent`)。 他在计算梯度时,既不是使用单个观测点,也不是使用所有观测点,而是每次用一小部分观测点,在实际中常用的有16,32~1024个观测点,根据内存和模型规模选取合适的。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/4/1659571887339433583.png) 批量随机梯度下降法的代码例子也不看了。
  • [其他] 《深度学习入门》笔记 - 10
    接着看梯度下降,用循环来实现。 ```python #循环迭代20次 x,y=0.5,0.8 w=0;lr=0.5 #lr学习率=0.5 w_record=[] loss_record=[] ​ for iter in range(20): pred=x*w loss=((pred-y)**2)/2 w_record.append(w) loss_record.append(loss) delta=pred-y w=w-lr*(delta*x) if (iter%5==0 or iter==19): print('iter: %2d; w: %0.2f; Loss: %0.3f'%(iter,w,loss)) w_record.append(w) loss_record.append((x*w-y)**2/2) ``` ```html iter: 0; w: 0.20; Loss: 0.320 iter: 5; w: 0.88; Loss: 0.084 iter: 10; w: 1.23; Loss: 0.022 iter: 15; w: 1.41; Loss: 0.006 iter: 19; w: 1.49; Loss: 0.002 ``` 把这循环的过程中的变化可视化出来: ```python #模型参数w 和 残差平方和RSS 随迭代的变化曲线 w_vec=np.linspace(-1,4,100) rss_vec=[] for w_tmp in w_vec: rss_tmp=(y-x*w_tmp)**2/2 rss_vec.append(rss_tmp) plt.plot(w_vec,rss_vec) plt.scatter(0,0.32,s=100,c='y',marker='o') for i in range(len(w_record)-1): plt.arrow(w_record[i],loss_record[i],\ w_record[i+1]-w_record[i],\ loss_record[i+1]-loss_record[i],width=0.01,\ color='y',head_width=0.05) plt.xlabel('w',fontsize=16) plt.ylabel('RSS',fontsize=16) plt.show() ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/3/1659535760107353372.png) 好了我们上面说的是最简单的情况,因为为了学习,是一个权重或叫参数w,一个自变量x,并且只有一个观测点(x,y)。 在实际情况中,一般就不仅仅是学习的那么简单的情况。 数据会包含多个自变量,多个权重,很多个观测点。 用 $L(w)=L(w_1,w_2,...,w_p)$ 表示包含p个权重或参数的损失函数,它的梯度可以表示为: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/3/1659536268538613050.png) 它是由函数$L(w)$对各个参数的偏导数构成的向量。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/3/1659536366914343088.png) 上面就是`随机梯度下降法`(`Stochastic Gradient Descent` SGD)。随机是指每次只使用一个观察点计算梯度,在实现随机梯度下降的过程中,随机抽取观测点来计算梯度并更新参数。
  • [其他] 《深度学习入门》笔记 - 09
    继续线性回归模型,前面说了如何更新模型参数w,让预测值接近于真实值。现在我们来尝试迭代多次,看看效果。 从w=0开始 ```python #w初始值给0 x,y=0.5,0.8 w=0;lr=0.5 #lr学习率=0.5 pred=x*w loss=((pred-y)**2)/2 grad=(pred-y)*x print('自变量:'+str(x)) print('因变量:'+str(y)) print('初始权重:'+str(w)) print('预测值:'+str(pred)) print('差值:'+str(round(loss,ndigits=3))) print('梯度:'+str(grad)) 自变量:0.5 因变量:0.8 初始权重:0 预测值:0.0 差值:0.32 梯度:-0.4 ``` 然后更新w的值,做第一次迭代: ```python #第一次迭代 w=w-lr*grad pred=x*w loss=((pred-y)**2)/2 grad=(pred-y)*x print('自变量:'+str(x)) print('因变量:'+str(y)) print('权重:'+str(w)) print('预测值:'+str(pred)) print('差值:'+str(round(loss,ndigits=3))) print('梯度:'+str(round(grad,ndigits=3))) 自变量:0.5 因变量:0.8 权重:0.2 预测值:0.1 差值:0.245 梯度:-0.35 ``` 可以看到预测值和真实值的差值在变小(0.32 > 0.245),也就是在向着不断的收敛的方向。
  • [其他干货] CUDA编程(八)统一内存
    从前面的矩阵乘的代码中可以看出,要写好一个CUDA的代码,需要分配HOST内存(malloc或cudaMallocHost),需要分配DEVICE内存(cudaMalloc),需要将HOST内存数据复制到DEVICE(cudaMemcpy),需要完成GPU核函数的调用,需要把核函数的调用结果在复制回HOST(cudaMemcpy),还需要对前面的各种内存做释放工作(free,cudaFreeHost,cudaFree)。这些工作,虽然是套路,显然还是太繁琐了。于是,聪明的Nvidia在CUDA 6.0以上的版本提出了一个叫做Unified Memory(统一内存)的概念。它把GPU内存、CPU内存在编码层面屏蔽起来:它是可以从系统的任何CPU、GPU访问的单个内存地址空间。它允许应用程序分配可以从CPUs或GPUs上允许的代码读取或者写入数据。具体的方式如下所示:它把原来CPU上的malloc改为cudaMallocManaged,并且分配好的内存地址可以直接被GPU的核函数(图中的 qsort)使用(还记得原来的代码需要先cudaMallocHost/malloc,在cudaMemcpy吗?这里统统不要了。统一内存除了上面使用的 cudaMallocManaged函数来定义变量以外,还可以使用 __managed__ 标识符来表示这是一块统一内存。(这个前面可能还需要再加上 __device__ 标识符供 核函数使用。统一内存使用的时候要借助于 cudaDeviceSynchronize() 来确保CPU和GPU同步。统一内存不显式的区分HOST还是DEVICE的memory,它简化了代码,增强了代码的通用性。统一内存只能在HOST申请。这里面有几个误区需要澄清下:(1)张小白原来以为,只有 Nvidia Jetson Orin那种显存和内存合二为一的设备才有统一内存的概念。但其实并不是——满足 SM架构大于3.0(Kepler架构以上)都可以使用统一内存的方式来编程。逻辑上任何GPU卡或者设备都可以使用统一内存,但是从效果上来看,只有真正的融合为一体的设备(如Jetson AGX Orin),才有最好的统一内存的效果。(2)对于矩阵乘的代码而言,统一内存相当于对Global Memory的一个等效版本,而共享内存则是对SM内部的一种速度优化方式。两者是无关的。也就是说,你在使用统一内存的代码中可以同时使用共享内存。(2)使用了 __managed__ 标识符或 cudaMallocManaged 之后,确实代码中不需要 cudaMalloc,cudaMemcpy这些代码了。但是系统底层其实还会根据情况,决定自己是否需要执行相关的GPU内存分配和 HOST和DEVICE内存的互相拷贝的动作。举个例子,对于张小白的Nvidia Quardo P1000的显卡而言,HOST内存在自己的笔记本内存上(大概有64G),DEVICE内存在GPU显卡(大概有4G)。在这样的环境运行代码,系统仍然会做 申请HOST内存,申请DEVICE内存,HOST内存与DEVICE内存复制等动作。但是对于张小白新购置的了不起的Nvidia AGX Orin而言,HOST内存就是DEVICE内存(大概有32G)。两者不仅仅叫做统一内存,其实还叫做同一内存(张小白自创的)。也就是说,ARM CPU和Nvidia GPU共享一个物理内存。具体的说明可参见:https://zhuanlan.zhihu.com/p/486130961同一内存最大的好处就是:下面典型的三个动作,1、3都可以省略了:所以典型的代码就从左边的模式变成了右边的模式:(1)定义变量:仅需要定义unified memory的变量。节省了空间。(2)HOST->DEVICE:步骤省略(3)执行核函数:跟原来一样(4)DEVICE->HOST:步骤省略(5)显式同步:只是统一内存比原来的方式多一个CPU等待GPU完成的动作。注:上述图片(含代码)来自于上面链接中的文章。那么,统一内存到底是怎么实现的呢?这里借助了下图的做法:CUDA在现有内存池的结构上增加了一个 统一内存系统。开发人员可以直接访问任何内存或者显存资源。当CUDA发现需要访问GPU内存时,如果一开始定义在HOST侧,并且对其进行了初始化,CUDA会自动执行数据拷贝,所以,仍然会受制于PCI-E的带宽和延迟。我们可以看到在这个情况下,代码和运行时变量前后的变迁:好了,概念好像整理得差不多了。下面开始实战:我们把昨天的矩阵乘的代码(包含共享内存优化部分)拿过来,然后看看该怎么优化。原来的代码是这样的:matrix_mul.cuh#pragma once #include <stdio.h> #define CHECK(call) \ do \ { \ const cudaError_t error_code = call; \ if (error_code != cudaSuccess) \ { \ printf("CUDA Error:\n"); \ printf(" File: %s\n", __FILE__); \ printf(" Line: %d\n", __LINE__); \ printf(" Error code: %d\n", error_code); \ printf(" Error text: %s\n", \ cudaGetErrorString(error_code)); \ exit(1); \ } \ } while (0)matrix_mul_old.cu#include <stdio.h> #include <math.h> #include "error.cuh" #include "matrix_mul.cuh" #define BLOCK_SIZE 32 __global__ void gpu_matrix_mult(int *a,int *b, int *c, int m, int n, int k) { int row = blockIdx.y * blockDim.y + threadIdx.y; int col = blockIdx.x * blockDim.x + threadIdx.x; int sum = 0; if( col < k && row < m) { for(int i = 0; i < n; i++) { sum += a[row * n + i] * b[i * k + col]; } c[row * k + col] = sum; } } __global__ void gpu_matrix_mult_shared(int *d_a, int *d_b, int *d_result, int m, int n, int k) { __shared__ int tile_a[BLOCK_SIZE][BLOCK_SIZE]; __shared__ int tile_b[BLOCK_SIZE][BLOCK_SIZE]; int row = blockIdx.y * BLOCK_SIZE + threadIdx.y; int col = blockIdx.x * BLOCK_SIZE + threadIdx.x; int tmp = 0; int idx; for (int sub = 0; sub < gridDim.x; ++sub) { idx = row * n + sub * BLOCK_SIZE + threadIdx.x; tile_a[threadIdx.y][threadIdx.x] = row<n && (sub * BLOCK_SIZE + threadIdx.x)<n? d_a[idx]:0; idx = (sub * BLOCK_SIZE + threadIdx.y) * n + col; tile_b[threadIdx.y][threadIdx.x] = col<n && (sub * BLOCK_SIZE + threadIdx.y)<n? d_b[idx]:0; __syncthreads(); for (int k = 0; k < BLOCK_SIZE; ++k) { tmp += tile_a[threadIdx.y][k] * tile_b[k][threadIdx.x]; } __syncthreads(); } if(row < n && col < n) { d_result[row * n + col] = tmp; } } void cpu_matrix_mult(int *h_a, int *h_b, int *h_result, int m, int n, int k) { for (int i = 0; i < m; ++i) { for (int j = 0; j < k; ++j) { int tmp = 0.0; for (int h = 0; h < n; ++h) { tmp += h_a[i * n + h] * h_b[h * k + j]; } h_result[i * k + j] = tmp; } } } int main(int argc, char const *argv[]) { int m=100; int n=100; int k=100; //声明Event cudaEvent_t start, stop, stop2, stop3 , stop4 ; //创建Event CHECK(cudaEventCreate(&start)); CHECK(cudaEventCreate(&stop)); CHECK(cudaEventCreate(&stop2)); int *h_a, *h_b, *h_c, *h_cc; CHECK(cudaMallocHost((void **) &h_a, sizeof(int)*m*n)); CHECK(cudaMallocHost((void **) &h_b, sizeof(int)*n*k)); CHECK(cudaMallocHost((void **) &h_c, sizeof(int)*m*k)); CHECK(cudaMallocHost((void **) &h_cc, sizeof(int)*m*k)); for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { h_a[i * n + j] = rand() % 1024; } } for (int i = 0; i < n; ++i) { for (int j = 0; j < k; ++j) { h_b[i * k + j] = rand() % 1024; } } int *d_a, *d_b, *d_c; CHECK(cudaMalloc((void **) &d_a, sizeof(int)*m*n)); CHECK(cudaMalloc((void **) &d_b, sizeof(int)*n*k)); CHECK(cudaMalloc((void **) &d_c, sizeof(int)*m*k)); // copy matrix A and B from host to device memory CHECK(cudaMemcpy(d_a, h_a, sizeof(int)*m*n, cudaMemcpyHostToDevice)); CHECK(cudaMemcpy(d_b, h_b, sizeof(int)*n*k, cudaMemcpyHostToDevice)); unsigned int grid_rows = (m + BLOCK_SIZE - 1) / BLOCK_SIZE; unsigned int grid_cols = (k + BLOCK_SIZE - 1) / BLOCK_SIZE; dim3 dimGrid(grid_cols, grid_rows); dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE); //开始start Event cudaEventRecord(start); //非阻塞模式 cudaEventQuery(start); //gpu_matrix_mult<<<dimGrid, dimBlock>>>(d_a, d_b, d_c, m, n, k); gpu_matrix_mult_shared<<<dimGrid, dimBlock>>>(d_a, d_b, d_c, m, n, k); //开始stop Event cudaEventRecord(stop); //由于要等待核函数执行完毕,所以选择阻塞模式 cudaEventSynchronize(stop); //计算时间 stop-start float elapsed_time; CHECK(cudaEventElapsedTime(&elapsed_time, start, stop)); printf("start-》stop:Time = %g ms.\n", elapsed_time); CHECK(cudaMemcpy(h_c, d_c, (sizeof(int)*m*k), cudaMemcpyDeviceToHost)); //cudaThreadSynchronize(); //开始stop2 Event CHECK(cudaEventRecord(stop2)); //非阻塞模式 //CHECK(cudaEventSynchronize(stop2)); cudaEventQuery(stop2); //计算时间 stop-stop2 float elapsed_time2; cudaEventElapsedTime(&elapsed_time2, stop, stop2); printf("stop-》stop2:Time = %g ms.\n", elapsed_time2); //销毁Event CHECK(cudaEventDestroy(start)); CHECK(cudaEventDestroy(stop)); CHECK(cudaEventDestroy(stop2)); //CPU函数计算 cpu_matrix_mult(h_a, h_b, h_cc, m, n, k); int ok = 1; for (int i = 0; i < m; ++i) { for (int j = 0; j < k; ++j) { if(fabs(h_cc[i*k + j] - h_c[i*k + j])>(1.0e-10)) { ok = 0; } } } if(ok) { printf("Pass!!!\n"); } else { printf("Error!!!\n"); } // free memory cudaFree(d_a); cudaFree(d_b); cudaFree(d_c); cudaFreeHost(h_a); cudaFreeHost(h_b); cudaFreeHost(h_c); return 0; }先执行一下:没啥问题。我们来分析一下:上面的代码用到了 h_a, h_b, h_c, h_cc 4个HOST内存,还用到了 d_a, d_b, d_c 三个DEVICE内存。其中,abc是对应的。而cc是放CPU运算结果专用的。其实我们可以把h_cc直接改为malloc的内存就行了。但是为了好看,也可以将这4个HOST内存都改为统一内存。我们将统一内存起名为 u_a, u_b, u_c, u_cc吧!魔改开始:将代码中 h_a->u_a,h_b->u_b,h_c->u_c,h_cc->u_cc,其他变量做相应的适当修改。matrix_mul.cu#include <stdio.h> #include <math.h> #include "error.cuh" #include "matrix_mul.cuh" #define BLOCK_SIZE 32 __managed__ int u_a[100*100]; __managed__ int u_b[100*100]; __managed__ int u_c[100*100]; __managed__ int u_cc[100*100]; __global__ void gpu_matrix_mult(int *a,int *b, int *c, int m, int n, int k) { int row = blockIdx.y * blockDim.y + threadIdx.y; int col = blockIdx.x * blockDim.x + threadIdx.x; int sum = 0; if( col < k && row < m) { for(int i = 0; i < n; i++) { sum += u_a[row * n + i] * u_b[i * k + col]; } u_c[row * k + col] = sum; } } __global__ void gpu_matrix_mult_shared(int *u_a, int *u_b, int *u_result, int m, int n, int k) { __shared__ int tile_a[BLOCK_SIZE][BLOCK_SIZE]; __shared__ int tile_b[BLOCK_SIZE][BLOCK_SIZE]; int row = blockIdx.y * BLOCK_SIZE + threadIdx.y; int col = blockIdx.x * BLOCK_SIZE + threadIdx.x; int tmp = 0; int idx; for (int sub = 0; sub < gridDim.x; ++sub) { idx = row * n + sub * BLOCK_SIZE + threadIdx.x; tile_a[threadIdx.y][threadIdx.x] = row<n && (sub * BLOCK_SIZE + threadIdx.x)<n? u_a[idx]:0; idx = (sub * BLOCK_SIZE + threadIdx.y) * n + col; tile_b[threadIdx.y][threadIdx.x] = col<n && (sub * BLOCK_SIZE + threadIdx.y)<n? u_b[idx]:0; __syncthreads(); for (int k = 0; k < BLOCK_SIZE; ++k) { tmp += tile_a[threadIdx.y][k] * tile_b[k][threadIdx.x]; } __syncthreads(); } if(row < n && col < n) { u_result[row * n + col] = tmp; } } void cpu_matrix_mult(int *u_a, int *u_b, int *u_result, int m, int n, int k) { for (int i = 0; i < m; ++i) { for (int j = 0; j < k; ++j) { int tmp = 0.0; for (int h = 0; h < n; ++h) { tmp += u_a[i * n + h] * u_b[h * k + j]; } u_result[i * k + j] = tmp; } } } int main(int argc, char const *argv[]) { int m=100; int n=100; int k=100; //声明Event cudaEvent_t start, stop, stop2, stop3 , stop4 ; //创建Event CHECK(cudaEventCreate(&start)); CHECK(cudaEventCreate(&stop)); CHECK(cudaEventCreate(&stop2)); //int *h_a, *h_b, *h_c, *h_cc; //CHECK(cudaMallocHost((void **) &h_a, sizeof(int)*m*n)); //CHECK(cudaMallocHost((void **) &h_b, sizeof(int)*n*k)); //CHECK(cudaMallocHost((void **) &h_c, sizeof(int)*m*k)); //CHECK(cudaMallocHost((void **) &h_cc, sizeof(int)*m*k)); for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { u_a[i * n + j] = rand() % 1024; } } for (int i = 0; i < n; ++i) { for (int j = 0; j < k; ++j) { u_b[i * k + j] = rand() % 1024; } } //int *d_a, *d_b, *d_c; //CHECK(cudaMalloc((void **) &d_a, sizeof(int)*m*n)); //CHECK(cudaMalloc((void **) &d_b, sizeof(int)*n*k)); //CHECK(cudaMalloc((void **) &d_c, sizeof(int)*m*k)); // copy matrix A and B from host to device memory //CHECK(cudaMemcpy(d_a, h_a, sizeof(int)*m*n, cudaMemcpyHostToDevice)); //CHECK(cudaMemcpy(d_b, h_b, sizeof(int)*n*k, cudaMemcpyHostToDevice)); unsigned int grid_rows = (m + BLOCK_SIZE - 1) / BLOCK_SIZE; unsigned int grid_cols = (k + BLOCK_SIZE - 1) / BLOCK_SIZE; dim3 dimGrid(grid_cols, grid_rows); dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE); //开始start Event cudaEventRecord(start); //非阻塞模式 cudaEventQuery(start); //gpu_matrix_mult<<<dimGrid, dimBlock>>>(d_a, d_b, d_c, m, n, k); gpu_matrix_mult_shared<<<dimGrid, dimBlock>>>(u_a, u_b, u_c, m, n, k); //开始stop Event cudaEventRecord(stop); //由于要等待核函数执行完毕,所以选择阻塞模式 cudaEventSynchronize(stop); //计算时间 stop-start float elapsed_time; CHECK(cudaEventElapsedTime(&elapsed_time, start, stop)); printf("start-》stop:Time = %g ms.\n", elapsed_time); //CHECK(cudaMemcpy(h_c, d_c, (sizeof(int)*m*k), cudaMemcpyDeviceToHost)); //cudaThreadSynchronize(); //开始stop2 Event CHECK(cudaEventRecord(stop2)); //非阻塞模式 //CHECK(cudaEventSynchronize(stop2)); cudaEventQuery(stop2); //计算时间 stop-stop2 float elapsed_time2; cudaEventElapsedTime(&elapsed_time2, stop, stop2); printf("stop-》stop2:Time = %g ms.\n", elapsed_time2); //销毁Event CHECK(cudaEventDestroy(start)); CHECK(cudaEventDestroy(stop)); CHECK(cudaEventDestroy(stop2)); //CPU函数计算 cpu_matrix_mult(u_a, u_b, u_cc, m, n, k); int ok = 1; for (int i = 0; i < m; ++i) { for (int j = 0; j < k; ++j) { if(fabs(u_cc[i*k + j] - u_c[i*k + j])>(1.0e-10)) { ok = 0; } } } if(ok) { printf("Pass!!!\n"); } else { printf("Error!!!\n"); } // free memory //cudaFree(d_a); //cudaFree(d_b); //cudaFree(d_c); //cudaFreeHost(h_a); //cudaFreeHost(h_b); //cudaFreeHost(h_c); return 0; }执行一下:额,好像速度没啥变化。查看下性能:
  • [技术干货] 【论文分享】一种基于深度学习的物联网信道状态信息获取算法
    一种基于深度学习的物联网信道状态信息获取算法廖勇, 姚海梅, 花远肖重庆大学通信与测控中心,重庆 400044摘要针对基于大规模多输入多输出(MIMO)的物联网系统中用户侧将信道状态信息(CSI)发送到基站时反馈开销大的问题,提出一种基于深度学习的CSI反馈网络用来反馈CSI。该网络首先使用卷积神经网络(CNN)提取信道特征矢量和最大池化层通过降维来达到压缩CSI的目的,然后使用全连接和CNN将压缩的CSI解压,恢复原始信道。仿真结果表明,与现有的CSI反馈方法相比,所提出的CSI反馈网络恢复的CSI更接近原始信道,重构质量明显提高。关键词: 大规模MIMO ; 物联网 ; CSI反馈 ; 深度学习1 引言物联网(IoT,Internet of things)是新一代信息技术的重要组成部分,也是“信息化”时代的重要发展阶段。传统移动通信网络主要解决人与人的互联,而物联网解决的是万物互联[1]。IoT 技术将扮演至关重要的角色,因为IoT技术能够做到机器与机器(M2M,machine to machine)[2]和机器与人的连接,可以极大地促进人类社会发展。目前,有很多技术可以用于IoT信息的传输,如蓝牙、无线局域网和红外线等常见的信息传输方式,IoT 由于其极大的可扩展性和实用性正受到人们越来越多的关注。作为 5G 的关键技术之一,大规模多输入多输出(MIMO,multiple input multiple output)技术正在逐渐变得成熟。该技术与传统MIMO技术相比,在物理特性和性能上存在显著优势,具有高频谱效率和高能量效率等优点。目前,欧洲多国和中国等都在大力发展基于LTE的IoT,随着5G的高速推进,大规模MIMO系统也应用于IoT的典型应用场景中,该系统利用基站(BS,base station)大量发射天线来有效支持多个IoT设备的传输,能够很好地提高系统的频谱效率和能量效率[3,4]。基于大规模MIMO的IoT具有很多优势,但也存在一些技术上的挑战。在基于大规模 MIMO 的IoT系统中,为了保证传输过程的服务质量(QoS, quality of service),发送端通常需要进行预编码、自适应编码等操作。然而,这些操作都需要发送端获得下行信道状态信息(CSI,channel state information),如果没有获得准确的下行CIS,IoT系统将不能对连接的IoT设备提供高QoS的通信服务。因此,发送端获得准确的下行CSI对整个系统的通信性能具有显著影响。目前,国内外学者对发送端获取CSI的方法进行了大量研究,并提出了许多获取CSI的方法。文献[5]使用离散余弦变换(DCT,discrete cosine transform)矩阵作为稀疏矩阵,该方案从减少噪声的角度出发,针对性地研究了基于压缩感知(CS, compressed sensing)的CSI有限反馈方法。文献[6]和文献[7]提出适用于单天线、多用户场景的基于主成分分析(PCA,principal component analysis)降维的压缩反馈方法。文献[8]在 PCA 的基础上,通过自回归预测补偿反馈使 BS 获得 CSI。虽然学者已提出很多CSI反馈方法,但这些方法的反馈精度不高,误差较大。近年来深度学习发展迅速,在处理大数据方面表现出强大的能力,已有研究人员采用深度学习方法来实现CSI反馈。文献[9]提出基于深度学习的大规模MIMO的CSI反馈,利用全连接网络和残差网络(整个网络被命名为CsiNet)来实现CSI压缩和解压。文献[10]在文献[9]中CsiNet的基础上,增加了长短期记忆(LSTM,long short-term memory)网络(整个网络被命名为 CsiNet-LSTM),对时变大规模MIMO信道进行CSI反馈。但是,目前尚没有研究人员将深度学习用于IoT信道场景的CSI反馈。为此,本文针对频分双工(FDD,frequency division duplex)模式下基于大规模 MIMO 的 IoT系统的CSI获取问题展开研究,考虑传统CSI反馈方法的反馈精度不高以及进一步研究深度学习在基于大规模MIMO的IoT系统中CSI获取的适用性,提出一种基于深度学习的CSI反馈算法,主要贡献如下。本文提出一种创新的应用于大规模 MIMO 的IoT系统的基于深度学习的CSI反馈算法,该算法主要利用卷积神经网络(CNN,convolutional neural network)提取信道特征矢量,不同于文献[9]和文献[10],该算法不是采用全连接网络降维,而是采用最大池化层(maxpooling)降维来压缩数据,通过池化可去掉一些无关紧要的数据,保留最重要的特征,留下最能表达数据特征的信息。与全连接层相比,池化网络不仅可以压缩数据,还可以减少待估计参数的数量。然后使用全连接网络和 CNN 将压缩的CSI解压,从而恢复原始信道。最后,对大规模MIMO的IoT系统中基于深度学习的CSI反馈方法和常用的CSI反馈方法的性能效果进行仿真比较。2 结束语本文提出了一种基于深度学习的网络用于大规模MIMO的IoT系统中的CSI反馈,该网络通过离线训练和深度学习的非线性映射特性能够更好地学习信道结构特点。通过仿真,分析了所提算法在大规模MIMO的IoT系统中CSI反馈的性能表现。从仿真结果可以看出,本文所提出的方法不仅比传统的 CSI 反馈方法运行时间更少,而且具有更高的反馈精度,恢复的 CSI 质量明显提高。下一步将研究 IoT 通信场景中基于深度学习的信道估计的应用。The authors have declared that no competing interests exist.作者已声明无竞争性利益关系。3 原文链接http://www.infocomm-journal.com/wlw/article/2019/2096-3750/2096-3750-3-1-00008.shtml
  • [赋能培训] GDE直播公开课 · 第十一期:AI如何举一反三——迁移学习技术洞察
    分享背景:迁移学习旨在利用数据、任务和模型之间的相似性,将已学习到的模型、知识等应用于新的领域,达到事半功倍的效果。在计算机视觉、自然语言处理、语音识别等方面上迁移学习都具有广泛应用。❤ 观看直播入口:https://bbs.huaweicloud.com/live/cloud_live/202208111900.html活动说明:1.有效盖楼:留言“报名”或参与提问/建议或回复暗号的,均可参与盖楼。报名和提问可在同一楼层,也可在不同楼层。盖楼截止时间:8月11日 24:00;2.无效盖楼:复制别人的提问、建议等,以及其他不符合要求的灌水信息皆为无效盖楼。注意:同一用户盖楼不能连续超过3楼,总楼层数不能超过10楼。超过盖楼数将视为无效盖楼;3.无效盖楼且踩中获奖楼层,也将视为无效,奖品不予发放,该楼层奖项轮空;4.楼层总数取值仅限活动期间内,即2022年8月1日至8月11日所盖楼层,其余时间楼层无效;5.鼓励自主撰写建议,复制、改写他人内容将不纳入评选,同样的内容以首发为准;6.为保证活动有序进行,一经发现有作弊行为,将取消奖励资格;7.请务必使用个人账号参与活动;8.所有获奖用户,请于获奖后3日内完成实名认证,否则视为放弃奖励;9.本次活动如一个实名认证对应多个账号,只有一个账号可领取奖励;一个实名认证账号只能对应一个收件人,如同一账号填写多个不同收件人或不同账号填写同一收件人,均不予发放奖励;(举例说明:具备同一证件号(比如身份证号/护照ID/海外驾照ID/企业唯一识别号等)、同一手机号、同一设备、同一IP地址等,均视为同一实名用户)10.所有参加本活动的用户,均视为认可并同意遵守华为云社区的用户协议及隐私政策;11.GDE数智平台可能需要根据活动的实际举办情况对活动规则进行变更;若有变更,将以活动页面告知等方式及时通知;12.活动结束后将在活动帖和【GDE直播交流群】微信群中公布获奖名单,奖品配送范围为中国大陆地区,部分地区或因疫情原因延迟配送;13.GDE数智平台拥有活动解释权。
  • [其他] 《深度学习入门》笔记 - 08
    继续线性回归模型,这里先说`随机梯度下降法`。 先考虑一个简单的模型,没有截距,只有一个自变量: y=xw 当观测点为(x=0.5,y=0.8),w=3时,残差平方和是 ```python x,y=0.5,0.8 w=3 rss=(y-x*w)**2/2 print(rss) #0.24499999999999997 ``` `梯度`(gradient)记作$\nabla$,函数RSS(w)关于参数w的梯度,记作$\nabla_wRSS(w)$,简洁的记作$\nabla_w$ 。它是RSS(w)关于w的偏导数,即: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/31/1659234054602948874.png) 对于这里的简单模型,就有: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/31/1659234064409335715.png) 所以w=3时,$\nabla_w$=(0.5x3-0.8)x0.5=0.35 梯度就是w=3处的切线的斜率,这里梯度是正。如下图 ```python pred=x*w grad=(pred-y)*x print('w=3时,预测值:'+str(pred)) print('w=3时,残差平方和:'+str(round(rss,ndigits=3))) print('w=3时,RSS(w)的梯度:'+str(grad)) #w=3时,预测值:1.5 #w=3时,残差平方和:0.245 #w=3时,RSS(w)的梯度:0.35 w_vec=np.linspace(-1,4,100) rss_vec=[] for w_tmp in w_vec: rss_tmp=(y-x*w_tmp)**2/2 rss_vec.append(rss_tmp) plt.plot(w_vec,rss_vec) plt.scatter(w,rss,s=100,c='y',marker='o') plt.plot(np.linspace(2.5,3.5,50),\ np.linspace(2.5,3.5,50)*0.35-0.805,\ '--',linewidth=2.0) plt.xlabel('w',fontsize=16) plt.ylabel('RSS',fontsize=16) #ax=plt.gca() #ax.set_aspect(1) plt.show() ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/31/1659239540190972017.png) 这个切线的斜率看上去不是0.35的样子啊,明显要更陡一下。这是因为x轴和y轴的比例不一致而导致的视觉效果,如果轴的比例之后显示是这样的,这样看上去就对了 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/31/1659239689052128331.png) 这里预测值大于实际值,差值是0.7,很自然的,要让预测值更接近实际值的话,需要让预测值变小。想让预测值变小,很简单的就让w变小,变小多少呢?这里的梯度是一个正值,那就先变小一个梯度呗。也就是w-grad。 来看一下梯度为负的情况。当w=0时, ```python x,y=0.5,0.8 w=0 ​ pred=x*w rss=(pred-y)**2/2 grad=(pred-y)*x print('w=0时,预测值:'+str(pred)) print('w=0时,残差平方和:'+str(round(rss,ndigits=3))) print('w=0时,RSS(w)的梯度:'+str(grad)) #w=0时,预测值:0.0 #w=0时,残差平方和:0.32 #w=0时,RSS(w)的梯度:-0.4 ``` ```python plt.plot(w_vec,rss_vec) plt.scatter(w,rss,s=100,c='y',marker='o') plt.plot(np.linspace(-0.5,0.5,50),\ np.linspace(-0.5,0.5,50)*(-0.4)+0.32,\ '--',linewidth=2.0) plt.xlabel('w',fontsize=16) plt.ylabel('RSS',fontsize=16) #ax=plt.gca() #ax.set_aspect(1) plt.show() ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/31/1659243618936980199.png) 当w=0时,预测值小于真实值0.8。很自然的,要让预测值更接近实际值的话,需要让预测值变大。想让预测值变大,很简单的就让w变大,变大多少呢?这里的梯度是一个负值,那就先变大一个负的梯度呗。就是w+(-grad),巧了,也是w-grad。 所以无论w的初始值是在哪边,$w=w-\nabla_wRSS(w)$ 都可以让w朝着RSS变小的方向移动。RSS最小的地方,就是我们寻找的地方,因为在这个地方预测值和真实值的差异最小,也就是说预测值最接近真实值。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/31/1659244208189864369.png) 这个算法就是梯度下降法,在更新w的过程中,加入了一个系数$\alpha$,他是一个比较小的正数,叫做`学习步长`,这样可以让w更新的速度变慢一些,使得w更容易收敛。
  • [技术干货] (转)2022年最新深度学习入门指南
    概述深度学习是用于处理视觉相关任务的强大的方法。卷积神经网络是一种深度学习模型,我们用它来处理与计算机视觉相关的应用程序。在本指南中,我们将探索 CNN 的工作原理以及它们如何应用于图像分类任务。我们还将构建一个 CNN 模型,并使用 Keras 从头开始在训练数据集上对其进行训练。介绍我一直着迷于深度学习模型的潜力和力量,以及它们如何理解执行图像分类、图像分割、对象检测等任务。我们还遇到了一些分割算法,例如来自 X-的肿瘤/异常检测,他们在这方面的表现甚至优于医生。在本指南中,我们将全面介绍 CNN 及其在图像分类任务中的应用。我们将首先介绍卷积神经网络 (CNN) 背后的基本理论、它们的工作原理以及它们如何成为用于任何计算机视觉任务的最流行的模型之一。现在让我们开始吧……卷积神经网络CNN 或卷积神经网络是将图像作为输入并通过使用卷积运算学习图像中的局部模式的算法。而密集层/全连接层则从输入中学习全局模式。CNN 的学习局部模式具有两个特性:CNN 学习的模式是不变的,即在学习识别图像左下角的特定模式后,CNN 可以识别图像中的任何位置。但是,如果密集连接的网络出现在新位置的任何位置,则必须重新学习该模式。这使得 CNN 在处理和理解图像时具有数据效率。CNN 可以学习模式的空间层次,即第一个卷积层学习一个小的局部模式,如边缘或线,第二个卷积层学习由第一个卷积层学习的特征组成的更大的模式,依此类推。通过这种方式,CNN 学习和理解了越来越复杂和抽象的视觉概念。让我们看看下面的猫图,在这里我们可以看到,在第一个卷积层中,学习了边缘、曲线等模式。但在第二层 CNN 中,眼睛、鼻子或耳朵等特征是通过使用第一层的模式来检测的。通过这种方式,CNN了解图像并了解图像中的对象。 参考特征提取现在让我们探索并了解它是如何工作的。卷积运算卷积是应用于 3D 张量的操作,称为特征图。这些特征图由两个空间轴(高度和宽度)和一个深度轴(或通道轴)组成。如果我们考虑 RGB 图像的示例,高度和宽度构成空间轴,3 个颜色通道表示深度轴。类似地,对于黑白图像,深度为 1。但在其他层的输出中,深度不是由颜色通道表示,而是代表过滤器。过滤器对输入数据的特定方面进行编码,即过滤器可以对“面部存在”或“汽车结构”等概念进行编码。卷积运算由两个关键参数组成,内核大小:应用于图像的过滤器的大小。这些是典型的 3×3 或 5×5。输出特征图的深度:这是卷积计算的输出滤波器的数量。卷积操作只是在输入特征图上乘加加权滤波器,以生成另一个具有不同宽度、高度和深度的 3D 张量。卷积操作通过在 3D 输入特征图上滑动这些大小为 3×3 或 5×5 过滤器的窗口,在每个可能的位置停止,然后计算特征。我们可以在下面的 gif 中看到操作,3×3 内核在 5×5 输入特征图上运行以生成 3×3 输出。 参考卷积重要的是要注意网络从给定数据中学习所需的最佳过滤器。CNN 模型的权重是过滤器。现在让我们看看边框效果、填充和步幅。了解边框效果和填充现在再次让我们考虑 5×5 特征图(参考上面的 gif)。过滤器的大小为 3×3,因此有 9 个图块。现在在卷积操作期间,3×3 滤波器只能通过 5×5 特征图 9 次,因此我们的输出大小为 3×3。所以输出在这里从 5×5 缩小到 3×3,也就是说,在每个维度旁边缩小了两个图块。这里没有对输入特征图应用填充,因此称为有效填充。如果我们希望输出特征图与输入特征图的大小相同,我们需要使用填充。填充包括在输入特征图的每一侧添加适当数量的行和列,以使每个输入图块周围的中心卷积窗口成为可能。这种类型的填充称为相同的填充。以下 GIF 表示相同的填充。 源边框效果和填充现在我们可以看到,当我们向 5×5 特征图添加额外的填充并应用 3×3 过滤器时,我们将能够获得与输入特征图大小相同的输出特征图。如何找到要添加到给定过滤器大小和特征图的填充?当我们遇到不同大小的特征图和过滤器以及我们如何确定对于有效和相同的情况应该使用多少填充时,自然会出现这个问题。所以要回答这个问题,我们有确定填充的公式,即有效填充:因为有效填充意味着没有填充,所以padding的数量将为0。相同填充:我们使用相同的填充来保留输入特征图的大小。但是卷积的输出主要取决于过滤器的大小,与输入大小无关。因此,可以根据过滤器大小确定填充,如下所示:相同填充 =(过滤器大小 - 1)/ 2现在让我们看看另一个可以影响输出大小的因素,即步幅。了解步幅步幅是影响输出特征图大小的因素之一。步幅是应用过滤器的两个连续窗口之间的距离。在上面的例子中,我们已经看到过滤器作为窗口被应用于输入特征图,并被移动一个单位或步幅。当这种转变大于1时,我们将其定义为跨步的CNN。下面的GIF是一个大步为2的CNN的例子。我们还可以观察到,当我们使用步幅的值为 2(或大于 1)时,与常规卷积(当 stride 的值 = 1 时)相比,输出特征图的大小减小(下采样因子为 2) . 因此我们可以说使用步幅是对输入特征图进行下采样的方法之一。但它们在实践中很少使用,但它仍然是 CNN 的重要概念之一,了解它是很好的。现在在开始 CNN 的实现之前,让我们看一下用于对输入特征进行下采样的另一个重要概念,即池化。理解池化池化操作可以定义为一种通过使用不同的策略(例如取平均值、最大值、总和等)来积极减小/下采样输入特征图的大小的方法。现在让我们看看不同类型的池化1.最大池化:最大池化是一种广泛使用的池化策略,用于对输入特征图进行下采样。在这一层中,确定大小的窗口通过输入特征图,然后获得最大值并计算为下一层或输出特征图。我们可以在下面的 GIF 中看到,当我们使用过滤器大小 2 执行最大池化时,输入特征被下采样因子 2 或减半。我们可以通过以下公式确定使用最大池化后输出的大小:输出大小=输入大小/(池化过滤器大小) 还有其他类型的池化策略,例如考虑窗口平均值的平均池化和考虑窗口权重总和的求和池化。但最大池化一直是最流行和最广泛使用的池化策略。这是因为当我们考虑过滤器窗口的最大值时,我们将能够将有关输入特征/当前特征图的大部分可用信息转移到下一个特征图。因此,当我们通过神经网络的层进行传播时,减少了数据的丢失。既然我们对 CNN 的工作原理有了一些了解,那么现在让我们从头开始实现一个 CNN。从头开始训练基于 CNN 的图像分类器现在让我们在 MNIST 数据集上训练一个 CNN 模型。MNIST 数据集由 0 到 9 的手写数字图像组成,即 10 个类。训练集由 60000 张图像组成,测试集由 10000 张图像组成。让我们使用 CNN 从头开始训练图像分类器。我们将在Keras框架中实现代码。Keras 是最受欢迎和使用最广泛的深度学习库之一。它是作为高级 API 构建的,可以轻松使用 TensorFlow。要完成以下代码实现,建议使用带有 GPU 的 Jupyter Notebook。可以通过Google Colaboratory访问相同的内容,该实验室提供基于云的 Jupyter Notebook环境和免费的 Nvidia GPU。现在让我们开始吧获取 MNIST 数据集在下载数据集之前,让我们进行必要的导入,from tensorflow.keras.datasets import mnistfrom tensorflow.keras.utils import to_categoricalfrom tensorflow.keras import layersfrom tensorflow.keras import modelsimport numpy as npimport matplotlib.pyplot as pltfrom matplotlib import pyplot现在让我们下载数据,(train_images, train_labels), (test_images, test_labels) = mnist.load_data()上面的代码下载数据并缓存。由于我们正在加载预定义的数据集,因此该数据集已经被预处理并以元组的形式打包。现在让我们探索我们解压出来的这些张量的形状,int("Shape of training dataset: ",train_images.shape)print("Shape of test dataset: ",test_images.shape)print("Shape of training dataset labels: ",train_labels.shape)print("Shape of test dataset labels: ",test_labels.shape)输出: 从上面的输出我们可以看到,训练数据集有 60000 张图片,每张图片的大小为 28×28。同样,测试数据集有 10000 张图像,图像大小为 28×28。我们还可以看到标签没有任何形状,即它是一个标量值。让我们看看一些标签,print(train_labels)print(type(train_labels))输出:我们可以看到标签都在一个 NumPy 数组中。现在让我们看看我们的一些训练图像,# plot first few imagesfor i in range(9):   # define subplot   pyplot.subplot(330 + 1 + i)   # plot raw pixel data   pyplot.imshow(train_images[i], cmap=pyplot.get_cmap('gray'))# show the figurepyplot.show()输出: 我们可以通过绘制它们来可视化训练样本。在我们继续模型训练之前,让我们对我们的数据进行一些预处理。基本预处理现在让我们将图像从 (60000, 28, 28) 重塑为 (60000, 28, 28, 1) 大小,其中最后一个维度表示图像的深度。我们之前已经看到,每个图像的特征图都有三个维度,即宽度、高度和深度。由于 MNIST 训练集由黑白图像组成,我们可以将深度定义为 1。接下来,我们应该对数据集进行归一化,即将输入的所有值都在 0 和 1 之间。由于图像层的最大值是 255,我们将整个数据集除以 255。train_images = train_images.reshape((60000, 28, 28, 1))train_images = train_images.astype('float32') / 255现在让我们也对测试集应用相同的预处理。test_images = test_images.reshape((10000, 28, 28, 1))test_images = test_images.astype('float32') / 255最后,让我们将标签转换为分类格式,即它们目前作为标量,但我们正在执行 One-Hot 编码以将每个标量唯一地映射到向量。train_labels = to_categorical(train_labels)test_labels = to_categorical(test_labels)train_labels[:10]输出: 我们可以看到训练标签是独热编码。现在让我们使用 Keras 创建一个基本的 CNN 模型。使用 Tensorflow-Keras 创建 CNN 模型现在让我们使用 Keras 库创建一个基本模型,model = models.Sequential()model.add(layers.Conv2D(32, (3,3), activation='relu', input_shape=(28,28,1)))model.add(layers.MaxPool2D((2,2)))model.add(layers.Conv2D(64, (3,3), activation='relu'))model.add(layers.MaxPool2D((2,2)))model.add(layers.Conv2D(64, (3,3), activation='relu'))现在我们来分析一下上面的代码,首先,我们正在创建一个Sequential类型类的对象。Sequential 模型是一种模型,我们可以在其中添加和堆叠层以形成端到端模型。使用**.add**我们通过根据层指定各种参数来将层添加到我们的模型中。在上面的模型中,我们添加了一个卷积层(即 Keras 中的 Conv2D),它接受许多过滤器、内核大小和激活函数作为参数。接下来,添加最大池化层(即 Keras 中的 MaxPool2D)以启用池化操作。Keras 中提供了不同类型的层。模型的上述部分负责识别和检测输入数据中存在的模式。(我们上面讨论过的工作)现在最后让我们通过定义模型的输出数量来初始化头部。model.add(layers.Flatten())model.add(layers.Dense(64, activation='relu'))model.add(layers.Dense(10, activation='softmax'))现在我们的模型已经准备好了。我们可以使用**.summary()**方法查看模型中所有层的列表 。model.summary()输出: 现在让我们通过分配优化器、损失函数和模型训练时使用的指标来编译模型。model.compile(optimizer='rmsprop',             loss='categorical_crossentropy',             metrics=['accuracy'])现在让我们用训练数据和标签拟合模型并训练 5 个 epochsmodel.fit(train_images, train_labels, epochs=5, batch_size=64)结果: 从训练结果中我们可以看出,该模型能够达到高达 99% 的准确率,这真是令人印象深刻!!结论我们已经看到了卷积神经网络的底层功能以及它如何从图像中提取特征。因此,我们可以得出结论,卷积神经网络是在计算机视觉应用中产生最先进结果的技术之一。
  • [其他] 《深度学习入门》笔记 - 07
    继续线性模型, 有了预测值,和真实值比较一下,就知道预测结果准确不准确?用预测值减去真实值 $\hat{y}-y$ ,如果是正数,说明预测值偏高,如果是负数说明偏低。通常使用差的平方来衡量预测误差。 ```python target=ad.values[0][3] loss=(pred-target)**2 print(loss) #466.9921 ``` 而用残差平方之和,可以用来衡量模型总的预测误差。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/30/1659175611327837960.png) ```python rss=0 for i in range(len(ad)): input=ad.values[i][0:3] pred=linear_mode(input,w,b) target=ad.values[i][3] rss+=(pred-target)**2/2/len(ad) print(rss) #143.69051025000013 ``` 这里乘以常数1/2,说是为了稍后估计b和w方便一些。 很自然的,我们要最小化 $RSS(b,w)$,得到这种情况下b和w的估计值。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/30/1659176127446262514.png) $RSS(b,w)$ 也叫做目标函数或者损失函数,它值叫做预测误差或者模型误差。求它的最小值的方法有很多,最常见的方法是`求偏导数`,然后令这些偏导数等于零,解方程得到b和w的估计值。但是这个方法只适合少数结构比较简单的模型(比如线性回归模型),不能求解深度学习这类复杂模型的参数。 所以下面介绍的是深度学习中常用的优化算法:`梯度下降法`。其中有三个不同的变体:随机梯度下降法、全数据梯度下降法、和批量随机梯度下降法。
  • [其他] 《深度学习入门》笔记 - 06
    下面是一个简单的例子来介绍线性回归模型。 数据是在多个市场的3个不同渠道的广告投入以及商品销量。 这个模型的意义也就很明白了,那就是找出在这3个不同渠道广告投入与最终的商品销量之间的关系。 先把数据可视化: ```python %config InlineBackend.figure_format='retina' import pandas as pd import matplotlib.pyplot as plt import numpy as np ad = pd.read_csv('./Ad.csv') ad.head() ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/30/1659172287079530852.png) ```python plt.scatter('TV','sales',data=ad,label='TV',marker='D') plt.scatter('radio','sales',data=ad,marker='o',label='radio') plt.scatter('newspaper','sales',data=ad,marker='s',label='newspaper') plt.legend(loc='lower right') plt.xlabel('Advertising Budgets',fontsize=16) plt.ylabel('Sales',fontsize=16) plt.show ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/30/1659172325527435527.png) 再来看一下数学上对这些数据的表达, 第1个观测点的输入向量 x1=(x11,x12,x13), 第2个观测点的输入向量 x2=(x21,x22,x23) 那么因变量矩阵X表示为: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/30/1659172706887212234.png) 因变量向量y表示为: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/30/1659172723864238646.png) 前一节已经讲过线性回归模型的数学公式的表达,这里我们先假设给定截距项b和自变量权重w,至于误差这里不管,那么我们就可以写出预测函数了。 ```python def linear_mode(input,weight,b): prediction=np.sum(input*weight)+b return prediction b=10 w=np.array([0.1,0.1,0.1]) input_0=ad.values[0][0:3] print('第一个观测点的自变量向量:'+str(input_0)) pred=linear_mode(input_0,w,b) print('第一个观测点的预测值:'+str(pred)) ``` ```html 第一个观测点的自变量向量:[230.1 37.8 69.2] 第一个观测点的预测值:43.71 ```
总条数:1031 到第
上滑加载中