当前位置: 首页 > web >正文

《Python星球日记》 第62天:图像方向综合项目(猫狗分类)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)

目录

    • 一、项目概述
      • 1. 项目背景与意义
    • 二、项目需求分析
      • 1. 功能需求
      • 2. 性能需求
      • 3. 技术路线
    • 三、数据准备与增强
      • 1. 数据集选择
      • 2. 数据探索与分析
      • 3. 数据预处理
      • 4. 数据增强
    • 四、模型选择与训练
      • 1. 自建CNN模型
      • 2. 使用迁移学习
      • 3. 模型训练
    • 五、结果可视化与评估
      • 1. 训练过程可视化
      • 2. 模型评估
      • 3. 预测可视化
    • 六、模型部署建议
      • 1. 模型导出
      • 2. 模型部署选项
      • 3. 部署示例:Flask Web应用
      • 4. 其他部署考虑因素
    • 七、总结与扩展
      • 1. 项目总结
      • 2. 扩展方向
      • 3. 学习资源推荐
    • 八、参考资料

👋 专栏介绍: Python星球日记专栏介绍(持续更新ing)
上一篇: 《Python星球日记》 第61天:深度强化学习(DQN 与 PPO)基础

大家好,欢迎来到Python星球的第62天!🪐

一、项目概述

经过前面对卷积神经网络迁移学习深度学习基础知识的学习,今天我们将通过一个实际项目来综合应用这些技术 - 构建一个猫狗分类识别系统。这个项目不仅能让我们巩固所学知识,还能体验一个完整的深度学习项目流程,从数据准备到模型部署。

在这里插入图片描述

1. 项目背景与意义

在计算机视觉领域,图像分类是一个基础且重要的任务。猫狗分类是深度学习入门的经典案例,它不仅操作简单,而且能够直观地展示卷积神经网络的强大能力。完成这个项目后,你将能够:

  • 掌握数据预处理与增强技术
  • 理解如何选择和应用适合的深度学习模型
  • 学会评估模型性能并可视化结果
  • 了解如何部署训练好的模型

二、项目需求分析

我们的目标是构建一个能够准确区分猫和狗图片的深度学习系统。具体需求如下:

1. 功能需求

  • 输入:各种角度、光照条件下的猫或狗图片
  • 处理:通过深度学习模型分析图像特征
  • 输出:给出图片中是猫还是狗的预测,以及相应的置信度

2. 性能需求

  • 分类准确率较高
  • 推理速度较快,适合实时应用
  • 模型大小适中,便于部署

3. 技术路线

在这里插入图片描述

三、数据准备与增强

1. 数据集选择

在本项目中,我们将使用经典的猫狗数据集(Kaggle Dogs vs. Cats)。该数据集包含25,000张标记为猫或狗的图像,分辨率和质量各不相同,非常适合训练我们的模型。

# 下载数据集
!kaggle competitions download -c dogs-vs-cats
!unzip dogs-vs-cats.zip -d data/

2. 数据探索与分析

在开始处理数据之前,让我们先了解一下数据集的基本情况:

import os
import matplotlib.pyplot as plt
import random
from PIL import Image
import numpy as np# 设置数据路径
data_dir = 'data/train'
cats_dir = os.path.join(data_dir, 'cats')
dogs_dir = os.path.join(data_dir, 'dogs')# 创建猫狗分类文件夹
if not os.path.exists(cats_dir):os.makedirs(cats_dir)
if not os.path.exists(dogs_dir):os.makedirs(dogs_dir)# 将原始数据整理到对应文件夹
for img in os.listdir(data_dir):if img.startswith('cat'):os.rename(os.path.join(data_dir, img), os.path.join(cats_dir, img))elif img.startswith('dog'):os.rename(os.path.join(data_dir, img), os.path.join(dogs_dir, img))# 统计数量
num_cats = len(os.listdir(cats_dir))
num_dogs = len(os.listdir(dogs_dir))
print(f"猫图片数量: {num_cats}")
print(f"狗图片数量: {num_dogs}")# 展示几张样本图片
plt.figure(figsize=(12, 6))
for i in range(3):# 随机选择猫图片cat_img = random.choice(os.listdir(cats_dir))cat_path = os.path.join(cats_dir, cat_img)plt.subplot(2, 3, i+1)plt.imshow(np.array(Image.open(cat_path)))plt.title('Cat')plt.axis('off')# 随机选择狗图片dog_img = random.choice(os.listdir(dogs_dir))dog_path = os.path.join(dogs_dir, dog_img)plt.subplot(2, 3, i+4)plt.imshow(np.array(Image.open(dog_path)))plt.title('Dog')plt.axis('off')plt.tight_layout()
plt.show()

3. 数据预处理

图像预处理是提高模型性能的关键步骤。我们需要对图像进行如下处理:

from tensorflow.keras.preprocessing.image import ImageDataGenerator
from sklearn.model_selection import train_test_split
import shutil# 创建训练、验证和测试集目录
train_dir = 'data/train_split'
valid_dir = 'data/valid_split'
test_dir = 'data/test_split'for dir_path in [train_dir, valid_dir, test_dir]:for category in ['cats', 'dogs']:os.makedirs(os.path.join(dir_path, category), exist_ok=True)# 划分数据集
cat_files = os.listdir(cats_dir)
dog_files = os.listdir(dogs_dir)# 训练集:验证集:测试集 = 7:2:1
cat_train, cat_temp = train_test_split(cat_files, test_size=0.3, random_state=42)
cat_valid, cat_test = train_test_split(cat_temp, test_size=1/3, random_state=42)dog_train, dog_temp = train_test_split(dog_files, test_size=0.3, random_state=42)
dog_valid, dog_test = train_test_split(dog_temp, test_size=1/3, random_state=42)# 复制文件到对应目录
def copy_files(file_list, src_dir, dst_dir):for file in file_list:shutil.copy(os.path.join(src_dir, file), os.path.join(dst_dir, file))copy_files(cat_train, cats_dir, os.path.join(train_dir, 'cats'))
copy_files(cat_valid, cats_dir, os.path.join(valid_dir, 'cats'))
copy_files(cat_test, cats_dir, os.path.join(test_dir, 'cats'))copy_files(dog_train, dogs_dir, os.path.join(train_dir, 'dogs'))
copy_files(dog_valid, dogs_dir, os.path.join(valid_dir, 'dogs'))
copy_files(dog_test, dogs_dir, os.path.join(test_dir, 'dogs'))print(f"训练集: 猫 {len(cat_train)}, 狗 {len(dog_train)}")
print(f"验证集: 猫 {len(cat_valid)}, 狗 {len(dog_valid)}")
print(f"测试集: 猫 {len(cat_test)}, 狗 {len(dog_test)}")

4. 数据增强

数据增强是解决数据量不足和提高模型泛化能力的重要技术。通过对训练图像进行各种变换,我们可以生成更多样化的训练样本:

# 设置图像预处理参数
img_width, img_height = 224, 224  # 统一图像尺寸
batch_size = 32# 训练数据增强
train_datagen = ImageDataGenerator(rescale=1./255,             # 像素值归一化rotation_range=40,          # 随机旋转角度范围width_shift_range=0.2,      # 宽度偏移范围height_shift_range=0.2,     # 高度偏移范围shear_range=0.2,            # 剪切强度zoom_range=0.2,             # 缩放范围horizontal_flip=True,       # 水平翻转fill_mode='nearest'         # 填充模式
)# 验证和测试数据只需要归一化
valid_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)# 创建数据生成器
train_generator = train_datagen.flow_from_directory(train_dir,target_size=(img_width, img_height),batch_size=batch_size,class_mode='binary'
)valid_generator = valid_datagen.flow_from_directory(valid_dir,target_size=(img_width, img_height),batch_size=batch_size,class_mode='binary'
)test_generator = test_datagen.flow_from_directory(test_dir,target_size=(img_width, img_height),batch_size=batch_size,class_mode='binary'
)# 展示数据增强效果
def show_augmented_images():# 获取一批训练数据x_batch, y_batch = next(train_generator)# 显示几张增强后的图像plt.figure(figsize=(12, 8))for i in range(8):plt.subplot(2, 4, i+1)plt.imshow(x_batch[i])plt.title('Cat' if y_batch[i] < 0.5 else 'Dog')plt.axis('off')plt.tight_layout()plt.suptitle('数据增强效果展示', y=0.98)plt.show()show_augmented_images()

数据增强带来的好处包括:

  • 大幅扩充训练样本,减轻过拟合风险
  • 提高模型对各种环境下图像的适应能力
  • 增强模型对旋转、缩放、光照等变化的鲁棒性

四、模型选择与训练

在这个项目中,我们将探讨两种解决方案:自建CNN模型迁移学习

1. 自建CNN模型

首先,我们来构建一个简单而有效的卷积神经网络:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau
import time# 构建模型
def build_custom_cnn():model = Sequential([# 第一个卷积块Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(img_width, img_height, 3)),MaxPooling2D(2, 2),# 第二个卷积块Conv2D(64, (3, 3), activation='relu', padding='same'),MaxPooling2D(2, 2),# 第三个卷积块Conv2D(128, (3, 3), activation='relu', padding='same'),MaxPooling2D(2, 2),# 第四个卷积块Conv2D(256, (3, 3), activation='relu', padding='same'),MaxPooling2D(2, 2),# 展平层Flatten(),# 全连接层Dense(512, activation='relu'),Dropout(0.5),  # 防止过拟合Dense(1, activation='sigmoid')  # 二分类问题使用sigmoid激活函数])# 编译模型model.compile(optimizer='adam',loss='binary_crossentropy',metrics=['accuracy'])return model# 创建自定义CNN模型
custom_model = build_custom_cnn()
custom_model.summary()

在这里插入图片描述

2. 使用迁移学习

迁移学习是一种强大的技术,它利用预训练模型的知识来解决新问题。这在图像分类任务中特别有效:

from tensorflow.keras.applications import VGG16, ResNet50
from tensorflow.keras.layers import GlobalAveragePooling2D# 使用VGG16作为基础模型
def build_transfer_model():# 加载预训练的VGG16模型(不包括顶层分类器)base_model = VGG16(weights='imagenet', include_top=False, input_shape=(img_width, img_height, 3))# 冻结预训练层的权重for layer in base_model.layers:layer.trainable = False# 构建新模型model = Sequential([base_model,GlobalAveragePooling2D(),Dense(256, activation='relu'),Dropout(0.5),Dense(1, activation='sigmoid')])# 编译模型model.compile(optimizer='adam',loss='binary_crossentropy',metrics=['accuracy'])return model# 创建迁移学习模型
transfer_model = build_transfer_model()
transfer_model.summary()

在这里插入图片描述

3. 模型训练

接下来,我们使用准备好的数据集来训练这两个模型,并比较它们的性能:

# 设置回调函数
callbacks = [EarlyStopping(patience=5, restore_best_weights=True),ModelCheckpoint('best_model.h5', save_best_only=True),ReduceLROnPlateau(factor=0.1, patience=3, min_lr=1e-6)
]# 训练自建CNN模型
print("训练自建CNN模型...")
start_time = time.time()
custom_history = custom_model.fit(train_generator,steps_per_epoch=train_generator.samples // batch_size,epochs=30,validation_data=valid_generator,validation_steps=valid_generator.samples // batch_size,callbacks=callbacks
)
custom_train_time = time.time() - start_time
print(f"训练时间: {custom_train_time:.2f}秒")# 训练迁移学习模型
print("\n训练迁移学习模型...")
start_time = time.time()
transfer_history = transfer_model.fit(train_generator,steps_per_epoch=train_generator.samples // batch_size,epochs=15,  # 迁移学习通常需要更少的轮次validation_data=valid_generator,validation_steps=valid_generator.samples // batch_size,callbacks=callbacks
)
transfer_train_time = time.time() - start_time
print(f"训练时间: {transfer_train_time:.2f}秒")

五、结果可视化与评估

1. 训练过程可视化

训练完成后,我们通过可视化来分析模型的学习过程和性能:

def plot_training_history(history, title):plt.figure(figsize=(12, 5))# 训练与验证准确率plt.subplot(1, 2, 1)plt.plot(history.history['accuracy'], label='训练准确率')plt.plot(history.history['val_accuracy'], label='验证准确率')plt.title(f'{title} - 准确率')plt.xlabel('轮次')plt.ylabel('准确率')plt.legend()# 训练与验证损失plt.subplot(1, 2, 2)plt.plot(history.history['loss'], label='训练损失')plt.plot(history.history['val_loss'], label='验证损失')plt.title(f'{title} - 损失')plt.xlabel('轮次')plt.ylabel('损失')plt.legend()plt.tight_layout()plt.show()# 可视化训练过程
plot_training_history(custom_history, '自建CNN模型')
plot_training_history(transfer_history, '迁移学习模型')

2. 模型评估

接下来,我们对模型在测试集上的性能进行全面评估:

from sklearn.metrics import classification_report, confusion_matrix, roc_curve, auc
import seaborn as snsdef evaluate_model(model, generator, model_name):# 获取预测结果y_pred_prob = model.predict(generator)y_pred = (y_pred_prob > 0.5).astype(int)y_true = generator.classes# 计算混淆矩阵cm = confusion_matrix(y_true, y_pred)# 输出分类报告print(f"\n{model_name} 测试集评估结果:")print(classification_report(y_true, y_pred, target_names=['猫', '狗']))# 绘制混淆矩阵plt.figure(figsize=(8, 6))sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',xticklabels=['猫', '狗'],yticklabels=['猫', '狗'])plt.title(f'{model_name} - 混淆矩阵')plt.ylabel('真实标签')plt.xlabel('预测标签')plt.show()# 绘制ROC曲线fpr, tpr, _ = roc_curve(y_true, y_pred_prob)roc_auc = auc(fpr, tpr)plt.figure(figsize=(8, 6))plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC曲线 (AUC = {roc_auc:.3f})')plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')plt.xlim([0.0, 1.0])plt.ylim([0.0, 1.05])plt.xlabel('假阳性率')plt.ylabel('真阳性率')plt.title(f'{model_name} - ROC曲线')plt.legend(loc="lower right")plt.show()return roc_auc# 评估两个模型
custom_auc = evaluate_model(custom_model, test_generator, '自建CNN模型')
transfer_auc = evaluate_model(transfer_model, test_generator, '迁移学习模型')# 比较两个模型
print("\n模型比较:")
print(f"{'模型':<20} {'测试集AUC':<12} {'训练时间(秒)'}")
print(f"{'-'*45}")
print(f"{'自建CNN模型':<20} {custom_auc:<12.4f} {custom_train_time:.2f}")
print(f"{'迁移学习模型':<20} {transfer_auc:<12.4f} {transfer_train_time:.2f}")

在这里插入图片描述

3. 预测可视化

让我们对一些测试图像进行预测,并可视化结果:

def visualize_predictions(model, test_dir, model_name, num_samples=8):# 准备数据test_imgs = []test_labels = []# 随机选择猫和狗图片cat_dir = os.path.join(test_dir, 'cats')dog_dir = os.path.join(test_dir, 'dogs')cat_files = random.sample(os.listdir(cat_dir), num_samples//2)dog_files = random.sample(os.listdir(dog_dir), num_samples//2)# 加载并预处理图像from tensorflow.keras.preprocessing.image import load_img, img_to_arrayfor img_file in cat_files:img = load_img(os.path.join(cat_dir, img_file), target_size=(img_width, img_height))img_array = img_to_array(img) / 255.0test_imgs.append(img_array)test_labels.append(0)  # 0代表猫for img_file in dog_files:img = load_img(os.path.join(dog_dir, img_file), target_size=(img_width, img_height))img_array = img_to_array(img) / 255.0test_imgs.append(img_array)test_labels.append(1)  # 1代表狗# 转换为numpy数组test_imgs = np.array(test_imgs)# 预测predictions = model.predict(test_imgs)# 可视化结果plt.figure(figsize=(16, 8))for i in range(num_samples):plt.subplot(2, num_samples//2, i+1)plt.imshow(test_imgs[i])true_label = "Cat" if test_labels[i] == 0 else "Dog"pred_prob = predictions[i][0]pred_label = "Cat" if pred_prob < 0.5 else "Dog"title = f"真: {true_label}\n预测: {pred_label} ({pred_prob:.2f})"color = "green" if true_label == pred_label else "red"plt.title(title, color=color)plt.axis('off')plt.suptitle(f"{model_name} - 预测结果展示", fontsize=16)plt.tight_layout()plt.subplots_adjust(top=0.9)plt.show()# 可视化预测结果
visualize_predictions(custom_model, test_dir, '自建CNN模型')
visualize_predictions(transfer_model, test_dir, '迁移学习模型')

六、模型部署建议

训练好高性能模型后,我们需要将其部署到实际应用中。下面是一些常见的部署方法:

1. 模型导出

根据框架的不同,我们可以选择不同的导出格式:

# 保存TensorFlow/Keras模型为.h5格式
def save_model(model, model_name):model_path = f"{model_name}.h5"model.save(model_path)print(f"模型已保存到: {model_path}")# 保存两个模型
save_model(custom_model, "custom_cat_dog_classifier")
save_model(transfer_model, "transfer_cat_dog_classifier")# 如果使用PyTorch,则可以使用以下方式保存模型
"""
import torch# 假设model是一个PyTorch模型
torch.save(model.state_dict(), 'cat_dog_classifier.pt')
"""

2. 模型部署选项

3. 部署示例:Flask Web应用

下面是一个使用Flask创建简单Web应用的示例:

from flask import Flask, request, render_template, jsonify
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.image import img_to_array
from PIL import Image
import numpy as np
import io
import base64app = Flask(__name__)# 加载模型
model = load_model('transfer_cat_dog_classifier.h5')# 图像预处理函数
def preprocess_image(image, target_size):if image.mode != "RGB":image = image.convert("RGB")image = image.resize(target_size)image = img_to_array(image)image = np.expand_dims(image, axis=0)return image / 255.0@app.route('/', methods=['GET', 'POST'])
def predict():result = Noneimage_data = Noneif request.method == 'POST':# 检查是否有文件上传if 'file' not in request.files:return render_template('index.html', error='没有选择文件')file = request.files['file']if file.filename == '':return render_template('index.html', error='没有选择文件')# 读取图像文件image = Image.open(io.BytesIO(file.read()))# 保存图像为base64字符串以便在页面上显示buffered = io.BytesIO()image.save(buffered, format="JPEG")image_data = base64.b64encode(buffered.getvalue()).decode()# 预处理图像processed_image = preprocess_image(image, target_size=(224, 224))# 预测prediction = model.predict(processed_image)[0][0]# 判断结果if prediction < 0.5:label = "猫"confidence = 1 - predictionelse:label = "狗"confidence = predictionresult = {'prediction': label,'confidence': float(confidence * 100)}return render_template('index.html', result=result, image=image_data)if __name__ == '__main__':app.run(debug=True)

HTML模板 (templates/index.html) 可以这样设计:

<!DOCTYPE html>
<html>
<head><title>猫狗分类器</title><style>body {font-family: Arial, sans-serif;max-width: 800px;margin: 0 auto;padding: 20px;}.container {display: flex;flex-direction: column;align-items: center;}.prediction-container {margin-top: 20px;text-align: center;}img {max-width: 300px;margin-top: 20px;}.confidence-bar {height: 20px;background-color: #f0f0f0;border-radius: 10px;margin-top: 10px;overflow: hidden;}.confidence-level {height: 100%;background-color: #4CAF50;text-align: center;color: white;line-height: 20px;}</style>
</head>
<body><div class="container"><h1>猫狗图像分类器</h1><p>上传一张猫或狗的图片,AI将告诉你图中是猫还是狗。</p><form method="post" enctype="multipart/form-data"><input type="file" name="file" accept="image/*"><input type="submit" value="预测"></form>{% if error %}<p style="color: red;">{{ error }}</p>{% endif %}{% if result %}<div class="prediction-container"><h2>预测结果: {{ result.prediction }}</h2><div class="confidence-bar"><div class="confidence-level" style="width: {{ result.confidence }}%;">{{ result.confidence|round(2) }}%</div></div>{% if image %}<img src="data:image/jpeg;base64,{{ image }}" alt="上传的图片">{% endif %}</div>{% endif %}</div>
</body>
</html>

4. 其他部署考虑因素

  • 模型优化:考虑使用模型量化、剪枝等技术减小模型体积,加快推理速度
  • 批处理:如果需要处理大量图像,可以采用批处理方式提高效率
  • API设计:为不同平台设计合适的API接口
  • 安全性:防止恶意输入和潜在攻击
  • 监控与更新:定期监控模型性能,收集新数据进行更新

七、总结与扩展

1. 项目总结

在本项目中,我们完成了一个完整的计算机视觉任务——猫狗图像分类系统。我们经历了:

  • 数据准备与增强:收集、清洗和增强图像数据
  • 模型构建与训练:设计CNN架构和使用迁移学习
  • 评估与可视化:使用各种指标和可视化方法评估模型性能
  • 部署建议:探讨了将模型部署到实际应用的方法

通过这个项目,我们不仅掌握了深度学习的基本技能,还了解了实际项目的完整流程。

2. 扩展方向

这个项目还有很多可以改进和扩展的方向:

  • 多分类扩展:增加更多动物种类的分类
  • 目标检测:不仅分类图像,还可以定位图中的猫狗位置
  • 实时检测:结合视频流实现实时猫狗检测
  • 模型压缩:研究如何在保持准确率的同时减小模型大小
  • 解释性研究:使用Grad-CAM等技术可视化模型关注的区域

3. 学习资源推荐

如果你对深度学习和计算机视觉感兴趣,可以继续学习以下资源:

  • 书籍:《Deep Learning》(Goodfellow等著)、《动手学深度学习》(李沐等著)
  • 课程:Stanford CS231n、Fast.ai深度学习课程
  • 竞赛平台:Kaggle、天池等,可以参加各种计算机视觉挑战
  • 实践项目:尝试不同类型的图像处理任务,如分割、检测和生成

八、参考资料

  1. Kaggle Dogs vs. Cats竞赛:https://www.kaggle.com/c/dogs-vs-cats
  2. TensorFlow官方教程:https://www.tensorflow.org/tutorials
  3. PyTorch官方文档:https://pytorch.org/docs/stable/index.html
  4. 《Deep Learning with Python》(François Chollet著)
  5. 《Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow》(Aurélien Géron著)

通过这个猫狗分类的实战项目,我们将前面所学的深度学习知识应用到了实际问题中。希望这个项目能帮助你更好地理解深度学习的工作流程,并为你未来探索更复杂的计算机视觉任务奠定基础!


祝你学习愉快,Python星球的探索者!👨‍🚀🌠

创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)
如果你对今天的内容有任何问题,或者想分享你的学习心得,欢迎在评论区留言讨论!

http://www.xdnf.cn/news/5773.html

相关文章:

  • Java自动化测试
  • 2025年5月13日 奇门遁甲与股市
  • 学习笔记:黑马程序员JavaWeb开发教程(2025.4.3)
  • 麒麟 v10 卸载podman
  • 【大模型MCP协议】MCP官方文档(Model Context Protocol)一、开始——1. 介绍
  • pythonocc 拉伸特征
  • C语言 第六章 结构体(3)
  • 0前言(文章体系)
  • 数字滤波器应用介绍
  • 流体力学绪论(期末复习)
  • 【android bluetooth 框架分析 02】【Module详解 13】【CounterMetrics 模块介绍】
  • 继承关系下创建对象的具体流程
  • 生活破破烂烂,AI 缝缝补补(附提示词)
  • 进程间的通信
  • python-75-Nacos技术之Python+Nacos实现微服务架构
  • 打破效率枷锁,数企云外呼一骑绝尘
  • beyond compare 免密钥进入使用(删除注册表)
  • MacOS 上构建 gem5
  • 排错-harbor-db容器异常重启
  • PCB抄板过程、抄板软件介绍
  • 基于Qt6 + MuPDF在 Arm IMX6ULL运行的PDF浏览器(项目推介)
  • 做为一个平台,给第三方提供接口的时候,除了要求让他们申请 appId 和 AppSecret 之外,还应当有哪些安全选项,要过等保3级
  • BUUCTF Pwn hitcontraining_uaf WP
  • 学习黑客5分钟深入浅出理解系列之Windows注册表
  • Odoo 18 安全组与访问权限管理指南
  • SQLite 数据库常见问题及解决方法
  • 一般纯软工程学习路径
  • 使用达梦数据库官方管理工具SQLark导入与导出数据库表
  • 解决IDEA无法运行git的问题
  • CVE-2020-1957 漏洞报告