吳恩達深度學習作業CNN之ResNet實現(Pytorch)

課程中認識許多CNN架構。首先是經典網絡:

  • LeNet-5
  • AlexNet
  • VGG

之后是近年來的一些網絡:

  • ResNet
  • Inception
  • MobileNet

經典網絡

LeNet-5

LeNet-5是用于手寫數字識別(識別0~9的阿拉伯數字)的網絡。它的結構如下:

網絡是輸入是一張[32, 32, 1]的灰度圖像,輸入經過4個卷積+池化層,再經過兩個全連接層,輸出一個0~9的數字。這個網絡和我們上周見過的網絡十分相似,數據體的寬和高在不斷變小,而通道數在不斷變多。

這篇工作是1998年發表的,當時的神經網絡架構和現在我們學的有不少區別:

  • 當時padding還沒有得到廣泛使用,數據體的分辨率會越降越小。
  • 當時主要使用平均池化,而現在最大池化更常見。
  • 網絡只輸出一個值,表示識別出來的數字。而現在的多分類任務一般會輸出10個值并使用softmax激活函數。
  • 當時激活函數只用sigmoid和tanh,沒有人用ReLU。
  • 當時的算力沒有現在這么強,原工作在計算每個通道卷積時使用了很多復雜的小技巧。而現在我們直接算就行了。

LeNet-5只有6萬個參數。隨著算力的增長,后來的網絡越來越大了。

AlexNet

AlexNet是2012年發表的有關圖像分類的CNN結構。它的輸入是[227, 227, 3]的圖像,輸出是一個1000類的分類結果。

原論文里寫的是輸入形狀是[224, 224, 3],但實際上這個分辨率是有問題的,按照這個分辨率是算不出后續結果的分辨率的。但現在一些框架對AlexNet的復現中,還是會令輸入的分辨率是224。這是因為框架在第一層卷積中加了一個padding的操作,強行讓后續數據的分辨率和原論文對上了。

AlexNet和LeNet-5在架構上十分接近。但是,AlexNet做出了以下改進:

  • AlexNet用了更多的參數,一共有約6000萬個參數。
  • 使用ReLU作為激活函數。

AlexNet還提出了其他一些創新,但與我們要學的知識沒有那么多關系:

  • 當時算力還是比較緊張,AlexNet用了雙GPU訓練。論文里寫了很多相關的工程細節。
  • 使用了Local Response Normalization這種歸一化層。現在幾乎沒人用這種歸一化。

VGG-16

VGG-16也是一個圖像分類網絡。VGG的出發點是:為了簡化網絡結構,只用3x3等長(same)卷積和2x2最大池化。

可以看出,VGG也是經過了一系列的卷積和池化層,最后使用全連接層和softmax輸出結果。順帶一提,VGG-16里的16表示有16個帶參數的層。

VGG非常龐大,有138M(1.38億)個參數。但是它簡潔的結構吸引了很多人的關注。

吳恩達老師鼓勵大家去讀一讀這三篇論文。可以先看AlexNet,再看VGG。LeNet有點難讀,可以放到最后去讀。

ResNets(基于殘差的網絡)

非常非常深的神經網絡是很難訓練的,這主要是由梯度爆炸/彌散問題導致的。在這一節中,我們要學一種叫做“跳連(skip connection)”的網絡模塊連接方式。使用跳連,我們能讓淺層模塊的輸出直接對接到深層模塊的輸入上,進而搭建基于殘差的網絡,解決梯度爆炸/彌散問題,訓練深達100層的網絡。

殘差塊

回憶一下,在全連接網絡中,假如我們有中間層的輸出,是怎么由算出來的呢?我們之前用的公式如下:

也就是說,a^{[l]}要經過一個線性層、一個激活函數、一個線性層、一個激活函數,才能傳遞到a^{[l+2]},這條路徑非常長:

而在殘差塊(Residual block)中,我們使用了一種新的連接方法:

a^{[l]}的值被直接加到了第二個ReLU層之前的線性輸出上,這是一種類似電路中短路的連接方法(又稱跳連)。這樣,淺層的信息能更好地傳到深層了。

使用這種方法后,計算公式變更為:

殘差塊中還有一個要注意的細節。a^{[l+2]}=g(z^{[l+2]}+a^{[l]})這個式子能夠成立,實際上是默認了a^{[l+2]},a^{[l]}的維度相同。而一旦a^{[l+2]}的維度發生了變化,就需要用下面這種方式來調整了。

a^{[l+2]}=g(z^{[l+2]}+W'a^{[l]})

我們可以用一個W'來完成維度的轉換。為了方便理解,我們先讓所有a都是一維向量,W'是矩陣。這樣,假設的長度是256,a^{[l]}的長度是128,則W'的形狀就是256*128。

但實際上,a是一個三維的圖像張量,三個維度的長度都可能發生變化。因此,對于圖像,上式中的W'應該表示的是一個卷積操作。通過卷積操作,我們能夠減小圖像的寬高,調整圖像的通道數,使得a^{[l]}a^{[l+2]}的維度完全相同。

殘差網絡

在構建殘差網絡ResNet時,只要把這種殘差塊一個一個拼接起來即可。或者從另一個角度來看,對于一個“平坦網絡”("plain network", ResNet論文中用的詞,用于表示非殘差網絡),我們只要把線性層兩兩打包,添加跳連即可。

殘差塊起到了什么作用呢?讓我們看看在網絡層數變多時,平坦網絡和殘差網絡訓練誤差的變化趨勢:

理論上來說,層數越深,訓練誤差應該越低。但在實際中,對平坦網絡增加深度,反而會讓誤差變高。而使用ResNet后,隨著深度增加,訓練誤差起碼不會降低了。

正是有這樣的特性,我們可以用ResNet架構去訓練非常深的網絡。

為什么ResNet是有這樣的特性呢?我們還是從剛剛那個ResNet的公式里找答案。

假設我們設計好了一個網絡,又給它新加了一個殘差塊,即多加了兩個卷積層,那么最后的輸出可以寫成:

a^{[l+2]}=g(z^{[l+2]}+a^{[l]})

即?a^{[l+2]}=g(W^{[l+2]}a^{[l+1]}+b^{[l+2]}+a^{[l]})

由于正則化的存在,所有W和b都傾向于變的更小,極端情況下,W,b都變為0。那么,

a^{[l+2]}=g(W^{[l+2]}a^{[l+1]}+b^{[l+2]}+a^{[l]})

a^{[l+2]}=g(a^{[l]})

再不妨設g=ReLU,則a^{[l]}也是ReLU的輸出,有:

a^{[l+2]}=g(a^{[l]})

a^{[l+2]}=a^{[l]}

這其實是一個恒等映射,也就是說,新加的殘差塊對之前的輸出沒有任何影響。網絡非常容易學習到恒等映射。這樣,最起碼能夠保證較深的網絡不比淺的網絡差。

準備好了所有基礎知識,我們來看看完整的ResNet長什么樣。

ResNet有幾個參數量不同的版本。這里展示的叫做ResNet-34。完整的網絡很長,我們只用關注其中一小部分就行了。

一開始,網絡還是用一個大卷積核大步幅的卷積以及一個池化操作快速降低圖像的寬度,再把數據傳入殘差塊中。和我們剛剛學的一樣,殘差塊有兩種,一種是維度相同可以直接相加的(實線),一種是要調整維度的(虛線)。整個網絡就是由這若干個這樣的殘差塊組構成。經過所有殘差塊后,還是和經典的網絡一樣,用全連接層輸出結果。

這里,我們只學習了殘差連接的基本原理。ResNet的論文里還有更多有關網絡結構、實驗的細節。最好能讀一讀論文。當然,這周的編程實戰里我們也會復現ResNet,以加深對其的理解。

Inception 網絡

有了之前的知識,我們可以看Inception模塊的完整結構了。1x1卷積沒有什么特別的。為了減少3x3卷積和5x5卷積的計算量,做這兩種卷積之前都會用1x1卷積減少通道數。而為了改變池化結果的通道數,池化后接了一個1x1卷積操作。

實際上,理解了Inception塊,也就能看懂Inception網絡了。如下圖所示,紅框內的模塊都是Inception塊。而這個網絡還有一些小細節:除了和普通網絡一樣在網絡的最后使用softmax輸出結果外,這個網絡還根據中間結果也輸出了幾個結果。當然,這些都是早期網絡的設計技巧了。

MobileNet

MobileNet,顧名思義,這是一種適用于移動(mobile)設備的神經網絡。移動設備的計算資源通常十分緊缺,因此,MobileNet對網絡的計算量進行了極致的壓縮。

再回顧一下:一次卷積操作中主要的計算量如下:

計算量這么大,主要問題出在每一個輸出通道都要與每一個輸入通道“全連接”上。為此,我們可以考慮讓輸出通道只由部分的輸入通道決定。這樣一種卷積的策略叫逐深度可分卷積(Depthwise Separable Convolution)。

這里的depthwise是“逐深度”的意思,但我覺得“逐通道”這個稱呼會更容易理解一點。?

逐深度可分卷積分為兩步:逐深度卷積(depthwise convolution),逐點卷積(pointwise convolution)。逐深度卷積生成新的通道,逐點卷積把各通道的信息關聯起來。

之前,要對下圖中的三通道圖片做卷積,需要3個卷積核分別處理3個通道。而在逐深度卷積中,我們只要1個卷積核。這個卷積核會把輸入圖像當成三個單通道圖像來看待,分別對原圖像的各個通道進行卷積,并生成3個單通道圖像,最后把3個單通道圖像拼回一個三通道圖像。也就是說,逐深度卷積只能生成一幅通道數相同的新圖像。

下一步,是逐點卷積,也就是1x1卷積。它用來改變圖片的通道數。

之前的卷積有2160次乘法,現在只有432+240=672次,計算量確實減少了不少。實際上,優化后計算量占原計算量的比例是:

其中n_{c}^{'}是輸出通道數,f是卷積核邊長。一般來說計算量都會少10倍。

網絡結構

知道了MobileNet的基本思想,我們來看幾個不同版本的MobileNet。

MobileNet v1

MobileNet v2

兩個改進:

  1. 殘差連接
  2. 擴張(expansion)操作

殘差連接和ResNet一樣。這里我們關注一下第二個改進。

在MobileNet v2中,先做一個擴張維度的1x1卷積,再做逐深度卷積,最后做之前的逐點1x1卷積。由于最后的逐點卷積起到的是減小維度的作用,所以最后一步操作也叫做投影。

這種架構很好地解決了性能和效果之間的矛盾:在模塊之間,數據的通道數只有3,占用內存少;在模塊之內,更高通道的數據能擬合更復雜的函數。

EfficientNet

EfficientNet能根據設備的計算能力,自動調整網絡占用的資源。

讓我們想想,哪些因素決定了一個網絡占用的運算資源?我們很快能想到下面這些因素:

  • 圖像分辨率
  • 網絡深度
  • 特征的長度(即卷積核數量或神經元數量)

在EfficientNet中,我們可以在這三個維度上縮放網絡,動態改變網絡的計算量。EfficientNet的開源實現中,一般會提供各設備下的最優參數。

卷積網絡實現細節

使用開源實現

由于深度學習項目涉及很多訓練上的細節,想復現一個前人的工作是很耗時的。最好的學習方法是找到別人的開源代碼,在現有代碼的基礎上學習。

使用遷移學習

如第三門課第二周所學,我們可以用遷移學習,導入別人訓練好的模型里的權重為初始權重,加速我們自己模型的訓練。

還是以多分類任務的遷移學習為例(比如把一個1000分類的分類器遷移到一個貓、狗、其他的三分類模型上)。遷移后,新的網絡至少要刪除輸出層,并按照新的多分類個數,重新初始化一個輸出層。之后,根據新任務的數據集大小,凍結網絡的部分參數,從導入的權重開始重新訓練網絡的其他部分:

當然,可以多刪除幾個較深的層,也可以多加入幾個除了輸出層以外的隱藏層。

數據增強

由于CV任務總是缺少數據,數據增強是一種常見的提升網絡性能的手段。

常見的改變形狀的數據增強手段有:

  • 鏡像
  • 裁剪
  • 旋轉
  • 扭曲

此外,還可以改變圖像的顏色。比如對三個顏色通道都隨機加一個偏移量。

數據增強有一些實現上的細節:數據的讀取及增強是放在CPU上運行的,訓練是放在CPU或GPU上運行的。這兩步其實是獨立的,可以并行完成。最常見的做法是,在CPU上用多進程(發揮多核的優勢)讀取數據并進行數據增強,之后把數據搬到GPU上訓練。

Pytorch實現ResNet

用到的pytorch基礎知識

1.?TensorDataset

  db = TensorDataset(x, y)
  • TensorDataset:PyTorch 中的工具類,將?x?和?y?包裝成一個數據集對象。

  • 作用:確保?x[i]?和?y[i]?一一對應(類似?(輸入, 標簽)?的配對)。

2.?DataLoader
    DataLoader(db, batch_size, shuffle=True)
  • DataLoader:PyTorch 的核心工具,用于按批次加載數據。

    • db:上一步創建的數據集。

    • batch_size:每個批次的大小(如32)。

    • shuffle=True:是否打亂數據順序(每個epoch重新隨機排序,防止模型記住數據順序)

所以完整代碼是:

  • import torch
    from torch.utils.data import TensorDataset, DataLoader# 假設有輸入數據 x 和標簽 y(假設是張量)
    x = torch.randn(100, 3, 224, 224)  # 100張224x224的RGB圖像
    y = torch.randint(0, 10, (100,))    # 100個標簽(0~9的整數)# 創建 TensorDataset 和 DataLoader
    dataset = TensorDataset(x, y)
    dataloader = DataLoader(dataset, batch_size=32, shuffle=True)# 使用示例
    for batch_x, batch_y in dataloader:print(batch_x.shape, batch_y.shape)  # (32,3,224,224) 和 (32,)
3. torch.max

????????torch.max(out, dim=1)[0]:獲取最大值(例如?[2.5, 3.2, 4.6])。

  • ?????torch.max(out, dim=1)[1]獲取最大值對應的索引(即預測的類別編號)。

  • predictions = torch.max(out, dim=1)[1]
    print(predictions)  # 輸出:tensor([1, 0, 2])

代碼實現:

此次作業的主要目的是使用殘差網絡實現深層卷積神經網絡完成分類問題。

1. ResNets介紹:
1.1 - 深度神經網絡的問題

我們知道深度神經網絡可以表達出更加復雜的非線性函數,這就可以實現從輸入中提取更多不同的特征。但是隨著網絡層數的加多,梯度消失(vanishing gradient)的效應將被放大,這將導致算法在反向傳播時從最后一層傳播到第一層的過程中,算法乘了每一層的權重矩陣,因此梯度會很快地下降到接近0(或者很快地增加到一個很大的值)。

具體來說,在訓練是你會看到前面層的梯度會非常迅速地降為零:

1.2 - 殘差網絡

殘差網絡可以很好解決深度神經網絡的上訴問題,主要就是使用跳躍連接(skip connection)讓梯度可以直接反向傳遞給前面的層(earlier layers):

殘差塊主要有兩種,根據輸入輸出的維度是否相同劃分為對等塊(identity block)和卷積塊(convolutional block)。

?1.2.1-對等塊

ResNets中的對等塊表示輸入激活值的維度?a^{[l]}和輸出激活值的維度a^{[l+1]}
?相同的情況。

圖中上部的路徑表示跳躍連接,下部的路徑表示主路徑。為了加速訓練過程,并在每一層添加了BatchNorm的步驟。

在本次試驗中你將實現一個更加有效的ResNets的對等塊,即進行跨越3個隱藏層的跳躍連接而非2個:

1.2.2 - 卷積塊

ResNets中的卷積塊表示輸入激活值的維度?a^{[l]}和輸出激活值的維度a^{[l+1]}不相同的情況。對于不相同的情況我們對跳躍連接的部分再次應用一個卷積層(CONV2D)以此達到輸入輸出維度相同的目的。

?

這個應用到跳躍連接的卷積層和視頻中所說的矩陣?W_{s}擁有相同的作用,不過注意這個卷積層不會應用任何的非線性函數,因為這個路徑的作用僅僅是更改輸入層a^{[l]}的維度以便和輸出層a^{[l+3]}
?的維度相匹配。

?1.2.3?- 模型架構

殘差塊用的卷積核為kernel_size=3.模型的conv3_1,conv4_1,conv5_1之前做了寬高減半的downsample.conv2_x是通過maxpool(stride=2)完成的下采樣.其余的是通過conv2d(stride=2)完成的.

2. 構建ResNets模型
2.1 構建殘差塊?
class Residual(nn.Module):def __init__(self, in_channels, out_channels,stride = 1)->None:super().__init__()self.conv1 = nn.Conv2d(in_channels, out_channels, stride = stride, kernel_size =3, padding =1)self.bn1 = nn.BatchNorm2d(out_channels)self.relu = nn.ReLU()self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size =3, padding =1)self.bn2 = nn.BatchNorm2d(out_channels)if in_channels != out_channels:self.conv1x1 = nn.Conv2d(in_channels, out_channels,kernel_size=1,stride=stride)else:self.conv1x1 = Nonedef forward(self, x):o1 = self.relu(self.bn1(self.conv1(x)))o2 = self.bn2(self.conv2(o1))#print("o2 shape",o2.shape)#print("x:",x.shape)if self.conv1x1:x = self.conv1x1(x)return self.relu(o2+x)
2.2 殘差網絡
class ResNet(nn.Module):def __init__(self, in_channels, num_classes) -> None:super().__init__()self.model = nn.Sequential(nn.Conv2d(in_channels=in_channels,out_channels=64,stride = 2,kernel_size=7,padding = 3),#其中 padding=3 是 kernel_size//2,確保 padding='same' 的效果。nn.BatchNorm2d(64),nn.ReLU(),nn.MaxPool2d(kernel_size=3,stride=2,padding=1),Residual(64,64),Residual(64,64),Residual(64,64),Residual(64,128,stride=2),Residual(128,128),Residual(128,128),Residual(128,128),Residual(128,256,stride=2),Residual(256,256),Residual(256,256),Residual(256,256),Residual(256,256),Residual(256,256),Residual(256,512,stride=2),Residual(512,512),Residual(512,512),nn.AdaptiveAvgPool2d(output_size=1)## 自適應平均池化,指定輸出(H,W))self.fc = nn.Linear(512, num_classes)self.softmax = nn.Softmax(dim=1)#print(self.model)def forward(self, x):out = self.model(x)#print("out.shape:",out.shape)out = out.reshape(x.shape[0], -1)#print("out.shape:",out.shape)self.fc(out)return outdef predict(self, x):out = self.forward(x)out = self.softmax(out)return torch.max(out, dim=1)[1]
3. 數據預處理
def load_dataset():train_dataset = h5py.File('datasets/train_signs.h5','r')test_dataset = h5py.File('datasets/test_signs.h5','r')# 直接從把h5數組轉化為tensor太慢,先轉成numpy再轉到tensor更快train_set_x = torch.from_numpy(np.array(train_dataset['train_set_x']))train_set_y = torch.from_numpy(np.array(train_dataset['train_set_y']))test_set_x = torch.from_numpy(np.array(test_dataset['test_set_x']))test_set_y = torch.from_numpy(np.array(test_dataset['test_set_y']))classes = torch.tensor(test_dataset['list_classes'])train_set_x = train_set_x.permute(0,3,1,2) /255test_set_x = test_set_x.permute(0,3,1,2) /255return train_set_x,train_set_y,test_set_x,test_set_y,classesdef data_loader(x, y, batch_size = 32):db = TensorDataset(x, y)return DataLoader(db, batch_size, shuffle=True)train_X,train_Y,test_X,test_Y,classes = load_dataset()
print(f'The num of train set:{train_X.shape[0]}')
print(f'The num of test set:{test_X.shape[0]}')
print(f'The shape of train set(x): {train_X.shape}')
print(f'The shape of train set(y): {train_Y.shape}')
print(f'The number of class: {classes.shape[0]}')
4. 訓練模型
def train(train_X: np.ndarray,train_Y: np.ndarray,num_classes:int,batch_size=32,num_epoch=5):in_channels = train_X.shape[1]net = ResNet(in_channels, num_classes)loss_fn = torch.nn.CrossEntropyLoss()train_loader = data_loader(train_X, train_Y, batch_size)optimizer = torch.optim.Adam(net.parameters(), 5e-4)for e in range(num_epoch):for step, (batch_x, batch_y) in enumerate(train_loader):output = net.forward(batch_x)loss = loss_fn(output, batch_y)optimizer.zero_grad()loss.backward()optimizer.step()print(f'Epoch {e}. loss: {loss}')return net
net = train(train_X, train_Y, classes.shape[0])train_pred = net.predict(train_X)
print(f'Train accuracy: {torch.sum(train_pred == train_Y)/train_Y.shape[0]*100:.2f}%')
test_pred = net.predict(test_X)
print(f'Test accuracy: {torch.sum(test_pred == test_Y) / test_Y.shape[0] * 100:.2f}%')

?

clear memory?

%%javascript
IPython.notebook.save_checkpoint();
if (confirm("Clear memory?") == true)
{IPython.notebook.kernel.restart();
}

?

參考了

吳恩達深度學習C4W2殘差網絡[Pytorch實現]_吳恩達 殘差網絡 練習 pytorch-CSDN博客
https://zhuanlan.zhihu.com/p/544917913

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

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

相關文章

FPGA入門學習Day1——設計一個DDS信號發生器

目錄 一、DDS簡介 (一)基本原理 (二)主要優勢 (三)與傳統技術的對比 二、FPGA存儲器 (一)ROM波形存儲器 (二)RAM隨機存取存儲器 (三&…

SqlSugar與Entity Framework (EF)的SWOT分析

以下是基于 SWOT 分析法 對 SqlSugar 和 Entity Framework (EF) 的特性對比: SqlSugar 優勢 (Strengths) 高性能: SqlSugar 以輕量化設計著稱,執行速度更快,適合對性能要求較高的場景。在大數據量操作和復雜查詢中表現優異。 易…

學習記錄:DAY16

Maven 進階與前端實戰 前言 二輪考核的內容下來了,由整體項目構建轉為實現特定模塊的功能。對細節的要求更高了,而且有手搓線程池、手搓依賴注入等進階要求,又有得學力。嘻嘻,太簡單了,只要我手搓 Spring Boot 框架……

深度學習--卷積神經網絡調整學習率

文章目錄 前言一、學習率1、什么學習率2、什么是調整學習率3、目的 二、調整方法1、有序調整1)有序調整StepLR(等間隔調整學習率)2)有序調整MultiStepLR(多間隔調整學習率)3)有序調整ExponentialLR (指數衰減調整學習率)4)有序調整…

【消息隊列RocketMQ】四、RocketMQ 存儲機制與性能優化

一、RocketMQ 存儲機制詳解 1.1 存儲文件結構? RocketMQ 的存儲文件主要分布在store目錄下,該目錄是在broker.conf配置文件中通過storePathRootDir參數指定的,默認路徑為${user.home}/store 。主要包含以下幾種關鍵文件類型:? 1.1.1 Comm…

C++入門小館: 探尋vector類

嘿,各位技術潮人!好久不見甚是想念。生活就像一場奇妙冒險,而編程就是那把超酷的萬能鑰匙。此刻,陽光灑在鍵盤上,靈感在指尖跳躍,讓我們拋開一切束縛,給平淡日子加點料,注入滿滿的pa…

CSS-跟隨圖片變化的背景色

CSS-跟隨圖片變化的背景色 獲取圖片的主要顏色并用于背景漸變需要安裝依賴 colorthief獲取圖片的主要顏色. 并丟給背景注意 getPalette并不是個異步方法 import styles from ./styles.less; import React, { useState } from react; import Colortheif from colorthief;cons…

RAGFlow:構建高效檢索增強生成流程的技術解析

引言 在當今信息爆炸的時代,如何從海量數據中快速準確地獲取所需信息并生成高質量內容已成為人工智能領域的重要挑戰。檢索增強生成(Retrieval-Augmented Generation, RAG)技術應運而生,它將信息檢索與大型語言模型(L…

SpringBoot應用:MyBatis的select語句如何返回數組類型

在SpringBoot應用中&#xff0c;比如想返回一個表的主鍵id構成的Long型數組Long[]&#xff0c;需要在XxxMapper.xml文件中這樣定義select語句&#xff1a; <select id"selectIds" parameterType"int" resultType"Long">select id from sy…

【HFP】藍牙HFP協議來電處理機制解析

目錄 一、協議概述與技術背景 1.1 HFP協議演進 1.2 核心角色定義 1.3 關鍵技術指標 二、來電接入的核心交互流程 2.1 基礎流程概述&#xff1a;AG 的 RING 通知機制 2.2 HF 的響應&#xff1a;本地提醒與信令交互 三、帶內鈴聲&#xff08;In-Band Ring Tone&#xff0…

【每天一個知識點】如何解決大模型幻覺(hallucination)問題?

解決大模型幻覺&#xff08;hallucination&#xff09;問題&#xff0c;需要從模型架構、訓練方式、推理機制和后處理策略多方面協同優化。 &#x1f9e0; 1. 引入 RAG 框架&#xff08;Retrieval-Augmented Generation&#xff09; 思路&#xff1a; 模型生成前先檢索知識庫中…

基于STC89C52RC和8X8點陣屏、獨立按鍵的小游戲《打磚塊》

目錄 系列文章目錄前言一、效果展示二、原理分析三、各模塊代碼1、8X8點陣屏2、獨立按鍵3、定時器04、定時器1 四、主函數總結 系列文章目錄 前言 用的是普中A2開發板&#xff0c;外設有&#xff1a;8X8LED點陣屏、獨立按鍵。 【單片機】STC89C52RC 【頻率】12T11.0592MHz 效…

C++學習:六個月從基礎到就業——C++學習之旅:STL迭代器系統

C學習&#xff1a;六個月從基礎到就業——C學習之旅&#xff1a;STL迭代器系統 本文是我C學習之旅系列的第二十四篇技術文章&#xff0c;也是第二階段"C進階特性"的第二篇&#xff0c;主要介紹C STL迭代器系統。查看完整系列目錄了解更多內容。 引言 在上一篇文章中…

leetcode刷題——判斷對稱二叉樹(C語言版)

題目描述&#xff1a; 示例 1&#xff1a; 輸入&#xff1a;root [6,7,7,8,9,9,8] 輸出&#xff1a;true 解釋&#xff1a;從圖中可看出樹是軸對稱的。 示例 2&#xff1a; 輸入&#xff1a;root [1,2,2,null,3,null,3] 輸出&#xff1a;false 解釋&#xff1a;從圖中可看出最…

無法右鍵下載文檔?網頁PDF下載方法大全

適用場景&#xff1a;繞過付費限制/無法右鍵下載/動態加載PDF 方法1&#xff1a;瀏覽器原生下載&#xff08;成功率60%&#xff09; Chrome/Edge&#xff1a; 在PDF預覽頁點擊工具欄 ??下載圖標&#xff08;右上角&#xff09; 快捷鍵&#xff1a;CtrlS → 保存類型選PDF …

基于缺失數據的2024年山東省專項債發行報告

一、數據情況 本次報告選取了山東省財政局公開的2024年專項債數據,共計2723條,發行期數是從第1期到第58期,由于網絡原因,其中25期到32期,54到57期的數據有缺失,如下圖所示。 從上圖看出,一年52周,平均每周都有一期發布,因此持續做專項債的謀劃很重要,一定要持續謀劃…

Ubuntu數據連接訪問崩潰問題

目錄 一、分析問題 1、崩潰問題本地調試gdb調試&#xff1a; 二、解決問題 1. 停止 MySQL 服務 2. 卸載 MySQL 相關包 3. 刪除 MySQL 數據目錄 4. 清理依賴和緩存 5.重新安裝mysql數據庫 6.創建程序需要的數據庫 三、驗證 1、動態庫更新了 2、頭文件更新了 3、重新…

Linux系統編程 day10 接著線程(中期頭大,還要寫論文)

線程有點懵逼 線程之前函數回顧以及總結部分&#xff08;對不清楚的問題再思考&#xff09; 線程控制原語 進程控制原語 pthread_create(); fork(); pthread_self(); getpid(); pthread_exit(); exit(); pthread_join(); …

《潯川AI翻譯v6.1.0問題已修復公告》

《潯川AI翻譯v6.1.0問題已修復公告》 尊敬的潯川AI翻譯用戶&#xff1a; 感謝您對潯川AI翻譯的支持與反饋&#xff01;我們已針對 **v6.1.0** 版本中用戶反饋的多個問題進行了全面修復&#xff0c;并優化了系統穩定性。以下是本次修復的主要內容&#xff1a; 已修復問題 ?…

深入理解 java synchronized 關鍵字

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家&#xff0c;歷代文學網&#xff08;PC端可以訪問&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移動端可微信小程序搜索“歷代文學”&#xff09;總架構師&#xff0c;15年工作經驗&#xff0c;…