Pytorch 张量操作

在深度学习中,数据的表示和处理是至关重要的。PyTorch 作为一个强大的深度学习框架,其核心数据结构是张量(Tensor)。张量是一个多维数组,类似于 NumPy 的数组,但具有更强大的功能,尤其是在 GPU 上进行高效计算。本文将深入探讨 PyTorch 中的张量操作,包括创建张量、维度操作、索引与切片、数学运算等。

1. 基础操作

1.1 创建张量

import torch# 从数据创建张量
tensor_from_data = torch.tensor([[1, 2, 3], [4, 5, 6]])
print("从数据创建的张量:")
print(tensor_from_data)# 创建全零张量
zeros_tensor = torch.zeros((2, 3))
print("\n全零张量:")
print(zeros_tensor)# 创建全一张量
ones_tensor = torch.ones((2, 3))
print("\n全一张量:")
print(ones_tensor)# 创建随机张量
random_tensor = torch.rand((2, 3))
print("\n随机张量:")
print(random_tensor)
从数据创建的张量:
tensor([[1, 2, 3],[4, 5, 6]])全零张量:
tensor([[0., 0., 0.],[0., 0., 0.]])全一张量:
tensor([[1., 1., 1.],[1., 1., 1.]])随机张量:
tensor([[0.0066, 0.5273, 0.7934],[0.8753, 0.8566, 0.3123]])

1.2 张量属性

import torch# 从数据创建张量
tensor_from_data = torch.tensor([[1, 2, 3], [4, 5, 6]])
print("\n张量的形状:", tensor_from_data.shape)
print("张量的大小:", tensor_from_data.size())
print("张量的数据类型:", tensor_from_data.dtype)
张量的形状: torch.Size([2, 3])
张量的大小: torch.Size([2, 3])
张量的数据类型: torch.int64

1.3 张量索引和切片

张量支持类似于 NumPy 的索引和切片操作,可以方便地访问和修改张量中的元素。

import torch# 从数据创建张量
tensor_from_data = torch.tensor([[1, 2, 3], [4, 5, 6]])
value = tensor_from_data[1, 2]  # 获取第二行第三列的值
print("\n特定元素的值:", value)# 切片操作
sliced_tensor = tensor_from_data[:, 1]  # 获取所有行的第二列
print("\n切片后的张量:")
print(sliced_tensor)
特定元素的值: tensor(6)
切片后的张量:
tensor([2, 5])

1.4 张量维度操作

import torch# 创建一个 3 维张量
tensor_3d = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
print("\n3D 张量:")
print(tensor_3d)# 在第 0 维插入一个维度
unsqueezed_tensor = torch.unsqueeze(tensor_3d, 0)
print("\n在第 0 维插入维度后的张量:")
print(unsqueezed_tensor)# 去除大小为 1 的维度
squeezed_tensor = torch.squeeze(unsqueezed_tensor)
print("\n去除大小为 1 的维度后的张量:")
print(squeezed_tensor)# 展平张量
flat_tensor = torch.flatten(tensor_3d)
print("\n展平后的张量:")
print(flat_tensor)
3D 张量:
tensor([[[1, 2],[3, 4]],[[5, 6],[7, 8]]])在第 0 维插入维度后的张量:
tensor([[[[1, 2],[3, 4]],[[5, 6],[7, 8]]]])去除大小为 1 的维度后的张量:
tensor([[[1, 2],[3, 4]],[[5, 6],[7, 8]]])展平后的张量:
tensor([1, 2, 3, 4, 5, 6, 7, 8])

1.5 张量连接和分割

import torch# 创建两个张量
tensor_a = torch.tensor([[1, 2], [3, 4]])
tensor_b = torch.tensor([[5, 6], [7, 8]])# 在第 0 维连接
concatenated_tensor = torch.cat((tensor_a, tensor_b), dim=0)
print("\n在第 0 维连接后的张量:")
print(concatenated_tensor)# 在新维度上堆叠
stacked_tensor = torch.stack((tensor_a, tensor_b), dim=0)
print("\n在新维度上堆叠后的张量:")
print(stacked_tensor)# 垂直堆叠
vstacked_tensor = torch.vstack((tensor_a, tensor_b))
print("\n垂直堆叠后的张量:")
print(vstacked_tensor)# 水平堆叠
hstacked_tensor = torch.hstack((tensor_a, tensor_b))
print("\n水平堆叠后的张量:")
print(hstacked_tensor)# 将张量沿第 0 维分割成两个子张量
split_tensors = torch.split(concatenated_tensor, 2, dim=0)
print("\n分割后的张量:")
for i, t in enumerate(split_tensors):print(f"子张量 {i}:")print(t)# 将张量分割成 2 个子张量
chunked_tensors = torch.chunk(concatenated_tensor, 2, dim=0)
print("\n按数量分割后的张量:")
for i, t in enumerate(chunked_tensors):print(f"子张量 {i}:")print(t)
在第 0 维连接后的张量:
tensor([[1, 2],[3, 4],[5, 6],[7, 8]])在新维度上堆叠后的张量:
tensor([[[1, 2],[3, 4]],[[5, 6],[7, 8]]])垂直堆叠后的张量:
tensor([[1, 2],[3, 4],[5, 6],[7, 8]])水平堆叠后的张量:
tensor([[1, 2, 5, 6],[3, 4, 7, 8]])分割后的张量:
子张量 0:
tensor([[1, 2],[3, 4]])
子张量 1:
tensor([[5, 6],[7, 8]])按数量分割后的张量:
子张量 0:
tensor([[1, 2],[3, 4]])
子张量 1:
tensor([[5, 6],[7, 8]])

1.6 数学运算

import torch# 创建两个张量
tensor_a = torch.tensor([[1, 2], [3, 4]])
tensor_b = torch.tensor([[5, 6], [7, 8]])# 张量加法
result_add = tensor_a + tensor_b
print("\n张量加法结果:")
print(result_add)# 矩阵乘法
result_matmul = torch.matmul(tensor_a, tensor_b.T)  # 转置以进行矩阵乘法
print("\n矩阵乘法结果:")
print(result_matmul)
张量加法结果:
tensor([[ 6,  8],[10, 12]])矩阵乘法结果:
tensor([[17, 23],[39, 53]])

1.7 张量形状改变

import torch# 创建一个一维张量
tensor = torch.arange(12)  # 生成一个包含 0 到 11 的一维张量
print("原始张量:")
print(tensor)# 使用 reshape 改变形状为 (3, 4)
reshaped_tensor = tensor.reshape(3, 4)
print("\n调整后的张量 (3, 4):")
print(reshaped_tensor)
viewed_tensor = reshaped_tensor.view(4, 3)  # 将结果调整为 4x3 的形状
print("\n使用 view 调整后的张量 (4, 3):")
print(viewed_tensor)
# 创建一个 3D 张量
tensor_3d = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])  # 形状为 (2, 2, 2)# 使用 permute 重新排列维度
permuted_tensor = tensor_3d.permute(1, 0, 2)  # 交换第 0 和第 1 维
print("\n使用 permute 调整后的张量:")
print(permuted_tensor)
原始张量:
tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])调整后的张量 (3, 4):
tensor([[ 0,  1,  2,  3],[ 4,  5,  6,  7],[ 8,  9, 10, 11]])使用 view 调整后的张量 (4, 3):
tensor([[ 0,  1,  2],[ 3,  4,  5],[ 6,  7,  8],[ 9, 10, 11]])使用 permute 调整后的张量:
tensor([[[1, 2],[5, 6]],[[3, 4],[7, 8]]])

在 PyTorch 中,四维张量的元素原始排序遵循行优先(row-major)顺序,也称为 C 风格的顺序。这意味着在内存中,张量的元素是按最后一个维度的变化最快,前面的维度变化最慢的顺序排列的。

1.8 张量在内存中排序

假设我们有一个四维张量,其形状为 (D1, D2, D3, D4),其中:

  • D1:第一个维度的大小
  • D2:第二个维度的大小
  • D3:第三个维度的大小
  • D4:第四个维度的大小
import torch# 创建一个四维张量
tensor_4d = torch.arange(120).reshape(2, 3, 4, 5)  # 120个元素,形状为 (2, 3, 4, 5)print("四维张量:")
print(tensor_4d)

在这个例子中,torch.arange(120) 生成了一个包含从 0 到 119 的一维张量。然后使用 reshape(2, 3, 4, 5) 将其转换为四维张量。

  • 第一个维度(D1):变化最慢,表示有 2 个块。
  • 第二个维度(D2):每个块有 3 个层。
  • 第三个维度(D3):每层有 4 行。
  • 第四个维度(D4):每行有 5 列。

在内存中,元素的排列顺序如下:

tensor_4d[0, :, :, :]  # 第一个块
[[[ 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],  # 第三行],[[30, 31, 32, 33, 34],  # 第一行[35, 36, 37, 38, 39],  # 第二行[40, 41, 42, 43, 44],  # 第三行]
]tensor_4d[1, :, :, :]  # 第二个块
[[[45, 46, 47, 48, 49],  # 第一行[50, 51, 52, 53, 54],  # 第二行[55, 56, 57, 58, 59],  # 第三行],[[60, 61, 62, 63, 64],  # 第一行[65, 66, 67, 68, 69],  # 第二行[70, 71, 72, 73, 74],  # 第三行],[[75, 76, 77, 78, 79],  # 第一行[80, 81, 82, 83, 84],  # 第二行[85, 86, 87, 88, 89],  # 第三行]
]

四维张量的元素在内存中的原始排序遵循行优先顺序,意味着最后一个维度的变化最快,前面的维度变化最慢。理解这种排序方式对于有效地操作和处理多维数据非常重要。

2. 实战:模型音频频谱通过卷积层和GRU层

import torch
import torch.nn as nnspectrum1 = torch.tensor([[[1.0, 1.0, 1.0, 1.0],[2.0, 2.0, 2.0, 2.0],[3.0, 3.0, 3.0, 3.0]]], dtype=torch.float32)spectrum2 = torch.tensor([[[4.0, 4.0, 4.0, 4.0],[5.0, 5.0, 5.0, 5.0],[6.0, 6.0, 6.0, 6.0]]], dtype=torch.float32)print("频谱 1:")
print(spectrum1)
print("频谱 1 的维度 (batch_size, channels, bins, frames):", spectrum1.shape)print("\n频谱 2:")
print(spectrum2)
print("频谱 2 的维度 (batch_size, channels, bins, frames):", spectrum2.shape)# 在通道上堆叠频谱
stacked_spectra = torch.stack((spectrum1, spectrum2), dim=1)  # 在通道维度上堆叠
print("\n堆叠后的频谱 (维度: batch_size, channels, bins, frames):")
print(stacked_spectra)
print("堆叠后的频谱的维度 (batch_size, channels, bins, frames):", stacked_spectra.shape)# 定义简单的 CNN
class SimpleCNN(nn.Module):def __init__(self):super(SimpleCNN, self).__init__()self.conv1 = nn.Conv2d(in_channels=2, out_channels=5, kernel_size=(1, 1))  # 卷积层def forward(self, x):return self.conv1(x)# 创建 CNN 实例
cnn = SimpleCNN()# 将堆叠的频谱输入到 CNN
cnn_output = cnn(stacked_spectra)
print("\nCNN 输出:")
print(cnn_output)
print("CNN 输出的维度 (batch_size, out_channels, bins, frames):", cnn_output.shape)
batch_size = cnn_output.shape[0]
frames = cnn_output.shape[3]
out_channels = cnn_output.shape[1]
bins = cnn_output.shape[2]
cnn_output_permute = cnn_output.permute(0, 3, 1, 2)
gru_input = cnn_output_permute.reshape(cnn_output.shape[0], cnn_output.shape[3], -1)
print("\nGRU 输入的形状 (batch_size, frames, out_channels * bins):")
print(gru_input.shape)
print(gru_input)
频谱 1:
tensor([[[1., 1., 1., 1.],[2., 2., 2., 2.],[3., 3., 3., 3.]]])
频谱 1 的维度 (batch_size, channels, bins, frames): torch.Size([1, 3, 4])频谱 2:
tensor([[[4., 4., 4., 4.],[5., 5., 5., 5.],[6., 6., 6., 6.]]])
频谱 2 的维度 (batch_size, channels, bins, frames): torch.Size([1, 3, 4])堆叠后的频谱 (维度: batch_size, channels, bins, frames):
tensor([[[[1., 1., 1., 1.],[2., 2., 2., 2.],[3., 3., 3., 3.]],[[4., 4., 4., 4.],[5., 5., 5., 5.],[6., 6., 6., 6.]]]])
堆叠后的频谱的维度 (batch_size, channels, bins, frames): torch.Size([1, 2, 3, 4])CNN 输出:
tensor([[[[-2.5064, -2.5064, -2.5064, -2.5064],[-3.3889, -3.3889, -3.3889, -3.3889],[-4.2714, -4.2714, -4.2714, -4.2714]],[[ 0.6582,  0.6582,  0.6582,  0.6582],[ 1.3287,  1.3287,  1.3287,  1.3287],[ 1.9992,  1.9992,  1.9992,  1.9992]],[[ 0.6646,  0.6646,  0.6646,  0.6646],[ 0.2705,  0.2705,  0.2705,  0.2705],[-0.1235, -0.1235, -0.1235, -0.1235]],[[ 1.5735,  1.5735,  1.5735,  1.5735],[ 1.8892,  1.8892,  1.8892,  1.8892],[ 2.2049,  2.2049,  2.2049,  2.2049]],[[-1.1208, -1.1208, -1.1208, -1.1208],[-0.9246, -0.9246, -0.9246, -0.9246],[-0.7284, -0.7284, -0.7284, -0.7284]]]],grad_fn=<ConvolutionBackward0>)
CNN 输出的维度 (batch_size, out_channels, bins, frames): torch.Size([1, 5, 3, 4])GRU 输入的形状 (batch_size, frames, out_channels * bins):
torch.Size([1, 4, 15])
tensor([[[-2.5064, -3.3889, -4.2714,  0.6582,  1.3287,  1.9992,  0.6646,0.2705, -0.1235,  1.5735,  1.8892,  2.2049, -1.1208, -0.9246,-0.7284],[-2.5064, -3.3889, -4.2714,  0.6582,  1.3287,  1.9992,  0.6646,0.2705, -0.1235,  1.5735,  1.8892,  2.2049, -1.1208, -0.9246,-0.7284],[-2.5064, -3.3889, -4.2714,  0.6582,  1.3287,  1.9992,  0.6646,0.2705, -0.1235,  1.5735,  1.8892,  2.2049, -1.1208, -0.9246,-0.7284],[-2.5064, -3.3889, -4.2714,  0.6582,  1.3287,  1.9992,  0.6646,0.2705, -0.1235,  1.5735,  1.8892,  2.2049, -1.1208, -0.9246,-0.7284]]], grad_fn=<ReshapeAliasBackward0>)

2. 实战:模型音频频谱通过卷积层和 GRU 层

在这一部分,我们将展示如何使用 PyTorch 构建一个简单的模型,处理音频频谱数据。整体思路流程如下:

  1. 创建频谱数据:生成两个示例频谱 spectrum1spectrum2,形状为 (1, 3, 4)

    • 1 (batch_size):样本数量。
    • 3 (bins):频率分量数量。
    • 4 (frames):时间帧数量。
  2. 堆叠频谱:使用 torch.stack 将两个频谱在通道维度上堆叠,形成新的张量,形状为 (1, 2, 3, 4)

    • 1 (batch_size):样本数量。
    • 2 (channels):通道数量(两个频谱)。
    • 3 (bins):频率分量数量。
    • 4 (frames):时间帧数量。
  3. 定义卷积神经网络(CNN):构建一个简单的 CNN 模型,包含一个卷积层,用于提取频谱特征。

  4. 前向传播:将堆叠的频谱输入到 CNN 中,获取输出特征,输出形状为 (1, 5, 3, 4)

    • 1 (batch_size):样本数量。
    • 5 (out_channels):卷积层输出的通道数。
    • 3 (bins):频率分量数量。
    • 4 (frames):时间帧数量。
  5. 处理 CNN 输出:调整 CNN 输出的维度,以适应 GRU 层的输入格式。使用 permutereshape 将输出转换为形状 (1, 4, 15)

    • 1 (batch_size):样本数量。
    • 4 (frames):时间帧数量。
    • 15 (out_channels * bins):每个时间步的特征数量(5 * 3)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/74857.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小程序中跨页面组件共享数据的实现方法与对比

小程序中跨页面/组件共享数据的实现方法与对比 在小程序开发中&#xff0c;实现不同页面或组件之间的数据共享是常见需求。以下是几种主要实现方式的详细总结与对比分析&#xff1a; 一、常用数据共享方法 全局变量&#xff08;getApp()&#xff09;、本地缓存&#xff08;w…

vue中的 拖拽

拖拽总结 实现方式特点适用场景HTML5 原生拖拽 API✅ 直接使用 dataTransfer 进行数据传输 ✅ 兼容性好&#xff08;大部分浏览器支持&#xff09; ✅ 适合简单的拖拽场景低代码平台、表单生成器、组件拖拽Vue/React 组件库&#xff08;如 Vue Draggable、SortableJS&#xff…

MySQL 函数(入门版)

目录 一、字符串函数 1、常用的字符串函数 2、函数演示 3、具体案例 二、数值函数 1、常用的数值函数 2、函数演示 3、具体案例 三、日期函数 1、常用的日期函数 2、函数演示 3、具体案例 四、流程函数 1、常用的流程函数 2、函数演示 3、具体案例 在MySQL中&a…

基于快速开发平台与智能手表的区域心电监测与AI预警系统(源码+论文+部署讲解等)

需要源代码&#xff0c;演示视频&#xff0c;ppt设计原稿资料&#xff0c;请文末卡片联系 !](https://i-blog.csdnimg.cn/direct/242d53cd069940b5b7a6db2bb031d406.png#pic_center)

【神经网络】python实现神经网络(三)——正向学习的模拟演练

有了之前的经验(【神经网络】python实现神经网络(二)——正向推理的模拟演练),我们继续来介绍如何正向训练神经网络中的超参(包含权重以及偏置),本章大致的流程图如下: 一.损失函数 神经网络以某个指标为基准寻求最优权重参数,而这个指标即可称之为 “损失函数” 。(…

分区格式变RAW故障深度解析与数据恢复实战指南‌

分区格式变RAW的本质‌ 当存储设备&#xff08;如硬盘、U盘或移动硬盘&#xff09;的分区突然显示为RAW格式时&#xff0c;意味着操作系统无法识别其原有的文件系统结构&#xff08;如NTFS、FAT32等&#xff09;。此时&#xff0c;用户访问该分区会提示“需要格式化”或直接显示…

【QT】Qt5 QtWebEngine使用教程

目录 1、QtWebEngine相比于QtWebKit的优势2、项目配置2.1 确认 Qt 版本2.2 在.pro 文件中添加依赖3、显示网页4、实现Qt和网页JavaScript之间的交互4.1 Qt执行网页的JavaScript代码4.2 JavaScript调用Qt对象的函数QtWebEngine 是 Qt 框架中用于在应用程序中嵌入 Web 内容的模块…

网络安全-等级保护(等保) 1-0 等级保护制度公安部前期发文总结

################################################################################ 等级保护从1994年开始已经有相关文件下发&#xff0c;进行建设&#xff0c;后续今年多年制度完善&#xff0c;现在已进入等保2.0时代&#xff0c;相关政策已运行多年。 前期等保相关发文&…

视图函数的应用

1.实现将当前日期和时间编码为HTML文档并返回的简单视图函数 文章目录 1.实现将当前日期和时间编码为HTML文档并返回的简单视图函数1.1打开visualcode 按图示点击 创建新的终端1.2然后定义ViewDjango项目根目录下的路由文件urls.py&#xff0c;实现到SimpleView应用的路由路径1…

解锁 C 语言安全新姿势:C11 安全函数全解析

一、开篇:C 语言安全的新护盾 在 C 语言的编程世界里,缓冲区溢出等安全问题犹如潜藏的暗礁,时刻威胁着程序的稳定与安全。为了有效应对这些挑战,C11 标准引入了一系列安全函数,也被称为 “Annex K” 标准库函数。这些函数为字符串和内存操作函数注入了新的活力,通过增加…

BGP路由协议之属性2

Orgin 起源 公认必遵属性 起源名称标记描述IGPi如果路由是由始发的 BGP 路由器使用 network 命令注入到 BGP 的&#xff0c;那么该 BGP 路由的 origin 属性为 IGPEGPe如果路由是通过 EGP 学习到的&#xff0c;那么该 BGP 路由的 Origin 属性为 EGPIncomplete?如果路由是通过…

C#实现HiveQL建表语句中特殊数据类型的包裹

用C#实现搜索字符串中用’(‘和’)‘包裹的最外层的里面里面的字符串&#xff0c;将里面的记录按一个或多个空格、换行或tab&#xff0c;或者是它的在一起的组合作为分隔&#xff0c;分隔出多个字符串组&#xff0c;如果组中有字符串中同时包含’<‘和’>’&#xff0c;则…

脑电学习笔记

一&#xff0c;原理简介 使用eprime或者matlab给被试呈现刺激&#xff0c;并在某个时间发送Mark&#xff0c;脑电帽会同步采集被试的脑电信号&#xff0c;经放大器放大后&#xff0c;控制盒会把脑电信号和mark 信号同步到一起&#xff0c;通过usb线传入到采集系统&#xff08;比…

宏碁笔记本电脑擎7PRO搭载的 NVIDIA RTX 5080 显卡安装pytorch

宏碁笔记本电脑擎7PRO搭载的 NVIDIA RTX 5080 显卡是一款高性能移动 GPU&#xff0c;基于 NVIDIA 最新的 Blackwell 架构设计&#xff0c;通过修正架构&#xff08;Blackwell&#xff09;、显存类型与带宽&#xff08;GDDR7、960GB/s&#xff09;、Tensor Core 与 RT Core 全面…

ES6中增强对象

在 ES6 中&#xff0c;对象的使用变得更加方便了&#xff0c;可以在定义对象时通过属性简写、遍历作为属性名或省略对象函数属性的书写等方式来提高编码的效率&#xff1a; 其实就这么简单&#xff0c;大家可以好好看下上面的代码&#xff0c;有问题欢迎留言一起探讨&#xff0…

XSLFO XSLT:深入解析两种强大的XML转换技术

XSLFO & XSLT:深入解析两种强大的XML转换技术 引言 在XML(可扩展标记语言)的生态系统中,XSLFO(可扩展样式表语言格式化对象)和XSLT(可扩展样式表转换语言)是两种非常强大的技术。它们分别负责将XML文档转换为其他格式以及进行XML文档的转换。本文将深入探讨这两种…

Django4.0的快速查询以及分页

1. filter 方法 filter 是 Django ORM 中最常用的查询方法之一。它用来根据给定的条件过滤查询集并返回满足条件的对象。 articles Article.objects.all() # 使用 SearchFilter 进行搜索 search_param request.query_params.get(search, None) author_id request.query_pa…

在Vue3中格式化后端返回的Java Date类型数据为指定格式

在前端Vue3项目中&#xff0c;格式化后端返回的java.util.Date类型时间到yyyy-MM-dd HH:mm:ss格式&#xff0c;有几种常用方法&#xff1a; 方法一&#xff1a;使用JavaScript内置方法 <JAVASCRIPT> // 假设后端返回的数据结构为 { createTime: 2023-05-15T08:30:00.0…

单元测试原则之——不要模拟不属于你的类型

在单元测试中,不要模拟不属于你的类型(Don’t mock types you don’t own)是一个重要的原则。这是因为外部库或框架的类型(如第三方依赖)可能会在未来的版本中发生变化,而你的模拟可能无法反映这些变化,从而导致测试失效。 以下是一个基于Java Mockito 的示例,展示如何…

内网渗透(杂项集合) --- 中的多协议与漏洞利用技术(杂项知识点 重点) 持续更新

目录 1. NetBIOS 名称的网络协议在局域网中内网渗透中起到什么作用 2. 使用 UDP 端口耗尽技术强制所有 DNS 查找失败&#xff0c;这个技术如何应用在局域网内网渗透测试中 3. 在本地创建一个 HTTP 服务来伪造 WPAD 服务器 什么是 WPAD 服务器&#xff1f;这个服务器是干嘛的…