半监督图像分类训练框架
import random #导入 Python 自带的随机数库。 import torch #导入 PyTorch 核心库。 import torch.nn as nn #导入 PyTorch 的神经网络模块并简写为 nn。 import numpy as np #导入数值计算库 NumPy简写为 np。 import os #导入 操作系统交互库。 from PIL import Image #读取图片数据 从 PIL 库中导入 Image 工具。 from torch.utils.data import Dataset, DataLoader #从 PyTorch 工具库中导入两个数据加载核心类。Dataset自定义数据集告诉程序怎么读图片、标签。DataLoader批量加载数据分批喂给模型训练。 from tqdm import tqdm #导入 进度条库。 from torchvision import transforms #导入 图像预处理 / 增强工具。 import time #导入时间库 import matplotlib.pyplot as plt #导入 绘图库简写 plt from model_utils.model import initialize_model #从你自己写的工具文件里导入模型初始化函数。 def seed_everything(seed): #固定随机种子保证实验可复现 torch.manual_seed(seed) #固定 PyTorch CPU 上的随机数种子。 torch.cuda.manual_seed(seed) #固定 单张 GPU 上的随机数种子 torch.cuda.manual_seed_all(seed) #固定 所有 GPU 上的随机数种子 torch.backends.cudnn.benchmark False #关闭cuDNN自动优化 。打开会让训练快一点但会导致结果不完全一样 torch.backends.cudnn.deterministic True #让cuDNN用固定算法。保证每次卷积计算结果一模一样。 random.seed(seed) #固定 Python 自带 random 库的随机数 np.random.seed(seed) #固定 numpy 的随机数 os.environ[PYTHONHASHSEED] str(seed) #固定 Python 哈希随机值。保证字典、环境等顺序一致 ################################################################# seed_everything(0) #调用上面这个函数把种子固定为 0。以后你每次运行结果都完全一样 ############################################### HW 224 #图像统一缩放到224×224 train_transform transforms.Compose( #训练集用随机裁剪 / 旋转做数据增强避免过拟合把一串图像预处理操作打包在一起这是训练集用的预处理目的数据增强让模型更健壮、不容易过拟合 [ transforms.ToPILImage(), #224 224 3模型 3, 224, 224 必须先转成 PIL 格式才能继续处理 transforms.RandomResizedCrop(224), #随机裁剪 缩放 transforms.RandomRotation(50), #随机把图片左转或右转最多 50 度角度是随机的 transforms.ToTensor() #把 PIL 图片 → PyTorch 张量 Tensor ] ) val_transform transforms.Compose( #这是验证集用的预处理不做数据增强因为验证要测模型真实能力不能乱改图片 [ transforms.ToPILImage(), #224 224 3模型 3, 224, 224 transforms.ToTensor() #转成张量 通道换位 归一化 0~1 ] ) #继承 PyTorch 的Dataset实现自己的图片读取逻辑支持 3 种模式train标注训练集、val标注验证集、semi无标注集。 class food_Dataset(Dataset): # 初始化函数 def __init__(self, path, modetrain): #构造函数创建数据集时自动先运行这里 self.mode mode #把传进来的 mode 存到类自己身上 if mode semi: #如果是半监督无标注模式 这种模式只有图片没有标签 self.X self.read_file(path) #调用 read_file 函数读取所有图片 else: #否则就是训练集 or 验证集既有图片又有标签 self.X, self.Y self.read_file(path) #调用 read_file 读取图片 标签 self.Y torch.LongTensor(self.Y) #把标签从 numpy 数组 转成 PyTorch 长整型张量 if mode train: #如果是训练模式 self.transform train_transform #给训练集绑定训练增强随机裁剪、随机旋转… else: self.transform val_transform #绑定验证集预处理 def read_file(self, path): #从路径里把所有图片读进内存有标签就读标签没标签就只读图 if self.mode semi: file_list os.listdir(path) #os.listdir(path)列出这个文件夹下所有图片的文件名存到 file_list 里作用知道这个文件夹里有多少张图、叫什么名字 xi np.zeros((len(file_list), HW, HW, 3), dtypenp.uint8) #创建一个全是空的 numpy 数组形状len(file_list)图片数量HW, HW高、宽都是 2243RGB 三通道dtypenp.uint8图片像素格式0~255作用提前开好空间等下把图片一张一张放进去 # 列出文件夹下所有文件名字 for j, img_name in enumerate(file_list): #循环一张一张遍历所有图片 img_path os.path.join(path, img_name) #把文件夹路径 图片名字拼在一起得到完整路径如C:/xxx/00/001.jpg作用让程序找到这张图片 img Image.open(img_path) img img.resize((HW, HW)) #Image.open打开图片.resize((HW, HW))把图片统一缩放到 224×224作用所有图片大小统一模型才能吃 xi[j, ...] img #把这张图片放进刚才开好的空数组里,放到第 j 号位置,作用把图片存到内存里 print(读到了%d个数据 % len(xi)) #打印一共读了多少张图片 return xi #semi 模式只返回图片不返回标签 else: #否则训练集 / 验证集有标签 for i in tqdm(range(11)): #循环 11 次因为一共 11 类食物 file_dir path /%02d % i #拼出每个类别的文件夹路径 file_list os.listdir(file_dir) #列出这个类别文件夹下的所有图片.os.listdir 是 Python 内置 os 模块中最基础的文件 / 目录操作函数核心作用是列出指定目录下的所有文件和子目录的名称不包含路径新手可以把它理解成 “查看某个文件夹里有哪些东西的清单”。 xi np.zeros((len(file_list), HW, HW, 3), dtypenp.uint8) #开一个空数组存这个类别的所有图片 yi np.zeros(len(file_list), dtypenp.uint8) #开一个空数组存这个类别的所有标签 # 列出文件夹下所有文件名字 for j, img_name in enumerate(file_list): #遍历这个文件夹里的每一张图 img_path os.path.join(file_dir, img_name) img Image.open(img_path) #拼完整图片路径 img img.resize((HW, HW)) #打开 缩放成 224×224 xi[j, ...] img yi[j] i if i 0: #如果是第一个类别i0 X xi #直接把图片、标签赋值给 X、Y Y yi else: X np.concatenate((X, xi), axis0) #把新的图片拼接到总图片 X 后面 Y np.concatenate((Y, yi), axis0) print(读到了%d个数据 % len(Y)) #打印一共读了多少张带标签的图片 return X, Y def __getitem__(self, item): #DataLoader 批量取数据时会循环调用这个函数每次取 item 对应的样本 if self.mode semi: #如果是半监督无标注模式 return self.transform(self.X[item]), self.X[item] #self.X[item]取第 item 张原始图片numpy 数组224×224×3self.transform(self.X[item])对这张图做预处理val_transform只转张量return A, B返回两个值A预处理后的图片模型能直接用的张量B原始图片后续伪标注时会用到保留原始格式 else: return self.transform(self.X[item]), self.Y[item] #给模型喂数据时每次返回「一张处理好的图 它的真实标签」 def __len__(self): #DataLoader 知道数据集总共有多少样本才能计算「一共有多少批次」以及是否遍历完所有样本 return len(self.X) class semiDataset(Dataset): # 用训练好的模型给无标注数据打「伪标签」只保留置信度高于阈值thres0.99的样本作为半监督训练数据。 def __init__(self, no_label_loder, model, device, thres0.99): #no_label_loder无标注数据的加载器model已经训练过的模型deviceCPU 或 GPUthres0.99置信度阈值只有模型概率 99% 才要 x, y self.get_label(no_label_loder, model, device, thres) #让模型去预测无标签图片只留下高置信度的结果存在 x、y 里。 self.get_label(...)调用这个类内部的 get_label 函数 if x []: #如果一张符合条件的图都没有,标记 flagFalse后面不训练半监督 self.flag False else: self.flag True #告诉后面代码有半监督数据可以参与训练 self.X np.array(x) #把列表 x 转换成 numpy 数组 self.Y torch.LongTensor(y) #把伪标签列表 y 转换成PyTorch 长整型张量 self.transform train_transform #给半监督数据绑定 训练集增强 def get_label(self, no_label_loder, model, device, thres): #用模型预测无标注图片筛选高置信度样本 model model.to(device) #把模型搬到 GPU/CPU 上 pred_prob [] #pred_prob存每张图的最大置信度 创建空列表 labels [] #labels存每张图的预测类别 x [] #x最后保存置信度达标的原始图片 y [] #y保存对应图片的伪标签 soft nn.Softmax() #把模型输出的原始分数 → 变成 0~1 之间的概率 with torch.no_grad(): #推理时不计算梯度,预测必须加这句 for bat_x, _ in no_label_loder: #遍历无标签数据的每一个批次 bat_x一个批次的图片数据 bat_x bat_x.to(device) #把这一批图片数据也搬到 GPU/CPU pred model(bat_x) #把数据喂给模型 pred_soft soft(pred) #转成概率 pred_max, pred_value pred_soft.max(1) #pred_max每张图最大概率是多少 pred_value对应的类别是几 pred_prob.extend(pred_max.cpu().numpy().tolist()) #把所有图片的置信度存起来后面筛选用 labels.extend(pred_value.cpu().numpy().tolist()) #把所有图片的预测标签存进列表 for index, prob in enumerate(pred_prob): #只有模型非常确定的图片才留下 if prob thres: x.append(no_label_loder.dataset[index][1]) #no_label_loder.dataset找到原始无标签数据集。 y.append(labels[index]) #把这张图对应的伪标签加入 y return x, y #把筛选后的 图片 x 和 伪标签 y 返回 def __getitem__(self, item): #类里面必须的每次取一个样本 再拼成一个批次 这是 PyTorch Dataset 类要求实现的核心方法用于按索引获取单个样本。 return self.transform(self.X[item]), self.Y[item] #返回一个元组 (预处理后的图片张量, 伪标签)这是模型训练时标准的 “数据 - 标签” 对。 def __len__(self): #返回数据集一共有多少样本 return len(self.X) def get_semi_loader(no_label_loder, model, device, thres): #定义一个名为 get_semi_loader 的函数用于生成半监督数据的加载器。 semiset semiDataset(no_label_loder, model, device, thres) #实例化一个 semiDataset 对象即创建一个半监督数据集。 if semiset.flag False: #检查半监督数据集是否有效。 return None #当没有可用的半监督数据时返回 None。后续影响主训练函数 train_val 接收到 None 后会跳过半监督训练的步骤。 else: #当 semiset.flag 为 True 时说明有可用的半监督数据进入数据加载器的创建流程。 semi_loader DataLoader(semiset, batch_size16, shuffleFalse) #将半监督数据集 semiset 包装成一个 DataLoader。 return semi_loader # 将创建好的半监督数据加载器 semi_loader 返回给调用者。 class myModel(nn.Module): #自定义 CNN 模型 myModel备用定义一个名为 myModel 的类它继承自 PyTorch 的 nn.Module。这是所有 PyTorch 模型的基类提供了参数管理、前向传播、设备移动等核心功能。 def __init__(self, num_class): super(myModel, self).__init__() #3 *224 *224 - 512*7*7 - 拉直 -》全连接分类 self.conv1 nn.Conv2d(3, 64, 3, 1, 1) # 64*224*224输入 (3, 224, 224) → 输出 (64, 224, 224)。填充和步长保证了特征图尺寸不变。 self.bn1 nn.BatchNorm2d(64) #对 64 个通道的输出进行批归一化Batch Normalization。作用加速训练、稳定梯度、缓解内部协变量偏移。 self.relu nn.ReLU() #定义 ReLU 激活函数为网络引入非线性让模型能学习更复杂的特征。 self.pool1 nn.MaxPool2d(2) #64*112*112 self.layer1 nn.Sequential( #将通道数从 64 提升到 128并将特征图尺寸从 112×112 下采样到 56×56。维度变化(64, 112, 112) → (128, 56, 56)。 nn.Conv2d(64, 128, 3, 1, 1), # 128*112*112 nn.BatchNorm2d(128), nn.ReLU(), nn.MaxPool2d(2) #128*56*56 ) self.layer2 nn.Sequential( #功能通道数从 128 → 256尺寸从 56×56 → 28×28。 nn.Conv2d(128, 256, 3, 1, 1), nn.BatchNorm2d(256), nn.ReLU(), nn.MaxPool2d(2) #256*28*28 ) self.layer3 nn.Sequential( #通道数从 256 → 512尺寸从 28×28 → 14×14。 nn.Conv2d(256, 512, 3, 1, 1), nn.BatchNorm2d(512), nn.ReLU(), nn.MaxPool2d(2) #512*14*14 ) self.pool2 nn.MaxPool2d(2) #512*7*7 self.fc1 nn.Linear(25088, 1000) #25088-1000 第一个全连接层。 self.relu2 nn.ReLU() self.fc2 nn.Linear(1000, num_class) #1000-11 第二个全连接层也是最终的分类层。num_class这里是 11输出每个类别的预测分数。 def forward(self, x): #定义数据在模型中的流动顺序PyTorch 会自动根据此图进行反向传播。 x self.conv1(x) x self.bn1(x) x self.relu(x) x self.pool1(x) x self.layer1(x) x self.layer2(x) x self.layer3(x) x self.pool2(x) x x.view(x.size()[0], -1) x self.fc1(x) x self.relu2(x) x self.fc2(x) return x #核心训练函数 train_val半监督训练逻辑 定义训练与验证的主函数整合了有监督训练、半监督伪标注、模型保存和可视化。 def train_val(model, train_loader, val_loader, no_label_loader, device, epochs, optimizer, loss, thres, save_path): model model.to(device) semi_loader None #初始状态下没有伪标签数据设为 None。 plt_train_loss [] plt_val_loss [] #创建 4 个空列表用于记录每一轮的损失和准确率最后画图用。 plt_train_acc [] plt_val_acc [] max_acc 0.0 #初始化最高准确率用于筛选并保存 “最好” 的模型。 for epoch in range(epochs): #外层大循环控制训练的总轮数 train_loss 0.0 val_loss 0.0 train_acc 0.0 val_acc 0.0 semi_loss 0.0 semi_acc 0.0 start_time time.time() #记录本轮训练的开始时间用于计算耗时。 model.train() #将模型切换为训练模式。作用让 BatchNorm批归一化计算当前批次的均值方差让 Dropout 随机失活神经元。 for batch_x, batch_y in train_loader: #内层循环遍历有标签训练集的每一个批次。 x, target batch_x.to(device), batch_y.to(device) #将图片数据 batch_x 和标签 batch_y 移动到 GPU/CPU。 pred model(x) #数据喂入模型得到预测结果 pred train_bat_loss loss(pred, target) #用损失函数比较预测值和真实标签得到当前批次的损失。 train_bat_loss.backward() #计算梯度。PyTorch 会根据损失自动计算模型所有参数的梯度。 optimizer.step() # 更新参数 之后要梯度清零否则会累积梯度 计算梯度。PyTorch 会根据损失自动计算模型所有参数的梯度。 optimizer.zero_grad() #PyTorch 的梯度会累加。如果不清零下一个批次的梯度会叠加到当前梯度上导致参数更新错误。这一步必须有 train_loss train_bat_loss.cpu().item() #把每个批次的损失加起来最后求平均。 train_acc np.sum(np.argmax(pred.detach().cpu().numpy(), axis1) target.cpu().numpy()) #统计分类任务中预测正确的样本数并累加到准确率统计变量中 plt_train_loss.append(train_loss / train_loader.__len__()) #损失总损失 / 批次数量 plt_train_acc.append(train_acc/train_loader.dataset.__len__()) #记录准确率准确率正确总数 / 总样本数 if semi_loader! None: #判断是否存在可用的半监督数据加载器。 for batch_x, batch_y in semi_loader: #遍历半监督数据加载器中的每一个批次。 x, target batch_x.to(device), batch_y.to(device) #将当前批次的数据和标签移动到指定的计算设备 pred model(x) #将数据 x 输入模型进行前向传播得到预测结果 pred。 semi_bat_loss loss(pred, target) #计算当前批次半监督数据的损失值。 semi_bat_loss.backward() #执行反向传播计算模型参数的梯度。 optimizer.step() #根据计算出的梯度更新模型的参数。 optimizer.zero_grad() #更新参数 之后要梯度清零否则会累积梯度 semi_loss train_bat_loss.cpu().item() #累加当前批次的损失值到总半监督损失 semi_loss 中。 semi_acc np.sum(np.argmax(pred.detach().cpu().numpy(), axis1) target.cpu().numpy()) #计算当前批次半监督数据的预测准确率并累加到总半监督准确率 semi_acc 中。 print(半监督数据集的训练准确率为, semi_acc/train_loader.dataset.__len__()) #打印半监督数据的训练准确率。 model.eval() #将模型切换到评估验证模式。 with torch.no_grad(): for batch_x, batch_y in val_loader: x, target batch_x.to(device), batch_y.to(device) pred model(x) val_bat_loss loss(pred, target) val_loss val_bat_loss.cpu().item() val_acc np.sum(np.argmax(pred.detach().cpu().numpy(), axis1) target.cpu().numpy()) #计算当前批次验证数据的预测准确率并累加到总验证准确率 val_acc 中。 plt_val_loss.append(val_loss / val_loader.__len__()) #计算本轮的平均验证损失并将其添加到用于绘图的列表 plt_val_loss 中。 plt_val_acc.append(val_acc / val_loader.dataset.__len__()) #计算本轮的验证准确率并将其添加到用于绘图的列表 plt_val_acc 中。 if epoch%3 0 and plt_val_acc[-1] 0.6: #判断是否满足更新伪标签的条件。epoch%3 0每训练 3 个 epoch才尝试更新一次伪标签避免过于频繁导致模型震荡。plt_val_acc[-1] 0.6只有当模型在验证集上的准确率超过 60% 时才认为模型的预测足够可靠可以用来生成伪标签。 semi_loader get_semi_loader(no_label_loader, model, device, thres) #调用 get_semi_loader 函数生成新的半监督数据加载器。 if val_acc max_acc: #判断当前轮的验证准确率是否超过了历史最高值。 torch.save(model, save_path) #将当前模型保存到指定路径。 max_acc val_acc #更新历史最高验证准确率为当前轮的准确率。 print([%03d/%03d] %2.2f sec(s) TrainLoss : %.6f | valLoss: %.6f Trainacc : %.6f | valacc: %.6f % \ (epoch, epochs, time.time() - start_time, plt_train_loss[-1], plt_val_loss[-1], plt_train_acc[-1], plt_val_acc[-1]) ) # 打印训练结果。 注意python语法 %2.2f 表示小数位为2的浮点数 后面可以对应。 格式化打印本轮训练的关键指标。本轮的训练损失、验证损失、训练准确率和验证准确率方便实时监控训练状态。 plt.plot(plt_train_loss) #绘制训练损失曲线。 plt.plot(plt_val_loss) #在同一张图上绘制验证损失曲线。 plt.title(loss) #为当前图表设置标题为 “loss” plt.legend([train, val]) #添加图例区分两条曲线 plt.show() #显示绘制好的损失曲线图 plt.plot(plt_train_acc) #绘制训练准确率曲线。 plt.plot(plt_val_acc) #在同一张图上绘制验证准确率曲线。 plt.title(acc) #为当前图表设置标题为 “acc”。 plt.legend([train, val]) #添加图例区分训练准确率和验证准确率曲线。 plt.show() #显示绘制好的准确率曲线图。 # path rF:\pycharm\beike\classification\food_classification\food-11\training\labeled # train_path rF:\pycharm\beike\classification\food_classification\food-11\training\labeled # val_path rF:\pycharm\beike\classification\food_classification\food-11\validation train_path rC:\Users\张玮祺\Desktop\sdxx\5\food_classification\food-11_sample\training\labeled #路径定义 数据集 / 加载器初始化 指定有标签训练集的文件夹路径。 val_path rC:\Users\张玮祺\Desktop\sdxx\5\food_classification\food-11_sample\validation #指定有标签训练集的文件夹路径。 no_label_path rC:\Users\张玮祺\Desktop\sdxx\5\food_classification\food-11_sample\training\unlabeled\00 #指定无标签数据的文件夹路径。 train_set food_Dataset(train_path, train) #创建训练集对象。传入路径 模式 train会自动应用训练集的数据增强。 val_set food_Dataset(val_path, val) #创建验证集对象。传入路径 模式 val只做格式转换不增强。 no_label_set food_Dataset(no_label_path, semi) #创建无标注集对象。传入路径 模式 semi只读取图片不读取标签。 train_loader DataLoader(train_set, batch_size16, shuffleTrue) val_loader DataLoader(val_set, batch_size16, shuffleTrue) no_label_loader DataLoader(no_label_set, batch_size16, shuffleFalse) #因为后面打伪标签时需要按顺序对应图片索引打乱后会导致标签和图片错位。 # model myModel(11) model, _ initialize_model(vgg, 11, use_pretrainedTrue) #模型 / 优化器 / 超参数初始化 启动训练 lr 0.001 loss nn.CrossEntropyLoss() optimizer torch.optim.AdamW(model.parameters(), lrlr, weight_decay1e-4) device cuda if torch.cuda.is_available() else cpu save_path model_save/best_model.pth #指定最佳模型要存放在哪里。 epochs 15 thres 0.99 train_val(model, train_loader, val_loader, no_label_loader, device, epochs, optimizer, loss, thres, save_path) #作用调用我们之前逐行讲解的 train_val 函数正式开始训练。传参把上面定义的所有东西模型、数据、设备、超参数全部传给训练函数。结果程序开始运行你会看到控制台打印出每一轮的损失和准确率并在最后弹出损失 / 准确率曲线图。核心逻辑半监督学习的 “伪标签法”—— 先用有标签数据训模型模型达标后给无标签数据打高置信度伪标签把伪标签数据加入训练提升模型性能关键设计训练集做数据增强验证集不做→保证训练泛化、验证准确伪标签只保留置信度 0.99 的样本→避免错误标签污染训练每 3 轮更新一次伪标签→平衡模型稳定性和伪标签时效性保存验证集最优模型→避免训练后期过拟合运行流程准备数据→初始化模型→有监督训练→达标后生成伪标签→半监督训练→验证→保存模型→可视化。

相关新闻

双鱼座的男生、想要啥样子的老婆?

双鱼座的男生、想要啥样子的老婆?

1、聪明优雅,有自己的想法;2、有品位,有内容,有知识;3、体贴温柔,愿意和老公分享生活点滴;4、有一点小小的崇拜老公,相信老公是最棒的;5、不做作,不虚伪&…

2026/7/3 20:31:23 阅读更多 →
教育行业站群如何用Java解析局域网教学视频文件夹并分片秒传?

教育行业站群如何用Java解析局域网教学视频文件夹并分片秒传?

大文件传输系统技术方案设计 项目背景与需求分析 作为北京XX软件公司的项目负责人,近期产品部门提出了大文件传输系统的需求。经过与各业务部门的需求沟通和技术评估,我们面临以下核心挑战: 超大文件处理:需支持50GB以上文件的…

2026/7/4 13:43:06 阅读更多 →
【MySQL统计函数count详解】

【MySQL统计函数count详解】

MySQL统计函数count详解 1. count()概述2. count(1)和count(*)和count(列名)的区别3. count(*)的实现方式 1. count()概述 count() 是一个聚合函数,返回指定匹配条件的行数。开发中常用来统计表中数据,全部数据,不为null数据,或…

2026/5/17 8:50:31 阅读更多 →

最新新闻

Claude Code与Codex深度对比:AI编程副驾选型指南

Claude Code与Codex深度对比:AI编程副驾选型指南

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度 在 AI 编程助手领域,Claude Code 和 Codex 无疑是当前最受瞩目的两个顶级选手。许多开发者在选择日常主力工具时&#xff…

2026/7/5 23:49:15 阅读更多 →
Web即时通讯加密实战:从TLS到端到端加密的三种高效方案

Web即时通讯加密实战:从TLS到端到端加密的三种高效方案

1. 项目概述:为什么Web即时通讯必须谈加密?聊到Web即时通讯,很多人第一反应是功能实现:怎么建立WebSocket连接、怎么处理消息队列、怎么设计UI界面。但从业十年,我见过太多项目在初期对安全“偷懒”,结果在…

2026/7/5 23:47:14 阅读更多 →
基于YOLO26的文档表格识别技术解析与实践

基于YOLO26的文档表格识别技术解析与实践

1. 项目背景与核心价值文档表格识别一直是办公自动化和企业数字化转型中的关键痛点。传统OCR技术虽然能识别文字内容,但对于表格这种结构化数据的识别准确率往往不尽如人意。特别是在处理扫描件、倾斜拍摄或复杂排版的文档时,常规方法经常出现单元格错位…

2026/7/5 23:45:12 阅读更多 →
Java突变测试实战:Pitest与JUnit整合提升测试有效性

Java突变测试实战:Pitest与JUnit整合提升测试有效性

1. 项目概述:为什么我们需要Pitest? 在软件开发的日常里,我们写单元测试,运行JUnit,看到绿色的进度条,心里就踏实了。但这份“踏实”真的可靠吗?我经历过不止一次,一个看似覆盖全面的…

2026/7/5 23:43:10 阅读更多 →
FDSM模块提升YOLO26目标检测性能的技术解析

FDSM模块提升YOLO26目标检测性能的技术解析

1. 项目概述:FDSM模块如何提升YOLO26目标检测性能在目标检测领域,YOLO系列模型因其出色的实时性能而广受欢迎。然而,传统YOLO模型在处理复杂场景(如弱光环境、小目标或遮挡情况)时仍面临挑战。最近,我们团队…

2026/7/5 23:41:09 阅读更多 →
微信小程序用户数据解密:从session_key到AES-128-CBC的完整安全实践

微信小程序用户数据解密:从session_key到AES-128-CBC的完整安全实践

1. 项目概述与核心价值最近在做一个微信小程序项目,涉及到用户头像、昵称等敏感信息的获取与处理。这几乎是每个小程序开发者都会遇到的“必修课”,但微信为了用户隐私安全,对这些数据做了加密处理,不能直接在前端拿到明文。这就引…

2026/7/5 23:39:09 阅读更多 →

日新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻