python训练营打卡第48天
随机函数与广播机制
知识点回顾:
- 随机张量的生成:torch.randn函数
- 卷积和池化的计算公式(可以不掌握,会自动计算的)
- pytorch的广播机制:加法和乘法的广播机制
ps:numpy运算也有类似的广播机制,基本一致
作业:
自己多借助ai举几个例子帮助自己理解即可
随机函数的应用示例:
import numpy as np# 1. 均匀分布随机数:生成3x3矩阵,数值在[0, 1)之间
uniform_data = np.random.uniform(size=(3, 3))
print("均匀分布随机数:\n", uniform_data)# 2. 正态分布随机数:均值0,标准差1,生成100个样本
normal_data = np.random.normal(loc=0, scale=1, size=100)
print("\n正态分布随机数均值:", np.mean(normal_data)) # 接近0# 3. 随机采样:从数组中不放回地抽取3个元素
arr = np.array([1, 2, 3, 4, 5])
sampled = np.random.choice(arr, size=3, replace=False)
print("\n随机采样结果:", sampled)# 4. 随机排列:打乱数组顺序
arr_shuffled = np.random.permutation(arr) # 生成新数组,原数组不变
print("\n随机排列结果:", arr_shuffled)# 5. 随机种子:确保结果可复现
np.random.seed(42) # 相同种子下,多次运行结果一致
print("\n设置种子后的均匀随机数:", np.random.uniform(size=3))
输出结果:
1.均匀分布随机数:[[0.12155788 0.51018589 0.10955804][0.12604173 0.81162374 0.06650838][0.78791742 0.12684855 0.09960093]]2.正态分布随机数均值: -0.17274923180900453.随机采样结果: [1 3 2]4.随机排列结果: [1 4 2 5 3]5.设置种子后的均匀随机数: [0.37454012 0.95071431 0.73199394]
广播机制:
import numpy as np# 案例1:标量与二维数组运算
a = np.array([[1, 2], [3, 4]])
b = 2 # 标量
result = a * b # 等价于每个元素×2
print("标量广播结果:\n", result)
# 输出:
# [[2 4]
# [6 8]]# 案例2:一维数组与二维数组相加(列对齐)
c = np.array([10, 20, 30]) # 形状(3,)
d = np.array([[1, 2, 3], [4, 5, 6]]) # 形状(2, 3)
result = d + c # c扩展为(2, 3)(行复制)
print("\n一维与二维相加结果:\n", result)
# 输出:
# [[11 22 33]
# [14 25 36]]# 案例3:三维数组与二维数组相乘(维度扩展)
e = np.random.rand(2, 1, 4) # 形状(2, 1, 4)
f = np.random.rand(3, 4) # 形状(3, 4)
# 广播过程:f → (1, 3, 4) → (2, 3, 4),e → (2, 3, 4)
result = e * f # 需确保最终形状兼容
print("\n三维与二维广播结果形状:", result.shape) # 输出:(2, 3, 4)
输出结果:
1.标量广播结果:[[2 4][6 8]]2.一维与二维相加结果:[[11 22 33][14 25 36]]3.三维与二维广播结果形状: (2, 3, 4)
Numpy广播机制(与PyTorch基本一致):
import numpy as np
import timeit# 最佳实践1: 优先使用广播替代显式循环
def sum_with_loop():a = np.arange(1000)b = np.arange(1000)result = np.zeros_like(a)for i in range(len(a)):result[i] = a[i] + b[i]return resultdef sum_with_broadcasting():a = np.arange(1000)b = np.arange(1000)return a + b # 利用广播,无需显式循环# 性能对比
loop_time = timeit.timeit(sum_with_loop, number=1000)
broadcast_time = timeit.timeit(sum_with_broadcasting, number=1000)
print(f"循环耗时: {loop_time:.4f}秒") # 约0.15秒
print(f"广播耗时: {broadcast_time:.4f}秒") # 约0.002秒
print(f"广播比循环快 {loop_time/broadcast_time:.1f}倍") # 约75倍# 最佳实践2: 使用np.newaxis显式扩展维度
a = np.array([1, 2, 3]) # 形状 (3,)
b = np.array([4, 5]) # 形状 (2,)# 错误写法:a + b 会触发维度不兼容错误# 正确写法:使用np.newaxis扩展维度
result = a[:, np.newaxis] + b # a扩展为(3,1),b扩展为(1,2),结果为(3,2)
print("\n使用np.newaxis扩展维度:")
print(result)
# 输出:
# [[5 6]
# [6 7]
# [7 8]]# 最佳实践3: 内存高效的广播操作
# 错误写法:创建大中间数组
a = np.ones((1000, 1000))
b = np.ones((1000, 1000))
c = a * b # 创建临时数组,占用额外内存
result = c.sum()# 正确写法:使用out参数避免临时数组
result = np.zeros((1000, 1000))
np.multiply(a, b, out=result) # 直接将结果写入预分配的数组
final_result = result.sum()
输出结果:
循环耗时: 0.3540秒
广播耗时: 0.0039秒
广播比循环快 90.5倍使用np.newaxis扩展维度:
[[5 6][6 7][7 8]]
卷积:
import numpy as npdef convolution_2d(input_image, kernel, stride=1, padding=0):"""实现二维卷积操作参数:input_image: 输入图像,形状为 (height, width)kernel: 卷积核,形状为 (kernel_height, kernel_width)stride: 步长padding: 填充大小返回:卷积结果,形状为 (output_height, output_width)"""# 获取输入图像和卷积核的形状img_h, img_w = input_image.shapek_h, k_w = kernel.shape# 计算输出图像的形状output_h = (img_h + 2 * padding - k_h) // stride + 1output_w = (img_w + 2 * padding - k_w) // stride + 1# 填充输入图像padded_image = np.pad(input_image, ((padding, padding), (padding, padding)), mode='constant')# 初始化输出图像output = np.zeros((output_h, output_w))# 执行卷积操作for i in range(output_h):for j in range(output_w):# 提取当前卷积区域region = padded_image[i*stride:i*stride+k_h, j*stride:j*stride+k_w]# 计算卷积值(对应元素相乘后求和)output[i, j] = np.sum(region * kernel)return output# 示例:应用边缘检测卷积核
# 输入图像
image = np.array([[10, 10, 10, 0, 0, 0],[10, 10, 10, 0, 0, 0],[10, 10, 10, 0, 0, 0],[0, 0, 0, 10, 10, 10],[0, 0, 0, 10, 10, 10],[0, 0, 0, 10, 10, 10]
], dtype=np.float32)# 水平边缘检测卷积核(Sobel算子)
kernel_h = np.array([[1, 2, 1],[0, 0, 0],[-1, -2, -1]
], dtype=np.float32)# 垂直边缘检测卷积核(Sobel算子)
kernel_v = np.array([[1, 0, -1],[2, 0, -2],[1, 0, -1]
], dtype=np.float32)# 应用卷积
edge_h = convolution_2d(image, kernel_h, stride=1, padding=1)
edge_v = convolution_2d(image, kernel_v, stride=1, padding=1)# 计算边缘强度(水平和垂直方向的平方和开方)
edge_strength = np.sqrt(edge_h**2 + edge_v**2)print("原始图像:")
print(image)
print("\n水平边缘:")
print(np.round(edge_h, 1))
print("\n垂直边缘:")
print(np.round(edge_v, 1))
print("\n边缘强度:")
print(np.round(edge_strength, 1))
输出结果:
原始图像:
[[10. 10. 10. 0. 0. 0.][10. 10. 10. 0. 0. 0.][10. 10. 10. 0. 0. 0.][ 0. 0. 0. 10. 10. 10.][ 0. 0. 0. 10. 10. 10.][ 0. 0. 0. 10. 10. 10.]]水平边缘:
[[-30. -40. -30. -10. 0. 0.][ 0. 0. 0. 0. 0. 0.][ 30. 40. 20. -20. -40. -30.][ 30. 40. 20. -20. -40. -30.][ 0. 0. 0. 0. 0. 0.][ 0. 0. 10. 30. 40. 30.]]垂直边缘:
[[-30. 0. 30. 30. 0. 0.][-40. 0. 40. 40. 0. 0.][-30. 0. 20. 20. 0. 10.][-10. 0. -20. -20. 0. 30.][ 0. 0. -40. -40. 0. 40.][ 0. 0. -30. -30. 0. 30.]]边缘强度:
[[42.4 40. 42.4 31.6 0. 0. ][40. 0. 40. 40. 0. 0. ][42.4 40. 28.3 28.3 40. 31.6][31.6 40. 28.3 28.3 40. 42.4][ 0. 0. 40. 40. 0. 40. ][ 0. 0. 31.6 42.4 40. 42.4]]
池化:
import numpy as npdef max_pooling_2d(input_image, pool_size=2, stride=2):"""实现二维最大池化操作参数:input_image: 输入图像,形状为 (height, width)pool_size: 池化窗口大小stride: 步长返回:池化结果,形状为 (output_height, output_width)"""# 获取输入图像的形状img_h, img_w = input_image.shape# 计算输出图像的形状output_h = (img_h - pool_size) // stride + 1output_w = (img_w - pool_size) // stride + 1# 初始化输出图像output = np.zeros((output_h, output_w))# 执行最大池化操作for i in range(output_h):for j in range(output_w):# 提取当前池化区域region = input_image[i*stride:i*stride+pool_size, j*stride:j*stride+pool_size]# 计算区域内的最大值output[i, j] = np.max(region)return outputdef average_pooling_2d(input_image, pool_size=2, stride=2):"""实现二维平均池化操作参数:input_image: 输入图像,形状为 (height, width)pool_size: 池化窗口大小stride: 步长返回:池化结果,形状为 (output_height, output_width)"""# 获取输入图像的形状img_h, img_w = input_image.shape# 计算输出图像的形状output_h = (img_h - pool_size) // stride + 1output_w = (img_w - pool_size) // stride + 1# 初始化输出图像output = np.zeros((output_h, output_w))# 执行平均池化操作for i in range(output_h):for j in range(output_w):# 提取当前池化区域region = input_image[i*stride:i*stride+pool_size, j*stride:j*stride+pool_size]# 计算区域内的平均值output[i, j] = np.mean(region)return output# 示例:应用池化操作
# 输入图像
image = np.array([[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]
], dtype=np.float32)# 应用最大池化
max_pooled = max_pooling_2d(image, pool_size=2, stride=2)# 应用平均池化
avg_pooled = average_pooling_2d(image, pool_size=2, stride=2)print("原始图像:")
print(image)
print("\n最大池化结果:")
print(max_pooled)
print("\n平均池化结果:")
print(avg_pooled)
输出结果:
原始图像:
[[ 1. 2. 3. 4.][ 5. 6. 7. 8.][ 9. 10. 11. 12.][13. 14. 15. 16.]]最大池化结果:
[[ 6. 8.][14. 16.]]平均池化结果:
[[ 3.5 5.5][11.5 13.5]]
@浙大疏锦行