kldjalfkjdsasf
理论 在最近几篇关于轮廓的文章中,我们使用了与OpenCV提供的轮廓相关的几个函数。但是当我们使用cv.findContours()函数在图像中找到轮廓时,我们已经传递了一个参数Contour Retrieval Mode。我们通常传递cv.RETR_LIST或cv.RETR_TREE,它运行的效果很好。但它究竟意味着什么? 此外,在输出中,我们得到三个数组,第一个是图像,第二个是我们的轮廓,还有一个我们命名为层次结构的输出(请查看以前文章中的代码)。但我们从未在任何地方使用过这种层那么这个层次结构又是什么呢?它与前面提到的函数参数有什么关系? 这就是我们将在本文中处理的内容。 什么是层次结构? 通常我们使用cv.findContours()函数来检测图像中的对象,对吧?有时对象位于不同的位置。但在某些情况下,某些形状在其他形状内。就像嵌套的数字一样。在这种情况下,我们将外部一个称为父项,将内部项称为子项。这样,图像中的轮廓彼此之间存在某种关系。我们可以指定一个轮廓如何相互连接,例如,它是某个其他轮廓的子项,还是父项等。这种关系的表示称为层次结构。 考虑下面的示例图片: 在这张图片中,有一些形状,我从0-5编号。图2和2a表示最外侧盒子的外部和内部轮廓。 这里,轮廓0,1,2是外部或最外部的。我们可以说,它们在层次结构0中,或者只是它们处于相同的层次结构级别。 接下来是轮廓-2a。它可以被认为是轮廓-2的子节点(或者相反,轮廓-2是轮廓-2的父节点)。所以让它在层次结构-1中。类似地,轮廓-3是轮廓-2的子,它进入下一层次。最后,轮廓4,5是轮廓-3a的子节点,它们位于最后的层次结构级别。从我编号框的方式,我会说轮廓-4是轮廓-3a的第一个孩子(它也可以是轮廓-5)。 我提到这些东西来理解相同的层次结构,外部轮廓,子轮廓,父轮廓,第一个孩子等术语。现在让我们进入OpenCV。 OpenCV中的层次结构表示 因此每个轮廓都有自己的信息,关于它是什么层次结构,谁是它的子,谁是它的父等.OpenCV将它表示为四个值的数组:[Next,Previous,First_Child,Parent] “下一个表示同一层级的下一个轮廓。” 例如,在我们的图片中取出contour-0。谁是同一水平的下一个轮廓?它是轮廓-1。所以简单地说Next = 1.类似地,对于Contour-1,next是contour-2。所以Next = 2。 轮廓-2怎么样?同一级别没有下一个轮廓。所以简单地说,将Next = -1。轮廓-4怎么样?它与contour-5处于同一水平。所以它的下一个轮廓是轮廓-5,所以Next = 5。 “上一个表示同一层级的先前轮廓。” 与上述相同。轮廓-1的先前轮廓在同一水平面上为轮廓-0。类似地,对于轮廓-2,它是轮廓-1。而对于contour-0,没有先前的,所以把它作为-1。 “First_Child表示其第一个子轮廓。” 无需任何解释。对于轮廓-2,孩子是轮廓-2a。因此它获得了contour-2a的相应索引值。轮廓-3a怎么样?它有两个孩子。但我们只带第一个孩子。它是轮廓-4。因此,对于轮廓-3a,First_Child = 4。 “父表示其父轮廓的索引。” 它与First_Child相反。对于轮廓-4和轮廓-5,父轮廓都是轮廓-3a。对于轮廓-3a,它是轮廓-3,依此类推。 注意:如果没有子项或父项,则该字段将被视为-1 所以现在我们知道OpenCV中使用的层次结构样式,我们可以在上面给出的相同图像的帮助下检查OpenCV中的Contour Retrieval Modes。即cv.RETR_LIST,cv.RETR_TREE,cv.RETR_CCOMP,cv.RETR_EXTERNAL等标志是什么意思? 轮廓检索模式 RETR_LIST 这是四个标志中最简单的(从解释的角度来看)。它只是检索所有轮廓,但不创建任何父子关系。根据这条规则,父和子是平等的,他们只是轮廓。即它们都属于同一层次结构。 所以这里,层次结构数组中的第3和第4项始终为-1。但显然,Next和Previous术语将具有相应的值。请自行检查并验证。 下面是我得到的结果,每行是相应轮廓的层次结构细节。例如,第一行对应于轮廓0.下一个轮廓是轮廓1.所以Next = 1.没有先前的轮廓,所以Previous = -1。如前所述,剩下的两个是-1。 >>> hierarchy array([[[ 1, -1, -1, -1], [ 2, 0, -1, -1], [ 3, 1, -1, -1], [ 4, 2, -1, -1], [ 5, 3, -1, -1], [ 6, 4, -1, -1], [ 7, 5, -1, -1], [-1, 6, -1, -1]]]) 如果你没有使用任何层次结构功能,这是在代码中使用的不错选择。 RETR_EXTERNAL 如果使用此标志,则仅返回极端外部标志。所有儿童轮廓都被遗忘。我们可以说,根据这项法律,只有每个家庭中最年长的人才能得到照顾。它并不关心其他家庭成员:)。 那么,在我们的图像中,有多少极端外轮廓?即在等级0级?只有3个,即轮廓0,1,2,对吧?现在尝试使用此标志查找轮廓。这里,给予每个元素的值与上面相同。将其与上述结果进行比较。以下是我得到的: >>> hierarchy array([[[ 1, -1, -1, -1], [ 2, 0, -1, -1], [-1, 1, -1, -1]]]) 如果只想提取外轮廓,可以使用此标志。在某些情况下它可能有用。 RETR_CCOMP 此标志检索所有轮廓并将它们排列为2级层次结构。即对象的外部轮廓(即其边界)放置在层次结构-1中。对象内部的孔的轮廓(如果有的话)放在层次结构-2中。如果其中有任何对象,则其轮廓仅再次放置在层次结构-1中。它在层次结构-2中的漏洞等等。 只需考虑黑色背景上的“大白零”图像。零的外圆属于第一层次,零的内圈属于第二层次。 我们可以用简单的图像来解释它。在这里,我用红色(1或2)标记了红色轮廓的顺序和它们所属的层次结构。订单与OpenCV检测轮廓的顺序相同。 因此,考虑第一个轮廓,即轮廓-0。它是层次结构-1。它有两个孔,轮廓1和2,它们属于层次结构-2。因此对于轮廓-0,相同层级中的下一轮廓是轮廓-3。并且之前没有。它的第一个是子级是层次结构-2中的轮廓-1。它没有父级,因为它位于层次结构-1中。所以它的层次结构数组是[3,-1,1,-1] 现在采取轮廓-1。它在层次结构-2中。同一层次中的下一个(在轮廓-1的父下面)是轮廓-2。没有前一个。没有子,但父是轮廓-0。所以数组是[2,-1,-1,0]。 类似于contour-2:它在层次结构-2中。在contour-0下,同一层次中没有下一个轮廓。所以没有下一个。以前是轮廓-1。没有子,父是轮廓-0。所以数组是[-1,1,-1,0]。 轮廓-3:层次结构-1中的下一个是轮廓-5。上一个是轮廓-0。子是轮廓4而没有父。所以数组是[5,0,4,-1]。 轮廓 - 4:它在等高线3中的等级2中,并且没有兄弟。所以没有下一个,没有先前,没有子,父是轮廓-3。所以数组是[-1,-1,-1,3]。 剩下的你可以填写。这是我得到的最终答案: >>> hierarchy array([[[ 3, -1, 1, -1], [ 2, -1, -1, 0], [-1, 1, -1, 0], [ 5, 0, 4, -1], [-1, -1, -1, 3], [ 7, 3, 6, -1], [-1, -1, -1, 5], [ 8, 5, -1, -1], [-1, 7, -1, -1]]]) RETR_TREE 这是最后一个人,Mr.Perfect。它检索所有轮廓并创建完整的族层次结构列表。它甚至告诉,谁是爷爷,父,子,孙子,甚至超越… 。 例如,我拍摄了上面的图像,重写了cv.RETR_TREE的代码,根据OpenCV给出的结果重新排序轮廓并进行分析。同样,红色字母给出轮廓编号,绿色字母给出层次结构顺序。 取contour-0:它在层次结构-0中。 同一层次中的下一个轮廓是轮廓-7。没有以前的轮廓。子是轮廓-1。 没有父。 所以数组是[7,-1,1,-1]。 取等高线2:它在层次结构-1中。同一级别没有轮廓。没有前一个。子是轮廓-3。父是轮廓-1。所以数组是[-1,-1,3,1]。 剩下的,试试吧。 以下是完整的答案: >>> hierarchy array([[[ 7, -1, 1, -1], [-1, -1, 2, 0], [-1, -1, 3, 1], [-1, -1, 4, 2], [-1, -1, 5, 3], [ 6, -1, -1, 4], [-1, 5, -1, 4], [ 8, 0, -1, -1], [-1, 7, -1, -1]]])
目标: 学习提取一些常用的对象属性,如Solidity,Equivalent Diameter,Mask image,Mean Intensity 1. Aspect Ratio(长宽比) 它是对象的边界矩形的宽度与高度的比。 Aspect Ratio=WidthHeight Aspect Ratio= frac{Width}{Height} Aspect Ratio=HeightWidth​ x,y,w,h = cv.boundingRect(cnt) aspect_ratio = float(w)/h 2. Extent(大小比) 它是轮廓区域与边界矩形区域的比。 Extent=Object AreaBounding Rectangle Area Extent= frac{Object Area}{Bounding Rectangle Area} Extent=Bounding Rectangle AreaObject Area​ area = cv.contourArea(cnt) x,y,w,h = cv.boundingRect(cnt) rect_area = w*h extent = float(area)/rect_area 3. Solidity(密实比) Solidity是轮廓区域与其凸包区域的比率。 Solidity=Contour AreaConvex Hull Area Solidity= frac{Contour Area}{Convex Hull Area} Solidity=Convex Hull AreaContour Area​ area = cv.contourArea(cnt) hull = cv.convexHull(cnt) hull_area = cv.contourArea(hull) solidity = float(area)/hull_area 4. Equivalent Diameter(等效直径) 等效直径是圆的直径,其面积与轮廓面积相同。 Equivalent Diameter=4×Contour Areaπ Equivalent Diameter=sqrt{frac{4times Contour Area}{pi }} Equivalent Diameter=π4×Contour Area​​ area = cv.contourArea(cnt) equi_diameter = np.sqrt(4*area/np.pi) 5. Orientation(方向) 方向是对象定向的角度。以下方法还给出了主轴和短轴长度。 (x,y),(MA,ma),angle = cv.fitEllipse(cnt) 6. Mask & Pixel Points(掩模和像素点) 在某些情况下,我们可能需要包含该对象的所有点。它可以如下完成: mask = np.zeros(imgray.shape,np.uint8) cv.drawContours(mask,[cnt],0,255,-1) pixelpoints = np.transpose(np.nonzero(mask)) #pixelpoints = cv.findNonZero(mask) 这里,两个方法,一个使用Numpy函数,另一个使用OpenCV函数(最后一个注释行)给出相同的方法。 结果也相同,但略有不同。 Numpy以**(行,列)格式给出坐标,而OpenCV以(x,y)**格式给出坐标。所以答案基本上会互换。请注意,row=x和column=y。 7. 最大值,最小值及其位置 我们可以使用掩模图像找到这些参数。 min_val, max_val, min_loc, max_loc = cv.minMaxLoc(imgray,mask = mask) 8. 平均颜色或平均灰度 在这里,我们可以找到对象的平均颜色。或者它可以是灰度模式下物体的平均强度。我们再次使用相同的面具来做到这一点。 mean_val = cv.mean(im,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]) 例如,如果我将它应用于印度地图,我会得到以下结果:
目标: 本章节你需要学习以下内容: 查找轮廓的不同特征,如面积,周长,质心,边界框等 1. 矩 图像的矩可帮助你计算某些特征,如对象的质心,对象的面积等特征。具体定义可以查看图像的矩的维基百科页面 函数cv.moments()给出了计算的所有矩值的字典。 import numpy as np import cv2 as cv img = cv.imread('star.jpg',0) ret,thresh = cv.threshold(img,127,255,0) contours,hierarchy = cv.findContours(thresh, 1, 2) cnt = contours[0] M = cv.moments(cnt) print( M ) 从这一刻起,你可以提取有用的数据,如面积,质心等。质心由关系给出,Cx=M10M00 C_{x}=frac{M_{10}}{M_{00}} Cx​=M00​M10​​和 Cy=M01M00 C_{y}=frac{M_{01}}{M_{00}} Cy​=M00​M01​​。这可以按如下方式完成: cx = int(M['m10']/M['m00']) cy = int(M['m01']/M['m00']) 2. 轮廓面积 轮廓区域由函数cv.contourArea()或时刻M[‘m00’]给出。 area = cv.contourArea(cnt) 3. 轮廓周长 轮廓周长也被称为弧长。可以使用cv.arcLength()函数找到它。第二个参数指定形状是闭合轮廓(如果传递为True),还是仅仅是曲线。 perimeter = cv.arcLength(cnt,True) 4. 轮廓近似 它根据我们指定的精度将轮廓形状近似为具有较少顶点数的另一个形状。它是Douglas-Peucker算法的一种实现方式。 要理解这一点,可以假设你试图在图像中找到一个正方形,但是由于图像中的一些问题,你没有得到一个完美的正方形,而是一个“坏形状”(如下图第一张图所示)。现在你可以使用此功能来近似形状。在这里,第二个参数称为epsilon,它是从轮廓到近似轮廓的最大距离。这是一个准确度参数。需要选择适当的epsilon才能获得正确的输出。 epsilon = 0.1*cv.arcLength(cnt,True) approx = cv.approxPolyDP(cnt,epsilon,True) 下面,在第二幅图像中,绿线表示epsilon=弧长的10%的近似曲线。第三幅图像显示相同的epsilon=弧长的1%。第三个参数指定曲线是否关闭。 5. 凸包 凸包看起来类似于轮廓近似,但它不是(两者在某些情况下可能提供相同的结果)。这里,cv.convexHull()函数检查曲线的凸性缺陷并进行修正。一般而言,凸曲线是总是凸出或至少平坦的曲线。如果它在内部膨胀,则称为凸性缺陷。例如,检查下面的手形图像。红线表示手的凸包。双面箭头标记显示凸起缺陷,即船体与轮廓的局部最大偏差。 下面我们要讨论它的一些语法: hull = cv.convexHull(points[, hull[, clockwise[, returnPoints]] 参数详情: points:是我们传入的轮廓。 hull:是输出,通常我们忽略它。 clocwise:方向标志。如果为True,则输出凸包顺时针方向。否则,它逆时针方向。 returnPoints:默认为True。然后它返回凸包点的坐标。如果为False,则返回与凸包点对应的轮廓点的索引。 因此,为了获得如上图所示的凸包,以下就足够了: hull = cv.convexHull(cnt) 但是如果你想找到凸性缺陷,你需要传递returnPoints = False。为了理解它,我们将采用上面的矩形图像。首先,我发现它的轮廓为cnt。现在我发现它的凸包有returnPoints = True,我得到以下值:[[234 202],[51 202],[51 79],[234 79]]这四个角落 矩形点。 现在如果对returnPoints = False做同样的事情,我得到以下结果:[[129],[67],[0],[142]]。 这些是轮廓中相应点的索引。例如,检查第一个值:cnt [129] = [[234,202]],它与第一个结果相同(对于其他结果,依此类推)。 当我们讨论凸性缺陷时,你会再次看到它。 6. 检查凸性 函数cv.isContourConvex()可以检查曲线是否凸的,它只返回True或False,没有什么理解上的问题。 k = cv.isContourConvex(cnt) 7. 边界矩形 有两种类型的边界矩形。 7.a.直边矩形 它是一个直的矩形,它不考虑对象的旋转。因此,边界矩形的面积不是最小的。它由函数cv.boundingRect()找到。 设(x,y)为矩形的左上角坐标,(w,h)为宽度和高度。 x,y,w,h = cv.boundingRect(cnt) cv.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2) 7.b.旋转矩形 这里,以最小面积绘制边界矩形,因此它也考虑旋转。使用的函数是cv.minAreaRect()。它返回一个Box2D结构,其中包含以下detals - (center(x,y),(width,height),rotation of rotation)。但要画这个矩形,我们需要矩形的4个角。它是由函数cv.boxPoints()获得的 rect = cv.minAreaRect(cnt) box = cv.boxPoints(rect) box = np.int0(box) cv.drawContours(img,[box],0,(0,0,255),2) 两个矩形都显示在单个图像中。绿色矩形显示正常的边界矩形。红色矩形是旋转的矩形。 8. 最小外接圈 接下来,我们使用函数cv.minEnclosingCircle()找到对象的外接圆。它是一个完全覆盖物体的圆圈,面积最小。 (x,y),radius = cv.minEnclosingCircle(cnt) center = (int(x),int(y)) radius = int(radius) cv.circle(img,center,radius,(0,255,0),2) 9. 椭圆拟合 接下来是将椭圆拟合到一个对象上。它返回刻有椭圆的旋转矩形。 ellipse = cv.fitEllipse(cnt) cv.ellipse(img,ellipse,(0,255,0),2) 10. 拟合一条线 类似地,我们可以在一组点上拟合一条线。下图包含一组白点。 我们可以近似直线。 rows,cols = img.shape[:2] [vx,vy,x,y] = cv.fitLine(cnt, cv.DIST_L2,0,0.01,0.01) lefty = int((-x*vy/vx) + y) righty = int(((cols-x)*vy/vx)+y) cv.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)
利用torch.optim.SGD类实现最小化函数 f(X)=−(cos2x[0]+cos2x[1])2f(X)=-(cos^2x[0]+cos^2x[1])^2f(X)=−(cos2x[0]+cos2x[1])2 from math import pi import torch import torch.optim x = torch.tensor([pi/3,pi/6],requires_grad=True) #初始化 optimizer = torch.optim.SGD([x,],lr=0.1,momentum=0) for step in range(11): if step: optimizer.zero_grad() f.backward() optimizer.step() #求一次SDG f=-((x.cos()**2).sum())**2 print('step {}: x={},f(x)={}'.format(step,x.tolist(),f)) 输出 step 0: x=[1.0471975803375244, 0.5235987901687622],f(x)=-1.0 step 1: x=[0.8739925026893616, 0.35039371252059937],f(x)=-1.674528956413269 step 2: x=[0.6192374229431152, 0.1835097223520279],f(x)=-2.6563119888305664 step 3: x=[0.3111077845096588, 0.06654246151447296],f(x)=-3.617122173309326 step 4: x=[0.08941137790679932, 0.016069628298282623],f(x)=-3.9671425819396973 step 5: x=[0.01855570822954178, 0.0032690390944480896],f(x)=-3.99858021736145 step 6: x=[0.0037171822041273117, 0.0006542906630784273],f(x)=-3.999943256378174 step 7: x=[0.0007434850558638573, 0.00013086199760437012],f(x)=-3.999997615814209 step 8: x=[0.00014869740698486567, 2.617243444547057e-05],f(x)=-4.0 step 9: x=[2.973947994178161e-05, 5.234485797700472e-06],f(x)=-4.0 step 10: x=[5.947895260760561e-06, 1.0468970685906243e-06],f(x)=-4.0
import torch x = torch.ones(2, 2, requires_grad=True) print(x) print(x.grad_fn) y = x + 2 print(y) print(y.grad_fn) z = y * y * 3 out = z.mean() print(z, out) out.backward() # 等价于 out.backward(torch.tensor(1.)) print(x.grad) 输出: tensor([[1., 1.], [1., 1.]], requires_grad=True) None tensor([[3., 3.], [3., 3.]], grad_fn=) tensor([[27., 27.], [27., 27.]], grad_fn=) tensor(27., grad_fn=) tensor([[4.5000, 4.5000], [4.5000, 4.5000]]) 因为 out 是一个标量,所以调用 backward() 时不需要指定求导变量: out.backward() # 等价于 out.backward(torch.tensor(1.)) 可以看到最后求出的梯度: tensor([[4.5000, 4.5000], [4.5000, 4.5000]]) 验证下,我们令 out 为o , 因为 所以 所以上面的输出是正确的。 torch对torch求梯度时 x = torch.tensor([1.0, 2.0, 3.0, 4.0], requires_grad=True) y = 2 * x z = y.view(2, 2) print(z) 输出 tensor([[2., 4.], [6., 8.]], grad_fn=) 现在 y 不是一个标量,所以在调用 backward 时需要传入一个和y 同形的权重向量进行加权求和得到一个标量。 v = torch.tensor([[1.0, 0.1], [0.01, 0.001]], dtype=torch.float) z.backward(v) print(x.grad) 输出 tensor([2.0000, 0.2000, 0.0200, 0.0020]) 注意, x.grad 是和 x 同形的张量。 再来看看中断梯度追踪的例子: x = torch.tensor(1.0, requires_grad=True) y1 = x ** 2 with torch.no_grad(): y2 = x ** 3 y3 = y1 + y2 print(x.requires_grad) print(y1, y1.requires_grad) # True print(y2, y2.requires_grad) # False print(y3, y3.requires_grad) # True 输出 True tensor(1., grad_fn=) True tensor(1.) False tensor(2., grad_fn=) True 可以看到,上面的 y2 是没有 grad_fn 而且 y2.requires_grad=False 的,而 y3 是有 grad_fn的。如果我们将 y3 对 x 求梯度的话会是多少呢? y3.backward() print(x.grad) 输出 tensor(2.) 为什么是2呢?不应该是5吗?事实上,由于y2的定义是被torch.no_grad(): 包裹的,所以与y2有关的梯度是不会回传的,只有与y1有关的梯度才会回传,即x*x对x的梯度。 上面提到, y2.requires_grad=False ,所以不能调用 y2.backward() ,会报错: RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn 此外,如果我们想要修改 tensor 的数值,但是又不希望被 autograd 记录(即不会影响反向传播),那么我们可以对 tensor.data 进行操作。 x = torch.ones(1,requires_grad=True) print(x.data) # 还是⼀一个tensor print(x.data.requires_grad) # 但是已经是独⽴立于计算图之外 y = 2 * x x.data *= 100 # 只改变了了值,不不会记录在计算图,所以不不会影响梯度传播 y.backward() print(x) # 更更改data的值也会影响tensor的值 print(x.grad) 输出 tensor([1.]) False tensor([100.], requires_grad=True) tensor([2.]) pytorch官方文档  
创建一个 Tensor 并设置 requires_grad=True : import torch x = torch.ones(2, 2, requires_grad=True) print(x) print(x.grad_fn) 输出: tensor([[1., 1.], [1., 1.]], requires_grad=True) None 再做一下运算操作: y = x + 2 print(y) print(y.grad_fn) 输出: tensor([[3., 3.], [3., 3.]], grad_fn=) 注意x是直接创建的,所以它没有 grad_fn , 而 y是通过一个加法操作创建的,所以它有一个为 的 grad_fn 。 像x这种直接创建的称为叶子节点,叶子节点对应的 grad_fn 是 None 。 print(x.is_leaf, y.is_leaf) # True False 再来点复杂度运算操作: z = y * y * 3 out = z.mean() #平均值 print(z, out) 输出: tensor([[27., 27.], [27., 27.]], grad_fn=) tensor(27., grad_fn=) 通过 .requires_grad_() 来用in-place的方式改变 requires_grad 属性: a = torch.randn(2, 2) # 缺失情况下默认 requires_grad = False a = ((a * 3) / (a - 1)) print(a.requires_grad) # False a.requires_grad_(True) print(a.requires_grad) # True b = (a * a).sum() print(b.grad_fn) 输出: False True  
索引、 view 是不会开辟新内存的,而像 y = x + y 这样的运算是会新开内存的,然后将 y 指向新内存。为了演示这一点,我们可以使用Python自带的 id 函数:如果两个实例例的ID一致,那么它们所对应的内存地址相同;反之则不同。 x = torch.tensor([1, 2]) y = torch.tensor([3, 4]) id_before = id(y) y = y + x print(id(y) == id_before) # False 如果想指定结果到原来的 y 的内存,我们可以使用前面介绍的索引来进行替换操作。在下面的例子中, 我们把 x + y 的结果通过 [:] 写进 y 对应的内存中。 x = torch.tensor([1, 2]) y = torch.tensor([3, 4]) id_before = id(y) y[:] = y + x print(id(y) == id_before) # True 我们还可以使用运算符全名函数中的 out 参数或者自加运算符 += (也即 add_() )达到上述效果,例如 torch.add(x, y, out=y) 和 y += x ( y.add_(x) )。 x = torch.tensor([1, 2]) y = torch.tensor([3, 4]) id_before = id(y) torch.add(x, y, out=y) # y += x, y.add_(x) print(id(y) == id_before) # True  
PyTorch还支持一些线性函数,免得用起来的时候自己造轮子,具体用法参考官方文档。如下表所示: 函数 功能 trace 对⻆角线元素之和(矩阵的迹) diag 对⻆角线元素 triu/tril 矩阵的上三⻆角/下三⻆角,可指定偏移量量 mm/bmm 矩阵乘法, batch的矩阵乘法 addmm/addbmm/addmv/addr/badbmm.. 矩阵运算 t 转置 dot/cross 内积/外积 inverse 求逆矩阵 svd 奇异值分解 PyTorch中的 Tensor 支持超过一百种操作,包括转置、索引、切片、数学运算、线性代数、随机数等等,可参考官方文档  
当对两个形状不同的 Tensor 按元素运算时,可能会触发广播(broadcasting)机制:先适当复制元素使这两个 Tensor 形状相同后再按元素运算。例如   x = torch.arange(1, 3).view(1, 2) print(x) y = torch.arange(1, 4).view(3, 1) print(y) print(x + y) 输出 由于 x 和 y 分别是1行2列和3行1列的矩阵,如果要计算 x + y ,那么 x 中第一行的2个元素被广播(复制)到了第二行和第三行,而 y 中第一列的3个元素被广播(复制)到了第二列。如此,就可以对2个3行2列的矩阵按元素相加。  
PyTorch是什么? 基于Python的科学计算包,服务于以下两种场景: 作为NumPy的替代品,可以使用GPU的强大计算能力 提供最大的灵活性和高速的深度学习研究平台 开始 Tensors(张量) Tensors与Numpy中的 ndarrays类似,但是在PyTorch中 Tensors 可以使用GPU进行计算. 创建一个 5x3 矩阵, 但是未初始化: import torch x = torch.empty(5, 3) print(x) 创建一个随机初始化的矩阵: import torch x = torch.empty(5, 3) print(x) x = torch.rand(5, 3) print(x) 创建一个0填充的矩阵,数据类型为long: import torch x = torch.zeros(5, 3, dtype=torch.long) print(x) 创建tensor并使用现有数据初始化: import torch x = torch.tensor([5.5, 3]) print(x) 根据现有的张量创建张量。 这些方法将重用输入张量的属性,例如, dtype,除非设置新的值进行覆盖 import torch x = torch.tensor([5.5, 3]) print(x) x = x.new_ones(5, 3, dtype=torch.double) # new_* 方法来创建对象 print(x) x = torch.randn_like(x, dtype=torch.float) # 覆盖 dtype! print(x) # 对象的size 是相同的,只是值和类型发生了变化 获取 size print(x.size()) 输出:torch.Size([5, 3]) 注:使用size方法与Numpy的shape属性返回的相同,张量也支持shape属性,后面会详细介绍 Note ``torch.Size`` 返回值是 tuple类型, 所以它支持tuple类型的所有操作. 操作 操作有多种语法。 我们将看一下加法运算。 加法1 y = torch.rand(5, 3) print(x + y) 加法2 print(torch.add(x, y)) 提供输出tensor作为参数 result = torch.empty(5, 3) torch.add(x, y, out=result) print(result) 替换 # adds x to y y.add_(x) print(y) Note 任何 以``_`` 结尾的操作都会用结果替换原变量. 例如: ``x.copy_(y)``, ``x.t_()``, 都会改变 ``x``. 你可以使用与NumPy索引方式相同的操作来进行对张量的操作 print(x[:, 1]) tensor([-2.0126, 0.4692, -0.5764, 0.6688, -1.1600]) torch.view: 可以改变张量的维度和大小 注:torch.view 与Numpy的reshape类似 x = torch.randn(4, 4) y = x.view(16) z = x.view(-1, 8) # size -1 从其他维度推断 print(x.size(), y.size(), z.size()) 如果你有只有一个元素的张量,使用.item()来得到Python数据类型的数值 x = torch.randn(1) print(x) print(x.item())   Read later: 100+ Tensor operations, including transposing, indexing, slicing, mathematical operations, linear algebra, random numbers, etc., are described here _. NumPy 转换 将一个Torch Tensor转换为NumPy数组是一件轻松的事,反之亦然。 Torch Tensor与NumPy数组共享底层内存地址,修改一个会导致另一个的变化。 将一个Torch Tensor转换为NumPy数组 import torch a = torch.ones(5) print(a) b = a.numpy() print(b) 观察numpy数组的值是如何改变的。 import torch a = torch.ones(5) print(a) b = a.numpy() print(b) a.add_(1) print(a) print(b) NumPy Array 转化成 Torch Tensor 使用from_numpy自动转化 import torch import numpy as np a = np.ones(5) b = torch.from_numpy(a) np.add(a, 1, out=a) print(a) print(b) 所有的 Tensor 类型默认都是基于CPU, CharTensor 类型不支持到 NumPy 的转换. CUDA 张量 使用.to 方法 可以将Tensor移动到任何设备中 # is_available 函数判断是否有cuda可以使用 # ``torch.device``将张量移动到指定的设备中 if torch.cuda.is_available(): device = torch.device("cuda") # a CUDA 设备对象 y = torch.ones_like(x, device=device) # 直接从GPU创建张量 x = x.to(device) # 或者直接使用``.to("cuda")``将张量移动到cuda中 z = x + y print(z) print(z.to("cpu", torch.double)) # ``.to`` 也会对变量的类型做更改