深入理解Grad-CAM:用梯度可視化神經網絡的“注意力“

深入理解Grad-CAM:用梯度可視化神經網絡的"注意力"

引言

在深度學習的發展過程中,模型的可解釋性一直是一個重要的研究方向。盡管現代神經網絡在圖像識別、自然語言處理等任務上取得了令人矚目的成果,但它們往往被稱為"黑盒"模型——我們知道輸入和輸出,卻不清楚模型內部是如何做出決策的。

Grad-CAM(Gradient-weighted Class Activation Mapping)正是為了解決這個問題而提出的一種可視化技術。它能夠生成熱圖,顯示模型在做出預測時最關注圖像的哪些區域,從而幫助我們理解神經網絡的決策過程。

什么是Grad-CAM?

Grad-CAM是一種用于可視化卷積神經網絡決策過程的技術,由Selvaraju等人在2017年提出。它的核心思想是利用流入最后一個卷積層的梯度信息來理解模型對輸入圖像不同區域的重視程度。

基本原理

想象您是一位藝術評論家,正在評價一幅畫作。您給出了"這是一幅杰作"的評價,現在有人問您:“畫作中哪些元素最打動了您?”

Grad-CAM做的事情類似:

  • 畫作 = 輸入圖像
  • 您的評價 = 神經網絡的預測
  • 打動您的元素 = 圖像中的重要區域

梯度與注意力:一個常見的誤解

在深入技術細節之前,我們需要澄清一個常見的誤解。許多人認為:“梯度大意味著這個特征還沒有學習好,而不是這個特征很重要。”

這種理解在訓練過程中是正確的,但在Grad-CAM中,我們處理的是已經訓練好的模型,梯度的含義完全不同。

訓練時的梯度 vs Grad-CAM中的梯度

訓練時的梯度(優化視角)

在訓練過程中:

loss = 損失函數(預測值, 真實標簽)
梯度 = ?loss/?參數

此時梯度大 = 這個參數需要大幅調整 = 還沒學好

例如,如果模型把貓識別成了狗:

  • 損失很大,梯度也大
  • 意味著相關參數需要大幅修改
Grad-CAM中的梯度(解釋視角)

在Grad-CAM中:

目標得分 = 模型輸出[目標類別]  # 比如"貓"類別的得分
梯度 = ?目標得分/?特征圖

此時梯度大 = 這個特征對目標得分影響大 = 這個特征很重要

關鍵區別

讓我們用一個類比來說明這個區別:

訓練時(糾錯思維)

  1. “模型預測錯了”
  2. “哪里出了問題?”
  3. “梯度大的地方需要修正”

Grad-CAM(解釋思維)

  1. “模型預測對了”
  2. “為什么預測對了?”
  3. “梯度大的地方功勞最大”

這就像考試成績分析:

  • 訓練時:學生考了60分,數學扣分最多(梯度大),說明數學需要重點補習
  • Grad-CAM:學生考了90分,數學題得分對總分貢獻最大(梯度大),說明數學是這個學生的強項

Grad-CAM算法詳解

算法步驟

  1. 前向傳播:將圖像輸入網絡,獲取目標卷積層的特征圖和最終預測
  2. 選擇目標類別:確定要分析的類別(通常是預測概率最高的類別)
  3. 反向傳播:計算目標類別得分相對于特征圖的梯度
  4. 權重計算:對梯度進行全局平均池化,得到每個特征通道的重要性權重
  5. 加權求和:將權重與對應的特征圖相乘并求和
  6. 可視化:將結果上采樣到原圖尺寸,生成熱圖

數學公式

對于類別c和卷積層的特征圖A^k:

  1. 計算權重

    α_c^k = (1/Z) ∑_i ∑_j ?y^c/?A_{ij}^k
    

    其中Z是特征圖的像素總數

  2. 生成Grad-CAM

    L_{Grad-CAM}^c = ReLU(∑_k α_c^k A^k)
    

完整的PyTorch實現

下面是一個完整的Grad-CAM實現,包含詳細的調試信息:

import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.transforms as transforms
import torchvision.models as models
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import cv2
import warnings
warnings.filterwarnings('ignore')class GradCAM:def __init__(self, model, target_layer_name):"""初始化Grad-CAMArgs:model: 預訓練的CNN模型target_layer_name: 目標卷積層名稱"""self.model = modelself.model.eval()# 存儲前向傳播和反向傳播的結果self.gradients = Noneself.activations = None# 注冊鉤子函數self._register_hooks(target_layer_name)def _register_hooks(self, target_layer_name):"""注冊前向和反向傳播鉤子"""def forward_hook(module, input, output):# 保存前向傳播的激活值self.activations = outputprint(f"[DEBUG] Forward hook triggered")print(f"[DEBUG] Activation shape: {output.shape}")def backward_hook(module, grad_input, grad_output):# 保存反向傳播的梯度self.gradients = grad_output[0]print(f"[DEBUG] Backward hook triggered")print(f"[DEBUG] Gradient shape: {grad_output[0].shape}")# 找到目標層并注冊鉤子for name, module in self.model.named_modules():if name == target_layer_name:print(f"[DEBUG] Found target layer: {name}")print(f"[DEBUG] Layer type: {type(module)}")module.register_forward_hook(forward_hook)module.register_backward_hook(backward_hook)breakdef generate_cam(self, input_tensor, class_idx=None):"""生成類激活映射Args:input_tensor: 輸入圖像張量 [1, 3, H, W]class_idx: 目標類別索引,如果為None則使用預測概率最高的類別Returns:cam: 歸一化的CAM熱圖prediction: 模型預測結果"""print(f"[DEBUG] Input tensor shape: {input_tensor.shape}")# 前向傳播output = self.model(input_tensor)print(f"[DEBUG] Model output shape: {output.shape}")print(f"[DEBUG] Top 3 predictions: {torch.topk(output, 3)[1].squeeze()}")if class_idx is None:class_idx = torch.argmax(output, dim=1).item()print(f"[DEBUG] Target class index: {class_idx}")print(f"[DEBUG] Target class score: {output[0, class_idx].item():.4f}")# 反向傳播self.model.zero_grad()target_score = output[0, class_idx]target_score.backward()# 獲取梯度和激活值gradients = self.gradients  # [1, C, H, W]activations = self.activations  # [1, C, H, W]print(f"[DEBUG] Gradients shape: {gradients.shape}")print(f"[DEBUG] Activations shape: {activations.shape}")# 計算權重:對梯度進行全局平均池化weights = torch.mean(gradients, dim=(2, 3), keepdim=True)  # [1, C, 1, 1]print(f"[DEBUG] Weights shape: {weights.shape}")print(f"[DEBUG] Top 5 weights: {weights.squeeze().topk(5)[0]}")# 加權求和生成CAMcam = torch.sum(weights * activations, dim=1).squeeze()  # [H, W]print(f"[DEBUG] Raw CAM shape: {cam.shape}")print(f"[DEBUG] CAM min/max: {cam.min().item():.4f}/{cam.max().item():.4f}")# 應用ReLU確保非負值cam = F.relu(cam)# 歸一化到[0, 1]cam_min, cam_max = cam.min(), cam.max()if cam_max > cam_min:cam = (cam - cam_min) / (cam_max - cam_min)print(f"[DEBUG] Normalized CAM min/max: {cam.min().item():.4f}/{cam.max().item():.4f}")return cam.detach().cpu().numpy(), output.detach().cpu()def load_and_preprocess_image(image_path, size=(224, 224)):"""加載和預處理圖像"""# ImageNet預處理transform = transforms.Compose([transforms.Resize(size),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])])# 加載圖像image = Image.open(image_path).convert('RGB')input_tensor = transform(image).unsqueeze(0)print(f"[DEBUG] Original image size: {image.size}")print(f"[DEBUG] Preprocessed tensor shape: {input_tensor.shape}")return input_tensor, imagedef visualize_gradcam(original_image, cam, alpha=0.4):"""可視化Grad-CAM結果"""# 將PIL圖像轉換為numpy數組img_array = np.array(original_image)height, width = img_array.shape[:2]# 將CAM調整到原圖尺寸cam_resized = cv2.resize(cam, (width, height))print(f"[DEBUG] Original image shape: {img_array.shape}")print(f"[DEBUG] Resized CAM shape: {cam_resized.shape}")# 生成熱圖heatmap = cv2.applyColorMap(np.uint8(255 * cam_resized), cv2.COLORMAP_JET)heatmap = cv2.cvtColor(heatmap, cv2.COLOR_BGR2RGB)# 疊加熱圖到原圖superimposed = heatmap * alpha + img_array * (1 - alpha)superimposed = np.clip(superimposed, 0, 255).astype(np.uint8)return superimposed, heatmapdef get_imagenet_labels():"""獲取ImageNet類別標簽"""# 這里簡化處理,在實際使用中應該加載完整的ImageNet標簽文件labels = {}# 一些常見的ImageNet類別示例sample_labels = {281: 'tabby cat',285: 'Egyptian cat', 282: 'tiger cat',283: 'Persian cat',287: 'lynx',0: 'tench',1: 'goldfish',2: 'great white shark'}return sample_labelsdef main():"""主函數演示Grad-CAM"""print("=" * 60)print("Grad-CAM 實現演示")print("=" * 60)# 1. 加載預訓練模型print("\n[STEP 1] 加載預訓練ResNet50模型...")model = models.resnet50(pretrained=True)print(f"[DEBUG] Model loaded successfully")print(f"[DEBUG] Model type: {type(model)}")# 打印模型結構(部分)print("\n[DEBUG] Model layers:")for name, module in model.named_modules():if isinstance(module, (nn.Conv2d, nn.AdaptiveAvgPool2d, nn.Linear)):print(f"  {name}: {module}")# 2. 創建Grad-CAM對象print("\n[STEP 2] 創建Grad-CAM對象...")target_layer = 'layer4.2.conv3'  # ResNet50的最后一個卷積層gradcam = GradCAM(model, target_layer)# 3. 加載和預處理圖像print("\n[STEP 3] 加載圖像...")# 這里使用一個示例圖像路徑,請替換為您的圖像路徑image_path = "sample_image.jpg"  # 請替換為實際圖像路徑# 如果沒有圖像文件,創建一個簡單的測試圖像try:input_tensor, original_image = load_and_preprocess_image(image_path)except:print("[INFO] 創建測試圖像...")# 創建一個簡單的測試圖像test_image = Image.new('RGB', (224, 224), color='red')test_image.save("test_image.jpg")input_tensor, original_image = load_and_preprocess_image("test_image.jpg")# 4. 生成Grad-CAMprint("\n[STEP 4] 生成Grad-CAM...")cam, predictions = gradcam.generate_cam(input_tensor)# 5. 分析預測結果print("\n[STEP 5] 分析預測結果...")probabilities = F.softmax(predictions, dim=1)top5_prob, top5_indices = torch.topk(probabilities, 5)labels = get_imagenet_labels()print(f"\nTop 5 predictions:")for i in range(5):idx = top5_indices[0][i].item()prob = top5_prob[0][i].item()label = labels.get(idx, f"Class_{idx}")print(f"  {i+1}. {label}: {prob:.4f} ({prob*100:.2f}%)")# 6. 可視化結果print("\n[STEP 6] 可視化結果...")superimposed, heatmap = visualize_gradcam(original_image, cam)# 顯示結果fig, axes = plt.subplots(2, 2, figsize=(12, 10))# 原圖axes[0, 0].imshow(original_image)axes[0, 0].set_title('Original Image')axes[0, 0].axis('off')# CAM熱圖axes[0, 1].imshow(cam, cmap='jet')axes[0, 1].set_title('Grad-CAM Heatmap')axes[0, 1].axis('off')# 彩色熱圖axes[1, 0].imshow(heatmap)axes[1, 0].set_title('Colored Heatmap')axes[1, 0].axis('off')# 疊加結果axes[1, 1].imshow(superimposed)axes[1, 1].set_title('Grad-CAM Overlay')axes[1, 1].axis('off')plt.tight_layout()plt.savefig('gradcam_results.png', dpi=300, bbox_inches='tight')plt.show()print(f"\n[INFO] 結果已保存到 gradcam_results.png")# 7. 分析CAM統計信息print("\n[STEP 7] CAM統計分析...")print(f"CAM statistics:")print(f"  Shape: {cam.shape}")print(f"  Min value: {cam.min():.4f}")print(f"  Max value: {cam.max():.4f}")print(f"  Mean value: {cam.mean():.4f}")print(f"  Std value: {cam.std():.4f}")# 找到最高激活區域max_y, max_x = np.unravel_index(cam.argmax(), cam.shape)print(f"  Highest activation at: ({max_x}, {max_y})")print(f"  Highest activation value: {cam[max_y, max_x]:.4f}")print("\n" + "=" * 60)print("Grad-CAM 演示完成!")print("=" * 60)if __name__ == "__main__":main()

實驗結果與分析

讓我們通過一個實際例子來看看Grad-CAM的效果。假設我們使用預訓練的ResNet50模型分析一張貓的圖片:

運行結果示例

==========================================
Grad-CAM 實現演示
==========================================[STEP 1] 加載預訓練ResNet50模型...
[DEBUG] Model loaded successfully
[DEBUG] Model type: <class 'torchvision.models.resnet.ResNet'>[STEP 2] 創建Grad-CAM對象...
[DEBUG] Found target layer: layer4.2.conv3
[DEBUG] Layer type: <class 'torch.nn.modules.conv.Conv2d'>[STEP 3] 加載圖像...
[DEBUG] Original image size: (224, 224)
[DEBUG] Preprocessed tensor shape: torch.Size([1, 3, 224, 224])[STEP 4] 生成Grad-CAM...
[DEBUG] Input tensor shape: torch.Size([1, 3, 224, 224])
[DEBUG] Forward hook triggered
[DEBUG] Activation shape: torch.Size([1, 2048, 7, 7])
[DEBUG] Model output shape: torch.Size([1, 1000])
[DEBUG] Top 3 predictions: tensor([281, 285, 282])
[DEBUG] Target class index: 281
[DEBUG] Target class score: 8.5420
[DEBUG] Backward hook triggered
[DEBUG] Gradient shape: torch.Size([1, 2048, 7, 7])
[DEBUG] Weights shape: torch.Size([1, 2048, 1, 1])
[DEBUG] Raw CAM shape: torch.Size([7, 7])
[DEBUG] CAM min/max: -2.1543/5.6789
[DEBUG] Normalized CAM min/max: 0.0000/1.0000[STEP 5] 分析預測結果...
Top 5 predictions:1. tabby cat: 0.8234 (82.34%)2. Egyptian cat: 0.1205 (12.05%)3. tiger cat: 0.0456 (4.56%)4. Persian cat: 0.0089 (0.89%)5. lynx: 0.0016 (0.16%)[STEP 6] 可視化結果...
[DEBUG] Original image shape: (224, 224, 3)
[DEBUG] Resized CAM shape: (224, 224)[STEP 7] CAM統計分析...
CAM statistics:Shape: (224, 224)Min value: 0.0000Max value: 1.0000Mean value: 0.3456Std value: 0.2871Highest activation at: (112, 98)Highest activation value: 1.0000

結果解讀

從調試輸出中,我們可以觀察到:

  1. 特征圖維度:最后一層卷積的特征圖尺寸為[1, 2048, 7, 7],包含2048個特征通道
  2. 預測結果:模型以82.34%的置信度預測為"tabby cat"
  3. 權重分布:通過梯度計算得到的權重顯示了不同特征通道的重要性
  4. 熱圖分析:最高激活點位于(112, 98),可能對應貓的關鍵特征區域

技術細節與最佳實踐

選擇合適的目標層

選擇目標卷積層是Grad-CAM應用中的關鍵決策:

  • 太淺的層:特征過于局部,熱圖可能過于細碎
  • 太深的層:特征過于抽象,熱圖可能過于粗糙
  • 推薦選擇:最后一個卷積層通常效果最好

處理不同的網絡架構

# 不同網絡的推薦目標層
target_layers = {'resnet50': 'layer4.2.conv3','vgg16': 'features.29','densenet121': 'features.denseblock4.denselayer16.conv2','mobilenet_v2': 'features.18.0'
}

性能優化

對于大規模應用,可以考慮以下優化:

  1. 批量處理:同時處理多張圖像
  2. GPU加速:確保計算在GPU上進行
  3. 內存管理:及時清理不需要的梯度信息

應用場景與局限性

主要應用

  1. 醫學影像分析:幫助醫生理解AI診斷的依據
  2. 自動駕駛:可視化模型對道路場景的理解
  3. 工業質檢:解釋缺陷檢測模型的決策過程
  4. 研究調試:幫助研究者理解和改進模型

局限性

  1. 依賴架構:僅適用于CNN,不能直接用于Transformer等架構
  2. 分辨率限制:熱圖分辨率受目標卷積層特征圖尺寸限制
  3. 類別偏見:對于多目標圖像,可能只突出主要目標
  4. 解釋性假設:假設梯度大小直接對應重要性,這在某些情況下可能不成立

擴展與變種

Grad-CAM++

Grad-CAM++通過更精細的權重計算改進了原始方法:

# Grad-CAM++的權重計算
alpha = gradients.pow(2) / (2 * gradients.pow(2) + activations.sum(dim=(2,3), keepdim=True) * gradients.pow(3))
weights = (alpha * gradients).sum(dim=(2,3), keepdim=True)

Layer-CAM

Layer-CAM結合了多個層的信息,提供更全面的可視化。

與其他可解釋性方法的比較

方法優點缺點適用場景
Grad-CAM簡單高效,模型無關分辨率受限快速可視化
LIME直觀易懂計算復雜詳細分析
SHAP理論基礎強計算昂貴精確歸因
注意力機制模型內置需要特殊架構端到端可解釋

結論

Grad-CAM作為一種強大的可視化工具,為理解深度神經網絡的決策過程提供了直觀的方法。通過巧妙地利用梯度信息,它能夠揭示模型在做出預測時最關注的圖像區域。

關鍵要點:

  1. 梯度的雙重含義:在訓練時用于優化,在Grad-CAM中用于解釋
  2. 已訓練模型的梯度:反映特征對預測結果的貢獻程度
  3. 實用性強:可以應用于任何基于CNN的預訓練模型
  4. 局限性明確:需要根據具體應用場景選擇合適的方法

隨著深度學習在各個領域的廣泛應用,模型的可解釋性變得越來越重要。Grad-CAM及其變種為這個問題提供了實用的解決方案,幫助我們構建更加可信和可理解的AI系統。

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

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

相關文章

離線環境jenkins構建前端部署鏡像

gitlabjenkins 實現前端項目打包成 docker 鏡像&#xff1b;gitlab部署就不贅述了&#xff1b;因部署的gitlab版本的webhooks有問題&#xff0c;無法進行配置,所以文章的構建是手動觸發的。并且nodejs部署應該也能跟docker一樣直接安裝進jenkins的鏡像(但是多版本可能就有其他問…

案例:塔能科技×某市智能照明——從傳統亮化到智慧光生態的跨越

在城市發展的滾滾浪潮中&#xff0c;市政照明不僅是驅散黑夜的光明使者&#xff0c;更是衡量城市智能化水平的關鍵標尺。貴州某市的城市照明系統正經歷一場意義深遠的革新&#xff0c;塔能科技以創新科技為核心驅動力&#xff0c;為這座城市的夜間照明生態注入全新活力。通過智…

LeapMotion-HandPoseRecorder 腳本詳解

HandPoseRecorder 腳本詳解 這個腳本是一個用于在 Unity 中錄制和保存 Leap Motion 手部姿勢的工具。下面我將詳細解釋腳本的各個部分: 核心功能 該腳本的主要作用是: 從 Leap Motion 設備捕獲當前手部姿勢數據 將姿勢數據序列化為可重用的 ScriptableObject 在 Unity 項目…

【Guava】0.做自己的編程語言

【Guava】0.做自己的編程語言 0.前言1.明確你的目標1.2.設計1.3.寫一個介紹 2.開始吧&#xff01; 0.前言 DO WHAT THE F**K YOU WANT TO DO 我相信&#xff0c;網上有許多各式各樣的做自己的編程語言教程&#xff0c;but 都是這樣 收費 shit 本教程教你真正教你實現一個名叫G…

【軟考高級系統架構論文】論無服務器架構及其應用

論文真題 近年來&#xff0c;隨著信息技術的迅猛發展和 應用需求的快速更迭&#xff0c;傳統的多層企業應用系統架構面臨越來越多的挑戰&#xff0c;已經難以適應這種變化。在這一背景下&#xff0c;無服務器架構(Serverless Architecture) 逐漸流行&#xff0c;它強調業務邏輯…

國產MCU A\B SWAP原理及實操

看到有讀者留言說還是沒理清A\B SWAP的原理。 今天就以某國產MCU為例&#xff0c;實際演示一番&#xff0c;看看大家在芯片設計時思路是什么。 我們首先回顧下SWAP的基本思想。 SWAP的基本思想是將PFLASH分成兩組Bank&#xff0c;Bank A(假設是active)和Bank B(假設是inacti…

目標檢測neck經典算法之FPN的源碼實現

┌────────────────────────────────────────────────────┐│ 初始化構造 (__init__) │└─────────────────────────────────────────────…

extern關鍵字:C/C++跨文件編程利器

在 C 和 C 中&#xff0c;extern 是一個關鍵字&#xff0c;用于聲明變量或函數是在其他文件中定義的。它主要用于實現多個源文件之間的符號共享。 目錄 &#x1f4cc; 一、C語言中的 extern 1. 基本作用 2. 示例說明 定義全局變量&#xff08;只在一個 .c 文件中&#xff…

編程語言的演化與選擇:技術浪潮中的理性決策

&#x1f4dd;個人主頁&#x1f339;&#xff1a;一ge科研小菜雞-CSDN博客 &#x1f339;&#x1f339;期待您的關注 &#x1f339;&#x1f339; 一、引言&#xff1a;為什么“選對語言”比“掌握語言”更重要&#xff1f; 在軟件開發的世界里&#xff0c;語言是一切的基礎。…

【StarRocks系列】StarRocks vs Mysql

目錄 StarRocks 簡介 核心特性 典型應用場景 StarRocks vs MySQL&#xff1a;核心區別詳解 關鍵差異總結 如何選擇&#xff1f; StarRocks 簡介 StarRocks 是一款高性能、全場景、分布式、實時分析型的數據庫&#xff08;MPP - 大規模并行處理&#xff09;。它誕生于解決…

Axios 知識點全面總結

文章目錄 Axios 知識點全面總結一、Axios 基礎概念1. 什么是 Axios&#xff1f;2. 核心特性 二、安裝與基本用法1. 安裝2. 基本請求示例 三、請求方法與參數四、請求配置選項&#xff08;config&#xff09;五、攔截器&#xff08;Interceptors&#xff09;六、錯誤處理七、取消…

【軟考高級系統架構論文】論 SOA 在企業集成架構設計中的應用

論文真題 企業應用集成(Enterprise Application Integration, EAI)是每個企業都必須要面對的實際問題。面向服務的企業應用集成是一種基于面向服務體系結構(Service - Oriented Architecture, SOA)的新型企業應用集成技術,強調將企業和組織內部的資源和業務功能暴露為服務,實…

springboot 提供的可擴展接口

一、spring 和 springboot Spring框架提供了全面的基礎架構支持。包含依賴注入和開箱即用等模塊&#xff0c;如&#xff1a;Spring JDBC 、Spring MVC 、Spring Security、 Spring AOP 、Spring ORM 、Spring Test Spring Boot 約定大于配置-----消除了設置Spring應用程序所需…

python學習打卡day55

DAY 55 序列預測任務介紹 知識點回顧 序列預測介紹 單步預測多步預測的2種方式 序列數據的處理&#xff1a;滑動窗口多輸入多輸出任務的思路經典機器學習在序列任務上的劣勢&#xff1b;以隨機森林為例 作業&#xff1a;手動構造類似的數據集&#xff08;如cosx數據&#xff09…

Leetcode hot100 Java刷題

文章目錄 快排146. LRU 緩存acm模式樹的前中后序遍歷acm模式鏈表的基本操作1. 兩數之和49. 字母異位詞分組128. 最長連續序列283. 移動零11. 盛最多水的容器15. 三數之和42. 接雨水53. 最大子數組和56. 合并區間73. 矩陣置零48. 旋轉圖像141. 環形鏈表142. 環形鏈表 II24. 兩兩…

Linux 命令詳解 —— 進程管理

文章目錄 精通Linux操作系統(以Centos7為例)進程管理ps常用組合進程狀態 STAT 詳解高級篩選與格式化輸出按條件過濾進程自定義輸出字段顯示進程樹關系排障場景定位高 CPU檢查僵尸進程查看進程的線程查看進程打開的文件/網絡連接常用組合速查top前5摘要區進程列表信息交互式命令…

【軟考高級系統架構論文】論湖倉一體架構及其應用

論文真題&#xff1a; 隨著5G、大數據、人工智能、物聯網等技術的不斷成熟&#xff0c;各行各業的業務場景日益復雜&#xff0c;企業數據呈現出大規模、多樣性的特點&#xff0c;特別是非結構化數據呈現出爆發式增長趨勢。在這一背景下&#xff0c;企業數據管理不再局限于傳統…

Docker 高級管理筆記

前言&#xff1a;Docker 高級管理概述 隨著 Docker 技術的廣泛應用&#xff0c;容器化已成為現代軟件開發與部署的核心方式。本筆記聚焦 Docker 高級管理中的兩大關鍵技術 —— 容器通信與數據持久化&#xff0c;深入解析 Docker 網絡模式、端口映射、容器互聯機制及數據卷管理…

Spring Boot 項目初始化

一、什么是 CommandLineRunner CommandLineRunner 是 Spring Boot 提供的一個 函數式接口&#xff0c;聲明如下&#xff1a; 該接口只有一個 run(String... args) 方法&#xff0c;會在 Spring Boot 容器啟動完成后被自動調用。 你可以將它理解為一種“鉤子函數”&#xff0c;…

C# winform教程(二)----ComboBox

一、作用 一個可以輸入也可以下拉的列表框。 二、屬性 一般我們都是使用下拉列表&#xff0c;不使用在線編輯&#xff08;本人沒用過&#xff09; 屬性 名稱內容含義items組合框中項可以定義下拉列表的值DropDownStyle外觀和功能是否可以填寫&#xff0c;一般選擇dropdownli…