## 深度学习必备库 – Numpy Numpy是Numerical Python的简称，是Python中高性能科学计算和数据分析的基础包。Numpy提供了一个多维数组类型ndarray，它具有矢量算术运算和复杂广播的能力，可以实现快速的计算并且能节省存储空间。

## 1. 基础数据结构ndarray数组

ndarray数组是Numpy中的基础数据结构式， 本小结将介绍：

### 1.1 为什幺引入ndarray数组

ndarray数组中所有元素的数据类型是相同的，数据地址是连续的，批量操作数组元素时速度更快；list列表中元素的数据类型可以不同，需要通过寻址方式找到下一个元素

ndarray数组中实现了比较成熟的广播机制，矩阵运算时不需要写for循环

Numpy底层是用c语言编写的，内置了并行计算功能，运行速度高于纯Python代码

# Python原生的list
# 假设有两个list
a = [1, 2, 3, 4, 5]
b = [2, 3, 4, 5, 6]
# 完成如下计算
# 1 对a的每个元素 + 1
# a = a + 1 不能这幺写，会报错
# a[:] = a[:] + 1 也不能这幺写，也会报错
for i in range(5):
a[i] = a[i] + 1
a
[2, 3, 4, 5, 6]
########################################################
# 使用ndarray
import numpy as np
a = np.array([1, 2, 3, 4, 5])
a = a + 1
a
array([2, 3, 4, 5, 6])

#  计算 a和b中对应位置元素的和，是否可以这幺写？ python中的+运算？
a = [1, 2, 3, 4, 5]
b = [2, 3, 4, 5, 6]
c = a + b
# 检查输出发现，不是想要的结果
c
[1, 2, 3, 4, 5, 2, 3, 4, 5, 6]
########################################################
# 使用for循环，完成两个list对应位置元素相加
c = []
for i in range(5):
c.append(a[i] + b[i])
c
[3, 5, 7, 9, 11]
#########################################################
# 使用numpy中的ndarray完成两个ndarray相加
import numpy as np
a = np.array([1, 2, 3, 4, 5])
b = np.array([2, 3, 4, 5, 6])
c = a + b
c
array([ 3,  5,  7,  9, 11])

ndarray数组的矢量计算能力使得不需要写for循环，就可以非常方便的完成数学计算，在操作矢量或者矩阵时，可以像操作普通的数值变量一样编写程序，使得代码极其简洁。

# 二维数组维度 2x5
# array([[ 1,  2,  3,  4,  5],
#         [ 6,  7,  8,  9, 10]])
d = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
# c是一维数组，维度5
# array([ 4,  6,  8, 10, 12])
c = np.array([ 4,  6,  8, 10, 12])
e = d + c
e
array([[ 5,  8, 11, 14, 17],
[10, 13, 16, 19, 22]])

### 1.2 如何创建ndarray数组

import numpy as np
# 从list创建array
a = [1,2,3,4,5,6]
b = np.array(a)
b
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])
#########################################################
# 通过np.array创建
# 通过指定start, stop (不包括stop)，interval来产生一个1为的ndarray
# 类似于python中常用的range函数，用法一致
a = np.arange(0, 20, 2)
a
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])
#########################################################
# 创建全0的ndarray
a = np.zeros([3,3])    # 注意此处为zeros，[],()都可以
# a = np.zeros((3,3))
a
array([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
#########################################################
# 创建全1的ndarray
a = np.ones([3,3])
a
array([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
#########################################################
# 创建单位矩阵的ndarray
a = np.eyes(3)    # 因为单位矩阵，所以是eye,只需传入一个参数
a
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])

ndarray的属性包括形状shape、数据类型dtype、元素个数size和维度ndim等，下面的程序展示如何查看这些属性

# 数组的数据类型 ndarray.dtype
# 数组的形状 ndarray.shape，1维数组(N, )，二维数组(M, N)，三维数组(M, N, K)
# 数组的维度大小，ndarray.ndim, 其大小等于ndarray.shape所包含元素的个数
# 数组中包含的元素个数 ndarray.size，其大小等于各个维度的长度的乘积
a = np.ones([3, 3])
print('a, dtype: {}, shape: {}, size: {}, ndim: {}'.format(a.dtype, a.shape, a.size, a.ndim))
a, dtype: float64, shape: (3, 3), size: 9, ndim: 2

# 转化数据类型
# 不同数据类型的数组可以进行计算，但计算完之后的结果会改变
a = np.ones((3, 3))
b = a.astype(np.int64)
c = a.astype(np.float32)
d = b + c
print('b, dtype: {}, shape: {}'.format(b.dtype, b.shape))
print('c, dtype: {}, shape: {}'.format(c.dtype, c.shape))
print('d, dtype: {}, shape: {}'.format(d.dtype, d.shape))
b, dtype: int64, shape: (3, 3)
c, dtype: float32, shape: (3, 3)
d, dtype: float64, shape: (3, 3)
#######################################################
# 改变形状
c = a.reshape([1, 9])
print('c, dtype: {}, shape: {}'.format(c.dtype, c.shape))

### 1.3 ndarray 数组的基本运算

ndarray数组可以像普通的数值型变量一样进行加减乘除操作，这一小节将介绍两种形式的基本运算：

# 标量除以数组，用标量除以数组的每一个元素
arr = np.array([[1., 2., 3.], [4., 5., 6.]])
1. / arr
array([[1.        , 0.5       , 0.33333333],
[0.25      , 0.2       , 0.16666667]])

#######################################################
# 标量乘以数组，用标量乘以数组的每一个元素
arr = np.array([[1., 2., 3.], [4., 5., 6.]])
2.0 * arr
array([[ 2.,  4.,  6.],
[ 8., 10., 12.]])

#######################################################
# 标量加上数组，用标量加上数组的每一个元素
arr = np.array([[1., 2., 3.], [4., 5., 6.]])
2.0 + arr
array([[3., 4., 5.],
[6., 7., 8.]])

#######################################################
# 标量减去数组，用标量减去数组的每一个元素
arr = np.array([[1., 2., 3.], [4., 5., 6.]])
2.0 - arr
array([[ 1.,  0., -1.],
[-2., -3., -4.]])

# 数组 减去 数组， 用对应位置的元素相减
arr1 = np.array([[1., 2., 3.], [4., 5., 6.]])
arr2 = np.array([[11., 12., 13.], [21., 22., 23.]])
arr1 - arr2
array([[12., 14., 16.],
[25., 27., 29.]])

#######################################################
# 数组 乘以 数组，用对应位置的元素相乘
arr1 * arr2
array([[ 11.,  24.,  39.],
[ 84., 110., 138.]])

#######################################################
# 数组开根号，将每个位置的元素都开根号
arr ** 0.5
array([[1.        , 1.41421356, 1.73205081],
[2.        , 2.23606798, 2.44948974]])

### 1.4 ndarray数组的索引和切片

# 1维数组索引和切片
a = np.arange(30)
a     # 从0开始计算第10号位置
10

#######################################################
a = np.arange(30)
b = a[4:7]
b
array([4, 5, 6])

#######################################################
# 将一个标量值赋值给一个切片时，该值会自动传播到整个选区（如下图所示）
# 切片并不是生成一列新的数组，而仅仅只把其中一段拿出来操作，对切片的操作会影响原始数据
a = np.arange(30)
a[4:7] = 10
a
array([ 0,  1,  2,  3, 10, 10, 10,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29])

#######################################################
# 数组切片是原始数组的视图。这意味着数据不会被复制，
# 视图上的任何修改都会直接反映到源数组上
a = np.arange(30)
arr_slice = a[4:7]
arr_slice = 100
a, arr_slice
(array([  0,   1,   2,   3, 100,   5,   6,   7,   8,   9,  10,  11,  12,
13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,
26,  27,  28,  29]), array([100,   5,   6]))

#######################################################
# 通过copy给新数组创建不同的内存空间
a = np.arange(30)
arr_slice = a[4:7]
arr_slice = np.copy(arr_slice)
arr_slice = 100
a, arr_slice
(array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]),
array([100,   5,   6]))

#######################################################
# 多维数组索引和切片
a = np.arange(30)
arr3d = a.reshape(5, 3, 2)  # 可以理解为按从大块往小块分
arr3d
array([[[ 0,  1],
[ 2,  3],
[ 4,  5]],
[[ 6,  7],
[ 8,  9],
[10, 11]],
[[12, 13],
[14, 15],
[16, 17]],
[[18, 19],
[20, 21],
[22, 23]],
[[24, 25],
[26, 27],
[28, 29]]])
#######################################################
# 只有一个索引指标时，会在第0维上索引，后面的维度保持不变
arr3d
array([[0, 1],
[2, 3],
[4, 5]])

# 两个索引指标
arr3d
array([2, 3])

### 1.5 ndarray数组的统计计算

mean 均值
std 标准差
var 方差
sum 求和
max 最大值
min 最小值

# 计算均值，使用arr.mean() 或 np.mean(arr)，二者是等价的
arr = np.array([[1,2,3], [4,5,6], [7,8,9]])
arr.mean(), np.mean(arr)
(5.0, 5.0)

# 求和
arr.sum(), np.sum(arr)
(45, 45)

# 求最大值
arr.max(), np.max(arr)
(9, 9)

# 求最小值
arr.min(), np.min(arr)
(1, 1)

#######################################################
# 指定计算的维度
# axis = 0 表示列
# axis = 1 表示行
# 沿着第1维求平均，也就是将[1, 2, 3]取平均等于2，[4, 5, 6]取平均等于5，[7, 8, 9]取平均等于8
arr.mean(axis = 1)
array([2., 5., 8.])

# 沿着第0维求和，也就是将[1, 4, 7]求和等于12，[2, 5, 8]求和等于15，[3, 6, 9]求和等于18
arr.sum(axis=0)
array([12, 15, 18])

# 沿着第0维求最大值，也就是将[1, 4, 7]求最大值等于7，[2, 5, 8]求最大值等于8，[3, 6, 9]求最大值等于9
arr.max(axis=0)
array([7, 8, 9])

# 沿着第1维求最小值，也就是将[1, 2, 3]求最小值等于1，[4, 5, 6]求最小值等于4，[7, 8, 9]求最小值等于7
arr.min(axis=1)
array([1, 4, 7])

# 计算标准差
arr.std()
2.581988897471611

# 计算方差
arr.var()
6.666666666666667

# 找出最大元素的索引
arr.argmax(), arr.argmax(axis=0), arr.argmax(axis=1)
(8, array([2, 2, 2]), array([2, 2, 2]))

# 找出最小元素的索引
arr.argmin(), arr.argmin(axis=0), arr.argmin(axis=1)
(0, array([0, 0, 0]), array([0, 0, 0]))

## 2. 随机数np.random

### 2.1 创建随机ndarray数组

# 生成均匀分布随机数，随机数取值范围在[0, 1)之间
a = np.random.rand(3, 3)
a
array([[0.08833981, 0.68535982, 0.95339335],
[0.00394827, 0.51219226, 0.81262096],
[0.61252607, 0.72175532, 0.29187607]])

# 生成均匀分布随机数，指定随机数取值范围和数组形状
a = np.random.uniform(low = -1.0, high = 1.0, size=(2,2))
a
array([[ 0.83554825,  0.42915157],
[ 0.08508874, -0.7156599 ]])

# 生成标准正态分布随机数
a = np.random.randn(3, 3)
a
array([[ 1.484537  , -1.07980489, -1.97772828],
[-1.7433723 ,  0.26607016,  2.38496733],
[ 1.12369125,  1.67262221,  0.09914922]])

# 生成正态分布随机数，指定均值loc和方差scale
a = np.random.normal(loc = 1.0, scale = 1.0, size = (3,3))
a
array([[2.39799638, 0.72875201, 1.61320418],
[0.73268281, 0.45069099, 1.1327083 ],
[0.52385799, 2.30847308, 1.19501328]])

### 2.2 设置随机种子

# 可以多次运行，观察程序输出结果是否一致
# 如果不设置随机数种子，观察多次运行输出结果是否一致
np.random.seed(10)
a = np.random.rand(3, 3)
a
array([[0.77132064, 0.02075195, 0.63364823],
[0.74880388, 0.49850701, 0.22479665],
[0.19806286, 0.76053071, 0.16911084]])

### 2.3 随机打乱ndarray数组顺序

# 生成一维数组
a = np.arange(0, 30)
# 打乱一维数组顺序
print('before random shuffle: ', a)
np.random.shuffle(a)
print('after random shuffle: ', a)
('before random shuffle: ', array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]))
('after random shuffle: ', array([10, 21, 26,  7,  0, 23,  2, 17, 18, 20, 12,  6,  9,  3, 25,  5, 13,
14, 24, 29,  1, 28, 11, 15, 27, 16, 19,  4, 22,  8]))

# 生成一维数组
a = np.arange(0, 30)
# 将一维数组转化成2维数组
a = a.reshape(10, 3)
# 打乱一维数组顺序
print('before random shuffle: \n{}'.format(a))
np.random.shuffle(a)
print('after random shuffle: \n{}'.format(a))
before random shuffle:
[[ 0  1  2]
[ 3  4  5]
[ 6  7  8]
[ 9 10 11]
[12 13 14]
[15 16 17]
[18 19 20]
[21 22 23]
[24 25 26]
[27 28 29]]
after random shuffle:
[[15 16 17]
[12 13 14]
[27 28 29]
[ 3  4  5]
[ 9 10 11]
[21 22 23]
[18 19 20]
[ 0  1  2]
[ 6  7  8]
[24 25 26]]

### 2.4 随机选取元素

# 随机选取一选部分元素
a = np.arange(30)
b = np.random.choice(a, size=5)
b
array([ 0, 24, 12,  5,  4])

## 3. 线性代数操作

Numpy中实现了线性代数中常用的各种操作，并形成了numpy.linalg线性代数相关的模块。其中包括：

diag 以一维数组的形式返回方阵的对角线（或非对角线）元素，或将一维数组转换为方阵（非对角线元素为0）
dot 矩阵乘法
trace 计算对角线元素的和
det 计算矩阵行列式
eig 计算方阵的特征值和特征向量
inv 计算方阵的逆

# 矩阵相乘
a = np.arange(12)
b = a.reshape([3, 4])
c = a.reshape([4, 3])
# 矩阵b的第二维大小，必须等于矩阵c的第一维大小
d = b.dot(c) # 等价于 np.dot(b, c)
print('a: \n{}'.format(a))
print('b: \n{}'.format(b))
print('c: \n{}'.format(c))
print('d: \n{}'.format(d))
a:
[ 0  1  2  3  4  5  6  7  8  9 10 11]
b:
[[ 0  1  2  3]
[ 4  5  6  7]
[ 8  9 10 11]]
c:
[[ 0  1  2]
[ 3  4  5]
[ 6  7  8]
[ 9 10 11]]
d:
[[ 42  48  54]
[114 136 158]
[186 224 262]]

# numpy.linalg  中有一组标准的矩阵分解运算以及诸如求逆和行列式之类的东西
# np.linalg.diag 以一维数组的形式返回方阵的对角线（或非对角线）元素，
# 或将一维数组转换为方阵（非对角线元素为0）
e = np.diag(d)
f = np.diag(e)
print('d: \n{}'.format(d))
print('e: \n{}'.format(e))
print('f: \n{}'.format(f))
d:
[[ 42  48  54]
[114 136 158]
[186 224 262]]
e:
[ 42 136 262]
f:
[[ 42   0   0]
[  0 136   0]
[  0   0 262]]

# trace, 计算对角线元素的和
g = np.trace(d)
g
440

# det，计算行列式
h = np.linalg.det(d)
h
1.3642420526593978e-11

# eig，计算特征值和特征向量
i = np.linalg.eig(d)
i
(array([4.36702561e+02, 3.29743887e+00, 3.13152204e-14]),
array([[ 0.17716392,  0.77712552,  0.40824829],
[ 0.5095763 ,  0.07620532, -0.81649658],
[ 0.84198868, -0.62471488,  0.40824829]]))

# inv，计算方阵的逆
tmp = np.random.rand(3, 3)
j = np.linalg.inv(tmp)
j
array([[-0.59449952,  1.39735912, -0.06654123],
[ 1.56034184, -0.40734618, -0.48055062],
[ 0.10659811, -0.62164179,  1.30437759]])

## 4. Numpy保存与导入文件

# 使用np.fromfile从文本文件'housing.data'读入数据
# 这里要设置参数sep = ' '，表示使用空白字符来分隔数据
# 空格或者回车都属于空白字符，读入的数据被转化成1维数组
d = np.fromfile('./work/housing.data', sep = ' ')
d
array([6.320e-03, 1.800e+01, 2.310e+00, ..., 3.969e+02, 7.880e+00,
1.190e+01])

# 产生随机数组a
a = np.random.rand(3,3)
np.save('a.npy', a)

# 从磁盘文件'a.npy'读入数组
b = np.load('a.npy')

## 5. Numpy应用举例

### 5.1 计算激活函数 # ReLU和Sigmoid激活函数示意图
import numpy as np
# %matplotlib inline    # 在Jupyter notebook 中在图框中显示
import matplotlib.pyplot as plt
import matplotlib.patches as patches
#设置图片大小
plt.figure(figsize=(8, 3))
# x是1维数组，数组大小是从-10. 到10.的实数，每隔0.1取一个点
x = np.arange(-10, 10, 0.1)
# 计算 Sigmoid函数
s = 1.0 / (1 + np.exp(- x))
# 计算ReLU函数
# clip 函数， x表示输入的值， a_min表示小于a_min都赋值为a_min； a_max同理； None表示不进行操作。
y = np.clip(x, a_min = 0., a_max = None)
#########################################################
# 以下部分为画图程序
# 设置两个子图窗口，将Sigmoid的函数图像画在左边
# 121 表示 一行2列， 占第一列
f = plt.subplot(121)
# 画出函数曲线
plt.plot(x, s, color='r')
# 添加文字说明
plt.text(-5., 0.9, r'$y=\sigma(x)$', fontsize=13)
# 设置坐标轴格式
currentAxis=plt.gca()
currentAxis.xaxis.set_label_text('x', fontsize=15)
currentAxis.yaxis.set_label_text('y', fontsize=15)
# 将ReLU的函数图像画在左边
# 122 表示 一行2列， 占第二列
f = plt.subplot(122)
# 画出函数曲线
plt.plot(x, y, color='g')
# 添加文字说明
plt.text(-3.0, 9, r'$y=ReLU(x)$', fontsize=13)
# 设置坐标轴格式
currentAxis=plt.gca()
currentAxis.xaxis.set_label_text('x', fontsize=15)
currentAxis.yaxis.set_label_text('y', fontsize=15)
plt.show() ### 5.2 图像处理

# 导入需要的包
# 后续会有教程解释 matplotlib 和 PIL 这两个库
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
# 读入图片
image = Image.open('./car.jpg')    # 可改成绝对路径
image = np.array(image)
# 查看数据形状，其形状是[H, W, 3]，
# 其中H代表高度， W是宽度，3代表RGB三个通道
plt.imshow(image)

(612, 612, 3) # 垂直方向翻转
# 这里使用数组切片的方式来完成，
# 相当于将图片最后一行挪到第一行，
# 倒数第二行挪到第二行，...,
# 第一行挪到倒数第一行
# 对于行指标，使用::-1来表示切片，
# 负数步长表示以最后一个元素为起点，向左走寻找下一个点
# 对于列指标和RGB通道，仅使用:表示该维度不改变
image2 = image[::-1, :, :]
plt.imshow(image2) # 水平方向翻转
image3 = image[:, ::-1, :]
plt.imshow(image3) # 保存图片
im3 = Image.fromarray(image3)
im3.save('im3.jpg')

#  高度方向裁剪
H, W = image.shape, image.shape
# 注意此处用整除，H_start必须为整数
H1 = H // 2
H2 = H
image4 = image[H1:H2, :, :]
plt.imshow(image4) # 调整亮度
image6 = image * 0.5
plt.imshow(image6.astype('uint8')) #间隔行列采样，图像尺寸会减半，清晰度变差
image10 = image[::2, ::2, :]
plt.imshow(image10)
image10.shape

(306, 306, 3)
{"ft_lang_label":"__label__zh","ft_lang_prob":0.5359741,"math_prob":0.99951124,"size":14044,"snap":"2022-27-2022-33","text_gpt3_token_len":7847,"char_repetition_ratio":0.19935897,"word_repetition_ratio":0.1437061,"special_character_ratio":0.5498434,"punctuation_ratio":0.33901855,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.9895252,"pos_list":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18],"im_url_duplicate_count":[null,1,null,1,null,1,null,1,null,1,null,1,null,1,null,1,null,1,null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2022-06-26T22:42:53Z\",\"WARC-Record-ID\":\"<urn:uuid:33477908-70cb-44ce-afb1-aa7d0e61732c>\",\"Content-Length\":\"65056\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:83982baf-b625-4eb3-b297-3be15b5f0729>\",\"WARC-Concurrent-To\":\"<urn:uuid:fd85fc7b-6792-4a1d-a0e1-3ff0e206354e>\",\"WARC-IP-Address\":\"172.67.157.1\",\"WARC-Target-URI\":\"https://flashgene.com/archives/87398.html\",\"WARC-Payload-Digest\":\"sha1:4A7WYNCC2C744KTGMIY76LXJCND7D3PE\",\"WARC-Block-Digest\":\"sha1:CK4NNEDMXXBYOD4W6DHFCP7VMUDNWBX4\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2022/CC-MAIN-2022-27/CC-MAIN-2022-27_segments_1656103322581.16_warc_CC-MAIN-20220626222503-20220627012503-00477.warc.gz\"}"}