动手学——预备知识

什么是张量

  张量这个名词原先起源于一个力学概念,然后被数学抽象出来成了一个分支。简单来说张量是用来表示在一个n维空间中某个位置的线性映射

也就是说n维矢量空间,可以通过线性表达映射到各个维度的坐标上,其坐标就是$n$维空间的$n$个分量也就是$n$个向量。也就是说这些分量也按照某些规则进行线性变换。

从而改变也就是改变这个矢量空间。那么来回来看深度学习好像就是就是一个高维度的矩阵进行运算从而得出结果,这么看似乎好像Tensor还挺符合这个模式。

  但是要注意标量不是与之对应的概念,标量又称纯量,只有大小、没有方向、可用实数表示的一个量。

目的是为区别与向量。张量似乎可以理解成向量与举证的结合体。

TensorFlow这个名字还挺有意思

  如果说从数学角度理解张量很抽象的话,来看看Tensorflow似乎不一样的发现。

  • TensorFlow使用Tensor来表示数据
  • TensorFlow在内部将张量表示为基本数据类型的n维数组

  连起来似乎就是TensorFlow所有数据都是一个n维的数组,只是给我们找了在数学中与其对应的概念叫张量(Tensor)

Pytorch基本函数

torch.arange()

torch.arange(start=0, end, step=1, *, out=None, dtype=None, 
            layout=torch.strided, device=None, requires_grad=False)

  arange函数与numpy中的arange类似,torch.arange返回一个大小为$\lceil\frac{\text{end}-\text{start}}{step}\rceil$的一维张量,

并使用从start开始区间为[start, end)的公差step。

torch.cat()

torch.cat(tensors, dim=0, *, out=None)

  连接给定维度中给定的张量序列seq。所有张量必须具有相同的形状(连接维度除外)或为空。torch.cat()可以看作torch.split()torch.chunk()的逆运算

  • tensors (张量序列)任何相同类型的张量的python序列。提供的非空张量必须具有相同的形状,cat维度除外
  • dim(int, 选项)张量连接的维度
  • out(Tensor, 选项)输出张量
>>> x = torch.randn(2, 3)
>>> x
tensor([[ 0.6580, -1.0969, -0.4614],
        [-0.1034, -0.5790,  0.1497]])
>>> torch.cat((x, x, x), 0)
tensor([[ 0.6580, -1.0969, -0.4614],
        [-0.1034, -0.5790,  0.1497],
        [ 0.6580, -1.0969, -0.4614],
        [-0.1034, -0.5790,  0.1497],
        [ 0.6580, -1.0969, -0.4614],
        [-0.1034, -0.5790,  0.1497]])
>>> torch.cat((x, x, x), 1)
tensor([[ 0.6580, -1.0969, -0.4614,  0.6580, -1.0969, -0.4614,  0.6580,
         -1.0969, -0.4614],
        [-0.1034, -0.5790,  0.1497, -0.1034, -0.5790,  0.1497, -0.1034,
         -0.5790,  0.1497]])

可以看出上述例子,如果dim为0时结果为x直接按行拼接,如果dim为1时就按照列拼接也可以理解为按照一维进行拼接。也就是说二维张量dim最大为1, 三维张量dim最大为2。

torch.zeros()与torch.zeros_like()

  • torch.zeros_like(tensor):是根据给定张量,生成与其形状相同的全0张量
  • torch.zeros(size):其形状由变量参数size定义,返回一个由标量值0填充的张量

张量计算

  在张量计算中我们要注意,只有具有相同形状的张量才能进行计算。

广播机制

  广播机制说白了就是为了解决不同形状的张量进行相加,注意矩阵是无法做到这一点的。通过下面的例子可

可以说明这一点。

a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b
a + b

运行结果为:

(tensor([[0],
         [1],
         [2]]),
 tensor([[0, 1]]))

tensor([[0, 1],
        [1, 2],
        [2, 3]])

内存节省

  在Cpp中对变量运算进行操作访问的实质是其地址。所以操作时变量地址是不会改变的。

#include <stdio.h>
int main() {
    int a = 2, b = 3;
    printf("%d\n", &a);

    a = a + b;
    printf("%d", &a);

    return 0;
}

运行结果为:

-197368776
-197368776

  但是在Python中由于操作的变量相加或者其他数值运算时是会开辟新的地址进行存储

before = id(Y) # id()函数表示查看对象内存地址
Y = Y + X
id(Y) == before # 说明Y的对象地址已经改变

结果为:

False

  为了解决这个问题我们可以采用不同的语法来解决:

X[:] = X + Y
X += Y

  当然这种方法解决内存问题只是从语法层面对我们进行了规范。但是当程序执行过程中RAM中有大量对象处于

活动状态时,可能会出现内存问题,特别是在对可用内存总量有限制的情况下。下面概述了一些减小对象大小的方法,这些

方法可以显著减少纯Python程序所需的RAM数量。

Dict

  在小程序中,特别是在脚本中,使用内置的dict来表示结构信息是非常简单方便的:

>>> ob = {'x':1, 'y':2, 'z':3}
>>> x = ob['x']
>>> ob['y'] = y

  随着Python 3.6中使用一组有序键的更紧凑实现方式的出现,dict变得更有吸引力。但是,让我们看看它在

RAM中的内存大小:

>>> print(sys.getsizeof(ob))
240

它需要大量内存,特别是当你突然需要创建大量实例时:

实例数量 对象大小
1000000 240Mb
10000000 2.40Gb
100000000 24Gb

实例类

  对于那些喜欢将所有东西放置在类中的人来说,最好将结构定义为一个可以通过属性名访问的类

class Point:
        #
        def __init__(self, x, y, z):
                self.x = x
                self.y = y
                self.z = z

>>> ob   = Point(1, 2, 4)
>>> x    = ob.x
>>> ob.y = y

  类实例的结构很有趣:

实例数量 对象大小
1000000 240Mb
10000000 2.40Gb
100000000 24Gb

  这里的weakref是对这个对象的所谓弱引用列表的一个引用,dict字段是对类实例字典的引用,它包含实例

属性的值(注意64位的引用平台会占用8个字节)。从Python 3.3开始,共享空间用于在字典中存储类的所有实例的键。这减

少了RAM中实例堆栈的大小:

>>> print(sys.getsizeof(ob), sys.getsizeof(ob.__dict__))
56 112

因此,大量的类实例占用的内存比一个普通字典(dict)占用的要小:

实例数量 对象大小
1000000 168Mb
10000000 1.68Gb
100000000 16.8Gb

很容易看出,由于实例字典的大小,RAM中实例的大小仍然很大。

带有slots的类实例

  通过消除 dict和weakref,可以显著减小RAM中的类实例的大小。这通过一个带有slots的小“技巧”是可能实现的:

class Point:
        __slots__ = 'x', 'y', 'z'

        def __init__(self, x, y, z):
                self.x = x, self.y = y, self.z = z

>>> ob = Point(1, 2, 3)
>>> print(sys.getsizeof(ob))
64

RAM中对象大小明显减少:

字段 大小(字节)
PyGC_Head 24
PyObject_HEAD 16
x 8
y 8
z 8
总数: 64

在类定义中使用slots可以显著减少大量实例对内存空间的占用:

实例数量 对象大小
1000000 64Mb
10000000 640Mb
100000000 6.4Gb

要自动化使用 slots创建一个类的过程,有一个库namedlist可以使用。

namedlist.namedlist函数会创建一个带有slots的类:

Point = namedlist('Point', ('x', 'y'))

另一个包attrs允许你使用和不使用slots自动创建类。

数据处理

  Pandas的用途十分广泛,Pandas与张量也可以兼容。下面主要介绍使用pandas预处理原始数据,并将原

始数据转换为张量格式的步骤。

os.makedirs()与os.mkdir

  首先说os.mkdir(path),他的功能是一级一级的创建目录,前提是前面的目录已存在,如果不存在会报异

常,比较麻烦,但是存在即有他的道理,当你的目录是根据文件名动态创建的时候,你会发现他虽然繁琐但是很有保障,不

会因为你的一时手抖,创建而创建了双层或者多层错误路径.

import os
 
os.mkdir('d:\hello')    #  正常
os.mkdir('d:\hello\hi') #  正常
 
#  如果d:\hello目录不存在
#  则os.mkdir('d:\hello\hi')执行失败

  然后是os.makedirs(path),单从写法上就能猜出他的区别,他可以一次创建多级目录,哪怕中间目录不存

在也能正常的(替你)创建.

import os
 
os.makedirs('d:\hello')    #  正常
os.makedirs('d:\hello\hi') #  正常
 
#  如果d:\hello目录不存在
#  则os.mkdir('d:\hello\hi')  #  仍然正常

自动积分框架

grad_fn: 记录的是创建该张量时所用的方法

例如:MulBackward0、AddBackward0、MeanBackward1等等

matplotlib基础用法

Matplotlib 绘图基础

xlim()和ylim()在Matplotlib种设置轴的限制

  matplotlib.pyplot.xlim()matplotlib.pyplot.ylim()可用于分别设置或获取 X 轴和 Y 轴的

范围限制。如果在这些方法中传递参数,则它们将设置各个轴的极限,如果不传递任何参数,则将获得各个轴的范围。例如

绘制$\sin(2 \pi x) + 1$的图像。

import numpy as np
import matplotlib.pyplot as plt

x = np.linespace(0, 10, 500)
y = np.sin(2 * np.pi * x) + 1

fig = plt.figure(figsize=(8, 6))
plt.plot(x, y)
plt.title("Setting range of Axes", fontsize=25)
plt.xlabel("x",fontsize=18)
plt.ylabel("1+sinx",fontsize=18)
plt.xlim(4,8)
plt.ylim(-0.5,2.5)
plt.show()

set_xlim() 和 set_ylim() 方法来设置轴限制

  matplotlib.axes.Axes.set_xlimmatplotlib.axes.Axes.set_ylim 也用于设置在结果图上查

看的数字范围的限制。

import numpy as np
import matplotlib.pyplot as plt

x=np.linspace(0,10,500)
y=np.sin(2 * np.pi * x)+1

fig = plt.figure(figsize=(8, 6))
axes = plt.axes()
axes.set_xlim([4, 8])
axes.set_ylim([-0.5, 2.5])
plt.plot(x, y)
plt.title("Setting range of Axes",fontsize=25)
plt.xlabel("x",fontsize=18)
plt.ylabel("1+sinx",fontsize=18)
plt.show()

使用 axis() 方法在 Matplotlib 中设置轴的限制

  也可以使用 matplotlib.pyplot.axis() 来设置轴的范围限制。语法如下:

plt.axis([xmin, xmax, ymin, ymax])

该方法消除了用于控制 X 轴和 Y 轴的单独功能的需要。

import numpy as np
import matplotlib.pyplot as plt

x=np.linspace(0,10,50)
y=np.sin(2 * np.pi * x)+1

fig = plt.figure(figsize=(8, 6))
plt.axis([4, 9, -0.5, 2.5])
plt.plot(x, y)
plt.title("Setting range of Axes",fontsize=25)
plt.xlabel("x",fontsize=18)
plt.ylabel("1+sinx",fontsize=18)
plt.show()

axse.plot绘制

  Format Setting分别是标记、线条、颜色

fmt = '[marker][line][color]'

Markers
标记设置

character description
'.' point marker
',' pixel marker
'o' circle marker
'v' triangle_down marker
'^' triangle_up marker
'<' triangle_left marker
'>' triangle_right marker
'1' tri_down marker
'2' tri_up marker
'3' tri_left marker
'4' tri_right marker
'8' octagon marker
's' square marker
'p' pentagon marker
'P' plus (filled) marker
'*' star marker
'h' hexagon1 marker
'H' hexagon2 marker
'+' plus marker
'x' x marker
'X' x (filled) marker
'D' diamond marker
'd' thin_diamond marker
'_' hline marker

Line Styles
曲线颜色

character description
'-' solid line style
'--' dashed line style
'-.' dash-dot line style
':' dotted line style

例如:

'b'    # blue markers with default shape
'or'   # red circles
'-g'   # green solid line
'--'   # dashed line with default color
'^k:'  # black triangle_up markers connected by a dotted line

Colors
支持的颜色缩写是单字母代码

character color
'b' blue
'g' green
'r' red
'c' cyan
'm' magenta
'y' yellow
'k' black
'w' white

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!