使用Pytorch训练一个图像分类器
一、准备数据集
一般来说,当你不得不与图像、文本或者视频资料打交道时,会选择使用python的标准库将原始数据加载转化成numpy数组,甚至可以继续转换成torch.*Tensor。
- 对图片而言,可以使用Pillow库和OpenCV库
- 对视频而言,可以使用scipy库和librosa库
- 对文本而言,可以使用基于原生Python或Cython加载,或NLTK和SpaCy等。
Pytorch特别针对视觉方面创建torchvision库,其中包含能够加载ImageNet、CIFAR10和MNIST等数据集的数据加载功能,对图像的数据增强功能,即torchvision.datasets和torch.utils.data.DataLoader。
这为大家搭建数据集提供了极大的便利,避免了需要自己写样板代码的情况。
本次我们使用CIFAR10数据集。这是一个含有“飞机”、“汽车”、“鸟”、“猫”、“鹿”、“狗”、“青蛙”、“马”、“轮船”和“卡车”等10个分类的数据集。数据集中每张图像均为[C×H×W]=[3×32×32]即3通道的高32像素宽32像素的彩色图像。
CIFAR-10数据集示例
二、训练图像分类器
下面的步骤大概可以分成5个有序部分:
- 用 torchvision 载入(loading)并归一化(normalize)CIFAR10训练数据集和测试数据集
- 定义卷积神经网络(CNN)
- 定义损失函数和优化器
- 训练网络
- 测试网络
P.S. 以下给出的代码均为在CPU上运行的代码。但本人在pycharm中运行的为自己修改过的在GPU上训练的代码,示例结果和截图也都是GPU运行的结果。
2.1 载入并归一化CIFAR10数据集
用torchvision载入CIFAR10
import torch
import torchvision
import torchvision.transforms as transforms
torchvision加载的数据集是PILImage,数据范围[0,1]。我们需要使用transform函数将其归一化(normalize)为[-1,1]。
细心的伙伴发现了我将英文的normalize翻译成了“归一化”而不是标准化,这是因为接下来的代码你会看到预处理阶段transformer变量存储的处理操作仅仅是运用了normalize的计算规则将数据范围进行了缩放,并没有改变数据的分布,因此翻译成“归一化”更合理。
NOTE.(抄的原文,以防有小伙伴真的遇到这个意外问题)
If running on Windows and you get a BrokenPipeError,
try setting the num_worker of torch.utils.data.DataLoader() to 0。
--snip--transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])batch_size = 4trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size, shuffle=True, num_workers=2)testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size, shuffle=False, num_workers=2)classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
transform中的ToTensor和Normalize函数究竟在做什么,以及为什么要归一化等问题感兴趣的小伙伴可以阅读附录中的序号1~3文章,其中
- 博主“小研一枚”[1]通过源码为我们讲解函数的计算行为定义等知识点
- 答主"Transformer"[2]通过知乎专栏为我们做了几组代码实例。而我们则要看清文章、留言区争论的核心与我们真正求索的问题之间的区别和联系,避免被争论本身误导
- 答主“JMD”[3]则为我们科普归一化的相关知识
书归正题,上述代码第一次运行的结果可能是这样子的:
数据集加载运行日志
此时,我们可以使用numpy库和matplotlib库查看数据集中的图片和标签。
import matplotlib.pyplot as plt
import numpy as np# functions to show an image
def imshow(img):img = img / 2 + 0.5 # unnormalizenpimg = img.numpy()plt.imshow(np.transpose(npimg, (1, 2, 0)))plt.show()# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()# show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join(f'{classes[labels[j]]:5s}' for j in range(batch_size)))
但是如果你就这样将代码copy+paste在pycharm中直接接续在载入数据的代码下面点击“运行”,有可能得到的是一个RuntimeError,并建议你按照惯例设置if __name__ == '__main__':
所以,我建议将目前为止的代码优化成下面的样子:
import torch
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader # 如果torch.utils.data.DataLoader()有报错提示“在 '__init__.py' 中找不到引用 'data'则增加此语句或者其他语句 ”
import matplotlib.pyplot as plt
import numpy as np
# ①←后续如果继续导入packages,请直接在这里插入代码transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])batch_size = 4trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size, shuffle=True, num_workers=2)testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size, shuffle=False, num_workers=2)classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')def imshow(img):"""显示图像的函数"""img = img / 2 + 0.5 # 去归一化npimg = img.numpy()# 上面transform.ToTensor()操作后数据编程CHW[通道靠前模式],需要转换成HWC[通道靠后模式]才能plt.imshow()plt.imshow(np.transpose(npimg, (1, 2, 0))) # 转置前将排在第0位的Channel(C)放在最后,所以是(1,2,0)plt.show()# ②←后续再有定义class、function等在此插入代码编写if __name__ == '__main__':# 随机输出一个mini-batch的图像dataiter_tr = iter(trainloader) # 取一个batch的训练集数据# images_tr, labels_tr = dataiter_tr.next() 根据你的python选择迭代器调用语句images_tr, labels_tr = next(dataiter_tr) # 切分数据和标签imshow(torchvision.utils.make_grid(images_tr)) # 生成网格图print(' '.join(f'{classes[labels_tr[j]]:5s}' for j in range(batch_size))) # 打印标签值# print(' '.join('%5s' % classes[labels_tr[j]] for j in range(batch_size))) 如果你使用python3.6之前的版本,那么有可能无法使用f字符串语句,只能使用.format()方法# ③←后续的程序执行语句在此插入
输出图像示例:
随机输出一个mini-batch的训练集图像
标签输出:bird cat deer ship
2.2 定义一个卷积神经网络
可以将之前写过的识别手写数字MNIST的神经网络迁移到这里来。
# 在①后插入import代码
import torch.nn as nn
import torch.nn.functional as F# 在②后插入神经网络定义代码
class Net(nn.Module):"""定义一个卷积神经网络及前馈函数"""def __init__(self):"""初始化网络:定义卷积层、池化层和全链接层"""super().__init__() # 继承父类属性。P.S. 如果看到super(Net, self).__init__()写法亦可self.conv1 = nn.Conv2d(3, 6, 5) # 使用2套卷积核。输入(B×3×32×32),输出(B×6×28×28)self.pool = nn.MaxPool2d(2, 2) # 最大池化操作,输出时高、宽减半,(B×6×14×14) (B×16×5×5)self.conv2 = nn.Conv2d(6, 16, 5) # 使用4套卷积核,卷积核大小为5×5。(B×16×10×10)self.fc1 = nn.Linear(16 * 5 * 5, 120) # 全链接层。将数据扁平化成一维,共400个输入,120个输出self.fc2 = nn.Linear(120, 84) # 全链接层。120个输入,84个输出self.fc3 = nn.Linear(84, 10) # 全链接层。84个输入,10个输出用于分类def forward(self, x):"""前馈函数,规定数据正向传播的规则"""x = self.pool(F.relu(self.conv1(x))) # 输入 > conv1卷积 > ReLu激活 > maxpool最大池化x = self.pool(F.relu(self.conv2(x))) # > conv2卷积 > ReLu激活 > maxpool最大池化# x = torch.flatten(x, 1) # 如果你不喜欢下一种写法实现扁平化,可以使用这条语句代替x = x.view(-1, 16 * 5 * 5) # 相当于numpy的reshape。此处是将输入数据变换成不固定行数,因此第一个参数是-1,完成扁平化x = F.relu(self.fc1(x)) # 扁平化数据 > fc1全链接层 > ReLu激活x = F.relu(self.fc2(x)) # > fc2全链接层 > ReLu激活x = self.fc3(x) # > fc3全链接层 > 输出return x# 在③后插入神经网络实例化代码
net = Net() # 实例化神经网络
2.3 定义损失函数和优化器
我们使用多分类交叉熵损失函数(Classification Cross-Entropy loss)[4]和随机梯度下降法(SGD)的动量改进版(momentum)[5][6]
# 在①后插入import代码
import torch.optim as optim# 在③后插入代码
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
这里必须做一个扩展。
在2.2中我们可以看到神经网络中,每个层的输出都经过了激活函数的激活作用。但是在输出层后却缺少了激活函数而貌似“直接作用了损失函数”。
简单地说,原因就在于torch.nn.CrossEntropyLoss()将nn.LogSoftmax()激活函数和nn.NLLLoss()负对数似然损失函数集成在一起。
logsoftmax是argmax => softargmax => softmax => logsoftmax逐步优化的求极大值的index的期望的方法。负对数似然损失函数(Negtive Log Likehood)就是计算最小化真实分布 P(y|x) 与模型输出分布 P(y^|x) 的距离,等价于最小化两者的交叉熵。实际使用函数时,是one-hot编码后的标签与logsoftmax结果相乘再求均值再取反,这个过程博主“不愿透漏姓名的王建森”在他的博客中做过实验[7]讲解。
上述结论的详尽说明请参考知乎上Cassie的创作《吃透torch.nn.CrossEntropyLoss()》[8]、知乎上Gordon Lee的创作《交叉熵和极大似然估计的再理解》[9]。
P.S. 对于torch.nn.CrossEntropyLoss()的官网Doc中提到的"This is particularly useful when you have an unbalanced training set."关于如何处理不均衡样品的几个解决办法,可以参考Quora上的问答《In classification, how do you handle an unbalanced training set?》[10]以及热心网友对此问答的翻译[11]。
2.4 训练神经网络
事情变得有趣起来了!我们只需要遍历我们的迭代器,将其输入进神经网络和优化器即可。
如果想在GPU上训练请参考文章开头给出的【学习源】链接中的末尾部分有教授如何修改代码的部分。
--snip--# 在③后插入代码for epoch in range(5): # 数据被遍历的次数running_loss = 0.0 # 每次遍历前重新初始化loss值for i, data in enumerate(trainloader, 0):inputs, labels = data # 切分数据集optimizer.zero_grad() # 梯度清零,避免上一个batch迭代的影响# 前向传递 + 反向传递 + 权重优化outputs = net(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()# 输出日志running_loss += loss.item() # Tensor.item()方法是将tensor的值转化成python numberif i % 2000 == 1999: # 每2000个mini batches输出一次# print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000)) 如果python3.6之前版本可以使用这个代码print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 2000:.3f}')running_loss = 0.0print('Finished Training')
Out:
model will be trained on device: 'cuda:0'
某一次输出结果日志整理一下如下表:
Finished Training
将loss数据整理并画图(选做):
--snip--x = np.linspace(2000, 12000, 6, dtype=np.int32)
# 数据每次训练输出都不一样,给出画图代码,至于数据,大家寄几填吧~
epoch_01 = np.array([...])
epoch_02 = np.array([...])
epoch_03 = np.array([...])
epoch_04 = np.array([...])
epoch_05 = np.array([...])plt.plot(x, epoch_01, 'ro-.', x, epoch_02, 'bo-.', x, epoch_03, 'yo-.', x, epoch_04, 'ko-.', x, epoch_05, 'go-.')
plt.legend(['Epoch_1', 'Epoch_2', 'Epoch_3', 'Epoch_4', 'Epoch_5'])
plt.xlabel('number of mini-batches')
plt.ylabel('loss')
plt.title('Loss during CIFAR-10 training procedure in Convolution Neural Networks')
plt.show()
通过数据我们可以看出loss的下降趋势:
- 第一个epoch的最明显
- 第二个epoch继续降低,但趋势更平缓
- 后三个epoch在开始较前一个epoch有较明显下降,但下降幅度递减
- *后三个epoch在该epoch内下降趋势平缓,或出现小幅震荡并保持低于前一个epoch
现在我们可以快速保存训练完成的模型到指定的路径。
--snip--PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)
保存的文件
2.5 测试神经网络
我们已经用训练集数据将神经网络训练了5次(epoches=5)。但我们还需要核实神经网络是否真的学到了什么。
我们将以神经网络预测的类别标签和真实标签进行对比核实。如果预测正确,则将样本添加到正确预测列表中。
首先我们像查看训练集的一个mini batch图像一样,看一下一部分测试集图像。
--snip--dataiter_te = iter(testloader)images_te, labels_te = next(dataiter_te) # 另一种备用写法参考训练集部分imshow(torchvision.utils.make_grid(images_te))print('GroundTruth: ', ' '.join('%5s' % classes[labels_te[j]] for j in range(batch_size))) # 另一种备用写法参考训练集部分
随机输出一个mini-batch的测试集图像
Out:
GroundTruth: cat ship ship plane
下面,我们载入之前保存的模型(注:保存和再载入模型不是必要步骤,这里这么做是为了演示这些操作):
--snip--net = Net()
net.load_state_dict(torch.load(PATH))
OK,现在让我们看看神经网络如何看待这些图像的分类的:
--snip--outputs = net(images) # 看一下神经网络对上述展示图片的预测结果
输出的是10个分类的“能量(energy)”。某个分类的能量越高,意味着神经网络认为该图像越符合该分类。因此我们可以获得那个能量的索引。
--snip--_, predicted = torch.max(outputs, 1) # torch.max(input, dim)返回按照dim方向的最大值和其索引print('Predicted: ', ' '.join(f'{classes[predicted[j]]:5s}' for j in range(batch_size)))
Out:
Predicted: cat ship ship ship
看起来不错。下面就试一试在全部测试集上的表现:
correct = 0total = 0# 由于这不是在训练模型,因此对输出不需要计算梯度等反向传播过程with torch.no_grad():for data in testloader:images_pre, labels_pre = dataoutputs = net(images_pre) # 数据传入神经网络,计算输出_, predicted = torch.max(outputs.data, 1) # 获取最大能量的索引total += labels_pre.size(0) # 计算预测次数correct += (predicted == labels_pre).sum().item() # 计算正确预测次数print(f'Accuracy of the network on the 10000 test images: {100 * correct // total} %')
Out:
Accuracy of the network on the 10000 test images: 61 %
感觉预测的准确率比随机从10个类中蒙一个类(概率10%)要高,看来神经网络确实学到了一些东西。
当然,我们还可以看一下对于不同的类的学习效果:
--snip--# 生成两个dict,分别用来存放预测正确数量和总数量的个数
correct_pred = {classname: 0 for classname in classes}
total_pred = {classname: 0 for classname in classes}# 启动预测过程,无需计算梯度等
with torch.no_grad():for data in testloader:images_cl, labels_cl = dataoutputs = net(images_cl)_, predictions = torch.max(outputs, 1)# 开始计数for label, prediction in zip(labels_cl, predictions):if label == prediction:correct_pred[classes[label]] += 1total_pred[classes[label]] += 1# 分类别打印预测准确率
for classname, correct_count in correct_pred.items():accuracy = 100 * float(correct_count) / total_pred[classname]print(f'Accuracy for class: {classname:5s} is {accuracy:.1f} %')
Out:
Accuracy for class: plane is 66.2 %
Accuracy for class: car is 80.7 %
Accuracy for class: bird is 39.1 %
Accuracy for class: cat is 53.4 %
Accuracy for class: deer is 64.6 %
Accuracy for class: dog is 35.8 %
Accuracy for class: frog is 67.9 %
Accuracy for class: horse is 69.5 %
Accuracy for class: ship is 75.0 %
Accuracy for class: truck is 65.5 %
至此,我们完成了练习!
在结束前,让我们反思一下准确率为何会呈现上述样子,我推测:
- 数据集本身缺陷,如图片太小(32×32)不足以让卷积神经网络提取到足够特征,类别划分不合理(汽车&卡车,以及飞机&鸟等较其他类别而言是否太过相似),各类别图像数量和图像本身质量等
- 数据的预处理不足,预处理阶段对数据的增强不够,是否可以加入旋转/镜像/透视、裁剪、亮度调节、噪声/平滑等处理
- 神经网络本身的结构、参数设置等是否合理,如卷积/全链接层数的规定、卷积核相关的定义、损失函数的选择、batch size/epoch的平衡等(希望可以通过学习后续的Alexnet、VGG、Resnet、FastRCNN、YOLO等受到启发)
- 避免偶然。不能以单次的结果去评价,评价应当建立在若干次重复试验的基础上
本文翻译至:Training a Classifier — PyTorch Tutorials 2.6.0+cu124 documentation
参考
- pytorch的transform中ToTensor接着Normalize http://t.csdn.cn/bCDSU
- pytorch中归一化transforms.Normalize的真正计算过程 - Transformer的文章 - 知乎 https://zhuanlan.zhihu.com/p/414242338
- 标准化/归一化的目的和作用 - JMD的文章 - 知乎 https://zhuanlan.zhihu.com/p/465264729
- Doc--torch.nn.CrossEntropyLoss CrossEntropyLoss — PyTorch 2.6 documentation
- Doc-torch.optim.SGD SGD — PyTorch 2.6 documentation
- 深度学习中常用优化器的总结 - Alex Chung的文章 - 知乎 https://zhuanlan.zhihu.com/p/166362509
- ^交叉熵的数学原理及应用——pytorch中的CrossEntropyLoss()函数 交叉熵的数学原理及应用——pytorch中的CrossEntropyLoss()函数 - 不愿透漏姓名的王建森 - 博客园
- ^吃透torch.nn.CrossEntropyLoss() - Cassie的文章 - 知乎 https://zhuanlan.zhihu.com/p/159477597
- 交叉熵和极大似然估计的再理解 - Gordon Lee的文章 - 知乎 https://zhuanlan.zhihu.com/p/165139520
- In classification, how do you handle an unbalanced training set? https://www.quora.com/In-classification-how-do-you-handle-an-unbalanced-training-set
- 如何处理训练样本不均衡的问题 http://t.csdn.cn/FDVYJ
相关文章:
使用Pytorch训练一个图像分类器
一、准备数据集 一般来说,当你不得不与图像、文本或者视频资料打交道时,会选择使用python的标准库将原始数据加载转化成numpy数组,甚至可以继续转换成torch.*Tensor。 对图片而言,可以使用Pillow库和OpenCV库对视频而言…...
除了成本核算,还有哪些财务分析工具可以提高工作效率?
除了成本核算,财务工作中还有多种分析工具可以提高工作效率,以下是详细介绍: 一、数据可视化工具 Power BI:这是一款强大的数据可视化工具,通过创建交互式报表、仪表板和图表来展示财务数据。它易于使用,提…...
【SSM】Spring + SpringMVC + Mybatis
SSM课程,以下为该课程的笔记 bean:IOC容器创建的对象 P12 bean的生命周期 在bean中定义init()和destroy()方法,然后在xml中配置方法名,让bean对象能找到对应的生命周期方法。 或通过实现接口的方式定义声明周期方法。 P13 sett…...
Windows图形界面(GUI)-QT-C/C++ - QT Tab Widget
公开视频 -> 链接点击跳转公开课程博客首页 -> 链接点击跳转博客主页 目录 一、概述 1.1 什么是 QTabWidget? 1.2 使用场景 二、常见样式 2.1 选项卡式界面 2.2 动态添加和删除选项卡 2.3 自定义选项卡标题和图标 三、属性设置 3.1 添加页面&…...
pstricks PGFTikz 在CTeX套装中绘图Transparency或Opacity失效的问题
我在CTeX中画图的时候,习惯用Geogebra先画好,然后生成pstricks或PGFTikz代码: 这样不用插入eps或pdf之类的图片,也是一种偷懒的方法。以前往arXiv.org上面传论文也是这样:代码出图,就不用另外上传一幅eps或…...
操作系统和中间件的信息收集
在浏览器中收集操作系统与中间件信息时,主要通过客户端JavaScript(用于操作系统/浏览器信息)和服务器端脚本(用于中间件信息)实现。以下是分步指南: 一、客户端操作系统信息收集(JavaScript&am…...
Android --- handler详解
handler 理解 handler 是一套Android 消息传递机制,主要用于线程间通信。 tips: binder/socket 用于进程间通信。 参考: Android 进程间通信-CSDN博客 handler 就是主线程在起了一个子线程,子线程运行并生成message ,l…...
C++:结构体和类
在之前的博客中已经讲过了C语言中的结构体概念了,重复的内容在这儿就不赘述了。C中的结构体在C语言的基础上还有些补充,在这里说明一下,顺便简单地讲一下类的概念。 一、成员函数 结构体类型声明的关键字是 struct ,在C中结构体…...
初级数据结构:栈和队列
目录 一、栈 (一)、栈的定义 (二)、栈的功能 (三)、栈的实现 1.栈的初始化 2.动态扩容 3.压栈操作 4.出栈操作 5.获取栈顶元素 6.获取栈顶元素的有效个数 7.检查栈是否为空 8.栈的销毁 9.完整代码 二、队列 (一)、队列的定义 (二)、队列的功能 (三)…...
携程Java开发面试题及参考答案 (200道-下)
insert 一行数据的时候加的是什么锁?为什么? 在 MySQL 中,当执行 INSERT 操作插入一行数据时,加锁的情况会因存储引擎和具体的事务隔离级别而有所不同。一般来说,在 InnoDB 存储引擎下,INSERT 操作加的是行级排他锁(Row Exclusive Lock),以下详细说明原因。 行级排他…...
Python从0到100(八十六):神经网络-ShuffleNet通道混合轻量级网络的深入介绍
前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…...
98,【6】 buuctf web [ISITDTU 2019]EasyPHP
进入靶场 代码 <?php // 高亮显示当前 PHP 文件的源代码,通常用于调试或展示代码,方便用户查看代码逻辑 highlight_file(__FILE__);// 从 GET 请求中获取名为 _ 的参数值,并赋值给变量 $_ // 符号用于抑制可能出现的错误信息ÿ…...
54【ip+端口+根目录通信】
上节课讲到,根目录起到定位作用,比如我们搭建一个php网站后,注册系统是由根目录的register.php文件执行,那么我们给这个根目录绑定域名https://127.0.0.1,当我们浏览器访问https://127.0.0.1/register.php时࿰…...
计算机网络 应用层 笔记 (电子邮件系统,SMTP,POP3,MIME,IMAP,万维网,HTTP,html)
电子邮件系统: SMTP协议 基本概念 工作原理 连接建立: 命令交互 客户端发送命令: 服务器响应: 邮件传输: 连接关闭: 主要命令 邮件发送流程 SMTP的缺点: MIME: POP3协议 基本概念…...
解析PHP文件路径相关常量
PHP文件路径相关常量包括以下几个常量: __FILE__:表示当前文件的绝对路径,包括文件名。 __DIR__:表示当前文件所在的目录的绝对路径,不包括文件名。 dirname(__FILE__):等同于__DIR__,表示当前…...
蓝桥与力扣刷题(234 回文链表)
题目:给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。 示例 1: 输入:head [1,2,2,1] 输出:true示例 2: 输入&…...
【协议详解】卫星通信5G IoT NTN SIB33-NB 信令详解
一、SIB33信令概述 在5G非地面网络(NTN)中,卫星的高速移动性和广域覆盖特性使得地面设备(UE)需要频繁切换卫星以维持连接。SIB32提供了UE预测当前服务的卫星覆盖信息,SystemInformationBlockType33&#x…...
c语言练习题【数据类型、递归、双向链表快速排序】
练习1:数据类型 请写出以下几个数据的数据类型 整数 a a 的地址 存放a的数组 b 存放a的地址的数组 b的地址 c的地址 指向 printf 函数的指针 d 存放 d的数组 整数 a 的类型 数据类型是 int a 的地址 数据类型是 int*(指向 int 类型的指针) …...
SliverAppBar的功能和用法
文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了SliverGrid组件相关的内容,本章回中将介绍SliverAppBar组件.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 我们在本章回中介绍的SliverAppBar和普通的AppBar类似,它们的…...
deepseek 本地化部署和小模型微调
安装ollama 因为本人gpu卡的机器系统是centos 7, 直接使用ollama会报 所以ollama使用镜像方式进行部署, 拉取镜像ollama/ollama 启动命令 docker run -d --privileged -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama 查看ollama 是否启动…...
Heptagon 同步语言介绍
同步语言于20世纪80年代创立,用于建模、设计和实现实时关键反应系统。随着被控制系统的复杂性不断增加,执行速度成为一个重要标准。与此同时,处理器在核心数量上的增长超过了速度的提升。因此,我们正在寻求一种并行执行方式&#…...
Golang 并发机制-5:详解syn包同步原语
并发性是现代软件开发的一个基本方面,Go(也称为Golang)为并发编程提供了一组健壮的工具。Go语言中用于管理并发性的重要包之一是“sync”包。在本文中,我们将概述“sync”包,并深入研究其最重要的同步原语之一…...
数组排序算法
数组排序算法 用C语言实现的数组排序算法。 排序算法平均时间复杂度最坏时间复杂度最好时间复杂度空间复杂度是否稳定适用场景QuickO(n log n)O(n)O(n log n)O(log n)不稳定大规模数据,通用排序BubbleO(n)O(n)O(n)O(1)稳定小规模数据,教学用途InsertO(n)…...
利用腾讯云cloud studio云端免费部署deepseek-R1
1. cloud studio 1.1 cloud studio介绍 Cloud Studio(云端 IDE)是基于浏览器的集成式开发环境,为开发者提供了一个稳定的云端工作站。支持CPU与GPU的访问。用户在使用 Cloud Studio 时无需安装,随时随地打开浏览器即可使用。Clo…...
Codeforces Round 1002 (Div. 2)(A-D)
题目链接:Dashboard - Codeforces Round 1002 (Div. 2) - Codeforces A. Milya and Two Arrays 思路 数组a中不同数的数量*数组b的,就是能够组成不同数的数量 代码 void solve(){int n;cin>>n;int cnt10;int cnt20;map<int,bool> mp;ma…...
半导体器件与物理篇7 微波二极管、量子效应和热电子器件
基本微波技术 微波频率:微波频率涵盖约从0.1GHz到3000GHz,相当于波长从300cm到0.01cm。 分布效应:电子部件在微波频率,与其在较低频率的工作行为不同。 输运线:一个由电阻、电容、电感三种等效基本电路部件所组成的…...
Hot100之图论
200岛屿数量 题目 思路解析 把访问过的格子插上棋子 思想是先污染再治理,我们有一个inArea()函数,是判断是否出界了 我们先dfs()放各个方向遍历,然后我们再把这个位置标为0 我们岛屿是连着…...
CSS 样式化表格:从基础到高级技巧
CSS 样式化表格:从基础到高级技巧 1. 典型的 HTML 表格结构2. 为表格添加样式2.1 间距和布局2.2 简单的排版2.3 图形和颜色2.4 斑马条纹2.5 样式化标题 3. 完整的示例代码4. 总结 在网页设计中,表格是展示数据的常见方式。然而,默认的表格样式…...
DeepSeek相关技术整理
相关介绍 2024年12月26日,DeepSeek V3模型发布(用更低的训练成本,训练出更好的效果)671B参数,激活37B。2025年1月20日,DeepSeek-R1模型发布(仅需少量标注数据(高质量长cotÿ…...
Spring Boot框架下的单元测试
1. 什么是单元测试 1.1 基本定义 单元测试(Unit Test) 是对软件开发中最小可测单位(例如一个方法或者一个类)进行验证的一种测试方式。在 Java 后端的 Spring Boot 项目中,单元测试通常会借助 JUnit、Mockito 等框架对代码中核心逻辑进行快…...
OpenAI 实战进阶教程 - 第四节: 结合 Web 服务:构建 Flask API 网关
目标 学习将 OpenAI 接入 Web 应用,构建交互式 API 网关理解 Flask 框架的基本用法实现 GPT 模型的 API 集成并返回结果 内容与实操 一、环境准备 安装必要依赖: 打开终端或命令行,执行以下命令安装 Flask 和 OpenAI SDK: pip i…...
xss-labs靶场
xss-labs靶场 xss攻击类型 反射型xss 即攻击者将恶意脚本嵌入到url或者表单中,当用户访问特定的url或者提交表单时(用户端请求时),恶意脚本会执行 攻击需要用户点击恶意链接或访问包含恶意参数的url触发 存储型xss 即攻击者将恶意脚本提交…...
Eigen::Tensor使用帮助
0 引言 用python实现了某些算法之后,想转成C来获取更高的性能。但是python数组的操作太灵活了,尤其是3维、4维、5维等高维数组,以及它们的广播、数组坐标、切片等机制。还有numpy的pad、where等操作更是给C转换带来了更多的麻烦。 查阅了相…...
高阶开发基础——快速入门C++并发编程4
目录 使用call_once来确保调用的唯一性 先看我们的原始的单例模式 使用call_once来确保调用的唯一性 一个相似的概念是——单例模式,笔者找到的是stack_overflow的一个问答,如果不喜欢看英文,可以考虑看一下这个CSDN回答: c - H…...
C++基础day1
前言:谢谢阿秀,指路阿秀的学习笔记 一、基础语法 1.构造和析构: 类的构造函数是一种特殊的函数,在创建一个新的对象时调用。类的析构函数也是一种特殊的函数,在删除所创建的对象时调用。 构造顺序:父类->子类 析…...
Deepseek:网页版OR本地部署版本?
使用本地部署的 DeepSeek 还是网页版的 DeepSeek,取决于具体需求和使用场景。以下是两者的对比及推荐建议: 响应速度 网页版 DeepSeek:响应速度受网络状况和服务器负载影响较大。如果网络不稳定或服务器繁忙,可能会出现延迟甚至…...
【文件上传】
目录 一. 介绍二. 本地存储三. 阿里云OSS3.1 准备工作3.2 入门程序3.3 案例集成3.4 程序优化 \quad 一. 介绍 \quad 三要素缺一不可 \quad 二. 本地存储 \quad 解决相同命名覆盖问题 \quad 三. 阿里云OSS \quad \quad 3.1 准备工作 \quad \quad 3.2 入门程序 \quad \quad 3.3…...
股票入门知识
股票入门(更适合中国宝宝体制) 股市基础知识 本文介绍了股票的基础知识,股票的分类,各板块发行上市条件,股票代码,交易时间,交易规则,炒股术语,影响股价的因素…...
Debezium Oracle Connector SCN处理优化指南
Debezium Oracle Connector SCN处理优化指南 📌 问题场景 SCN跳跃场景: 起始SCN:15,000(含数据变更)结束SCN:1,000,000(无中间数据)默认批次大小:10,000 → 需执行985次无效查询🚀 优化方案 1. 自适应批次调整 代码位置:LogMinerStreamingChangeEventSource.j…...
2021版小程序开发5——小程序项目开发实践(1)
2021版小程序开发5——小程序项目开发实践(1) 学习笔记 2025 使用uni-app开发一个电商项目; Hbuidler 首选uni-app官方推荐工具:https://www.dcloud.io/hbuilderx.htmlhttps://dev.dcloud.net.cn/pages/app/list 微信小程序 管理后台:htt…...
软件测试02----用例设计方法
今天目标 1.能对穷举场景设计测试点 2.能对限定边界规则设计测试点 3.能对多条件依赖关系进行设计测试点 4.能对项目业务进行设计测试点 一、解决穷举场景 重点:使用等价类划分法 1.1等价类划分法 重点:有效等价和单个无效等价各取1个即可。 步骤&#…...
分享半导体Fab 缺陷查看系统,平替klarity defect系统
分享半导体Fab 缺陷查看系统,平替klarity defect系统;开发了半年有余。 查看Defect Map,Defect image,分析Defect size,defect count trend. 不用再采用klarity defect系统(license 太贵) 也可以…...
C语言-----数据结构从门到精通
1.数据结构基本概念 数据结构是计算机中存储、组织数据的方式,旨在提高数据的访问和操作效率。它是实现高效算法和程序设计的基石。 目标:通过思维导图了解数据结构的知识点,并掌握。 1.1逻辑结构 逻辑结构主要四种类型: 集合:结构中的数据元素之…...
存储器知识点3
1.只读存储器中内容断电后不会丢失,通常存储固定不变的内容,不需要定时刷新。 2.虚拟存储器将主存和辅存地址空间统一编址,其大小受到辅助存储器容量的限制。使得主存空间得到了扩充,需要硬件支持,并由操作系统调度。…...
Weevely代码分析
亲测php5和php8都无效,只有php7有效 ailx10 1949 次咨询 4.9 网络安全优秀回答者 互联网行业 安全攻防员 去咨询 上一次做weevely实验可以追溯到2020年,当时还是weevely3.7,现在的是weevely4 生成php网页木马依然差不多…… php菜刀we…...
leetcode解题思路分析(一百六十三)1409 - 1415 题
查询带键的排列 给定一个正整数数组 queries ,其取值范围在 1 到 m 之间。 请你根据以下规则按顺序处理所有 queries[i](从 i0 到 iqueries.length-1): 首先,你有一个排列 P[1,2,3,…,m]。 对于当前的 i ,找…...
【MATLAB例程】TOA和AOA混合的高精度定位程序,适用于三维、N锚点的情况
代码实现了一个基于到达角(AOA)和到达时间(TOA)混合定位的例程。该算法能够根据不同基站接收到的信号信息,自适应地计算目标的位置,适用于多个基站的场景 文章目录 主要功能代码结构运行结果程序代码 主要功…...
PyTorch框架——基于深度学习YOLOv8神经网络学生课堂行为检测识别系统
基于YOLOv8深度学习的学生课堂行为检测识别系统,其能识别三种学生课堂行为:names: [举手, 读书, 写字] 具体图片见如下: 第一步:YOLOv8介绍 YOLOv8 是 ultralytics 公司在 2023 年 1月 10 号开源的 YOLOv5 的下一个重大更新版本…...
智慧园区系统对比不同智能管理模式提升企业运营效率与安全性
内容概要 在当今竞争激烈的市场中,企业需要不断提高运营效率与安全性,以应对复杂的环境。这时,“智慧园区系统”应运而生,成为一种有效的解决方案。智能管理模式的多样性让企业在选择系统时有了更多的选择,而在这些模…...
读书笔记 | 《最小阻力之路》:用结构思维重塑人生愿景
一、核心理念:结构决定行为轨迹 橡皮筋模型:愿景张力的本质 书中提出:人类行为始终沿着"现状"与"愿景"之间的张力路径运动,如同橡皮筋拉伸产生的动力。 案例:音乐家每日练习的坚持,不…...