【卷積神經網絡詳解與實例】8——經典CNN之VGG

1 開發背景

VGGNet是牛津大學視覺幾何組(Visual Geometry Group)提出的模型,該模型在2014ImageNet圖像分類與定位挑戰賽 ILSVRC-2014中取得在分類任務第二,定位任務第一的優異成績。其核心貢獻在于系統性地探索了網絡深度對性能的影響,并證明了通過堆疊非常小的卷積核(3x3)可以顯著提升網絡性能

論文地址:1409.1556] Very Deep Convolutional Networks for Large-Scale Image Recognitionhttps://arxiv.org/abs/1409.1556

論文詳解:(51 封私信 / 60 條消息) 經典神經網絡超詳細解讀(五)VGG網絡(論文精讀+網絡詳解+代碼實戰) - 知乎https://zhuanlan.zhihu.com/p/27715976701


2 網絡結構

2.1 VGG的核心思想

  1. 采用更深的網絡結構:相比 AlexNet(8 層),VGG 增加到了 16 或 19 層,提升了網絡的表達能力。

  2. 使用小卷積核(3×3)和多個連續的卷積層:所有卷積層都使用 3×3 卷積核,而不是較大的 5×5 或 7×7,這樣可以:

  • 每個卷積層后面通常跟著一個ReLU激活函數,多個卷積層可以增加非線性能力(兩個 3×3 卷積相當于一個 5×5 卷積);

  • 與使用大尺寸卷積核的單一層相比,多個小卷積層的堆疊可以提供更好的特征提取能力

  • 減少參數數量(比單個 5×5 卷積的參數更少)。

這里說明一下為什么兩個 3×3 卷積相當于一個 5×5 卷積,以及為什么參數數量變少了:

如上圖所示,如果我們使用一個 5×5 的卷積核對一個 5×5 的圖像進行卷積,最后可以得到一個 1×1 的輸出,而使用 3×3 的卷積核進行兩次連續的卷積,同樣可以得到 1×1 的輸出。同理,三個 3×3 的卷積相當于一個 7×7 的卷積。一個3×3卷積核在處理具有64個輸入通道的特征圖時,只有577(3×3×64+1=577,加的1是偏置項)個參數,而一個11x11的卷積核則有7745(11×11×64+1)個參數,7745>577×2。這種參數數量的顯著差異意味著使用小卷積核可以大幅減少網絡的參數量


2.2 VGG架構實驗

上圖是論文中六次實驗的結果圖。VGG網絡的卷積層全部為3*3的卷積核,用conv3-xxx來表示,xxx表示通道數。

  • 第一組(A)就是個簡單的卷積神經網絡,沒有啥花里胡哨的地方。

  • 第二組(A-LRN)在第一組的卷積神經網絡的基礎上加了LRN(LRN:局部響應歸一化,一種正則化手段,現在已不是主流了,LRN是Alexnet中提出的方法,在Alexnet中有不錯的表現 )

  • 第三組(B)在A的基礎上加了兩個conv3,即多加了兩個3*3卷積核

  • 第四組(C)在B的基礎上加了三個conv1,即多加了三個1*1卷積核

  • 第五組(D)在C的基礎上把三個conv1換成了三個3*3卷積核

  • 第六組(E)在D的基礎上又加了三個conv3,即多加了三個3*3卷積核

結論:

1.第一組和第二組進行對比,LRN在這里并沒有很好的表現;

2.第四組和第五組進行對比,conv3比conv1好使;

3.統籌看這六組實驗,會發現隨著網絡層數的加深,模型的表現會越來越好。

據此可簡單總結:作者一共實驗了6種網絡結構,其中VGG16(D)和VGG19(E)分類效果最好(16、19層隱藏層),證明了增加網絡深度能在一定程度上影響最終的性能。 兩者沒有本質的區別,只是網絡的深度不一樣。


2.3 VGG16架構

下面以VGG16為例做具體講解。

VGG16是最常用和最經典的VGG變體之一。數字“16”指的是網絡中包含權重參數的層數,即13個卷積層 + 3個全連接層 = 16層。池化層和激活層不計入此數。

具體參數如下:

階段層類型輸出尺寸 (C x H x W)參數配置 (核大小/步長/填充)輸出通道數備注
輸入Input Image3 x 224 x 224--假設輸入圖像尺寸為224x224
Block 1Conv3-6464 x 224 x 2243x3 / 1 / 164
Conv3-6464 x 224 x 2243x3 / 1 / 164
MaxPool64 x 112 x 1122x2 / 2-尺寸減半
Block 2Conv3-128128 x 112 x 1123x3 / 1 / 1128
Conv3-128128 x 112 x 1123x3 / 1 / 1128
MaxPool128 x 56 x 562x2 / 2-尺寸減半
Block 3Conv3-256256 x 56 x 563x3 / 1 / 1256
Conv3-256256 x 56 x 563x3 / 1 / 1256
Conv3-256256 x 56 x 563x3 / 1 / 1256
MaxPool256 x 28 x 282x2 / 2-尺寸減半
Block 4Conv3-512512 x 28 x 283x3 / 1 / 1512
Conv3-512512 x 28 x 283x3 / 1 / 1512
Conv3-512512 x 28 x 283x3 / 1 / 1512
MaxPool512 x 14 x 142x2 / 2-尺寸減半
Block 5Conv3-512512 x 14 x 143x3 / 1 / 1512
Conv3-512512 x 14 x 143x3 / 1 / 1512
Conv3-512512 x 14 x 143x3 / 1 / 1512
MaxPool512 x 7 x 72x2 / 2-尺寸減半
FCFlatten1 x 25088--將特征圖展平為一維向量
FC-40964096-4096全連接層1
FC-40964096-4096全連接層2
FC-10001000-1000輸出層 (Softmax)

關鍵點說明

  1. 卷積塊(Conv Block): 每個Block由連續的2-3個3x3卷積層組成,通道數在Block內保持不變(如Block1都是64通道),在Block之間通過池化層后翻倍(64->128->256->512->512)。Block5的通道數沒有繼續翻倍,保持512。

  2. 池化層(MaxPool): 位于每個卷積塊之后,負責降低空間維度(H, W減半),增加感受野,并一定程度上提供平移不變性。

  3. 全連接層(FC)

    • 在最后一個池化層(輸出尺寸為512 x 7 x 7)之后,將特征圖展平(Flat) 成一個長度為 512 * 7 * 7 = 25088 的一維向量。

    • 然后依次通過兩個4096維的全連接層(通常使用ReLU激活和Dropout防止過擬合)。

    • 最后是一個1000維的全連接層(對應ImageNet的1000類),使用Softmax函數輸出每個類別的預測概率。

  4. 激活函數: 所有卷積層和全連接層(除最后一層)之后都使用ReLU激活函數,引入非線性。

  5. 參數量: VGG16的總參數量約為138 million (1.38億)。其中,絕大部分參數(約90%)集中在最后的三個全連接層(FC-4096: ~102M, FC-4096: ~102M, FC-1000: ~4M)。卷積層的參數量相對較少(約27M)。


3 VGG的優缺點

3.1 核心優點

  1. 設計簡潔統一,易于理解與實現

    • 規則化結構:所有卷積層均使用 3×3核(除少量1×1卷積),所有池化層均使用 2×2最大池化,模塊化設計清晰。

    • 配置單一:卷積層參數固定(stride=1, padding=1),池化層參數固定(stride=2),無需復雜調參。

    • 代碼復用性高:相同結構的卷積塊可循環實現,降低工程復雜度。

  2. 深度網絡性能的強力證明

    • 深度提升效果顯著:通過對比VGG11(11層)到VGG19(19層),證明增加深度能持續提升精度(ImageNet Top-5錯誤率從10.4%降至7.3%)。

    • 小卷積核的優越性

      相同感受野的情況下,堆疊小卷積核相比使用大卷積核具有更多的激活函數、更豐富的特征,更強的辨別能力。卷積后都伴有激活函數,可使決策函數更加具有辨別能力;此外,3x3比7x7就足以捕獲細節特征的變化:3x3的9個格子,最中間的格子是一個感受野中心,可以捕獲上下左右以及斜對角的特征變化;3個3x3堆疊近似一個7x7,網絡深了兩層且多出了兩個非線性ReLU函數,網絡容量更大,對于不同類別的區分能力更強。

  3. 強大的特征提取能力

    • 分層特征學習

      • 淺層(Block1-2)學習邊緣、紋理等低級特征。

      • 中層(Block3-4)學習物體部件等中級特征。

      • 深層(Block5-FC)學習語義等高級特征。

    • 特征泛用性強:預訓練模型(尤其VGG16/19)的卷積層特征被廣泛用于遷移學習(如目標檢測、圖像分割)。


3.2 顯著缺點

  1. 參數量巨大,計算成本高昂

    • 參數分布

      層類型參數量(VGG16)占比
      卷積層≈14.7M10.6%
      全連接層≈123.6M89.4%
      總計≈138.3M100%
    • 計算開銷

      • 訓練需大量GPU資源(如VGG19訓練耗時約2-3周,單卡V100)。

      • 推理速度慢(224×224圖像在V100上約100ms,遠低于MobileNet)。

  2. 全連接層(FC)的致命缺陷

    • 參數冗余:FC層占90%參數,但貢獻有限(后續研究表明可用全局平均池化替代)。

    • 空間限制:FC層要求固定輸入尺寸(如224×224),需對圖像裁剪/縮放,可能丟失信息。

    • 過擬合風險:FC層參數多,在小型數據集上易過擬合(需強正則化如Dropout)。

  3. 內存與存儲瓶頸

    • 顯存占用高:訓練時需存儲大量中間特征圖(如Block5輸出512×7×7,需≈10MB顯存)。

    • 模型體積大:VGG16模型文件超500MB,不適合移動端/嵌入式部署。

  4. 用多個 3×3卷積代替大卷積,會增加網絡深度,深度過大可能帶來梯度消失或梯度爆炸問題。

    • 解決方案

      • 使用 Batch Normalization(BN) 進行歸一化。

      • 采用 ResNet 殘差連接,保證梯度有效傳播。

  5. 結構僵化,缺乏創新性

    • 無多尺度處理:未采用Inception的多分支結構或ResNet的殘差連接,特征融合能力弱。

    • 通道設計保守:通道數僅按2倍增長(64→128→256→512),未探索更高效的通道分配策略。

總結:VGG是深度學習史上的“功勛模型”,其簡潔設計和深度探索為后續發展鋪平道路。盡管因參數冗余、計算低效逐漸退出主流,但其特征提取能力設計思想仍具重要參考價值。在資源充足或需要強特征的任務中,VGG仍是可靠選擇;但對效率敏感的場景,需選擇更現代的輕量架構。


4 基于Pytorch實現

以下項目實現了 VGG 網絡,并在 CIFAR10 上驗證了該網絡的有效性,項目目錄如下:

VGG_CIFAR10/│├── model/ ? ? ? ? ? ? ? ? ?# 存放模型定義│ ? ├── __init__.py│ ? └── VGG.py ? ? ? ? ? ? ?# VGG模型定義│├── data/ ? ? ? ? ? ? ? ? ? # 存放數據集(CIFAR-10會自動下載)│├── utils/ ? ? ? ? ? ? ? ? ?# 工具函數│ ? ├── __init__.py│ ? ├── transforms.py ? ? ? # 數據預處理│ ? └── visualization.py ? ? # 可視化工具│├── train.py ? ? ? ? ? ? ? ?# 訓練腳本├── test.py ? ? ? ? ? ? ? ? # 測試腳本└── config.py ? ? ? ? ? ? ? # 配置文件

配置文件

?# config.pyimport torch?# 訓練參數class Config:# 數據參數data_dir = './data'batch_size = 128num_workers = 4?# 模型參數num_classes = 10model_name = 'vgg16'?# 訓練參數num_epochs = 50learning_rate = 0.01momentum = 0.9weight_decay = 5e-4lr_step_size = 30lr_gamma = 0.1?# 其他device = 'cuda' if torch.cuda.is_available() else 'cpu'save_path = './checkpoints/'log_interval = 100

模型定義

?
# VGG.pyimport torchimport torch.nn as nn??class VGG(nn.Module):def __init__(self, num_classes=10, init_weights=True):super(VGG, self).__init__()?# 特征提取部分(卷積層)self.features = nn.Sequential(# Block 1nn.Conv2d(3, 64, kernel_size=3, padding=1),nn.BatchNorm2d(64), ?# 添加批歸一化nn.ReLU(inplace=True),nn.Conv2d(64, 64, kernel_size=3, padding=1),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),?# Block 2nn.Conv2d(64, 128, kernel_size=3, padding=1),nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.Conv2d(128, 128, kernel_size=3, padding=1),nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),?# Block 3nn.Conv2d(128, 256, kernel_size=3, padding=1),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, padding=1),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, padding=1),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),?# Block 4nn.Conv2d(256, 512, kernel_size=3, padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),?# Block 5nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2))?# 分類器部分(全連接層)self.avgpool = nn.AdaptiveAvgPool2d((7, 7))self.classifier = nn.Sequential(nn.Linear(512 * 7 * 7, 4096),nn.ReLU(inplace=True),nn.Dropout(0.5), ?# 增加Dropout率nn.Linear(4096, 4096),nn.ReLU(inplace=True),nn.Dropout(0.5),nn.Linear(4096, num_classes))?if init_weights:self._initialize_weights()?def forward(self, x):x = self.features(x)x = self.avgpool(x)x = torch.flatten(x, 1)x = self.classifier(x)return x?def _initialize_weights(self):for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')if m.bias is not None:nn.init.constant_(m.bias, 0)elif isinstance(m, nn.BatchNorm2d):nn.init.constant_(m.weight, 1)nn.init.constant_(m.bias, 0)elif isinstance(m, nn.Linear):nn.init.normal_(m.weight, 0, 0.01)nn.init.constant_(m.bias, 0)??def vgg16(num_classes=10):return VGG(num_classes=num_classes)

工具函數

?# transforms.pyimport torchvision.transforms as transforms?def get_train_transform():return transforms.Compose([transforms.RandomCrop(32, padding=4),transforms.RandomHorizontalFlip(),transforms.RandomRotation(15), ?# 添加隨機旋轉transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2), ?# 顏色抖動transforms.ToTensor(),transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),])?def get_test_transform():return transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),])
?# visualization.pyimport matplotlib.pyplot as pltimport numpy as npimport torch??'''用于訓練可視化'''def plot_results(train_losses, train_accs, test_losses, test_accs):plt.figure(figsize=(12, 4))?# 繪制損失曲線plt.subplot(1, 2, 1)plt.plot(train_losses, label='Train Loss')plt.plot(test_losses, label='Test Loss')plt.xlabel('Epoch')plt.ylabel('Loss')plt.legend()plt.title('Training and Test Loss')?# 繪制準確率曲線plt.subplot(1, 2, 2)plt.plot(train_accs, label='Train Accuracy')plt.plot(test_accs, label='Test Accuracy')plt.xlabel('Epoch')plt.ylabel('Accuracy')plt.legend()plt.title('Training and Test Accuracy')?plt.tight_layout()plt.savefig('results.png')plt.show()??'''用于測試可視化'''def visualize_predictions(model, device, test_loader, classes, num_samples=8):# 獲取一批測試數據dataiter = iter(test_loader)images, labels = next(dataiter)?# 選擇前num_samples個樣本images = images[:num_samples]labels = labels[:num_samples]?# 將圖像移至設備images = images.to(device)?# 獲取模型預測outputs = model(images)_, predicted = torch.max(outputs, 1)?# 將預測和標簽移回CPUpredicted = predicted.cpu().numpy()labels = labels.numpy()?# 顯示圖像plt.figure(figsize=(12, 6))for i in range(num_samples):plt.subplot(2, 4, i + 1)# 反歸一化img = images[i].cpu().numpy().transpose(1, 2, 0)mean = np.array([0.4914, 0.4822, 0.4465])std = np.array([0.2023, 0.1994, 0.2010])img = std * img + meanimg = np.clip(img, 0, 1)plt.imshow(img)plt.title(f"True: {classes[labels[i]]}\nPred: {classes[predicted[i]]}")plt.axis('off')plt.tight_layout()plt.savefig('predictions.png')plt.show()

模型訓練

?# train.pyimport torchimport torch.optim as optimimport torch.nn.functional as Ffrom torch.utils.data import DataLoaderfrom torchvision import datasetsimport osimport timefrom tqdm import tqdm?from model.VGG import vgg16from utils.transforms import get_train_transform, get_test_transformfrom utils.visualization import plot_resultsfrom config import Config??def train(model, device, train_loader, optimizer, epoch, config):model.train()running_loss = 0.0correct = 0total = 0?progress_bar = tqdm(train_loader, desc=f'Epoch {epoch}/{config.num_epochs}')for batch_idx, (inputs, targets) in enumerate(progress_bar):inputs, targets = inputs.to(device), targets.to(device)?# 前向傳播outputs = model(inputs)loss = F.cross_entropy(outputs, targets)?# 反向傳播與優化optimizer.zero_grad()loss.backward()optimizer.step()?# 統計信息running_loss += loss.item()_, predicted = outputs.max(1)total += targets.size(0)correct += predicted.eq(targets).sum().item()?# 更新進度條progress_bar.set_postfix({'Loss': running_loss / (batch_idx + 1),'Acc': 100. * correct / total})?train_loss = running_loss / len(train_loader)train_acc = 100. * correct / totalreturn train_loss, train_acc??def test(model, device, test_loader):model.eval()test_loss = 0correct = 0total = 0?with torch.no_grad():for inputs, targets in test_loader:inputs, targets = inputs.to(device), targets.to(device)outputs = model(inputs)test_loss += F.cross_entropy(outputs, targets, reduction='sum').item()_, predicted = outputs.max(1)total += targets.size(0)correct += predicted.eq(targets).sum().item()?test_loss /= len(test_loader.dataset)test_acc = 100. * correct / totalreturn test_loss, test_acc??def main():config = Config()?# 創建保存目錄os.makedirs(config.save_path, exist_ok=True)?# 加載數據集train_set = datasets.CIFAR10(root=config.data_dir, train=True, download=True, transform=get_train_transform())test_set = datasets.CIFAR10(root=config.data_dir, train=False, download=True, transform=get_test_transform())?train_loader = DataLoader(train_set, batch_size=config.batch_size, shuffle=True, num_workers=config.num_workers)test_loader = DataLoader(test_set, batch_size=config.batch_size, shuffle=False, num_workers=config.num_workers)?# 創建模型model = vgg16(num_classes=config.num_classes).to(config.device)?# 打印模型結構print(model)?# 定義優化器和損失函數optimizer = optim.SGD(model.parameters(),lr=config.learning_rate,momentum=config.momentum,weight_decay=config.weight_decay)scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=config.lr_step_size, gamma=config.lr_gamma)?# 訓練記錄train_losses = []train_accs = []test_losses = []test_accs = []best_acc = 0.0?# 訓練循環for epoch in range(1, config.num_epochs + 1):start_time = time.time()?# 訓練train_loss, train_acc = train(model, config.device, train_loader, optimizer, epoch, config)?# 測試test_loss, test_acc = test(model, config.device, test_loader)?# 學習率調整scheduler.step()?# 記錄train_losses.append(train_loss)train_accs.append(train_acc)test_losses.append(test_loss)test_accs.append(test_acc)?# 保存最佳模型if test_acc > best_acc:best_acc = test_acctorch.save(model.state_dict(), os.path.join(config.save_path, 'best_model.pth'))?# 打印信息elapsed_time = time.time() - start_timeprint(f'Epoch {epoch}/{config.num_epochs} - Time: {elapsed_time:.2f}s')print(f'Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}%')print(f'Test Loss: {test_loss:.4f}, Test Acc: {test_acc:.2f}%')print(f'Best Test Acc: {best_acc:.2f}%\n')?# 繪制結果plot_results(train_losses, train_accs, test_losses, test_accs)?print(f'Training completed. Best test accuracy: {best_acc:.2f}%')??if __name__ == '__main__':main()

模型測試

# test.py?
import torchfrom torch.utils.data import DataLoaderfrom torchvision import datasetsimport os?from model.VGG import vgg16from utils.transforms import get_test_transformfrom utils.visualization import visualize_predictionsfrom config import Config??def main():config = Config()?# 加載測試數據集test_set = datasets.CIFAR10(root=config.data_dir, train=False, download=True, transform=get_test_transform())test_loader = DataLoader(test_set, batch_size=config.batch_size, shuffle=False, num_workers=config.num_workers)?# 加載模型model = vgg16(num_classes=config.num_classes).to(config.device)model.load_state_dict(torch.load(os.path.join(config.save_path, 'best_model.pth')))model.eval()?# 類別標簽classes = ('plane', 'car', 'bird', 'cat', 'deer','dog', 'frog', 'horse', 'ship', 'truck')?# 可視化預測結果visualize_predictions(model, config.device, test_loader, classes)??if __name__ == '__main__':main()

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

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

相關文章

【分享】中小學教材課本 PDF 資源獲取指南

很多人都不知道,其實官方提供的中小學教材課本 PDF 文檔是完全免費且正版的,無需使用掃描版,清晰度和質量都非常高。 這些資源就藏在國家中小學智慧教育平臺(basic.smartedu.cn)上。這個平臺涵蓋了從小學到高中的各個…

js趣味游戲 貪吃蛇

以下是關于JavaScript趣味游戲的系統性整理,涵蓋經典案例、開發工具、教程資源及創意方向,助您快速掌握JS游戲開發的核心邏輯:一、經典JS趣味游戲案例貪吃蛇(Snake Game)核心機制:鍵盤控制蛇的移動方向&…

【Redis#11】Redis 在 C++ 客戶端下的安裝使用流程(一條龍服務)

一、安裝使用 --Ubuntu 下啟用 1. 前置依賴 - hiredis hiredis 是一個用 C 語言實現的 Redis 客戶端庫,redis-plus-plus 庫基于 hiredis 實現。在開始之前,請確保已安裝 libhiredis-dev,可以通過以下命令安裝: sudo apt install l…

kibana+elasticsearch console查詢示例

kibana console查詢入口如下 http://localhost:5601/app/dev_tools#/console/shell 1 整體查詢 獲取index為newbook的所有數據 GET newbook/_search 2 通用查詢 獲取index為newbook的數據中,bookname包含“西游”的所有數據。 GET newbook/_search { "query&q…

軟考系統架構設計師之軟件風格篇

一、軟件架構風格-數據流風格 數據-》第1步處理-》數據-》第2步處理-》數據-》第N步處理 【分步處理】 優點: 1、松耦合【高內聚-低耦合】 2、良好的重用性/可維護性; 3、可擴展性【標準接口適配】 4、良好的隱蔽性; 5、支持并行。 缺點 1、交互性較差; 2、復雜性較…

初始QML

由于項目原因,最近要進行qml相關開發,我之前也沒有搞過qml,因此開一個qml系列的專欄,記錄自己關于qml的相關學習新建第一個qml工程按如下圖所示方法新建一個最簡單的qml工程:編譯運行可以看到是一個標題為“hello word…

Coze源碼分析-資源庫-創建知識庫-基礎設施/存儲/安全

6. 基礎設施層 基礎設施層為知識庫創建功能提供底層技術支撐,包括數據存儲、緩存、消息隊列、文檔處理、向量化等核心服務。 6.1 數據存儲服務 6.1.1 MySQL數據庫 文件位置: backend/infra/rdb/mysql.go // MySQLConfig MySQL配置 type MySQLConfig struct {Host …

【iOS】設計模式復習

目錄 觀察者模式 通知機制 基本使用 注冊觀察者 創建一個通知 發送通知 通知與多線程 使用異步發送通知 NSNotificationQueue通知隊列 在子線程中運行觀察者函數 實現原理 named表 nameless表 wildcard表 添加觀察者 發送通知 移除通知? KVO機制 基本使用 …

RK3568 NPU :RKNN-ToolKit2環境搭建

1. 安裝Miniconda3 下載 Linux 64 位 Miniconda 最新版安裝腳本 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh安裝 Miniconda bash Miniconda3-latest-Linux-x86_64.sh -u安裝完成后會自動設置環境變量。打開新的終端,發現用戶名前…

Ubuntu 24.04 Server 版系統安裝及配置

Ubuntu 24.04 Server 版安裝及配置 文章目錄Ubuntu 24.04 Server 版安裝及配置一、獲取安裝文件二、虛擬機配置三、安裝界面四、配置網絡五、擴容根分區 一、獲取安裝文件二、虛擬機配置三、安裝界面 選擇English(US)問是否升級內核配置鍵盤手動配置ipv4…

Java 事務失效場景全解析

在 Java 開發中,事務管理是保證數據一致性的核心機制,尤其是在 Spring 框架中,Transactional注解的使用極大簡化了事務配置。然而,在實際開發中,事務常常會因為一些細節問題而失效,導致數據異常。本文將詳細…

【Coze搞錢實戰】14. 抖音直播間自動回復機器人實戰教程:三小時搭建智能客服,互動率提升150%(保姆級無代碼指南)

摘要:抖音直播間高頻問題重復回復、觀眾互動不及時是運營痛點。本文針對新手和進階用戶,提供無代碼的自動回復機器人搭建方案:新手1小時完成基礎配置(Coze+抖音對接),進階用戶通過促銷倒計時、粉絲分層、熱點借勢三大策略提升互動率150%。方案基于某女裝直播間實測數據(…

云計算核心知識梳理

云計算作為新一代信息技術的核心,其體系涵蓋特點、定義、技術演進及分類等多個維度,以下是對相關知識的系統整合與解讀。 一、云計算的核心特點 / 優勢 云計算的優勢圍繞資源利用效率、服務靈活性和管理便捷性展開,具體可拆解為五大核心特性: 按需自助服務:用戶無需人工干…

安卓13_ROM修改定制化-----安卓 13 系統 ROM 定制化與低版本系統的核心區別

安卓系統憑借其全球領先的市場占有率,開放特性為廠商和開發者提供了深度定制的空間,形成了豐富的ROM生態圈。從最初的安卓1.0到最新的安卓15,系統在功能、性能和安全方面不斷迭代升級,同時也為ROM定制帶來了新的機遇與挑戰。特別是從安卓11開始,谷歌對系統架構和安全機制進…

【Java后端】Spring Boot 2.7.x 和 Swagger 3.0.x (springfox 3.x) 的兼容性問題

springfox 在 Spring Boot 2.6 開始就有很多兼容性 bug(主要是 Spring MVC PathPatternParser 的引入),導致在 Spring Boot 2.6/2.7 里經常出現 無法啟動 / 無法訪問 swagger-ui.html 的情況。🔎 問題原因Spring Boot 2.6 開始默認…

Vue3+ts使用oidc-client-ts

配置 OIDC 客戶端 在項目中創建 authOptions 對象,定義 OIDC 認證所需的配置項: export const authOptions {authority: https://xxxxxxxxx/UserCenter, // 認證服務器 URLclient_id: xxxx, // 客戶端 IDredirect_uri: http://localhost:3000/callbac…

從 “數據中轉站“ 到 “邊緣智能中樞“:區域網關的技術突圍與開發范式重構

在物聯網架構中,區域網關長期被視為 "邊緣與云端的橋梁"—— 負責協議轉換、數據轉發、設備接入等基礎功能。但隨著邊緣計算興起與 AI 模型輕量化,區域網關正經歷從 "被動轉發" 到 "主動決策" 的范式躍遷。 本文將從開發視角拆解區域網關的三大…

Django全棧班v1.04 Python基礎語法 20250913 早上

print 函數基本用法 print函數會自加換行符,一個print,會打印一行輸出。 print("第一行") print("第二行") print("第三行")輸出結果:print 輸出多個值 一個print可以同時輸出多個值,這多個值會在一…

面試鴨Java八股之Kafka

Kafka是什么?它的主要應用場景有哪些? Kafka是一種分布式流事件處理平臺,最初由 LinkedIn 開發,現在是 Apache 基金會的一部分。它的核心功能主要包括消息隊列、流處理和數據集成。Kafka以高吞吐量、低延遲、可擴展和高容錯性著稱。 Kafka…

ARM32平臺Bus Error深度排查:從調用棧到硬件原理的完整拆解

ARM32平臺Bus Error深度排查:從調用棧到硬件原理的完整拆解 在嵌入式開發中,Bus Error(信號7)是個容易讓人頭疼的問題——它不像SIGSEGV(段錯誤)那樣直觀,常與硬件內存布局、指針破壞等底層問題…