Python60日基礎學習打卡Day45

之前的神經網絡訓練中,為了幫助理解借用了很多的組件,比如訓練進度條、可視化的loss下降曲線、權重分布圖,運行結束后還可以查看單張圖的推理效果。

如果現在有一個交互工具可以很簡單的通過按鈕完成這些輔助功能那就好了,他就是可視化工具tensorboard

tensorboard的基本操作

1.1 發展歷史

TensorBoard 是 TensorFlow 生態中的官方可視化工具,用于實時監控訓練過程、可視化模型結構、分析數據分布、對比實驗結果等。它通過網頁端交互界面,將枯燥的訓練日志轉化為直觀的圖表和圖像,幫助開發者快速定位問題、優化模型,就像給機器學習模型訓練過程裝了一個「監控屏幕」。你可以用它直觀看到訓練過程中的數據變化(比如損失值、準確率)、模型結構、數據分布等,不用盯著一堆枯燥的數字看,對新手非常友好。2019 年后與 PyTorch 兼容,變得更通用了。功能進一步豐富,比如支持3D 可視化、模型參數調試等。我們目前只需要要用到最經典的幾個功能即可

  1. 保存模型結構
  2. 保存訓練集和驗證集的loss變化曲線,不需要手動打印了
  3. 保存每一個層結構權重分布
  4. 保存預測圖片的預測信息

1.2 tensorboard的原理

TensorBoard 的核心原理就是在訓練過程中,把訓練過程中的數據(比如損失、準確率、圖片等)先記錄到日志文件里,再通過工具把這些日志文件可視化成圖表,這樣就不用自己手動打印數據或者用其他工具畫圖。

所以核心就是2個步驟:

  • 數據怎么存?—— 先寫日志文件

訓練模型時,TensorBoard 會讓程序把訓練數據(比如損失值、準確率)和模型結構等信息,寫入一個特殊的日志文件(.tfevents 文件)

  • 數據怎么看?—— 用網頁展示日志

寫完日志后,TensorBoard 會啟動一個本地網頁服務,自動讀取日志文件里的數據,用圖表、圖像、文本等形式展示出來。如果只用 print(損失值) 或者自己用 matplotlib 畫圖,不僅麻煩,還得手動保存數據、寫代碼。而 TensorBoard 能自動把這些數據 “存下來 + 畫出來”,還能生成網頁版的可視化界面,隨時刷新查看!

下面是tensorboard的核心代碼解析,無需運行 看懂大概在做什么即可

1.3 日志目錄自動管理

log_dir = 'runs/cifar10_mlp_experiment'
if os.path.exists(log_dir):i = 1while os.path.exists(f"{log_dir}_{i}"):i += 1log_dir = f"{log_dir}_{i}"  #一個編號有了就趕緊生成下一個
writer = SummaryWriter(log_dir) #關鍵入口,用于寫入數據到日志目錄

自動避免日志目錄重復。若 runs/cifar10_mlp_experiment 已存在,會生成 runs/cifar10_mlp_experiment_1、_2 等新目錄,確保每次訓練的日志獨立存儲。

方便對比不同訓練任務的結果(如不同超參數實驗)

1.4 記錄標量數據(Scalar)

# 記錄每個 Batch 的損失和準確率
writer.add_scalar('Train/Batch_Loss', batch_loss, global_step)
writer.add_scalar('Train/Batch_Accuracy', batch_acc, global_step)# 記錄每個 Epoch 的訓練指標
writer.add_scalar('Train/Epoch_Loss', epoch_train_loss, epoch)
writer.add_scalar('Train/Epoch_Accuracy', epoch_train_acc, epoch)

在 tensorboard的SCALARS 選項卡中查看曲線,支持多 run 對比?。

1.5 可視化模型結構(Graph)

dataiter = iter(train_loader)
images, labels = next(dataiter)
images = images.to(device)
writer.add_graph(model, images)  # 通過真實輸入樣本生成模型計算圖

TensorBoard 界面:在 GRAPHS 選項卡中查看模型層次結構(卷積層、全連接層等)。

1.6 可視化圖像(Image)

# 可視化原始訓練圖像
img_grid = torchvision.utils.make_grid(images[:8].cpu()) # 將多張圖像拼接成網格狀(方便可視化),將前8張圖像拼接成一個網格
writer.add_image('原始訓練圖像', img_grid)# 可視化錯誤預測樣本(訓練結束后)
wrong_img_grid = torchvision.utils.make_grid(wrong_images[:display_count])
writer.add_image('錯誤預測樣本', wrong_img_grid)

展示原始圖像、數據增強效果、錯誤預測樣本等。

1.7 記錄權重和梯度直方圖(Histogram)

if (batch_idx + 1) % 500 == 0:for name, param in model.named_parameters():writer.add_histogram(f'weights/{name}', param, global_step)  # 權重分布if param.grad is not None:writer.add_histogram(f'grads/{name}', param.grad, global_step)  # 梯度分布

在 HISTOGRAMS 選項卡中查看不同層的參數分布隨訓練的變化。監控模型參數(如權重 weights)和梯度(grads)的分布變化,診斷訓練問題(如梯度消失 / 爆炸)。

1.8 啟動tensorboard

運行代碼后,會在指定目錄(如 runs/cifar10_mlp_experiment_1)生成 .tfevents 文件,存儲所有 TensorBoard 數據。

在終端執行(需進入項目根目錄):

tensorboard --logdir=runs # 假設日志目錄在 runs/ 下

打開瀏覽器,輸入終端提示的 URL(通常為?http://localhost:6006)。

二、tensorboard實戰

2.1 cifar-10 MLP實戰?

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import numpy as np
import matplotlib.pyplot as plt
import os# 設置隨機種子以確保結果可復現
torch.manual_seed(42)
np.random.seed(42)# 1. 數據預處理
transform = transforms.Compose([transforms.ToTensor(),                # 轉換為張量transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # 標準化處理
])# 2. 加載CIFAR-10數據集
train_dataset = datasets.CIFAR10(root='./data',train=True,download=True,transform=transform
)test_dataset = datasets.CIFAR10(root='./data',train=False,transform=transform
)# 3. 創建數據加載器
batch_size = 64
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)# CIFAR-10的類別名稱
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')# 4. 定義MLP模型(適應CIFAR-10的輸入尺寸)
class MLP(nn.Module):def __init__(self):super(MLP, self).__init__()self.flatten = nn.Flatten()  # 將3x32x32的圖像展平為3072維向量self.layer1 = nn.Linear(3072, 512)  # 第一層:3072個輸入,512個神經元self.relu1 = nn.ReLU()self.dropout1 = nn.Dropout(0.2)  # 添加Dropout防止過擬合self.layer2 = nn.Linear(512, 256)  # 第二層:512個輸入,256個神經元self.relu2 = nn.ReLU()self.dropout2 = nn.Dropout(0.2)self.layer3 = nn.Linear(256, 10)  # 輸出層:10個類別def forward(self, x):# 第一步:將輸入圖像展平為一維向量x = self.flatten(x)  # 輸入尺寸: [batch_size, 3, 32, 32] → [batch_size, 3072]# 第一層全連接 + 激活 + Dropoutx = self.layer1(x)   # 線性變換: [batch_size, 3072] → [batch_size, 512]x = self.relu1(x)    # 應用ReLU激活函數x = self.dropout1(x) # 訓練時隨機丟棄部分神經元輸出# 第二層全連接 + 激活 + Dropoutx = self.layer2(x)   # 線性變換: [batch_size, 512] → [batch_size, 256]x = self.relu2(x)    # 應用ReLU激活函數x = self.dropout2(x) # 訓練時隨機丟棄部分神經元輸出# 第三層(輸出層)全連接x = self.layer3(x)   # 線性變換: [batch_size, 256] → [batch_size, 10]return x  # 返回未經過Softmax的logits# 檢查GPU是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 初始化模型
model = MLP()
model = model.to(device)  # 將模型移至GPU(如果可用)criterion = nn.CrossEntropyLoss()  # 交叉熵損失函數
optimizer = optim.Adam(model.parameters(), lr=0.001)  # Adam優化器# 創建TensorBoard的SummaryWriter,指定日志保存目錄
log_dir = 'runs/cifar10_mlp_experiment'
# 如果目錄已存在,添加后綴避免覆蓋
if os.path.exists(log_dir):i = 1while os.path.exists(f"{log_dir}_{i}"):i += 1log_dir = f"{log_dir}_{i}"
writer = SummaryWriter(log_dir)# 5. 訓練模型(使用TensorBoard記錄各種信息)
def train(model, train_loader, test_loader, criterion, optimizer, device, epochs, writer):model.train()  # 設置為訓練模式# 記錄訓練開始時間,用于計算訓練速度global_step = 0# 可視化模型結構dataiter = iter(train_loader)images, labels = next(dataiter)images = images.to(device)writer.add_graph(model, images)  # 添加模型圖# 可視化原始圖像樣本img_grid = torchvision.utils.make_grid(images[:8].cpu())writer.add_image('原始訓練圖像', img_grid)for epoch in range(epochs):running_loss = 0.0correct = 0total = 0for batch_idx, (data, target) in enumerate(train_loader):data, target = data.to(device), target.to(device)  # 移至GPUoptimizer.zero_grad()  # 梯度清零output = model(data)  # 前向傳播loss = criterion(output, target)  # 計算損失loss.backward()  # 反向傳播optimizer.step()  # 更新參數# 統計準確率和損失running_loss += loss.item()_, predicted = output.max(1)total += target.size(0)correct += predicted.eq(target).sum().item()# 每100個批次記錄一次信息到TensorBoardif (batch_idx + 1) % 100 == 0:batch_loss = loss.item()batch_acc = 100. * correct / total# 記錄標量數據(損失、準確率)writer.add_scalar('Train/Batch_Loss', batch_loss, global_step)writer.add_scalar('Train/Batch_Accuracy', batch_acc, global_step)# 記錄學習率writer.add_scalar('Train/Learning_Rate', optimizer.param_groups[0]['lr'], global_step)# 每500個批次記錄一次直方圖(權重和梯度)if (batch_idx + 1) % 500 == 0:for name, param in model.named_parameters():writer.add_histogram(f'weights/{name}', param, global_step)if param.grad is not None:writer.add_histogram(f'grads/{name}', param.grad, global_step)print(f'Epoch: {epoch+1}/{epochs} | Batch: {batch_idx+1}/{len(train_loader)} 'f'| 單Batch損失: {batch_loss:.4f} | 累計平均損失: {running_loss/(batch_idx+1):.4f}')global_step += 1# 計算當前epoch的平均訓練損失和準確率epoch_train_loss = running_loss / len(train_loader)epoch_train_acc = 100. * correct / total# 記錄每個epoch的訓練損失和準確率writer.add_scalar('Train/Epoch_Loss', epoch_train_loss, epoch)writer.add_scalar('Train/Epoch_Accuracy', epoch_train_acc, epoch)# 測試階段model.eval()  # 設置為評估模式test_loss = 0correct_test = 0total_test = 0# 用于存儲預測錯誤的樣本wrong_images = []wrong_labels = []wrong_preds = []with torch.no_grad():for data, target in test_loader:data, target = data.to(device), target.to(device)output = model(data)test_loss += criterion(output, target).item()_, predicted = output.max(1)total_test += target.size(0)correct_test += predicted.eq(target).sum().item()# 收集預測錯誤的樣本wrong_mask = (predicted != target).cpu()if wrong_mask.sum() > 0:wrong_batch_images = data[wrong_mask].cpu()wrong_batch_labels = target[wrong_mask].cpu()wrong_batch_preds = predicted[wrong_mask].cpu()wrong_images.extend(wrong_batch_images)wrong_labels.extend(wrong_batch_labels)wrong_preds.extend(wrong_batch_preds)epoch_test_loss = test_loss / len(test_loader)epoch_test_acc = 100. * correct_test / total_test# 記錄每個epoch的測試損失和準確率writer.add_scalar('Test/Loss', epoch_test_loss, epoch)writer.add_scalar('Test/Accuracy', epoch_test_acc, epoch)# 計算并記錄訓練速度(每秒處理的樣本數)# 這里簡化處理,假設每個epoch的時間相同samples_per_epoch = len(train_loader.dataset)# 實際應用中應該使用time.time()來計算真實時間print(f'Epoch {epoch+1}/{epochs} 完成 | 訓練準確率: {epoch_train_acc:.2f}% | 測試準確率: {epoch_test_acc:.2f}%')# 可視化預測錯誤的樣本(只在最后一個epoch進行)if epoch == epochs - 1 and len(wrong_images) > 0:# 最多顯示8個錯誤樣本display_count = min(8, len(wrong_images))wrong_img_grid = torchvision.utils.make_grid(wrong_images[:display_count])# 創建錯誤預測的標簽文本wrong_text = []for i in range(display_count):true_label = classes[wrong_labels[i]]pred_label = classes[wrong_preds[i]]wrong_text.append(f'True: {true_label}, Pred: {pred_label}')writer.add_image('錯誤預測樣本', wrong_img_grid)writer.add_text('錯誤預測標簽', '\n'.join(wrong_text), epoch)# 關閉TensorBoard寫入器writer.close()return epoch_test_acc  # 返回最終測試準確率# 6. 執行訓練和測試
epochs = 20  # 訓練輪次
print("開始訓練模型...")
print(f"TensorBoard日志保存在: {log_dir}")
print("訓練完成后,使用命令 `tensorboard --logdir=runs` 啟動TensorBoard查看可視化結果")final_accuracy = train(model, train_loader, test_loader, criterion, optimizer, device, epochs, writer)
print(f"訓練完成!最終測試準確率: {final_accuracy:.2f}%")

TensorBoard日志保存在: runs/cifar10_mlp_experiment_1 可以在命令行中進入目前的環境,然后通過tensorboard --logdir=xxxx(目錄)即可調出本地鏈接,點進去就是目前的訓練信息,可以不斷F5刷新來查看變化。

在TensorBoard界面中,你可以看到:

  • SCALARS 選項卡:展示損失曲線、準確率變化、學習率等標量數據----Scalar意思是標量,指只有大小、沒有方向的量。
  • IMAGES 選項卡:展示原始訓練圖像和錯誤預測的樣本
  • GRAPHS 選項卡:展示模型的計算圖結構
  • HISTOGRAMS 選項卡:展示模型參數和梯度的分布直方圖

2.2 cifar-10 CNN實戰

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import numpy as np
import matplotlib.pyplot as plt
import os# 設置隨機種子以確保結果可復現
torch.manual_seed(42)
np.random.seed(42)# 1. 數據預處理
transform = transforms.Compose([transforms.ToTensor(),                # 轉換為張量transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # 標準化處理
])# 2. 加載CIFAR-10數據集
train_dataset = datasets.CIFAR10(root='./data',train=True,download=True,transform=transform
)test_dataset = datasets.CIFAR10(root='./data',train=False,transform=transform
)# 3. 創建數據加載器
batch_size = 64
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)# CIFAR-10的類別名稱
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')# 4. 定義MLP模型(適應CIFAR-10的輸入尺寸)
class MLP(nn.Module):def __init__(self):super(MLP, self).__init__()self.flatten = nn.Flatten()  # 將3x32x32的圖像展平為3072維向量self.layer1 = nn.Linear(3072, 512)  # 第一層:3072個輸入,512個神經元self.relu1 = nn.ReLU()self.dropout1 = nn.Dropout(0.2)  # 添加Dropout防止過擬合self.layer2 = nn.Linear(512, 256)  # 第二層:512個輸入,256個神經元self.relu2 = nn.ReLU()self.dropout2 = nn.Dropout(0.2)self.layer3 = nn.Linear(256, 10)  # 輸出層:10個類別def forward(self, x):# 第一步:將輸入圖像展平為一維向量x = self.flatten(x)  # 輸入尺寸: [batch_size, 3, 32, 32] → [batch_size, 3072]# 第一層全連接 + 激活 + Dropoutx = self.layer1(x)   # 線性變換: [batch_size, 3072] → [batch_size, 512]x = self.relu1(x)    # 應用ReLU激活函數x = self.dropout1(x) # 訓練時隨機丟棄部分神經元輸出# 第二層全連接 + 激活 + Dropoutx = self.layer2(x)   # 線性變換: [batch_size, 512] → [batch_size, 256]x = self.relu2(x)    # 應用ReLU激活函數x = self.dropout2(x) # 訓練時隨機丟棄部分神經元輸出# 第三層(輸出層)全連接x = self.layer3(x)   # 線性變換: [batch_size, 256] → [batch_size, 10]return x  # 返回未經過Softmax的logits# 檢查GPU是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 初始化模型
model = MLP()
model = model.to(device)  # 將模型移至GPU(如果可用)criterion = nn.CrossEntropyLoss()  # 交叉熵損失函數
optimizer = optim.Adam(model.parameters(), lr=0.001)  # Adam優化器# 創建TensorBoard的SummaryWriter,指定日志保存目錄
log_dir = 'runs/cifar10_mlp_experiment'
# 如果目錄已存在,添加后綴避免覆蓋
if os.path.exists(log_dir):i = 1while os.path.exists(f"{log_dir}_{i}"):i += 1log_dir = f"{log_dir}_{i}"
writer = SummaryWriter(log_dir)# 5. 訓練模型(使用TensorBoard記錄各種信息)
def train(model, train_loader, test_loader, criterion, optimizer, device, epochs, writer):model.train()  # 設置為訓練模式# 記錄訓練開始時間,用于計算訓練速度global_step = 0# 可視化模型結構dataiter = iter(train_loader)images, labels = next(dataiter)images = images.to(device)writer.add_graph(model, images)  # 添加模型圖# 可視化原始圖像樣本img_grid = torchvision.utils.make_grid(images[:8].cpu())writer.add_image('原始訓練圖像', img_grid)for epoch in range(epochs):running_loss = 0.0correct = 0total = 0for batch_idx, (data, target) in enumerate(train_loader):data, target = data.to(device), target.to(device)  # 移至GPUoptimizer.zero_grad()  # 梯度清零output = model(data)  # 前向傳播loss = criterion(output, target)  # 計算損失loss.backward()  # 反向傳播optimizer.step()  # 更新參數# 統計準確率和損失running_loss += loss.item()_, predicted = output.max(1)total += target.size(0)correct += predicted.eq(target).sum().item()# 每100個批次記錄一次信息到TensorBoardif (batch_idx + 1) % 100 == 0:batch_loss = loss.item()batch_acc = 100. * correct / total# 記錄標量數據(損失、準確率)writer.add_scalar('Train/Batch_Loss', batch_loss, global_step)writer.add_scalar('Train/Batch_Accuracy', batch_acc, global_step)# 記錄學習率writer.add_scalar('Train/Learning_Rate', optimizer.param_groups[0]['lr'], global_step)# 每500個批次記錄一次直方圖(權重和梯度)if (batch_idx + 1) % 500 == 0:for name, param in model.named_parameters():writer.add_histogram(f'weights/{name}', param, global_step)if param.grad is not None:writer.add_histogram(f'grads/{name}', param.grad, global_step)print(f'Epoch: {epoch+1}/{epochs} | Batch: {batch_idx+1}/{len(train_loader)} 'f'| 單Batch損失: {batch_loss:.4f} | 累計平均損失: {running_loss/(batch_idx+1):.4f}')global_step += 1# 計算當前epoch的平均訓練損失和準確率epoch_train_loss = running_loss / len(train_loader)epoch_train_acc = 100. * correct / total# 記錄每個epoch的訓練損失和準確率writer.add_scalar('Train/Epoch_Loss', epoch_train_loss, epoch)writer.add_scalar('Train/Epoch_Accuracy', epoch_train_acc, epoch)# 測試階段model.eval()  # 設置為評估模式test_loss = 0correct_test = 0total_test = 0# 用于存儲預測錯誤的樣本wrong_images = []wrong_labels = []wrong_preds = []with torch.no_grad():for data, target in test_loader:data, target = data.to(device), target.to(device)output = model(data)test_loss += criterion(output, target).item()_, predicted = output.max(1)total_test += target.size(0)correct_test += predicted.eq(target).sum().item()# 收集預測錯誤的樣本wrong_mask = (predicted != target).cpu()if wrong_mask.sum() > 0:wrong_batch_images = data[wrong_mask].cpu()wrong_batch_labels = target[wrong_mask].cpu()wrong_batch_preds = predicted[wrong_mask].cpu()wrong_images.extend(wrong_batch_images)wrong_labels.extend(wrong_batch_labels)wrong_preds.extend(wrong_batch_preds)epoch_test_loss = test_loss / len(test_loader)epoch_test_acc = 100. * correct_test / total_test# 記錄每個epoch的測試損失和準確率writer.add_scalar('Test/Loss', epoch_test_loss, epoch)writer.add_scalar('Test/Accuracy', epoch_test_acc, epoch)# 計算并記錄訓練速度(每秒處理的樣本數)# 這里簡化處理,假設每個epoch的時間相同samples_per_epoch = len(train_loader.dataset)# 實際應用中應該使用time.time()來計算真實時間print(f'Epoch {epoch+1}/{epochs} 完成 | 訓練準確率: {epoch_train_acc:.2f}% | 測試準確率: {epoch_test_acc:.2f}%')# 可視化預測錯誤的樣本(只在最后一個epoch進行)if epoch == epochs - 1 and len(wrong_images) > 0:# 最多顯示8個錯誤樣本display_count = min(8, len(wrong_images))wrong_img_grid = torchvision.utils.make_grid(wrong_images[:display_count])# 創建錯誤預測的標簽文本wrong_text = []for i in range(display_count):true_label = classes[wrong_labels[i]]pred_label = classes[wrong_preds[i]]wrong_text.append(f'True: {true_label}, Pred: {pred_label}')writer.add_image('錯誤預測樣本', wrong_img_grid)writer.add_text('錯誤預測標簽', '\n'.join(wrong_text), epoch)# 關閉TensorBoard寫入器writer.close()return epoch_test_acc  # 返回最終測試準確率# 6. 執行訓練和測試
epochs = 20  # 訓練輪次
print("開始訓練模型...")
print(f"TensorBoard日志保存在: {log_dir}")
print("訓練完成后,使用命令 `tensorboard --logdir=runs` 啟動TensorBoard查看可視化結果")final_accuracy = train(model, train_loader, test_loader, criterion, optimizer, device, epochs, writer)
print(f"訓練完成!最終測試準確率: {final_accuracy:.2f}%")

由于已近搭載了tensorboard,上述代碼中一些之前可視化的冗余部分可以刪除了。

tensorboard的代碼還有有一定的記憶量,實際上深度學習的經典代碼都是類似于八股文,看多了就習慣了,難度遠遠小于考研數學等需要思考的內容

實際上對目前的ai而言,你只需要先完成最簡單的demo,然后讓他給你加上tensorboard需要打印的部分即可。---核心是弄懂tensorboard可以打印什么信息,以及如何看可視化后的結果,把ai當成記憶大師用到的時候通過它來調取對應的代碼即可。

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

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

相關文章

React項目的狀態管理:Redux Toolkit

目錄 1、搭建環境 2、Redux Toolkit 包含了什么 3、使用示例 (1)創建user切片 (2)合并切片得到store (3)配置store和使用store 使用js來編寫代碼,方便理解一些 1、搭建環境 首先&#xf…

父組件prop傳向子組件的值,被子組件直接v-model綁定 功能不生效

隱式修改組件屬性會導致功能異常 實際操作中發現,即便是父組件把簡單數據通過prop傳給了子組件,子組件再使用v-model綁定,也不行,響應式還是對異常 原vue2業務中存在組件定義某個類型為Object的屬性,然后將該屬性對象…

c#bitconverter操作,不同變量類型轉byte數組

緣起:串口數據傳輸的基礎是byte數組,write(buff,0,num)或者writeline(string),如果是字符串傳輸就是string變量就可以了,但是在modbus這類hex傳遞時,就要遇到轉換了,拼湊byte數組時需要各種變量的值傳遞,解…

【Redis】set 類型

set 一. set 類型介紹二. set 命令sadd、smembers、sismemberscard、spop、srandmembersmove、srem集合間操作交集:sinter、sinterstore并集:sunion、sunionstore差集:sdiff、sdiffstore 三. set 命令小結四. set 內部編碼方式五. set 使用場…

02-Redis常見命令

02-Redis常見命令 Redis數據結構介紹 Redis是一個key-value的數據庫,key一般是String類型,不過value的類型多種多樣: 貼心小建議:命令不要死記,學會查詢就好啦 Redis為了方便學習,將操作不同數據類型的命…

Rk3568驅動開發_GPIO點亮LED_12

需求: 用配置寄存器方式控制點燈非常原始,現在采用更方便的Linux提供的pctrl和gpio子系統編寫字符驅動 1.設備樹配置: 現將開發板中呼吸燈關閉掉防止占用到我需要使用的引腳 /* Narnat 2025-5-29 RK3568 GPIO 無需設置pinctrl*/gpioled{co…

阿里云ACP云計算備考筆記 (3)——云存儲RDS

目錄 第一章 云存儲概覽 1、云存儲通用知識 ① 發展歷史 ② 云存儲的優勢 2、云存儲分類 3、文件存儲業務場景 第二章 塊存儲 1、塊存儲分類 2、云盤的優勢 3、創建云盤 4、管理數據盤 ① 格式化數據盤 ② 掛載數據盤 ③ 通過 API 掛載云盤 5、管理系統盤 ① 更…

亞矩陣云手機實測體驗:穩定流暢背后的技術邏輯?

最近在測試一款云手機服務時,發現亞矩陣的表現出乎意料地穩定。作為一個經常需要多設備協作的開發者,我對云手機的性能、延遲和穩定性要求比較高。經過一段時間的體驗,分享一下真實感受,避免大家踩坑。 ??1. 云手機能解決什么問…

STM32H562----------ADC外設詳解

1、ADC 簡介 STM32H5xx 系列有 2 個 ADC,都可以獨立工作,其中 ADC1 和 ADC2 還可以組成雙模式(提高采樣率)。每個 ADC 最多可以有 20 個復用通道。這些 ADC 外設與 AHB 總線相連。 STM32H5xx 的 ADC 模塊主要有如下幾個特性: 1、可配置 12 位、10 位、8 位、6 位分辨率,…

【Android】雙指旋轉手勢

一,概述 本文參考android.view.ScaleGestureDetector,對雙指旋轉手勢做了一層封裝,采用了向量計算法簡單實現,筆者在此分享下。 二,實例 如下,使用RotateGestureDetector即可委托,實現旋轉手…

B站的視頻怎么下載下來——Best Video下載器

B站(嗶哩嗶哩)作為國內最受歡迎的視頻平臺之一,聚集了無數優質內容:動漫番劇、游戲實況、學習課程、紀錄片、Vlog、鬼畜剪輯……總有那么些視頻讓人想反復觀看、離線觀看,甚至剪輯創作。 但你是否遇到過這樣的煩惱&am…

基于SFC的windows系統損壞修復程序

前言 在平時使用Windows操作系統時會遇到很多因為系統文件損壞而出現的錯誤 例如:系統應用無法打開 系統窗口(例如開始菜單)無法使用 電腦藍屏或者卡死 是如果想要修復很多人只能想到重裝系統。但其實Windows有一個內置的系統文件檢查器可以修復此類錯誤。 原理 SFC命令…

智紳科技 —— 智慧養老 + 數字健康,構筑銀發時代安全防護網

在老齡化率突破 21.3% 的當下,智紳科技以 "科技適老" 為核心理念,構建 "監測 - 預警 - 干預 - 照護" 的智慧養老閉環。 其自主研發的七彩喜智慧康養平臺,通過物聯網、AI 和邊緣計算技術,實現對老年人健康與安…

用函數實現模塊化程序設計(適合考研、專升本)

函數 定義:本質上是一段可以被連續調用、功能相對獨立的程序段 c語言是通過“函數”實現模塊化的。根據分類標準不同函數分為以下幾類。 用戶角度:庫函數、自定義函數 函數形式:有參函數、無參函數 作用域:外部函數、內部函數 …

OpenCV 滑動條調整圖像亮度

一、知識點 1、int createTrackbar(const String & trackbarname, const String & winname, int * value, int count, TrackbarCallback onChange 0, void * userdata 0); (1)、創建一個滑動條并將其附在指定窗口上。 (2)、參數說明: trackbarname: 創建的…

vcs仿真產生fsdb波形的兩種方式

目錄 方法一: 使用verilog自帶的系統函數 方法二: 使用UCLI command 2.1 需要了解什么是vcs的ucli,怎么使用ucli? 2.2 使用ucli dump波形的方法 使用vcs仿真產生fsdb波形有兩種方式,本文參考《vcs user guide 20…

【前端】每日一道面試題6:解釋Promise.any和Promise.allSettled的使用場景及區別。

Promise.any() 和 Promise.allSettled() 是 JavaScript 中用于處理異步操作的兩種不同策略的 Promise 組合器,它們的核心區別在于邏輯目標與結果處理方式: 1. Promise.any() 使用場景: 需要獲取 首個成功結果(類似競速成功優先&…

數據鏈路層__

文章目錄 數據鏈路層基本概念(1)鏈路管理:面向連接的服務(2)幀同步:成幀1、字符計數法2、字符填充法(帶填充的首尾界符法)3、帶填充的首位標志法4、物理層編碼違例法 (3&…

coze智能體后端接入問題:

是否一定要按照coze官方API文檔格式調用? 不一定:以下面代碼為例(給了注釋) app.route(/compare_models, methods[POST]) def compare_models():print("收到 compare_models 請求!") #begin-這一部分代碼作用:從前端接…

如何輕松、安全地管理密碼(新手指南)

很多人會為所有賬戶使用相同、易記的密碼,而且常常多年不換。雖然這樣方便記憶,但安全性非常低。 您可能聽說過一些大型網站的信息泄露事件,同樣的風險也可能存在于您的WordPress網站中。如果有不法分子獲取了訪問權限,您的網站和…