数模历程

  对于数模的学习主要围绕两个比赛:全国大学生数学建模(CUMCM)和美国大学生数学建模竞赛(MCM/ICM),主要的学习方法就是借助比赛中的优秀论文进行学习,分析。望能学习处理问题的相关思路和数学方法。会将将论文进行专题性质的探讨以及优秀论文中出现的一些算法进行详尽研究。

  数学建模,一直觉得是很神奇的,让我们更加理性的,真实的看待这个世界。每个建立的模型,都相当于一个黑盒子,它是由各类数学方法,思想构建的,当你输入相关的因素或将它置于一个适合它的环境,它给你,你之前看不到的,或是不久的未来。仿佛创造出一个新的工具,可以看清未来的一角。(我不管我就喜欢瞎bb)

     得儿,驾

Opencv16_21章,学习笔记

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import cv2

16.平滑处理,模糊处理

使用低通滤波器可以达到图像模糊的目的。这对与去除噪音很有帮助。其实就是去除图像中的高频成分

自定义滤波器

img = cv2.imread("001.PNG")
cv2.imshow("img1",img)
cv2.imshow("img2",dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

kernel = np.ones((5,5),np.float32) / 25
dst = cv2.filter2D(img,-1,kernel)
plt.subplot(121),plt.imshow(img),plt.title("Original")
plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(dst),plt.title("Averaging")
plt.xticks([]),plt.yticks([])
plt.show()

png

dst.shape
(312, 600, 3)
img.shape
(312, 600, 3)

平均

归一化卷积框,与上面的例子相似

blur = cv2.blur(img,(5,5))#利用5 * 5的卷积核进行优化

高斯模糊

构建高斯核,需要输入高宽,以及x,y的标准差

blur = cv2.GaussianBlur(img,(5,5),0)# 0表示标准差由函数自己定义

中值模糊

卷积框对应像素的中值来替代中心像素的值,常用来去除椒盐噪声

blur = cv2.medianBlur(img,5)

双边滤波(双重高斯,厉害)

在保持边界清晰的情况下有效的去除噪音。但是这种操作与其他滤波器相比会比较慢。
双边滤波在同时使用空间高斯权重和灰度值相似性高斯权重,灰度值相似性高斯函数确保只有
与中心像素灰度值相近的才会被用来做模糊运算。

blur = cv2.bilateralFilter(img,9,75,75)#9 邻域直径,两个75 分别是空间高斯函数标准差,灰度值相似性高斯函数标准差
cv2.imshow("img1",img)
cv2.imshow("img2",blur)
cv2.waitKey(0)
cv2.destroyAllWindows()

17.形态学转换

一般情况下对二值化图像进行的操作。
只有0和1才能进行相关操作计算

腐蚀

卷积核沿着图像滑动,如果与卷积核对应的原图像的所有像素值都是1,那么中心元素就保持原来的像素值,否则就变为零。

img1 = cv2.imread("001.PNG",0)
kernel = np.ones((3,3),np.uint8) * 255
erosion = cv2.erode(th_img1,kernel,iterations = 1)

膨胀

卷积核对应的原图像的像素值中只要有一个是1,中心元素的像素值就是1

dilation = cv2.dilate(th_img1,kernel,iterations = 1)

开运算

先进性腐蚀再进行膨胀就叫做开运算。被用来去除噪声

opening = cv2.morphologyEx(th_img1, cv2.MORPH_OPEN, kernel)

闭运算

先膨胀再腐蚀。它经常被用来填充前景物体中的小洞,或者前景物体上的小黑点。

closing = cv2.morphologyEx(img1, cv2.MORPH_CLOSE, kernel)

形态学梯度

就是一幅图像膨胀与腐蚀的差别,看上去就像前景物体的轮廓

gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)

礼帽

原始图像与进行开运算之后得到的图像的差。

tophat = cv2.morphologyEx(img1, cv2.MORPH_TOPHAT, kernel)

黑帽

进行闭运算之后得到的图像与原始图像的差

blackhat = cv2.morphologyEx(img1, cv2.MORPH_BLACKHAT, kernel)
cv2.imshow("img1",th_img1)
cv2.imshow("img2",tophat)
cv2.waitKey(0)
cv2.destroyAllWindows()
ret1, th_img1 = cv2.threshold(img1, 200, 255, cv2.THRESH_BINARY)

结构化元素

构建一个椭圆形/圆形的核

# Rectangular Kernel
cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
# Elliptical Kernel
cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
# Cross-shaped Kernel
cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))

array([[0, 0, 1, 0, 0],
       [0, 0, 1, 0, 0],
       [1, 1, 1, 1, 1],
       [0, 0, 1, 0, 0],
       [0, 0, 1, 0, 0]], dtype=uint8)
img2 = np.ones((300,300),np.uint8)
cv2.imshow("img2",img2 * 150)
cv2.waitKey(0)
cv2.destroyAllWindows()

总结:

关于灰度图:
0-255表示的是不同灰度,0表示纯黑,255表示白色,150表示灰度

总结:

高斯平滑与微分操作的结合体,所以它的抗噪声能力很好,这句话很有意思,即每个核都有抗噪能力,只是大小,由核本身的原因,

问题:

对于二值图的核都是1,而二值图都是255,那么它的核应该是255吗

18.图像梯度

OpenCV 提供了三种不同的梯度滤波器,或者说高通滤波器:Sobel,
Scharr 和Laplacian。我们会意义介绍他们。
Sobel,Scharr 其实就是求一阶或二阶导数。Scharr 是对Sobel(使用
小的卷积核求解求解梯度角度时)的优化。Laplacian 是求二阶导数

img=cv2.imread('001.PNG',0)
#cv2.CV_64F 输出图像的深度(数据类型),可以使用-1, 与原图像保持一致np.uint8
laplacian=cv2.Laplacian(img,cv2.CV_64F)
# 参数1,0 为只在x 方向求一阶导数,最大可以求2 阶导数。
sobelx=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
# 参数0,1 为只在y 方向求一阶导数,最大可以求2 阶导数。
sobely=cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)
plt.figure(figsize=(12, 10))
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()

png

当我们可以通过参
数-1 来设定输出图像的深度(数据类型)与原图像保持一致,但是我们在代
码中使用的却是cv2.CV_64F。如果原图像的深度是
np.int8 时,所有的负值都会被截断变成0,换句话说就是把把边界丢失掉。
所以如果这两种边界你都想检测到,最好的的办法就是将输出的数据类型
设置的更高,比如cv2.CV_16S,cv2.CV_64F 等。取绝对值然后再把它转回
到cv2.CV_8U。

总结:

特别注意:对于求导后输出的数值,需要比原来的数值范围大,否则如果用-1的默认,那么在边界上会少了负值的那一部分

19.Canny 边缘检测

1.噪声去除
2.计算图像梯度
3.非极大值抑制
4.滞后阈值

img = cv2.imread('001.PNG',0)
edges = cv2.Canny(img,100,200)
plt.figure(figsize=(12, 10))
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()

png

问题:

非极大值抑制的理解

20.图像金字塔

需要对同一图像的不同分辨率的子图像进行处理。们需要创建创建一组图像,这些图像是具有不同分辨率的原始图像

1.高斯金字塔 和 拉普拉金字塔

Li = Gi – PyrUp (Gi+1)
拉普拉斯金字塔可以有高斯金字塔计算
拉普拉金字塔的图像看起来就像边界图,其中很多像素都是0。他们经常
被用在图像压缩中。

img = cv2.imread('001.PNG')
lower_reso = cv2.pyrDown(lower_reso)#四分之一
higher_reso2 = cv2.pyrUp(img)#四倍
height,width  = img.shape[:2]
res=cv2.resize(img,(2*width,2*height),interpolation=cv2.INTER_CUBIC)#通过其他方式进行缩放
cv2.imshow("img",img)
cv2.imshow("img1",higher_reso2)
cv2.imshow("img2",res)
cv2.waitKey(0)
cv2.destroyAllWindows()

问题:

img2(resize)的放缩比img1(pyrUp)的放缩更加清晰,pyrUp的填充方式是什么

2 使用金字塔进行图像融合

高斯金字塔计算拉普拉斯金字塔,每一层进行融合

A = cv2.imread("apple.jpg")
B = cv2.imread("orange.jpg")

#构建高斯金字塔
G = A.copy()
gpA = [G]
for i in range(6):
    G = cv2.pyrDown(G)
    gpA.append(G)

G = B.copy()
gpB = [G]
for i in range(6):
    G = cv2.pyrDown(G)
    gpB.append(G)


i = 1
GE = cv2.pyrUp(gpA[i])
L = cv2.subtract(gpA[i - 1],GE)
lpA = [gpA[5]]#此时为最小高斯
for i in range(5,0,-1):
    GE = cv2.pyrUp(gpA[i])
    L = cv2.subtract(gpA[i - 1],GE)
    lpA.append(L)

lpB = [gpB[5]]
for i in range(5,0,-1):
    GE = cv2.pyrUp(gpB[i])
    L = cv2.subtract(gpB[i - 1],GE)
    lpB.append(L)

## 此时在拉普拉斯的图像矩阵中,大部分都是很接近0在100以内,因此从视觉上,人眼无法明确感知,但是对于机器他们真实存在
## 正是这种强大,数学量级决定了,机器的强大,神经网络捕捉到人眼所不能感知的特征
## 将两个拉普拉斯进行拼接
LS = []
for la,lb in zip(lpA,lpB):
    rows,cols,dpt = la.shape
    ls = np.hstack((la[:,0:int(cols/2)],lb[:,int(cols/2):]))
    LS.append(ls)
## 此时在LS中显示的还是人眼不可识别的图案,还是低量    
ls_ = LS[0]
for i in range(1,6):
    ls_ = cv2.pyrUp(ls_)
    ls_ = cv2.add(ls_,LS[i])
## 卧槽这里是将拉普拉斯金字塔进行累加直接就得到一个肉眼可见的举证
b= ls
for _ in range(1,6):
    b = cv2.add(b,b)
cv2.imshow("Pyramid_blending2.jpg",LS[0])
cv2.imshow("Direct_blending.jpg",real)
cv2.waitKey(0)
cv2.destroyAllWindows()

总结:

关于图形融合的原理:是得到一个LS的列表,在LS[0],是高斯金字塔的最小一个分辨率原图,主要是提供颜色的,此时已经拼接好了,
其他几层则是拉普拉斯的金字塔图,主要是提供类似边界的,因为直接将LS[0]放大会很模糊,所以利用拉普拉斯提供组合图的边界
其中一层的拉普拉斯图人眼不可观察,需要多层进行累加才能观察正如上面b

21.OpenCV 中的轮廓

1.查找轮廓和画出轮廓

im = cv2.imread('001.PNG')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,0)
#第三个参数为simple是会将不必要点不存储
image, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
img = cv2.drawContours(im, contours, -1, (0,0,0), 3)#在原始图像中画出
contours[0]
array([[[599, 124]],

       [[598, 125]],

       [[599, 125]]], dtype=int32)
plt.figure(figsize=(12,7))
plt.subplot(121),plt.imshow(image,cmap = 'gray'),plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB),cmap = 'gray'),plt.xticks([]),plt.yticks([])
(<matplotlib.axes._subplots.AxesSubplot at 0x18308d13080>,
 <matplotlib.image.AxesImage at 0x18308b62ac8>,
 ([], <a list of 0 Text xticklabel objects>),
 ([], <a list of 0 Text yticklabel objects>))

png

总结:

在用plt显示3通道图的时候,需要进行转换cv2.COLOR_BGR2RGB

2.轮廓特征

1.矩

cv2.moments() 会将计算得到的矩以一个字典的形式返回

img = cv2.imread('001.PNG',0)
ret,thresh = cv2.threshold(img,127,255,0)
_,contours,hierarchy = cv2.findContours(thresh,1,2)
cnt = contours[0]
M = cv2.moments(cnt)#此时的质为其中一个轮廓的质
print(M)
{'m00': 28.0, 'm10': 16506.0, 'm01': 8596.0, 'm20': 9730746.666666666, 'm11': 5067342.0, 'm02': 2638981.0, 'm30': 5736817107.0, 'm21': 2987339226.6666665, 'm12': 1555679299.5, 'm03': 810172693.0, 'mu20': 459.6666666660458, 'mu11': 0.0, 'mu02': 9.0, 'mu30': 9.5367431640625e-07, 'mu21': 3.166496753692627e-08, 'mu12': 0.0, 'mu03': 0.0, 'nu20': 0.5863095238087318, 'nu11': 0.0, 'nu02': 0.011479591836734693, 'nu30': 2.2988202195339494e-10, 'nu21': 7.632801510171317e-12, 'nu12': 0.0, 'nu03': 0.0}
# 计算重心
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
(cx,cy)
(589, 307)
#计算轮廓的面积
area = cv2.contourArea(cnt)
area
28.0
# 计算周长
perimeter = cv2.arcLength(cnt,True)
perimeter
31.656854152679443

轮廓近似

img = cv2.imread('002.jpg')
img1 = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(img1,127,255,0)

#在将轮廓提取的时候,必须是二值图(3通道,到灰度图,再到二值图)
_,contours,hierarchy = cv2.findContours(thresh,1,2)
cnt = contours[0]
epsilon = 0.02*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
#第二个参数叫epsilon,它是从原始轮廓到近似轮廓的最大距离。它是一个准确度参数。第三个参数设定弧线是否闭合
img2 = img.copy()
im = cv2.drawContours(img,[cnt] , -1, (0,255,0), 3)
imm = cv2.drawContours(img2,[approx] , -1, (0,0,255), 3)
plt.subplot(121),plt.imshow(cv2.cvtColor(im,cv2.COLOR_BGR2RGB)),plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(cv2.cvtColor(imm,cv2.COLOR_BGR2RGB)),plt.xticks([]),plt.yticks([])

(<matplotlib.axes._subplots.AxesSubplot at 0x1830e87ffd0>,
 <matplotlib.image.AxesImage at 0x1830e89f4e0>,
 ([], <a list of 0 Text xticklabel objects>),
 ([], <a list of 0 Text yticklabel objects>))

png

总结:

  1. 在使用cv2.drawContours(img,[cnt] , -1, (0,255,0), 3)进行画图的时候,需要注意两个点,1.是它会在输入的img上
    直接操作,2.它对于输入的轮廓上特征点,必须是list的形式,否则它只会画出单独的点
  2. 可以利用epsilon = 0.1*cv2.arcLength(cnt,True)
    approx = cv2.approxPolyDP(cnt,epsilon,True),可以将轮廓的特征点进一步压缩

凸包

hull = cv2.convexHull(cnt)
hull
array([[[199, 139]],

       [[ 16, 139]],

       [[ 16,  15]],

       [[199,  15]]], dtype=int32)
hull = cv2.convexHull(cnt,returnPoints= False)
#hull = cv2.convexHull(points[, hull[, clockwise[, returnPoints]]
#points 我们要传入的轮廓
# • hull 输出,通常不需要
# • clockwise 方向标志。如果设置为 True,输出的凸包是顺时针方向的。
# 否则为逆时针方向。
# • returnPoints 默认值为 True。它会返回凸包上点的坐标。如果设置
# 为 False,就会返回与凸包点对应的轮廓上的点。
hull
array([[128],
       [ 69],
       [  0],
       [143]], dtype=int32)
#凸包检测
cv2.isContourConvex(cnt)
False

边界矩形

x,y,w,h = cv2.boundingRect(cnt)#计算出轮廓的边界点
img = cv2.imread('002.jpg')
img2 = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
plt.imshow(cv2.cvtColor(img2,cv2.COLOR_BGR2RGB)),plt.xticks([]),plt.yticks([])
(<matplotlib.image.AxesImage at 0x1830e935978>,
 ([], <a list of 0 Text xticklabel objects>),
 ([], <a list of 0 Text yticklabel objects>))

png

旋转的边界矩形

# x,y,w,h = cv2.boundingRect(cnt)
# img3 = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)

rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
im = cv2.drawContours(im,[box],0,(0,0,255),2)
plt.imshow(cv2.cvtColor(img3,cv2.COLOR_BGR2RGB)),plt.xticks([]),plt.yticks([])
(<matplotlib.image.AxesImage at 0x1830e958b70>,
 ([], <a list of 0 Text xticklabel objects>),
 ([], <a list of 0 Text yticklabel objects>))

png

最小外接圆

(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img4 = cv2.circle(img,center,radius,(0,255,0),2)

椭圆拟合

ellipse = cv2.fitEllipse(cnt)
img5 = cv2.ellipse(img,ellipse,(0,255,0),2)

直线拟合

[vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01)
rows,cols = img.shape[:2]
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
img = cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)

总结:

上面的图形都是将轮廓提取出一些点,来画出外接的一些图形,可以在物体追踪的时候,标记目标目标

轮廓的性质

img = cv2.imread('002.jpg',0)
ret,thresh = cv2.threshold(img,127,255,0)
_,contours,hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]

#长宽比
x,y,w,h = cv2.boundingRect(cnt)
aspect_ratio = float(w)/h

#Extent:轮廓面积与边界矩形面积的比
area = cv2.contourArea(cnt)
x,y,w,h = cv2.boundingRect(cnt)
rect_area = w*h
extent = float(area)/rect_area
#Solidity:轮廓面积与凸包面积的比
hull = cv2.convexHull(cnt)
hull_area = cv2.contourArea(hull)
solidity = float(area)/hull_area
#Equivalent Diameter:与轮廓面积相等的圆形的直径
equi_diameter = np.sqrt(4*area/np.pi)

#方向:对象的方向,下面的方法还会返回长轴和短轴的长度
(x,y),(MA,ma),angle = cv2.fitEllipse(cnt)

mask = np.zeros(thresh.shape,np.uint8)
# 这里一定要使用参数-1, 绘制填充的的轮廓
plt.subplot(121),plt.imshow(mask,"gray"),plt.xticks([]),plt.yticks([])
cv2.drawContours(mask,[cnt],0,255,3)
plt.subplot(122),plt.imshow(mask,"gray"),plt.xticks([]),plt.yticks([])
(<matplotlib.axes._subplots.AxesSubplot at 0x204e105ccc0>,
 <matplotlib.image.AxesImage at 0x204e11ed3c8>,
 ([], <a list of 0 Text xticklabel objects>),
 ([], <a list of 0 Text yticklabel objects>))

png

#【7】最大值和最小值及它们的位置
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(thresh,mask = mask)
#【8】平均颜色及平均灰度
mean_val = cv2.mean(thresh,mask = mask)
#【9】极点:一个对象最上面,最下面,最左边,最右边的点
leftmost = tuple(cnt[cnt[:,:,0].argmin()][0])
rightmost = tuple(cnt[cnt[:,:,0].argmax()][0])
topmost = tuple(cnt[cnt[:,:,1].argmin()][0])
bottommost = tuple(cnt[cnt[:,:,1].argmax()][0])

img = cv2.imread('002.jpg')
cv2.circle(img,leftmost, 4,255, 5)
cv2.circle(img,rightmost, 4,255, 5)
cv2.circle(img,topmost, 4,255, 5)
cv2.circle(img,bottommost, 4,255, 5)
# cv2.drawContours(img,[list(leftmost),rightmost,topmost,bottommost],0,255,3)
plt.imshow(img),plt.xticks([]),plt.yticks([])
(<matplotlib.image.AxesImage at 0x204e0df62e8>,
 ([], <a list of 0 Text xticklabel objects>),
 ([], <a list of 0 Text yticklabel objects>))

png

轮廓:更多函数m

画出凸缺陷

img = cv2.imread('002.jpg')
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(img_gray, 127, 255,0)
_,contours,hierarchy = cv2.findContours(thresh,2,1)
cnt = contours[0]
hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)
for i in range(defects.shape[0]):
    s,e,f,d = defects[i,0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv2.line(img,start,end,[0,255,0],2)
    cv2.circle(img,far,5,[0,0,255],-1)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

defects[1,0]
array([ 609,    0,  661, 7168], dtype=int32)
defects.shape
(4, 1, 4)

点到轮廓的距离

dist = cv2.pointPolygonTest(cnt,(50,50),True)

轮廓匹配

匹配原则:Hu 矩是归一化中心矩的线性组合,之所以这样做是为了能够获取代表
图像的某个特征的矩函数,这些矩函数对某些变化如缩放,旋转,镜像映射(除
了 h1)具有不变形。强大的性质(不变形性)决定了强大的用途

img1 = cv2.imread('002.jpg',0)
img2 = cv2.imread('001.jpg',0)
ret, thresh = cv2.threshold(img1, 127, 255,0)
ret, thresh2 = cv2.threshold(img2, 127, 255,0)
_,contours,hierarchy = cv2.findContours(thresh,2,1)
cnt1 = contours[0]
_,contours,hierarchy = cv2.findContours(thresh2,2,1)
cnt2 = contours[0]
ret = cv2.matchShapes(cnt1,cnt2,1,0.0)
print(ret)

0.0767338447391066
plt.subplot(121),plt.imshow(img1,"gray"),plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(img2,"gray"),plt.xticks([]),plt.yticks([])
(<matplotlib.axes._subplots.AxesSubplot at 0x204e5888080>,
 <matplotlib.image.AxesImage at 0x204e58d6240>,
 ([], <a list of 0 Text xticklabel objects>),
 ([], <a list of 0 Text yticklabel objects>))

png

轮廓的层次结构

需要注意的是它进行轮廓划分的时候,会将一个轮廓,当它包含着其他轮廓时,划分成两个例如3,3a
在模式选择里,RETR_EXTERNAL,用这种模式的话只会返回最外边的轮廓(第 0 级):轮廓
0,1,2
RETR_LIST,是提取所有的轮廓
RETR_CCOMP,在这种模式下会返回所有的轮廓并将轮廓分为两级组织结
构,闭环内轮廓为2,外为1,不闭环的为1
RETR_TREE,返回
所有轮廓,并且创建一个完整的组织结构列表。
[Next,Previous,First_Child,Parent]


2.27 一日小记

1. 优化了实训系统(第一阶段小数据集已经完成)

2. opencv_python的第3-5章的学习

3. 学习了pytorch 对于时序数据的处理(1.n元语法 2.第二种建立索引的方式 3.随机采样的实现)

4. Leetcode 鸡蛋掉落问题

2.26 一日小记

1. 优化了实训系统的一部分

2. 学习了,python 对于文本预处理(分词,构建词典,建立索引,以及spacy,nltk的应用)

3. 了解pytorch + PyG 构建GNN,实现推荐预测(关键之处,两点)
  - 构建符合固定结构的数据类型
  - MessagePassing的理解

4. Leetcode 鸡蛋掉落问题

pytorch学习笔记

0.待研究问题

1.Pytorch optimizer.step() 和loss.backward()和scheduler.step()的关系与区别 

1.线性回归

1.小批量随机梯度下降的理解:

(样本可以很好的体现整体的性质,不必对于整个进行计算,对于样本的计算结果的整体性也够用,如均值)

是对数据集进行随机均匀采样获取小批量的样本,获取这个小批量样本的平均损失的梯度,再将这个梯度用于整体参数的优化上

 B是小批量计算中的批量大小batch size

2.计算时间

# define a timer class to record time
class Timer(object):
    """Record multiple running times."""
    def __init__(self):
        self.times = []
        self.start()

    def start(self):
        # start the timer
        self.start_time = time.time()

    def stop(self):
        # stop the timer and record time into a list
        self.times.append(time.time() - self.start_time)
        return self.times[-1]

    def avg(self):
        # calculate the average and return
        return sum(self.times)/len(self.times)

    def sum(self):
        # return the sum of recorded time
        return sum(self.times)

3.pytorch的使用

features = torch.randn(num_examples,num_inputs,dtype=torch.float32) #生成num_examples行,num_inputs列的数组,其features的类型为torch.Tensor

labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()),
                       dtype=torch.float32)#利用np生成正态干扰
plt.scatter(features[:, 1].numpy(), labels.numpy(), 5);#对于Tensor的数据类型的生成图像,需要用.numpy()进行处理
features.size()#torch.Size([1000, 2])
len(features)#1000

indices = list(range(100))
random.shuffle(indices)#将indices的list进行随机化

b = torch.zeros(1, dtype=torch.float32)
b.requires_grad_(requires_grad=True)#为了方便追踪求导

#调用父类构造方法
class Conv2D(nn.Module):
    def __init__(self,kernel_size):
        super(Conv2D,self).___init__()#调用父类的__init__构造函数
        self.weight = nn.Parameter(torch.randn(kernel_size))
        self.bais = nn.Parameter(torch.randn(1))

    def forward(self,x):
        return corr2d(x,self.weight) + self.bias

4.将features的tensor流,每次取出batch_size个数

def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    random.shuffle(indices)  # random read 10 samples
    for i in range(0, num_examples, batch_size):#batch_size表示间隔[1,4,7,10]如此
        j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) #每次将1-4这里的所有的数输出 the last time may be not enough for a whole batch
        yield  features.index_select(0, j), labels.index_select(0, j)#其中yield相当于一个生成器,return + 指针,下次接着这里运行
        # .index_select函数相当于按照0表示index,而j,torch.LongTensor[1,2,34,54]的类型

batch_size = 10
for X, y in data_iter(batch_size, features, labels):
    print(X, '\n', y)
    break#打断了yield的使用

5.均方误差损失函数

def squared_loss(y_hat, y): 
    return (y_hat - y.view(y_hat.size())) ** 2 / 2#其中的.view相当于进行重新的行列排序

6.小批量随机梯度下降

def sgd(params,lr,batch_size):
    for param in params:
        param.data -= lr * param.grad /batch_size

7.训练线性模型

# super parameters init
lr = 0.03
num_epochs = 5

net = linreg
loss = squared_loss

# training
for epoch in range(num_epochs):  # training repeats num_epochs times
    # in each epoch, all the samples in dataset will be used once

    # X is the feature and y is the label of a batch sample
    for X, y in data_iter(batch_size, features, labels):
        l = loss(net(X, w, b), y).sum()  
        # calculate the gradient of batch sample loss 
        l.backward()  
        # using small batch random gradient descent to iter model parameters
        sgd([w, b], lr, batch_size)  
        # reset parameter gradient
        w.grad.data.zero_()
        b.grad.data.zero_()
    train_l = loss(net(features, w, b), labels)
    print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))

2.Softmax与分类模型

1.使用交叉损失函数的原因

在分类问题中,x1 = 0.6,x2 = 0.2,x3 = 0.2,与x1 = 0.6,x2 = 0.4,x3 = 0上的平方损失,前面比后面的小很多,其实都是与1,0,0进行比较,但是这样在分类问题中太过严格

 而其实质只有一个y=1的作为损失函数

训练的目的:是使得正确类的预测结果更加大

4.文本预处理

5.语言模型

6.过拟合、欠拟合及其解决方案

1.过拟合的原因

把训练数据吃的很透,1.训练数据太少,2.模型太过于复杂

2.模型复杂度与误差的关系

3.生成训练数据

n_train, n_test, true_w, true_b = 100, 100, [1.2, -3.4, 5.6], 5
features = torch.randn((n_train + n_test, 1))
poly_features = torch.cat((features, torch.pow(features, 2), torch.pow(features, 3)), 1) 
labels = (true_w[0] * poly_features[:, 0] + true_w[1] * poly_features[:, 1]
          + true_w[2] * poly_features[:, 2] + true_b)
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)

4.关于模型训练的函数

#num_epochs, loss = 100, torch.nn.MSELoss()

num_epochs,loss=100,torch.nn.MSELoss()#表示均方误差
def fit_and_plot(train_features,test_features,train_labels,test_labels):
    net = torch.nn.Linear(train_features.shape[-1],1)#这里相当于y = w*x这里,(train_features.shape[-1],1)是在初始化w
    batch_size = min(10,train_labels.shape[0]
    dataset = torch.utils.data.TensorDataset(train_features,train_labels)#将tensor的数据转化成TensorDataset的形式
    train_iter = torch.utils.data.DataLoader(dataset,batch_size,shuffle=True)#设置获取数

    optimizer = torch.optim.SGD(net.parameters(),lr=0.01)#这里梯度下降的参数是w
    train_ls,test_ls = [],[]

    for _ in range(num_epochs):
#################################################################
##这里关于backward和step函数的逻辑理解,他们是一体的吗
        for X,y in train_iter:
            l = loss(net(X),y.view(-1,1))#view相当于resize
            optimizer.zero_grad()#梯度清零
            l.backward()#求梯度
            optimizer.step()#迭代优化函数
 ###############################################################
        train_labels = train_labels.view(-1,1)
        test_labels = test_labels.view(-1,1)
        train_ls.append(loss(net(train_features),train_labels).item())#.item只能用于提取一个元素张量的那个元素值
        test_ls.append(loss(net(test_features),test_labels).item())
    print("final epoch:train loss",train_ls[-1],'test loss',test_ls[-1])
    semilogy(range(1,num_epochs + 1),train_ls,'epochs','loss',
             range(1,num_epochs + 1),test_ls,['train','test'])
    print('weight:',net.weight.data,'\nbias:',net.bias.data)

5.权重衰减(L2范数)

def fit_and_plot_pytorch(wd):
    # 对权重参数衰减。权重名称一般是以weight结尾
    net = nn.Linear(num_inputs, 1)
    nn.init.normal_(net.weight, mean=0, std=1)
    nn.init.normal_(net.bias, mean=0, std=1)
    optimizer_w = torch.optim.SGD(params=[net.weight], lr=lr, weight_decay=wd) # 对权重参数衰减
    optimizer_b = torch.optim.SGD(params=[net.bias], lr=lr)  # 不对偏差参数衰减

def fit_and_plot_pytorch(wd):#wd表示的是衰减的程度
    net = nn.Linear(num_inputs,1)#这里的参数矩阵为[100,1],并不是感知机的,这里是线性模型
    #初始化权重
    nn.init.normal_(net.weight,mean=0,std=1)
    nn.init.normal_(net.bias,mean=0,std=1)
    #优化函数类,weight_decay,进行权重衰减,是用于计算的
    optimizer_w = torch.optim.SGD(params=[net.weight],lr=lr,weight_decay=wd)
    optimizer_b = torch.optim.SGD(params=[net.bias],lr=lr)

    train_ls,test_ls = [],[]
    for _ in range(num_epochs):
        for X,y in train_iter:
            l = loss(net(X),y).mean()
            optimizer_w.zero_grad()
            optimizer_b.zero_grad()

            l.backward()#求梯度,函数梯度

             # 对两个optimizer实例分别调用step函数,从而分别更新权重和偏差,数值梯度
            optimizer_w.step()
            optimizer_b.step()
        train_ls.append(loss(net(train_features),train_labels).mean().item())
        test_ls.append(loss(net(test_features),test_labels).mean().item())
    d2l.semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'loss',
                 range(1, num_epochs + 1), test_ls, ['train', 'test'])
    print('L2 norm of w:', net.weight.data.norm().item())

6.丢弃法

降低过拟合

def dropout(X, drop_prob):
    X = X.float()
    assert 0 <= drop_prob <= 1
    keep_prob = 1 - drop_prob
    # 这种情况下把全部元素都丢弃
    if keep_prob == 0:
        return torch.zeros_like(X)
    mask = (torch.rand(X.shape) < keep_prob).float()##保证了有 keep_prob个0,其他的为1

    return mask * X / keep_prob

def evaluate_accuracy(data_iter,net):
    acc_sum,n = 0.0,0
    for X,y in data_iter:
        if isinstance(net,torch.nn.Module):#类型判断
            net.eval() # 评估模式, 这会关闭dropout
            acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()#train_ls.append(loss(net(train_features),train_labels).mean().item())相似
            net.train()   # 改回训练模式         
        else:# 自定义的模型
            if("is_training" in net.__code__.co_varnames):# 如果有is_training这个参数
                # 将is_training设置成False
                acc_sum += (net(X,is_training=False).argmax(dim = 1) == y).float().sum().item()
            else:
                 acc_sum += (net(X).argmax(dim=1) == y).float().sum().item() 

        n += y.shape[0]
    return acc_sum / n

18.卷积神经网络进阶

AlexNet网络

2.VGG block

其中卷积层,保持输入的长宽不变,池化层会使得长宽减半(池化层为2*2)

在发展中为了更加规范化开发,使用了VGG块

3.NiN block

使用卷积层+两层“全连接层”组成,其中为了不reshape所以使用卷积进行

VGG通过全连接层来控制结果数

NiN最后输出的一阶段是通过卷积层的通道数,来控制结果数

19.梯度消失、梯度爆炸

在激活函数的选择的地方讲过,在深层网络中尽量避免选择sigmoid和tanh激活函数,原因是这两个激活函数会把元素转换到[0, 1]和[-1, 1]之间,会加剧梯度消失的现象

1153 Decode Registration Card of PAT (25 分)

题目:

1153 Decode Registration Card of PAT (25 分)

大概描述:

依照三种方式对考生信息进行筛选,涉及到排序,以及常用STL库。

特征词:

排序 STL

使用语言:

C++

解题思路:

前两类的查找可以直接,较为方便,主要是第三类,主要是先获取到,一个unordered_map的解集,然后再进行排序,由于使用map存在超时的问题,所以使用unordered_map

得分 提交次数 时间:

25分 2次 120min

具体代码:

#include<vector>
#include<string>
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
const int a = 10005;
const int b = 105;

struct st{
  string s;
  int sc;
}sts[a];

struct s3{
  string a;
  int b;
};

int num1,num2;

bool cmp(struct st f,struct st t){
   return f.sc != t.sc ? f.sc > t.sc : f.s < t.s;
}

bool cmp1(struct s3 f,struct s3 t){
   return f.b != t.b ? f.b > t.b : f.a < t.a;
}

void w1(string s){
  vector<st> ans;
  for(int i = 0;i < num1;i++){
    if(s[0] == sts[i].s[0])
        ans.push_back(sts[i]);
  }
  sort(ans.begin(),ans.end(),cmp);
  if(ans.size() != 0)
    for(int i = 0;i < ans.size();i++){
      cout << ans[i].s << " " << ans[i].sc << "\n";
    }
  else
    cout << "NA" <<"\n";
}

void w2(string s){
  int g = 0,num = 0;
  for(int i = 0;i < num1;i++){
    if(s == sts[i].s.substr(1,3)){
        g++;
        num += sts[i].sc;
    }

  }
  if(g != 0)
    cout << g << " " << num <<"\n";
   else
    cout << "NA" << "\n";
}

void w3(string s){
   unordered_map<string,int> ans;
   vector<s3> an;
   //cout << "OOOK";
   for(int i = 0;i < num1;i++){
    //cout << sts[i].s.substr(4,9) <<"\n";
    if(s == sts[i].s.substr(4,6)){
        //cout << "OOOK";
        ans[sts[i].s.substr(1,3)]++;
    }
   }
   struct s3 v;
   for (auto it : ans){
       v.a = it.first;
       v.b = it.second;
       an.push_back(v);
   }
   sort(an.begin(),an.end(),cmp1);
   if(an.size() != 0){
     for(int i = 0;i < an.size();i++){
      cout << an[i].a << " " << an[i].b << "\n";
     }
   }
   else
    cout << "NA" << "\n";
    //cout << it.first << " " << it.second << "\n";
}

int main(){
   int t;
   string rs;
   cin >> num1 >> num2;
   for(int i = 0;i < num1;i++){
    cin >> sts[i].s >> sts[i].sc;

   }
   for(int i = 0;i < num2;i++){
    cin >> t >> rs;
    cout << "Case " << i + 1 <<": " << t << " " << rs << "\n";
    if(t == 1)
        w1(rs);
    if(t == 2)
        w2(rs);
    if(t == 3)
        w3(rs);
   }
}