python打卡訓練營打卡記錄day51

復習日

作業:day43的時候我們安排大家對自己找的數據集用簡單cnn訓練,現在可以嘗試下借助這幾天的知識來實現精度的進一步提高

數據預處理

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms, models
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import os
from sklearn.model_selection import train_test_split
from shutil import copyfiledata_root = "flowers"  # 數據集根目錄
classes = ["daisy", "tulip", "rose", "sunflower", "dandelion"]  for folder in ["train", "val", "test"]:os.makedirs(os.path.join(data_root, folder), exist_ok=True)# 數據集劃分
for cls in classes:cls_path = os.path.join(data_root, cls)if not os.path.isdir(cls_path):raise FileNotFoundError(f"類別文件夾{cls}不存在!請檢查數據集路徑。")imgs = [f for f in os.listdir(cls_path) if f.lower().endswith((".jpg", ".jpeg", ".png"))]if not imgs:raise ValueError(f"類別{cls}中沒有圖片文件!")# 劃分數據集(測試集20%,驗證集20% of 剩余數據,訓練集60%)train_val, test = train_test_split(imgs, test_size=0.2, random_state=42)train, val = train_test_split(train_val, test_size=0.25, random_state=42)  # 0.8*0.25=0.2(驗證集占比)# 復制到train/val/test下的類別子文件夾for split, imgs_list in zip(["train", "val", "test"], [train, val, test]):split_class_path = os.path.join(data_root, split, cls)os.makedirs(split_class_path, exist_ok=True)for img in imgs_list:src_path = os.path.join(cls_path, img)dst_path = os.path.join(split_class_path, img)copyfile(src_path, dst_path)# 設置中文字體支持
plt.rcParams["font.family"] = ["SimHei"]
plt.rcParams['axes.unicode_minus'] = False# 檢查GPU是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"使用設備: {device}")# 訓練集數據增強
train_transform = transforms.Compose([transforms.Resize((224, 224)),transforms.RandomCrop(224, padding=4),transforms.RandomHorizontalFlip(),transforms.ColorJitter(brightness=0.2, contrast=0.2),transforms.ToTensor(),transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
])# 測試集預處理
test_transform = transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor(),transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
])# 加載數據集
train_dataset = datasets.ImageFolder(root=os.path.join(data_root, "train"),  transform=train_transform
)val_dataset = datasets.ImageFolder(root=os.path.join(data_root, "val"),transform=test_transform
)test_dataset = datasets.ImageFolder(root=os.path.join(data_root, "test"),transform=test_transform
)# 創建數據加載器
batch_size = 32
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=2)
val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=2)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=2)# 獲取類別名稱
class_names = train_dataset.classes
print(f"檢測到的類別: {class_names}")

通道注意力

class ChannelAttention(nn.Module):"""通道注意力模塊(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_ratio=16):super(ChannelAttention, self).__init__()self.avg_pool = nn.AdaptiveAvgPool2d(1)self.fc = nn.Sequential(nn.Linear(in_channels, in_channels // reduction_ratio, bias=False),nn.ReLU(inplace=True),nn.Linear(in_channels // reduction_ratio, in_channels, bias=False),nn.Sigmoid())def forward(self, x):batch_size, channels, _, _ = x.size()avg_pool_output = self.avg_pool(x).view(batch_size, channels)channel_weights = self.fc(avg_pool_output).view(batch_size, channels, 1, 1)return x * channel_weights

空間注意力

class SpatialAttention(nn.Module):"""空間注意力模塊"""def __init__(self, kernel_size=7):super(SpatialAttention, self).__init__()self.conv = nn.Conv2d(2, 1, kernel_size, padding=kernel_size//2, bias=False)self.sigmoid = nn.Sigmoid()def forward(self, x):# 沿通道維度計算均值和最大值avg_out = torch.mean(x, dim=1, keepdim=True)max_out, _ = torch.max(x, dim=1, keepdim=True)# 拼接均值和最大值特征concat = torch.cat([avg_out, max_out], dim=1)# 卷積操作生成空間注意力圖spatial_att = self.conv(concat)spatial_att = self.sigmoid(spatial_att)# 應用空間注意力return x * spatial_att

CBAM注意力

class CBAM(nn.Module):"""CBAM注意力模塊:結合通道注意力和空間注意力"""def __init__(self, in_channels, reduction_ratio=16, kernel_size=7):super(CBAM, self).__init__()self.channel_attention = ChannelAttention(in_channels, reduction_ratio)self.spatial_attention = SpatialAttention(kernel_size)def forward(self, x):# 先應用通道注意力x = self.channel_attention(x)# 再應用空間注意力x = self.spatial_attention(x)return x

定義帶CBAM的ResNet18模型

class FlowerCNN(nn.Module):def __init__(self, num_classes=5):super(FlowerCNN, self).__init__()# 加載預訓練ResNet18resnet = models.resnet18(pretrained=True)# 構建特征提取器,在每個殘差塊階段后插入CBAM模塊self.features = nn.Sequential(resnet.conv1,resnet.bn1,resnet.relu,resnet.maxpool,resnet.layer1,              # 輸出通道64CBAM(64),                   # CBAM模塊(64通道)resnet.layer2,              # 輸出通道128CBAM(128),                  # CBAM模塊(128通道)resnet.layer3,              # 輸出通道256CBAM(256),                  # CBAM模塊(256通道)resnet.layer4,              # 輸出通道512CBAM(512)                   # CBAM模塊(512通道))self.gap = nn.AdaptiveAvgPool2d(1)# 自定義分類頭self.fc = nn.Sequential(nn.Flatten(),nn.Linear(512, 512),nn.ReLU(),nn.Dropout(0.5),nn.Linear(512, num_classes))def forward(self, x):x = self.features(x)x = self.gap(x) x = self.fc(x)return x

初始化模型

model = FlowerCNN(num_classes=5).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', patience=3)def train_model(model, train_loader, val_loader, epochs=10):best_val_acc = 0.0train_loss_history = []val_loss_history = []train_acc_history = []val_acc_history = []for epoch in range(epochs):model.train()running_loss = 0.0correct = 0total = 0for batch_idx, (data, target) in enumerate(train_loader):data, target = data.to(device), target.to(device)optimizer.zero_grad()outputs = model(data)loss = criterion(outputs, target)loss.backward()optimizer.step()running_loss += loss.item()_, predicted = torch.max(outputs.data, 1)total += target.size(0)correct += (predicted == target).sum().item()if (batch_idx+1) % 50 == 0:print(f"Epoch [{epoch+1}/{epochs}] Batch {batch_idx+1}/{len(train_loader)} "f"Loss: {loss.item():.4f} Acc: {(100*correct/total):.2f}%")epoch_train_loss = running_loss / len(train_loader)epoch_train_acc = 100. * correct / total# 驗證集評估model.eval()val_loss = 0.0val_correct = 0val_total = 0with torch.no_grad():for data, target in val_loader:data, target = data.to(device), target.to(device)outputs = model(data)val_loss += criterion(outputs, target).item()_, predicted = torch.max(outputs.data, 1)val_total += target.size(0)val_correct += (predicted == target).sum().item()epoch_val_loss = val_loss / len(val_loader)epoch_val_acc = 100. * val_correct / val_totalscheduler.step(epoch_val_loss)train_loss_history.append(epoch_train_loss)val_loss_history.append(epoch_val_loss)train_acc_history.append(epoch_train_acc)val_acc_history.append(epoch_val_acc)print(f"Epoch {epoch+1} 完成 | 訓練損失: {epoch_train_loss:.4f} 驗證準確率: {epoch_val_acc:.2f}%")if epoch_val_acc > best_val_acc:torch.save(model.state_dict(), "best_flower_model.pth")best_val_acc = epoch_val_accprint("保存最佳模型...")# 繪制訓練曲線plt.figure(figsize=(12, 4))plt.subplot(1, 2, 1)plt.plot(train_loss_history, label='訓練損失')plt.plot(val_loss_history, label='驗證損失')plt.title('損失曲線')plt.xlabel('Epoch')plt.ylabel('損失值')plt.legend()plt.subplot(1, 2, 2)plt.plot(train_acc_history, label='訓練準確率')plt.plot(val_acc_history, label='驗證準確率')plt.title('準確率曲線')plt.xlabel('Epoch')plt.ylabel('準確率 (%)')plt.legend()plt.tight_layout()plt.show()return best_val_acc

訓練模型

print("開始訓練...")
final_acc = train_model(model, train_loader, val_loader, epochs=15)
print(f"訓練完成!最佳驗證準確率: {final_acc:.2f}%")from torch.nn import functional as F
import cv2 
import numpy as np
import torchvision.transforms as transformsclass GradCAM:def __init__(self, model, target_layer_name="features.10.1.conv2"):"""target_layer_name說明:- features.10 對應resnet.layer4(索引10)- .1.conv2 對應layer4中第二個殘差塊的第二個卷積層"""self.model = model.eval()self.target_layer_name = target_layer_nameself.gradients = Noneself.activations = Nonefor name, module in model.named_modules():if name == target_layer_name:module.register_forward_hook(self.forward_hook)module.register_backward_hook(self.backward_hook)breakdef forward_hook(self, module, input, output):self.activations = output.detach()def backward_hook(self, module, grad_input, grad_output):self.gradients = grad_output[0].detach()def generate(self, input_image, target_class=None):outputs = self.model(input_image)if target_class is None:target_class = torch.argmax(outputs, dim=1).item()self.model.zero_grad()one_hot = torch.zeros_like(outputs)one_hot[0, target_class] = 1outputs.backward(gradient=one_hot)gradients = self.gradientsactivations = self.activationsweights = torch.mean(gradients, dim=(2, 3))cam = torch.sum(activations[0] * weights[0][:, None, None], dim=0)cam = F.relu(cam)cam = (cam - cam.min()) / (cam.max() - cam.min() + 1e-8)cam = F.interpolate(cam.unsqueeze(0).unsqueeze(0),size=(224, 224),mode='bilinear', align_corners=False).squeeze()return cam.cpu().numpy(), target_classdef visualize_gradcam(img_path, model, class_names, alpha=0.6):img = Image.open(img_path).convert("RGB")img = img.resize((224, 224))img_np = np.array(img) / 255.0transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize(mean=(0.485, 0.456, 0.406),std=(0.229, 0.224, 0.225))])input_tensor = transform(img).unsqueeze(0).to(device)grad_cam = GradCAM(model, target_layer_name="features.10.1.conv2")heatmap, pred_class = grad_cam.generate(input_tensor)heatmap = np.uint8(255 * heatmap)heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)heatmap = heatmap / 255.0heatmap_rgb = heatmap[:, :, ::-1]superimposed = cv2.addWeighted(img_np, 1 - alpha, heatmap, alpha, 0)plt.figure(figsize=(12, 4))plt.subplot(1, 3, 1)plt.imshow(img_np)plt.title(f"原始圖像\n真實類別: {img_path.split('/')[-2]}")plt.axis('off')plt.subplot(1, 3, 2)plt.imshow(heatmap_rgb)plt.title(f"Grad-CAM熱力圖\n預測類別: {class_names[pred_class]}")plt.axis('off')plt.subplot(1, 3, 3)plt.imshow(superimposed)plt.title("疊加熱力圖")plt.axis('off')plt.tight_layout()plt.show()
開始訓練...
Epoch [1/15] Batch 50/81 Loss: 0.6559 Acc: 70.81%
Epoch 1 完成 | 訓練損失: 0.7685 驗證準確率: 62.54%
保存最佳模型...
Epoch [2/15] Batch 50/81 Loss: 0.4877 Acc: 79.75%
Epoch 2 完成 | 訓練損失: 0.5815 驗證準確率: 72.83%
保存最佳模型...
Epoch [3/15] Batch 50/81 Loss: 0.4116 Acc: 82.88%
Epoch 3 完成 | 訓練損失: 0.4738 驗證準確率: 83.24%
保存最佳模型...
Epoch [4/15] Batch 50/81 Loss: 0.3755 Acc: 85.00%
Epoch 4 完成 | 訓練損失: 0.4515 驗證準確率: 82.31%
Epoch [5/15] Batch 50/81 Loss: 0.6060 Acc: 85.81%
Epoch 5 完成 | 訓練損失: 0.3845 驗證準確率: 75.84%
Epoch [6/15] Batch 50/81 Loss: 0.4477 Acc: 86.94%
Epoch 6 完成 | 訓練損失: 0.3705 驗證準確率: 82.77%
Epoch [7/15] Batch 50/81 Loss: 0.3701 Acc: 89.38%
Epoch 7 完成 | 訓練損失: 0.3345 驗證準確率: 84.97%
保存最佳模型...
Epoch [8/15] Batch 50/81 Loss: 0.2666 Acc: 89.75%
Epoch 8 完成 | 訓練損失: 0.3281 驗證準確率: 83.93%
Epoch [9/15] Batch 50/81 Loss: 0.1533 Acc: 89.44%
Epoch 9 完成 | 訓練損失: 0.3294 驗證準確率: 83.47%
Epoch [10/15] Batch 50/81 Loss: 0.2991 Acc: 90.94%
Epoch 10 完成 | 訓練損失: 0.2643 驗證準確率: 83.82%
Epoch [11/15] Batch 50/81 Loss: 0.4048 Acc: 90.94%
Epoch 11 完成 | 訓練損失: 0.2640 驗證準確率: 89.25%
保存最佳模型...
Epoch [12/15] Batch 50/81 Loss: 0.1055 Acc: 92.50%
Epoch 12 完成 | 訓練損失: 0.2396 驗證準確率: 81.62%
Epoch [13/15] Batch 50/81 Loss: 0.3020 Acc: 92.81%
Epoch 13 完成 | 訓練損失: 0.2298 驗證準確率: 83.24%
Epoch [14/15] Batch 50/81 Loss: 0.1166 Acc: 92.69%
Epoch 14 完成 | 訓練損失: 0.2228 驗證準確率: 86.47%
Epoch [15/15] Batch 50/81 Loss: 0.1193 Acc: 93.38%
Epoch 15 完成 | 訓練損失: 0.2004 驗證準確率: 85.43%

訓練完成!最佳驗證準確率: 89.25%

選擇訓練圖像

test_image_path = "flowers/tulip/100930342_92e8746431_n.jpg"  
visualize_gradcam(test_image_path, model, class_names)

day43簡單cnn模型訓練結果

準確率69.94%

對比分析

(一)損失曲線分析

1. 改進模型(ResNet18 + CBAM + GAP)

  • 訓練損失:隨 Epoch 推進持續下降(最終穩定在 0.2 左右),說明預訓練骨干網 + 注意力機制有效擬合數據模式,模型學習能力強。
  • 驗證損失:前期快速下降(Epoch 1-3),中期小幅震蕩(Epoch 4-12),后期趨于平穩。震蕩源于 CBAM 動態調整注意力區域,短期影響泛化性,但整體趨勢驗證模型未過度擬合。

2. 簡單 CNN 模型

  • 訓練損失:快速下降后陷入平緩(長期維持在 1 左右),反映模型復雜度不足,難以挖掘深層特征。
  • 驗證損失:始終高于訓練損失,且與訓練損失差距小,典型 “欠擬合”—— 模型未充分學習數據模式,泛化性極差。

(二)準確率曲線分析

1. 改進模型(ResNet18 + CBAM + GAP)

  • 訓練準確率:穩步攀升至 93%+,體現模型對訓練數據的強擬合能力,CBAM 注意力有效聚焦關鍵特征(如花瓣、花蕊)。
  • 驗證準確率:最高達 89.25%(Epoch 11),雖有波動但整體趨勢向上。波動因注意力機制對復雜場景(如花朵密集、背景干擾)的動態適應,驗證模型具備一定泛化性。

2. 簡單 CNN 模型

  • 訓練準確率:緩慢爬坡至 70%+,因模型結構簡單(如僅含基礎卷積、池化),無法提取細粒度特征(如郁金香與玫瑰的花瓣差異 )。
  • 驗證準確率:始終低于訓練集且波動大,最高僅 69.94%,暴露模型對未見過數據的弱適應能力,實際應用價值低。

(三)GradCAM 熱力圖對比

1. 改進模型

  • 樣本說明:輸入為郁金香(tulip)圖像,模型正確分類,熱力圖覆蓋花朵集中區域。
  • 優勢體現
    • 聚焦性:高亮區域精準覆蓋郁金香主體,CBAM 引導模型關注與類別強相關的視覺特征(如花瓣形狀、顏色分布 )。
    • 解釋性:疊加熱力圖清晰展示 “模型依據花朵區域判斷類別”,驗證注意力機制的有效性,為分類結果提供可解釋依據。

2. 簡單 CNN 模型

  • 樣本說明:同一張郁金香圖像,簡單 CNN 雖最終正確分類,但熱力圖激活區域分散、模糊
  • 缺陷暴露
    • 特征利用低效:模型依賴全局特征 “碰運氣”,未聚焦關鍵區分區域(如花瓣細節 ),遇到相似類別(如郁金香 vs 風信子 )極易誤判。
    • 解釋性差:熱力圖無法說明分類依據,實際應用中難排查錯誤原因,可靠性低。

總結

改進模型通過?“預訓練骨干網 + 注意力機制 + 全局池化”?組合,解決了簡單 CNN 的三大痛點:

  1. 特征提取弱:精準聚焦花朵關鍵區域,挖掘細粒度特征(如花瓣紋理、顏色漸變 )。
  2. 泛化性不足:訓練 / 驗證曲線趨勢驗證模型具備學習復雜模式的能力,適配真實場景干擾(如背景復雜、花朵密集 )。
  3. 解釋性差:GradCAM 熱力圖清晰展示分類依據,為模型可靠性提供可視化支撐。

@浙大疏錦行

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/86798.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/86798.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/86798.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網絡安全:OWASP防護守則

目錄 一、OWASP十大WEB弱點防護守則 二、防護守則 1、權限控制失效 2、加密失誤 3、注入 4、不安全設計 5、安全配置缺陷 6、易受攻擊和過時的組件 7、身份認證和會話管理失效 8、缺乏完整性校驗 9、缺乏監控與日志記錄 10、服務端請求偽造 三、核心防護原則總結 …

Dagster 實現數據質量自動化:6大維度檢查與最佳實踐

在當今數據驅動的世界中,數據質量的重要性不言而喻。數據質量測試是確保數據準確、完整、一致和可靠的關鍵步驟。本文將深入探討數據質量測試的六大維度,并提供相關的檢查方法和最佳實踐。 什么是數據質量測試? 數據質量測試涉及評估數據以確…

計算機視覺之三維重建(深入淺出SfM與SLAM核心算法)—— 2. 攝像機標定

文章目錄 1. 前置知識1.1. 非齊次線性方程組求解1.1.1. 傳統求解方法1.1.2. 奇異值分解法1.1.3. 牛頓法或者梯度下降法 1.2. 齊次線性方程組的最小二乘解1.3. 非線性方程組的最小二乘解 2. 相機標定2.1. 相機內參數求解2.1.1. 求解 u 0 u_0 u0? 和 v 0 v_0 v0?2.1.2. 求解 …

SQLLL

595-big-countries https://leetcode.com/problems/big-countries/description/ 面積最大的國家 -- select name, population, area from World where area > 3000000 or population > 25000000596-classes-with-at-least-5-students https://leetcode.com/problems/…

MySQL中觸發器詳解 觸發器在自動化任務中的應用場景

觸發器是mysql中與表關聯的數據庫對象,能在特定操作(如insert、update、delete)發生時自動執行預定義sql邏輯。其核心用途包括:1. 維護數據一致性,如訂單插入后自動減少庫存;2. 記錄審計日志,如…

MySQL 8.0的數據庫root用戶默認無法遠程登錄,需要修改root的遠程授權

mysql> grant all privileges on . to ‘root’‘%’; ERROR 1410 (42000): You are not allowed to create a user with GRANT mysql> use mysql; Reading table information for completion of table and column names You can turn off this feature to get a quick…

??MPI + OpenMP 環境配置指南(Windows/Linux)?

—— 讓你的并行計算飛起來 🚀 1. 簡介?? ??MPI (Message Passing Interface)??:用于多機分布式并行計算(進程級并行)。??OpenMP??:用于單機多線程并行計算(線程級并行)。??混合編…

新聞類鴻蒙應用功耗危機以及優化方案

🔋 ??一、功耗痛點:新聞類應用成“續航殺手”?? ??后臺進程失控?? ??高頻刷新??:未適配應用(如網易新聞、百度客戶端)默認每30秒后臺刷新內容,觸發CPU持續喚醒,單設備日均耗電增加1…

【小工具】-Doxygen01

0、前言 參考帖子。 使用Doxygen Documentation Generator自動添加注釋 Doxygen使用教程 代碼注釋規范之Doxygen 1、Doxygen介紹 Doxygen 是一個功能強大的開源文檔生成工具,主要用于從源代碼中自動提取注釋并生成專業的 API 文檔。它支持多種編程語言&#xff…

大模型Transformer觸頂帶來的“熱潮退去”,稀疏注意力架構創新或是未來

1. 大模型退潮:裸泳者離場,創新者浮出水面 資本熱潮逐漸冷卻,大模型賽道正經歷殘酷洗牌。過去兩年密集的“百模大戰”,本質是商業模式的軍備競賽,用數據規模與參數數量掩蓋技術同質化。當DeepSeek以61層精簡架構挑戰千…

Android編譯時打印所有引用的so庫路徑

在app module build.gradle 最后添加腳本 tasks.whenTaskAdded { task -> println("test 11 task.name:"task.name) if (task.name.startsWith(merge) && task.name.endsWith(NativeLibs)) { task.doFirst { prin…

暴雨亮相2025中關村論壇數字金融與金融安全大會

6月10日,由中關村金融科技產業發展聯盟與中關村互聯網金融研究院主辦的“2025中關村論壇系列活動——數字金融與金融安全大會”在中關村展示中心盛大召開。本次大會以“人工智能機遇:未來金融格局重塑及安全治理”為主題,匯聚政產學研各界精英…

mapstruct中的@Mapper注解詳解

在MapStruct中,Mapper注解是核心注解之一,用于標記一個接口或抽象類為MapStruct的映射器(Mapper)。MapStruct會在編譯時自動生成該接口的實現類,完成對象之間的屬性映射。以下是對Mapper注解的詳細解析: 1.…

uniapp+vue2+h5圖片下載保存,微信瀏覽器、非微信瀏覽器

小程序端 onDown() {// 檢查相冊權限uni.authorize({scope: scope.writePhotosAlbum,success: () > {this.downloadImage();},fail: () > {uni.showToast({title: "請授權相冊權限",icon: "none"});}}); }, downloadImage() {common.request(post, …

NumPy 與 OpenCV 版本兼容性深度解析:底層機制與解決方案

在計算機視覺項目中,NumPy 和 OpenCV 的兼容性問題常被低估,實則暗藏復雜的技術陷阱。下面從底層機制深入剖析核心兼容性問題及解決方案: 一、內存布局沖突:數組連續性陷阱 問題本質: OpenCV 的 C 內核要求 連續內存塊…

基于SpringBoot利用死信隊列解決RabbitMQ業務隊列故障重試無效場景問題

基于SpringBoot利用死信隊列解決RabbitMQ業務隊列故障重試無效場景問題 解決方案項目實戰1、生產者服務1.1、RabbitConfig定義相關交換機及死信隊列等配置數據1.2、TestController測試接口Controller 2、消費者服務2.1 BusinessQueueConsumer業務隊列監聽器2.2 DeadLetterConsu…

西安java面試總結1

這是我第二次的面試。其實第一次也算不上面試,去了讓我手寫了幾道題,三道算法題,一道SQL題,兩道邏輯思維題,做完之后也沒看我的解答,隨便看了一眼簡歷,覺得我是大二的,大三還有課&am…

【redis】線程IO模型

Redis線程IO模型 總結:在redis5.0及之前,redis線程io模型是單線程。那么Redis單線程如何處理那么多的并發客戶端連接的?原因兩點:1)非阻塞io 2)多路復用(事件輪詢) 以下&#xff0…

進程間通信詳解(三):Linux進程信號深度解析

文章目錄 一、Linux進程信號核心概念1.1 信號本質1.2 關鍵術語1.3 Linux 信號機制的核心流程: 二、信號產生機制全景2.1 通過終端按鍵產生信號2.1.1 基本操作 2.2 調用系統命令向進程發信號2.2.1 kill 命令:向指定進程發送信號2.2.2 killall 命令&#x…

C++ 日志系統實戰第五步:日志器的設計

全是通俗易懂的講解,如果你本節之前的知識都掌握清楚,那就速速來看我的項目筆記吧~ 本文項目代碼編寫收尾! 日志器類 (Logger) 設計(建造者模式) 日志器主要用于和前端交互。當我們需要使用日志系統打印 log 時&…