进阶知识补充
- 零、深度学习的技巧
- 1.偏差和方差解决技巧
- 2.深度网络层数
- 一、深度学习的核心
- 1. 参数(Weights and Biases)
- 2. 网络架构(Network Architecture)
- 2.1. 激活函数和其导数
- Sigmoid 函数:
- ReLU 函数:
- Tanh 函数:
- 3. 损失函数(Loss Function)
- 4. 优化算法(Optimization Algorithm)
- 5. 正则化(Regularization)
- 6. 学习率(Learning Rate)
- 二、训练集、开发集、测试集
- 1:训练集(Training Set):60-80%
- 2:开发集(或验证集,Development Set/Validation Set):10-20%
- 2:测试集(Test Set):10-20%
- 三:Bias(偏差)和Variance(方差)
- Bias(偏差)
- Variance(方差)
- Bias-Variance Tradeoff(偏差-方差权衡)
- 解决方法
- 四:正则化(Regularization)
- 1. 重要特征和最高权重
- 2. L1正则化(Lasso正则化):作用于损失函数和权重
- 3. L2正则化(Ridge正则化):作用于损失函数和权重
- 4. Dropout正则化:作用于激活值
- 5. 其它正则化:
- 1.歪门邪道--数据扩增(Data Augmentation)
- 2.早停法(Early Stopping):基于验证集
- 五、设置优化问
- 1. 输入:Normalizing化
- 2. 防止梯度爆炸/梯度消失
- 1.对于ReLU作为激活函数: He
- 2.对于Tanh\sigmoid 作为激活函数: Xavier
- 3. 梯度检查(Gradient Checking)
- 1. 中心差分公式
- 2. 梯度检查
- PS、Python技巧
- 1.随机数 randn 和 rand 的区别
- 2.同时设置网络层数
- 3.多维数组转一维向量
- 4.数组逐元素平方
- 5.拷贝数组举证
零、深度学习的技巧
1.偏差和方差解决技巧
1 先要解决偏差问题,当偏差解决以后再考虑方差问题。
2 当进行方差操作以后,一定要再次测试新的策略的会不会再次导致偏差问题, 也就是当解决了过拟合操作以后,一定要再次测试一下会不会欠拟合
减小偏差
- 使用更复杂的模型
- 增加模型参数
- 减少正则化强度
减小方差
- 使用更多的训练数据
- 简化模型
- 增加正则化强度(如L2正则化)
- 使用集成方法(如bagging和boosting)
2.深度网络层数
先设计逻辑回归,然后一点点增加隐藏层的数量。
一、深度学习的核心
在深度学习中,模型的核心组成可以包括以下几个方面:
1. 参数(Weights and Biases)
- 权重 ( w ):连接神经元之间的参数,用于调整输入特征的重要性。在深度神经网络中,每一个特征有自己的权重。宏观到每一层,不同的层都有自己的权重矩阵。
- 偏置 ( b ):每个神经元都有一个偏置,用于调整模型的输出,使其更灵活地拟合数据。
2. 网络架构(Network Architecture)
-
层数(Layers):深度神经网络由多层神经元组成,包括输入层、隐藏层和输出层,但是在计算层数的时候不包括输入层。
在计算的时候,一般使用 L L L 或者 l l l 来所在表示层的位置。
-
神经元数(Neurons):每一层中的神经元数,也决定了该层的复杂度。
用 n n n来表示神经元的数量,第 l l l 层的神经元表示为 n [ l ] n^{[l]} n[l]
-
线性函数(Linear Functions):提供基本的线性函数计算。
公式为 z = W x + b z=Wx+b z=Wx+b,第 l l l 层的线性函数为 z [ l ] = W [ l ] a [ l − 1 ] + b [ l ] z^{[l]}=W^{[l]}a^{[l-1]}+b^{[l]} z[l]=W[l]a[l−1]+b[l], a [ l − 1 ] a^{[l-1]} a[l−1]为前一层的激活值,对于第一层来说 a [ l − 1 ] a^{[l-1]} a[l−1]是输入层。
a i [ l ] a^{[l]}_i ai[l]表示第 l l l 层的第 i i i 个神经元的激活值
-
激活函数(Activation Functions) 转换线性函数到非线性,如ReLU、sigmoid、tanh等,用于引入非线性,使模型能够学习复杂的模式。
公式为 a [ l ] = g ( z [ l ] ) a^{[l]}=g(z^{[l]}) a[l]=g(z[l]),激活函数统一用 g g g 表示,激活值为 a a a,但是具体的算法则不一样。
2.1. 激活函数和其导数
Sigmoid 函数:
σ ( x ) = 1 1 + e − x \sigma(x) = \frac{1}{1 + e^{-x}} σ(x)=1+e−x1
导数公式:
d σ ( z [ l ] ) d z [ l ] = a [ l ] ( 1 − a [ l ] ) \frac{d \sigma(z^{[l]})}{dz^{[l]}} = a^{[l]} (1 - a^{[l]}) dz[l]dσ(z[l])=a[l](1−a[l])
ReLU 函数:
ReLU ( x ) = max ( 0 , x ) \text{ReLU}(x) = \max(0, x) ReLU(x)=max(0,x)
导数公式:
d ReLU ( z [ l ] ) d z [ l ] = { 1 if z [ l ] > 0 0 if z [ l ] ≤ 0 \frac{d \text{ReLU}(z^{[l]})}{dz^{[l]}} = \begin{cases} 1 & \text{if } z^{[l]} > 0 \\ 0 & \text{if } z^{[l]} \leq 0 \end{cases} dz[l]dReLU(z[l])={10if z[l]>0if z[l]≤0
Tanh 函数:
tanh ( x ) = e x − e − x e x + e − x \tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} tanh(x)=ex+e−xex−e−x
导数公式:
d tanh ( z [ l ] ) d z [ l ] = 1 − ( a [ l ] ) 2 \frac{d \tanh(z^{[l]})}{dz^{[l]}} = 1 - (a^{[l]})^2 dz[l]dtanh(z[l])=1−(a[l])2
tanh ′ ( x ) = 1 − tanh 2 ( x ) \tanh'(x) = 1 - \tanh^2(x) tanh′(x)=1−tanh2(x)
3. 损失函数(Loss Function)
- 用于衡量模型预测值与真实值之间的差距。常见的损失函数有均方误差(MSE)、交叉熵损失(Cross-Entropy Loss)等。
4. 优化算法(Optimization Algorithm)
- 用于更新模型参数以最小化损失函数。常见的优化算法有随机梯度下降(SGD)、Adam、RMSprop等。
5. 正则化(Regularization)
- 防止过拟合的技术,如L1/L2正则化、Dropout等。
6. 学习率(Learning Rate)
- 控制每次参数更新的步长大小,是优化过程中非常关键的超参数。
二、训练集、开发集、测试集
1:训练集(Training Set):60-80%
训练集的作用是为了学习数据的特征和模式,使模型能够对输入数据进行准确的预测。
主要的任务是获得学习到的 “模型”,也就是 W W W 和 b b b 的值。
2:开发集(或验证集,Development Set/Validation Set):10-20%
在使用训练集获得了模型,即确定了 W W W 和 b b b 的值以后使用开发集来测试==超参数==调优,例如学习率、正则化系数等。
2:测试集(Test Set):10-20%
检测最终的模型的性能,用于最终评估模型性能的数据集。
三:Bias(偏差)和Variance(方差)
Bias(偏差)
偏差是指模型预测值与真实值之间的差异。
高偏差表示模型在训练数据和测试数据上的预测都较差,即模型欠拟合(underfitting)。偏差高的模型通常过于简单,无法捕捉数据中的复杂模式。
特征:
- 模型过于简单
- 无法很好地拟合训练数据
- 训练误差和测试误差都很高
Variance(方差)
方差是指模型对训练数据中随机噪声的敏感程度。
高方差表示模型在训练数据上表现很好,但在测试数据上表现较差,即模型过拟合(overfitting)。方差高的模型通常过于复杂,只记住训练数据中的噪声,但无法泛化到新的数据。
特征:
- 模型过于复杂
- 在训练数据上表现很好,但在测试数据上表现较差
- 训练误差低,测试误差高
Bias-Variance Tradeoff(偏差-方差权衡)
偏差和方差之间存在权衡关系,即在降低偏差的同时可能会增加方差,反之亦然。理想情况下,模型应当在偏差和方差之间找到一个平衡点,以确保其在训练数据和测试数据上都能有较好的表现。
-
高偏差、低方差:
- 简单的线性模型
- 训练误差和测试误差都较高
-
低偏差、高方差:
- 复杂的神经网络模型
- 训练误差低,测试误差高
-
适中偏差、适中方差:
- 适度复杂的模型,如带有正则化的神经网络
- 训练误差和测试误差都在合理范围内
解决方法
先要解决偏差问题,当偏差解决以后再考虑方差问题。
当进行方差操作以后,一定要再次测试新的策略的会不会再次导致偏差问题
减小偏差
- 使用更复杂的模型
- 增加模型参数
- 减少正则化强度
减小方差
- 使用更多的训练数据
- 简化模型
- 增加正则化强度(如L2正则化)
- 使用集成方法(如bagging和boosting)
四:正则化(Regularization)
正则化就是利用一些手段,在模型训练解决解决模型过拟合、提高范化能力等问题。
不同的方法作用的阶段不同。
不过这之前需要明白一个概念:重要特征和最高权重
1. 重要特征和最高权重
PS:以下全为个人抽象理解,未来有极大概率修改这段话,切勿照本宣科。
在深度学习中,重要特征指的是“对预测影响较大”的特征值。
假设样本X有俩个特征:x1为0.9,x2为0.1
如果此时标签Y是1,那么x1就是重要特征,如果标签为0,那么x2就是重要特征。
虽然最后的关于特征的表示,是特征*权重,但是权重的值一般都比较小,类似0.01
即使一开始给非重要特征设置了高权重,在一次次优化中(类似梯度下降)非重要特征的权重也会变低,重要特征的权重会变高。
所以,从结果导向来看:重要特征就是权重高的特征,非重要特征就是权重低的特征
2. L1正则化(Lasso正则化):作用于损失函数和权重
J ( w , b ) = 1 m ∑ i = 1 m L ( y ^ i , y i ) + λ m ∑ ℓ = 1 L ∥ ω [ ℓ ] ∥ J(w, b) = \frac{1}{m} \sum_{i=1}^m L(\hat y^{i},y^{i})+ \frac{\lambda}{m} \sum_{\ell=1}^L \|\omega^{[\ell]}\| J(w,b)=m1i=1∑mL(y^i,yi)+mλℓ=1∑L∥ω[ℓ]∥
d ω [ ℓ ] = d J d ω [ ℓ ] + λ m sign ( ω [ ℓ ] ) \text{d}\omega^{[\ell]} = \frac{d J}{d \omega^{[\ell]}} + \frac{\lambda}{m} \text{sign}(\omega^{[\ell]}) dω[ℓ]=dω[ℓ]dJ+mλsign(ω[ℓ])
解析:简单来说Lasso正则化就是在原有的损失函数上增加了一个 λ \lambda λ 倍的权重的方差,这样会导致如下的结果:
1:由于增加的内容基于权重的值,对于大权重的特征来说,损失会增大,所以下一次更新的时候权重会被减少很多。
2:对于小权重的特征来说,损失也会增大,但也没有那么大,所以下一次更新的时候权重会减少但是没有很多。
抽象举例,假设:大权重为100,小权重为1
这样操作以后,下一次更新也许大权重会变为 1 , 减少了99,也许小权重会变为 0.1,减少了 0.9。
虽然大权重变少了很多,但是还是比小权重高,所以对于模型来说,大权重的特征的重要性还是比小权重的特征的重要性要高。
为什么降低特征的权重可以减少过拟合呢?
因为可以减少对个别特征的过度依赖。泛化能力不足往往与模型对个别特征的过度依赖有关
此外,降低权重还有另外作用,减小非重要特征的影响力–权重被趋近于0。减小网络复杂度
对第一层来说,减小的是输入的特征的权重,优化的是输入样本的特征。
但是对于深度网络的其它层来说,减小是上一层的输出的权重,优化的则是上一层神经网络
L1正则化前:
L1正则化后:
3. L2正则化(Ridge正则化):作用于损失函数和权重
J ( w , b ) = 1 m ∑ i = 1 m L ( y ^ i , y i ) + λ 2 m ∑ j = 1 n w j 2 J(w, b) = \frac{1}{m} \sum_{i=1}^m L(\hat y^{i},y^{i})+ \frac{\lambda}{2m}\sum_{j=1}^n w_j^2 J(w,b)=m1i=1∑mL(y^i,yi)+2mλj=1∑nwj2
d ω [ ℓ ] = d J d ω [ ℓ ] + λ m ω [ ℓ ] \text{d}\omega^{[\ell]} = \frac{d J}{d \omega^{[\ell]}} + \frac{\lambda}{m} \omega^{[\ell]} dω[ℓ]=dω[ℓ]dJ+mλω[ℓ]
实现代码:
#np.square 数组里面每一个值都平方
regularization =(1/m)*(lambd/2)*(np.sum(np.square(W1))+np.sum(np.square(W2))+np.sum(np.square(W3)))
原理于L1类似,差异如下:
区别
- L1正则化会导致模型参数中许多值变为零,从而产生稀疏解。这是因为L1正则化倾向于在优化过程中沿参数的坐标轴将其推向零,这也有助于特征选择。
- L2正则化倾向于将参数值均匀地减小,而不是将它们推向零。这可以防止任何一个参数对模型结果产生过大影响,从而减轻模型过拟合的问题。
对异常值的敏感度
- L1正则化对于异常值较不敏感,因为它倾向于选择某些特征而忽略其他特征,这使得模型更加简单且稳健。
- L2正则化由于平方项的存在,对异常值较敏感,特别是当异常值的平方会对损失函数造成很大影响时。
解的唯一性
- L1正则化可能会因为稀疏性而不产生唯一解,特别是当一些特征高度相关时。
- L2正则化通常会产生唯一解,因为添加的惩罚项 λ ∑ j = 1 n w j 2 \lambda \sum_{j=1}^n w_j^2 λ∑j=1nwj2 确保了损失函数是严格凸的。
应用场景
- L1正则化常用于特征选择和处理高维数据的场景,尤其是当一些特征是多余的或者相关性很高时。
- L2正则化更适用于防止模型复杂度过高,需要处理模型中所有特征大体上都是重要的情况。
选择L1还是L2正则化通常取决于具体问题的需求和数据的特性。
例如,如果你需要一个简化的模型来解释模型是如何做出决策的(特征选择),L1可能是更好的选择。
如果你需要确保模型的所有参数都小一些,从而增强模型的泛化能力,那么L2可能是更合适的选择。同时L2也是最常用的正则化手段。
在实践中,也可以同时使用L1和L2正则化,这种组合被称为弹性网(Elastic Net)正则化。
4. Dropout正则化:作用于激活值
Dropout正则化不能用于测试阶段!!Dropout正则化不能用于测试阶段!!
相较于L1和L2利用损失函数来间接的弱化一些不重要的神经元。
Dropout 的核心思想更直接,就是在每次训练迭代中,随机将一些神经元的激活值 a a a 设置为零,每次训练设置的神经元都不同。
Dropout 丢弃的不是神经元,丢弃的是当前个别神经元对于样本的预测,
或者说丢弃的是当前层某一个神经元对于原始输入样本的预测,每次训练迭代中丢的都不一样。
这个过程涉及到公式不明朗(在我看来是这样…)所以在此直接记录完成的代码。
# A的维度(当前神经元个数,样本的个数)
# A3:存放第三层的5个神经元对于最开始输入的10个样本的预测
A3 = np.random.randn(5, 10)# 开始
# 1.设置 keep_prob 为 0.8 (即有 0.2 的概率舍弃)
keep_prob = 0.8 #概率为1-keep_prob# 2.设计一个和A3形状一样的随机数矩阵
D3 = np.random.rand(a3.shape[0], a3.shape[1]) # 这里用的是rand,在[0,1) 之间取值
D3 = D3 < keep_prob #用True 和 False 随机填充 D3# 3.利用随机矩阵的True 和 False随机丢弃 A3中的值
A3 = A3 * D3 #True == 1 / False == 0# 4.扩大A3的值
A3 = A3/keep_prob #因为有(1-keep_prob)也就是20%,的值被舍弃了,所以扩大20%。
# A3= A3 /(8/10)
# A3= A3 *(10/8) #......
# 5.更新导数,从后向前
dA3 = dA3 * D3
dA3 = dA3 / keep_prob dA2 = dA2 * D2
dA2 = dA2 / keep_prob
#.....
其实我还是有点不理解第4步,不过先这样记着吧。
此外, Dropout正则化的设置比较灵活,对于重要的层,比如最后的输出层,我们也可以不用 Dropout,把 keep_prob 设置为1。
5. 其它正则化:
1.歪门邪道–数据扩增(Data Augmentation)
简单来说就是修改当前有的数据集为新数据。
举例数据集数据为图片,那么就可以水平反转图片、裁切图片、图片轻微扭曲。
2.早停法(Early Stopping):基于验证集
简单来说,就是在测试寻找超参数(迭代次数、学习率等)的时候,找一个对于验证集来说损失最低的 W W W和 b b b, 即使这个 W W W和 b b b不是对于训练集的损失最低。
五、设置优化问
除了正则化以外,一些对于设置的优化方式,也可以帮助建立更好的模型,主要有以下几点。
1. 输入:Normalizing化
目的:可以将数据聚合到以0为原点,的方差标准化为 1。
公式:
第一步,获得输入的均值:
μ = 1 m ∑ i = 1 m x ( i ) \mu = \frac{1}{m} \sum_{i=1}^{m} x^{(i)} μ=m1i=1∑mx(i)
第二步,计算方差:
σ 2 = 1 m ∑ i = 1 m ( x ( i ) − μ ) 2 \sigma^2 = \frac{1}{m} \sum_{i=1}^{m} (x^{(i)} - \mu)^2 σ2=m1i=1∑m(x(i)−μ)2
于是标准差:
σ = 1 m ∑ i = 1 m ( x ( i ) − μ ) 2 \sigma = \sqrt{\frac{1}{m} \sum_{i=1}^{m} (x^{(i)} - \mu)^2} σ=m1i=1∑m(x(i)−μ)2
第三步,最终的标准化数据:
x ′ = x − μ σ x' = \frac{x - \mu}{\sigma} x′=σx−μ
通过输入Normalizing化,可以让特征分布的更均匀,损失函数图形更像一个碗,更容易梯度下降。
2. 防止梯度爆炸/梯度消失
防止梯度爆炸/梯度消失,主要是因为对于深层的神经网络,如果用统一的方式初始化 W 矩阵,W的初始值都相同。
在后续涉及到 W 乘法操作中可能会导致 W 过大或者过小,继而导致 Z 过大或者过小。
所以我们需要优化W的初始化,即对不同的层用不同的W初始化函数
对于不同的激活函数,初始化的方法也不一样
1.对于ReLU作为激活函数: He
WL=np.random.rand(shape) * np.sqrt( 2.0 / 前一层的神经元个数) # np.sqrt( 2 / n[l-1])# W的形状为(n[l], n[l-1]),n[l]为当前神经元个数; n[l-1]为前一层的神经元个数
2.对于Tanh\sigmoid 作为激活函数: Xavier
WL=np.random.rand(shape) * np.sqrt( 1.0 / 前一层的神经元个数) # np.sqrt( 2 / n[l-1])# W的形状为(n[l], n[l-1]),n[l]为当前神经元个数; n[l-1]为前一层的神经元个数
也有第二个版本
WL=np.random.rand(shape) * np.sqrt( 2.0 / (前一层的神经元个数)+(当前层的神经元个数)) # np.sqrt( 2 / n[l-1])# W的形状为(n[l], n[l-1]),n[l]为当前神经元个数; n[l-1]为前一层的神经元个数
3. 梯度检查(Gradient Checking)
当模型出现一些问题的时候,我们需要逐一排查,其中一个很重要的部分是对 “梯度下降算法”和“反向传播”的检查,要进行梯度检查,必须要考虑下面这三点:
- 只有在debug的时候,代码调式的时候才可以使用。训练测试的时候不要使用。
- 考虑代码中是否用了正则化
- 不能与 dropout 一起用
1. 中心差分公式
梯度检查的核心来源于中心差分公式:
f ′ ( x ) ≈ f ( x + h ) − f ( x − h ) 2 h f'(x) \approx \frac{f(x + h) - f(x - h)}{2h} f′(x)≈2hf(x+h)−f(x−h)
函数 f ( x )在 x 的点导数 ≈ f ( x + 一个微小的偏量 ) − f ( x − 一个微小的偏量 ) 2 ∗ 一个微小的偏量 函数f(x)在x的点导数\approx \frac{f(x + 一个微小的偏量) - f(x -一个微小的偏量)}{2 * 一个微小的偏量} 函数f(x)在x的点导数≈2∗一个微小的偏量f(x+一个微小的偏量)−f(x−一个微小的偏量)
不过,这个方法有一个必要的前提条件,
那就是 h h h, 这个移动的标量必须是一个极小值,类似 1 0 − 7 . 所得到的误差一般为 h 2 10^{-7}. 所得到的误差一般为h^2 10−7.所得到的误差一般为h2
2. 梯度检查
- 首先,将所有 L L L 层网络中的每一个 W W W 和 b b b 聚合为一个单位
# θ ={w1,b1,w2,b2....wl,bl}
theta=np.concatenate([w1.flatten(),b1 , w2.flatten(),b2 , w3.flatten(),b3 , .....)}
- 然后,我们计算近似导数 dθ 的值
for i = L:dθ = (J(θ1,θ2,θ3.....θi+h,...,θL)-J(θ1,θ2,θ3.....θi-h,...,θL)) / 2h
- 计算似导数 dθ 集与实际导集合的相对公差(relative error)
relative error = ∥ d θ − grad analytic ∥ ∥ d θ ∥ + ∥ grad analytic ∥ \text{relative error} = \frac{\left\| dθ - \text{grad}_{\text{analytic}} \right\|}{\left\| dθ \right\| + \left\| \text{grad}_{\text{analytic}} \right\|} relative error=∥dθ∥+ gradanalytic dθ−gradanalytic
这个移动的标量必须是一个极小值,类似 1 0 − 7 10^{-7} 10−7
如果 h h h 是 1 0 − 7 10^{-7} 10−7 ,那么relative error的值应该在 1 0 − 5 10^{-5} 10−5 到 1 0 − 9 10^{-9} 10−9 之间,否则为异常。
PS、Python技巧
1.随机数 randn 和 rand 的区别
在Python中,特别是在涉及到科学计算和数据处理的库NumPy中,randn
和rand
函数都用来生成随机数,但它们的用途和输出的随机数类型有所不同:
-
numpy.random.randn
:randn
函数生成的是服从标准正态分布(均值为0,方差为1的正态分布)的随机数。- 可以生成任意形状的数组。
- 例如,
np.random.randn(2, 3)
会生成一个2行3列的数组,数组中的每个元素都是从标准正态分布中随机抽取的。
-
numpy.random.rand
:rand
函数生成的是在区间[0, 1)内均匀分布的随机数。- 同样可以生成任意形状的数组。
- 例如,
np.random.rand(2, 3)
会生成一个2行3列的数组,数组中的每个元素都是从[0, 1)区间的均匀分布中随机抽取的。
简而言之,主要区别在于:
randn
用于生成符合标准正态分布的随机数。rand
用于生成符合均匀分布的随机数。
具体到应用:
初始化权重矩阵:randn
Dropout正则化:rand
2.同时设置网络层数
3.多维数组转一维向量
flatten
操作通常用于将多维数组(如矩阵或张量)变为一维数组(向量)
import numpy as npa=np.random.rand(3,3)
print (a)
# [[0.81326486 0.08026124 0.38529666]
# [0.26521685 0.2712353 0.37634291]
# [0.70843229 0.34048282 0.32902673]]a=a.flatten()
print(a)
#[0.81326486 0.08026124 0.38529666 0.26521685 0.2712353 0.37634291 0.70843229 0.34048282 0.32902673]
4.数组逐元素平方
np.square
是 NumPy 库中的一个函数,用于逐元素计算输入数组中每个元素的平方。该函数返回一个新数组,其中包含输入数组的每个元素的平方值。这个操作是逐元素的,因此对于输入数组中的每个元素,都有对应的平方值。
import numpy as np
a=np.random.rand(3,3)
print(a)
# [[0.60829188 0.462002 0.53711763]
# [0.45887545 0.0317922 0.96084417]
# [0.62161485 0.53259823 0.95088835]]a=np.square(a)
print(a)
# [[0.37001901 0.21344585 0.28849535]
# [0.21056668 0.00101074 0.92322152]
# [0.38640502 0.28366088 0.90418865]]
5.拷贝数组举证
a=np.copy(b)
用于创建一个数组的拷贝
import numpy as np
a=np.random.rand(3,3)
print(a)
# [[0.60829188 0.462002 0.53711763]
# [0.45887545 0.0317922 0.96084417]
# [0.62161485 0.53259823 0.95088835]]b=np.copy(a)
print(b)
# [[0.60829188 0.462002 0.53711763]
# [0.45887545 0.0317922 0.96084417]
# [0.62161485 0.53259823 0.95088835]]