<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      深度學習基礎從0到0.1

      線性回歸

      一元線性回歸

      線性回歸,公式為Y=Wx+b,這里簡單一點,假設偏置b=0,我們設置損失函數為loss=(y-yi)2,y是真實值,yi是預測值,代入可得loss=(y-W*x)2,帶入x的值和y的值即可得到最終的loss函數,而后求其導數,導數為0時可取極值,進而得到w,通過這樣我們就可以得到最佳的擬合直線,而這就是線性回歸算法,不過這里更為簡單,還沒對w進行訓練。

      那么如果考慮偏置b的話,我們又該怎么做呢,實際上很簡單,我們只需要分別對兩個變量求偏導,令其等于0即可,而后聯立方程組解出變量值,示例如下

      image-20251014115656146

      這里首先得出了loss函數,接下來對其進行求偏導

      image-20251014115949698

      而后聯立方程求解即可得出最終變量值。

      image-20251014120013512

      梯度下降

      如果按照之前的思路,只要能夠保證函數是線性的,我們無需再對模型進行多次微調,只需要令參數的偏導數全等于0求出函數即可,但為什么深度學習里不這么做呢,原因如下

      1、參數可能有很多,成千上萬,計算量過大
      2、模型有可能不是線性的,引入了激活函數,比如Relu函數是分段的,對其求導,解方程將什么復雜
      

      示例如下圖,肉眼可見的方程是難以進行計算的

      image-20251014121652593

      因此,我們這里就需要學習梯度下降,通過它來進行調參。

      這里以一元函數為例,如下圖所示,我們假設初始點為Xo=4,

      image-20251014122756677

      接下來根據其導數進行調整,比如f'(x)>0,那說明函數是增大的,我們是想取f(x)的最小值,則我們需要向左調整,反之則向右調整;

      那么我們如何設定這個調整的步長呢,這里有一個簡單的方法,設置步長為負的f'(x),比如x=4的導數值f'(x)≈1,那我們就設置為-1,接下來x的值就會由4變為3,

      可以發現離最小值更近了一步,在x=3處的f'(x)為0.7,接下來進行-0.7,變為2.3。這樣不斷逼近最終就抵達了最低點。

      學習率

      上面可以看出更新的步長貌似還不錯,但是實際上還需要一個參數去調控步長,這個參數一把不大于1,比如0.01,因為有時候直接使用步長操作可能會錯過全局最小值,所以我們引進學習率調控。這里就可以開始更新參數了,我們在求導后,加上了學習率,然后不斷對參數進行更新迭代,直至達到最優版本。

      image-20251014123706602

      均方誤差

      之前我們定義的損失函數是所有樣本的label和預測值的誤差的平方和,實際上為了保證訓練穩定,一般會除以樣本數,而這其實就是我們的均方誤差。

      image-20251014123629650

      多元線性回歸

      剛剛所看的是一元線性回歸,那么多元線性回歸又當如何呢?實際上是一樣的處理過程,具體示例如下

      數據如下:

      溫度 價格(元) 銷量(個)
      10 3 60
      20 3 85
      25 3 100
      28 2.5 120
      30 2 140
      35 2.5 145
      40 2.5 163

      這里用X1表示溫度,X2表示價格,Y表示銷量。

      Wo表示截距(初始值),W1表示溫度權重,W2表示價格權重。

      預測銷量為Y=Wo+X1*W1+X2*W2

      損失函數loss=1/7∑(y-yi)2,代入就是loss=1/7∑(Wo+X1*W1+X2*W2-yi)2

      接下來就是用剛剛所學的梯度下降進行優化,而梯度下降首先就是求導數,所以我們分別對三個變量進行求偏導。

      image-20251016101215521

      在這之后設置學習率,而后更新參數即可

      image-20251016101235518

      具體實現代碼如下

      # Feature 數據
      X = [[10, 3], [20, 3], [25, 3], [28, 2.5], [30, 2], [35, 2.5], [40, 2.5]]
      y = [60, 85, 100, 120, 140, 145, 163]  # Label 數據
      # 初始化參數
      w = [0.0, 0.0, 0.0]  # w0, w1, w2
      lr = 0.0001  # 學習率
      num_iterations = 10000  # 迭代次數
      # 梯度下降
      for i in range(num_iterations):
          # 預測值
          y_pred = [w[0] + w[1] * x[0] + w[2] * x[1] for x in X]
          # 計算損失
          loss = sum((y_pred[j] - y[j]) ** 2 for j in range(len(y))) / len(y)
          # 計算梯度
          grad_w0 = 2 * sum(y_pred[j] - y[j] for j in range(len(y))) / len(y)
          grad_w1 = 2 * sum((y_pred[j] - y[j]) * X[j][0] for j in range(len(y))) / len(y)
          grad_w2 = 2 * sum((y_pred[j] - y[j]) * X[j][1] for j in range(len(y))) / len(y)
          # 更新參數
          w[0] -= lr * grad_w0
          w[1] -= lr * grad_w1
          w[2] -= lr * grad_w2
          # 打印損失
          if i % 100 == 0:
              print(f"Iteration {i}: Loss = {loss}")
      # 輸出最終參數
      print(f"Final parameters: w0 = {w[0]}, w1 = {w[1]}, w2 = {w[2]}")
      

      邏輯回歸

      一元邏輯回歸

      之前所學的線性回歸是為了擬合出一條直線/平面來擬合數據,以此達到預測數值的效果,而邏輯回歸則不同,它是為了分類問題,以這里的一元邏輯回歸為例:

      氣溫 是否出門
      -10 0
      3 1
      -3 0
      5 1
      -4 0
      7 1
      -6 0
      8 1

      以上是數據,當我們構造出圖像時,如下所示

      image-20251016102456975

      此時的他并不是一個具體的數值,而是非0即1的,這個時候我們就無法再使用線性回歸來預測,所以就引進了邏輯回歸來進行解決。

      這里就引入了激活函數sigmoid,效果圖如下

      image-20251016102616897

      這里還不夠擬合,所以我們可以在e-x前加入w,而后進行訓練不斷逼近即可。

      image-20251016102822348

      神經網絡

      梯度消失/爆炸

      梯度消失是指,當參數進行多次迭代更新后,參數的變化已經變的微乎其微,比如100次的乘上0.1,這個數就會無比的小,貼近0,這個時候更新參數十分緩慢,這個就是梯度消失。

      而梯度爆炸是指,當參數多次迭代后,由于導數值較大,比如100次的乘上1.2,這個數就會十分巨大,這會使我們的更新跳躍幅度過大,不利于更新。

      卷積

      當參數過多時,比如我們要處理一張灰白圖片,他的大小是6*6*1,所以我們如果一個像素一個參數處理,就需要處理36個參數,而如果我們使用3*3的卷積核,對每個像素先進行卷積操作,最終就只剩下4*4*1的大小,而且我們只需要處理10個參數(3*3的卷積核+偏置b),就大大縮小了計算量,而且無論多少個像素,我們需要處理的參數也就只有這10個,因此,我們引入了卷積來處理圖像。

      其優點具體如下

      1、圖片輸入特征多 圖片輸入特征多,但是一個3x3的卷積操作只有10個參數,就可以對整個圖片進行掃描。
      
      2、特征局部性 卷積操作的每個運算只在特定相鄰區域內進行,并不要所有輸入特征都參與運算。
      
      3、平移不變性 卷積操作在整個圖片上進行滑動檢測,就是假設圖片的特征具有平移不變性。
      

      特征圖的尺寸計算

      對聯分別的輸出尺寸變化公式如下

      輸出尺寸 = (輸入尺寸 - 卷積核大小 + 2*填充) / 步長 + 1
      

      而有時會出現除不盡的情況,這個時候我們通常是進行向下取整來進行處理。

      1*1卷積層

      1*1卷積層存在的意義是他可以以最低成本改變通道數,示例如下

      我們定義一個1x1卷積層,其卷積核的數量(即輸出通道數)為 C_out。每個卷積核的尺寸是 [1, 1, C_in]。也就是說,每個卷積核都有 C_in 個權重值(每個輸入通道對應一個權重)和一個偏置項。
      
      這個1x1的窗口在特征圖的空間維度(高度和寬度) 上滑動。因為窗口是1x1,所以它每次只“看”一個像素點。但是,這個像素點有 C_in 個通道,因此它看到的是一個包含 C_in 個數值的向量。
      對于特征圖上的每一個位置 (i, j),取出該位置所有 C_in 個通道的值,形成一個向量 [v1, v2, ..., v_Cin]。
      
      用第一個1x1卷積核(它也有 C_in 個權重 [w1_1, w2_1, ..., w_Cin_1])與這個向量進行點積(即對應元素相乘后求和),再加上偏置,就得到了輸出特征圖在位置 (i, j) 的第一個通道的值。
      輸出值_1 = (v1 * w1_1) + (v2 * w2_1) + ... + (v_Cin * w_Cin_1) + 偏置_1
      
      用第二個卷積核(權重為 [w1_2, w2_2, ...])與同一個輸入向量再進行點積,得到輸出特征圖在 (i, j) 的第二個通道的值。
      
      ...
      
      重復此過程,直到用完所有的 C_out 個卷積核。
      
      經過上述操作,輸入的 C_in 個通道的信息,在每個像素點上都被混合、加權,并投影到了一個全新的 C_out 維空間。
      
      因為計算是在每個空間位置上獨立、并行地完成的,所以輸出的空間尺寸(高度和寬度)保持不變,但通道數從 C_in 變成了 C_out。
      

      全局平均池化層

      我們知道一般遇到的是最大池化層,從n*n的區域中拿出一個最大的值,即為最大池化操作,它只關注了局部的特征。而這里的全局平均池化是什么意思呢?

      實際上,他就類似我們的平均數,比如當前有十個數,它就會求和再除10,這樣就得到了全局平均池化的輸出,這個不會引入額外參數,而且有每個像素的特征。

      貓狗分類實戰

      import os
      import random
      from PIL import Image
      from torchvision import transforms  # ? 從 torchvision 導入
      import torch
      from torch import device, nn
      from torch.utils.data import DataLoader, Dataset, TensorDataset
      
      def verify_images(image_folder):
          classes = ["Cat", "Dog"]
          class_to_idx = {"Cat": 0, "Dog": 1}
          samples = []
          for cls_name in classes:
              cls_dir = os.path.join(image_folder, cls_name)
              for fname in os.listdir(cls_dir):
                  if not fname.lower().endswith(('.jpg', '.jpeg', '.png')):
                      continue
                  path = os.path.join(cls_dir, fname)
                  try:
                      with Image.open(path) as img:
                          img.verify()
                      samples.append((path, class_to_idx[cls_name]))
                  except Exception:
                      print(f"Warning: Skipping corrupted image {path}")
          return samples
      
      class ImageDataset(Dataset):
          def __init__(self, samples, transform=None):
              self.samples = samples
              self.transform = transform
      
          def __len__(self):
              return len(self.samples)
      
          def __getitem__(self, idx):
              path, label = self.samples[idx]
              with Image.open(path) as img:
                  img = img.convert("RGB")    
                  if self.transform:
                      img = self.transform(img)
              return img, label
      class CNNmodel(nn.Module):
          def __init__(self): 
              super().__init__()
              self.model = nn.Sequential(
                  nn.Conv2d(3, 16, 3, padding=1), 
                  nn.ReLU(),
                  nn.MaxPool2d(2, 2), 
                  
                  nn.Conv2d(16, 32, 3, padding=1),
                  nn.ReLU(),
                  nn.MaxPool2d(2, 2),
                  
                  nn.Conv2d(32, 64, 3, padding=1),
                  nn.ReLU(),
                  nn.MaxPool2d(2, 2),
                  
                  nn.Conv2d(64, 128, 3, padding=1),
                  nn.ReLU(),
                  nn.MaxPool2d(2, 2), 
                  
                  nn.Conv2d(128,1,1),
                  nn.AdaptiveAvgPool2d(1),
                  nn.Flatten(),
                  nn.Sigmoid()
              )
          def forward(self, x):
              return self.model(x)
          
      def evaluate(model,test_dataloader):
          model.eval()
          val_correct = 0
          val_total = 0
      
          with torch.no_grad():
              for inputs,labels in test_dataloader:
                  inputs = inputs.to(device)
                  labels = labels.float().unsqueeze(1).to(device) 
      
                  outputs = model(inputs)
                  preds = (outputs >= 0.5).float()
                  val_correct += (preds == labels).sum().item()
                  val_total += labels.size(0)
          val_acc = val_correct / val_total
          return val_acc
      
      if __name__ == "__main__":
          DATA_DIR = r"D:\Computer Graphic\review\examples\datasets\archive\PetImages"
          BATCH_SIZE = 64
          IMG_SIZE = 128
          EPOCHS = 10
          LR = 0.001
          PRINT_STEP = 100
      
          device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
      
          all_samples = verify_images(DATA_DIR)
          random.seed(42)
          random.shuffle(all_samples)
          train_size = int(len(all_samples) * 0.8)
          train_samples = all_samples[:train_size]
          valid_samples = all_samples[train_size:]
      
          data_transform = transforms.Compose([
              transforms.Resize((IMG_SIZE,IMG_SIZE)),#統一圖片大小
              transforms.ToTensor(),#轉換為張量
              transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])  #標準化
          ])
      
          train_dataset = ImageDataset(train_samples,data_transform)
          valid_dataset = ImageDataset(valid_samples,data_transform)
      
          train_dataloader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True,num_workers=4)
          valid_dataloader = DataLoader(valid_dataset, batch_size=BATCH_SIZE, shuffle=False,num_workers=4)
      
          model = CNNmodel().to(device)
          criterion = nn.BCELoss()
          optimizer = torch.optim.Adam(model.parameters(), lr=LR)
      
          for epoch in range(EPOCHS):
              print(f"\nEpoch {epoch+1}/{EPOCHS}")
              model.train()
              running_loss = 0.0
      
              for step,(inputs,labels) in enumerate(train_dataloader):
                  inputs = inputs.to(device)
                  labels = labels.float().unsqueeze(1).to(device)
      
                  optimizer.zero_grad()
                  outputs = model(inputs)
                  loss = criterion(outputs, labels)
                  loss.backward()
                  optimizer.step()
      
                  running_loss += loss.item()
      
                  if (step + 1) % PRINT_STEP == 0:
                      avg_loss = running_loss / PRINT_STEP
                      print(f"Step {step+1}- Loss: {avg_loss:.4f}")
                      running_loss = 0.0
                      
              val_acc = evaluate(model, valid_dataloader)
              print(f"Validation Accuracy after epoch {epoch+1}: {val_acc:.4f}")
      

      image-20251017155221905

      圖像增強

      什么是圖像增強,圖像增強是在保持圖像語義不變的情況下,生成多樣化的新數據。例如旋轉,裁剪,顏色亮度等的改變,都是圖像增強的一種方式。

      幾何變化

      旋轉,這個是我們所熟悉的操作,將物體按照一定角度進行旋轉操作后,表達語義仍然不變。

      image-20251017160038919

      那么如何在PyTorch里實現旋轉操作呢,具體如下。

      首先我們定義一個函數,它可以對圖片應用PyTorch里Transform對象的操作,進而展示圖片。

      def imshow(img_path,transform):
          img = Image.open(img_path)
          fig,ax = plt.subplots(1,2,figsize=(15,4))
          ax[0].set_title(f"Original Image {img.size}")
          ax[0].imshow(img)
          img = transform(img)
          ax[1].set_title(f"Transformed Image {img.size}")
          ax[1].imshow(img)
          plt.show()
      

      而后通過以下代碼即可實現在-30-30度之間隨機旋轉

      path = r"D:\Computer Graphic\review\examples\datasets\archive\PetImages\Cat\238.jpg"
      transform = transforms.RandomRotation(degrees=30)
      imshow(path,transform)
      

      image-20251017161412161

      翻轉

      具體有水平和垂直,水平如下

      path = r"D:\Computer Graphic\review\examples\datasets\archive\PetImages\Cat\2239.jpg"
      transform = transforms.RandomHorizontalFlip(p=1.0)#p代表翻轉概率
      imshow(path,transform)
      

      image-20251017161300456

      垂直翻轉如下

      transform = transforms.RandomVerticalFlip(p=1.0)
      

      image-20251017161914867

      裁剪

      不難理解,隨機裁剪出圖像區域

      transform = transforms.RandomCrop(size=(120, 120))
      

      image-20251017162045721

      透視變換

      改變圖片的形狀,進行一定的角度扭曲

      transform = transforms.RandomPerspective(
          distortion_scale=0.5,  # 控制變形強度,0~1,越大越扭曲
          p=1.0,                 # 應用該變換的概率
          interpolation=transforms.InterpolationMode.BILINEAR
      )
      

      image-20251017162209921

      顏色變化

      顏色具體可調的是亮度、對比度、飽和度和色調

      transforms.ColorJitter(
          brightness=0.5,#圖像亮暗程度,設置為x時從[1-x,1+x]隨機旋轉
          contrast=0.5,#圖像對比度,范圍[1-x,1+x]
          saturation=0.5,#圖像飽和度,范圍[1-x,1+x]
          hue=0.1#色調,范圍[-0.5,0.5],設置為0.1則為[-0.1,0.1]
      )
      

      image-20251017162442631

      模糊

      對圖像進行高斯模糊

       # 對圖像進行高斯模糊,kernel size 為 5,sigma 可調節模糊強度
      transform = transforms.GaussianBlur(kernel_size=5, sigma=(0.1, 3.0))
      

      其中kernel_size是指高斯模糊卷積核的大小,它決定模糊的范圍,必須為奇數,設置越大則模糊效果越明顯。后面傳入的元組(0.1,3.0),表示在里面隨機旋轉一個值,sigma越大越模糊

      image-20251017162657385

      遮罩

      遮罩是指隨機遮擋一個或多個連續的方形區域,讓模型忽略局部信息更關注上下文特征,有利于提升模型魯棒性。

      PyTorch里沒有直接實現,這里需要自己進行實現

      from PIL import Image
      import numpy as np
      import random
      
      def cutout_pil_multi(image, mask_size=50, num_masks=3):
          """
          對圖像應用多個 Cutout 遮擋塊
      
          參數:
          - image: PIL.Image 對象
          - mask_size: 每個遮擋塊的大?。ㄕ叫芜呴L)
          - num_masks: 遮擋塊的數量
          """
          image_np = np.array(image).copy()
          h, w = image_np.shape[0], image_np.shape[1]
      
          for _ in range(num_masks):
              y = random.randint(0, h - 1)
              x = random.randint(0, w - 1)
      
              y1 = max(0, y - mask_size // 2)#防止變為負數
              y2 = min(h, y + mask_size // 2)#防止溢出
              x1 = max(0, x - mask_size // 2)
              x2 = min(w, x + mask_size // 2)
      
              # 遮擋區域設置為黑色
              image_np[y1:y2, x1:x2, :] = 0
      
          return Image.fromarray(image_np)
      

      而后調用函數即可

      imshow(path, cutout_pil_multi)
      

      image-20251017162924945

      這個時候我們就可以利用已有的圖像增強技術給貓狗分類實戰加上,相當于擴充了訓練集。

          train_transform = transforms.Compose([  
              transforms.Resize((150,150)),
              transforms.RandomCrop(size=(IMG_SIZE,IMG_SIZE)),
              transforms.RandomHorizontalFlip(p=0.5),
              transforms.ColorJitter(
                  brightness=0.5,
                  contrast=0.3,
                  saturation=0.4,
                  hue=0.1
              ),
              transforms.RandomRotation(degrees=15),
              transforms.ToTensor(),
              transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
          ])
      
      
      
          valid_transform = transforms.Compose([
              transforms.Resize((IMG_SIZE,IMG_SIZE)),#統一圖片大小
              transforms.ToTensor(),#轉換為張量
              transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])  #標準化
          ])
      
          train_dataset = ImageDataset(train_samples,train_transform)
          valid_dataset = ImageDataset(valid_samples,valid_transform)
      

      image-20251017165214439

      語義分割

      什么是語義分割呢,將圖片所有的像素都賦予語義信息的任務就是語義分割,如下圖所示

      image-20251017205004872

      循環神經網絡(RNN)

      循環神經網絡常用于序列問題,序列問題是指數據之間存在順序的問題,輸入、輸出或兩者都是有序的數據序列,元素上存在時間或位置上的聯系,在考慮這類問題時,我們需要考慮前面/未來的信息。

      事實上,他的工作方式如下圖所示

      image-20251018104602583

      它會將上一層的隱藏層輸出,傳給下一層的輸入,將這兩個拼接就組成了下一個的輸入,然后傳入隱藏層再到輸出層得到預測結果,之后循環往復,直至最終。

      由此可得當前隱狀態的計算公式

      image-20251018105525315

      其中,xt表示t時刻輸入,yt表示t時刻輸出,ht表示t時刻更新后記憶,即隱狀態。wh表示隱藏層權重,bh表示隱藏層偏置,wy表示輸出層權重,by表示輸出層偏置。

      輸出層使用了Softmax函數作為激活函數

      image-20251018110059661

      通過觀測可以得出,其實只有第一層隱藏層我們在循環使用,第二層是普通層,因此第一層也被我們稱為循環層。循環層的遞歸調用就是RNN的本質。每一時間步對之前所有的時間步的循環層的調用,輸出關鍵隱狀態ht。對于普通層,可以看成是每一時間步利用ht向量作為輸入,進行的額外的分類或者回歸任務。普通層不是RNN的核心,它只是為了完成每一步的特定任務添加的任務層。

      LSTM

      之前的RNN只能記住前一時刻的信息,只有短時記憶,而在現實生活中例如語音識別、天氣預報、股票預測這些情況下我們都需要進行長期時間的記憶,這個時候我們就引入了長短期記憶網絡(LSTM)。

      image-20251019123312860

      我們對新輸入的信息進行存儲作為Z,而后再傳入上一時間狀態的Ht-1和這一時刻的信息Xt,經過sigmoid函數作為輸入門,即輸入信息的控制函數,將Z和這個相乘得到新的輸入,此操作將判定一部分新信息可以進入記憶,一部分則被丟棄。這個時候它就是受控新信息,接下來他就該進入長時記憶了,那么如何進入長時記憶呢,這里我們首先需要取出長時記憶,然后選擇一部分需要遺忘的記憶,以此來給出空間存儲新的記憶,這個時候依然使用sigmoid函數作為遺忘門函數進行篩選,然后將長期記憶乘上遺忘門的函數再加上tanh激活函數處理的新信息Z乘上控制函數,作為待輸出記憶,也就是要存儲的長期記憶,而后對這部分再進行tanh激活函數處理,再經過sigmoid輸出門處理,判斷那部分需要輸出,得到的輸出就是隱狀態的輸出了。多個時間步就是以Ct-1作為前一步的長時記憶,經過當前時間步處理后,生成新的長時記憶Ct和隱狀態ht,傳至下一個記憶中心。公式如下所示

      image-20251019160048912

      不過這里的wh、wi、wo實際上都是兩個權重,以wh為例,我們的wh包含了前一時刻ht-1隱狀態輸出的權重wh,還包含了這一時刻輸入xt的權重wx

      代碼實現如下

      import torch
      from torch import nn
      from d2l import torch as d2l
      
      batch_size, num_steps = 32, 35
      train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
      def get_lstm_params(vocab_size, num_hiddens, device):
          num_inputs = num_outputs = vocab_size
      
          def normal(shape):
              return torch.randn(size=shape, device=device)*0.01
      
          def three():
              return (normal((num_inputs, num_hiddens)),
                      normal((num_hiddens, num_hiddens)),
                      torch.zeros(num_hiddens, device=device))
      
          W_xi, W_hi, b_i = three()  # 輸入門參數
          W_xf, W_hf, b_f = three()  # 遺忘門參數
          W_xo, W_ho, b_o = three()  # 輸出門參數
          W_xc, W_hc, b_c = three()  # 候選記憶元參數
          # 輸出層參數
          W_hq = normal((num_hiddens, num_outputs))
          b_q = torch.zeros(num_outputs, device=device)
          # 附加梯度
          params = [W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc,
                    b_c, W_hq, b_q]
          for param in params:
              param.requires_grad_(True)
          return params
      def init_lstm_state(batch_size, num_hiddens, device):
          return (torch.zeros((batch_size, num_hiddens), device=device),
                  torch.zeros((batch_size, num_hiddens), device=device))
      def lstm(inputs, state, params):
          [W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c,
           W_hq, b_q] = params
          (H, C) = state
          outputs = []
          for X in inputs:
              I = torch.sigmoid((X @ W_xi) + (H @ W_hi) + b_i)
              F = torch.sigmoid((X @ W_xf) + (H @ W_hf) + b_f)
              O = torch.sigmoid((X @ W_xo) + (H @ W_ho) + b_o)
              C_tilda = torch.tanh((X @ W_xc) + (H @ W_hc) + b_c)
              C = F * C + I * C_tilda
              H = O * torch.tanh(C)
              Y = (H @ W_hq) + b_q
              outputs.append(Y)
          return torch.cat(outputs, dim=0), (H, C)
      vocab_size, num_hiddens, device = len(vocab), 256, d2l.try_gpu()
      num_epochs, lr = 500, 1
      model = d2l.RNNModelScratch(len(vocab), num_hiddens, device, get_lstm_params,
                                  init_lstm_state, lstm)
      d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)
      

      GRU

      GRU,即門控循環單元,他相較LSTM變得更加精簡了一些,他只有重置門和更新門,去除了LSTM的記憶細胞,也實現了記錄長時記憶和更新的效果。

      image-20251019162613893

      這里通過重置門實現遺忘部分信息,這里的重置門實際上就是LSTM的遺忘門,他將上一時刻的記憶ht-1和當前時刻的輸入Xt進行拼接,而后經過線性層,再加上偏置進行sigmoid激活函數處理,就得到了Gr。

      這個得到的Gr再乘上ht-1,就是我門經過重置后的長時記憶。重置后的長期記憶和當前輸入xt合并,然后經過一個線性層(權重為wh),加tanh激活,就得到當前層的備用輸出ht~。

      此時得到的備用輸出還是無法直接輸出,因為GRU只能靠隱狀態來傳遞長時記憶,這里需要將長期保留的記憶加進來再作為當前時間步的隱狀態作為輸出。這里怎樣決定哪些維度保留長期記憶,哪些維度作為備用輸出的隱狀態呢,答案是使用更新門函數進行處理,這個函數同時決定保留多少長期記憶,更新多少當前步產生的記憶。

      首先用sigmoid更新門生成一個更新向量,而后和備用輸出相乘,獲得要更新到長期記憶里的信息。然后用1減去更新向量,這樣就得到了對長期記憶的保留向量。用保留向量與長期記憶按位點乘,就得到了保留的長期記憶,在和更新信息相加,就得到了這一步輸出的長期記憶,ht。

      image-20251019163653216

      其實現代碼如下

      import torch
      from torch import nn
      from d2l import torch as d2l
      
      batch_size, num_steps = 32, 35
      train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
      
      def get_params(vocab_size, num_hiddens, device):
          num_inputs = num_outputs = vocab_size
      
          def normal(shape):
              return torch.randn(size=shape, device=device)*0.01
      
          def three():
              return (normal((num_inputs, num_hiddens)),
                      normal((num_hiddens, num_hiddens)),
                      torch.zeros(num_hiddens, device=device))
      
          W_xz, W_hz, b_z = three()  # 更新門參數
          W_xr, W_hr, b_r = three()  # 重置門參數
          W_xh, W_hh, b_h = three()  # 候選隱狀態參數
          # 輸出層參數
          W_hq = normal((num_hiddens, num_outputs))
          b_q = torch.zeros(num_outputs, device=device)
          # 附加梯度
          params = [W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q]
          for param in params:
              param.requires_grad_(True)
          return params
      def init_gru_state(batch_size, num_hiddens, device):
          return (torch.zeros((batch_size, num_hiddens), device=device), )
      
      def gru(inputs, state, params):
          W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q = params
          H, = state
          outputs = []
          for X in inputs:
              Z = torch.sigmoid((X @ W_xz) + (H @ W_hz) + b_z)
              R = torch.sigmoid((X @ W_xr) + (H @ W_hr) + b_r)
              H_tilda = torch.tanh((X @ W_xh) + ((R * H) @ W_hh) + b_h)
              H = Z * H + (1 - Z) * H_tilda
              Y = H @ W_hq + b_q
              outputs.append(Y)
          return torch.cat(outputs, dim=0), (H,)
      
      vocab_size, num_hiddens, device = len(vocab), 256, d2l.try_gpu()
      num_epochs, lr = 500, 1
      model = d2l.RNNModelScratch(len(vocab), num_hiddens, device, get_params,
                                  init_gru_state, gru)
      d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)
      

      深度循環神經網絡

      之前的都是只有一層隱藏層,而這里則是指使用多層隱藏層,每個隱狀態都連續地傳遞到當前層的下一個時間步和下一層的當前時間步。

      它與之前的LSTM代碼幾乎一致,唯一不同的是這里多加了隱藏層,之前我們得到隱狀態后即為結束,這里需要傳到下一個隱藏層。

      import torch
      from torch import nn
      from d2l import torch as d2l
      
      batch_size, num_steps = 32, 35
      train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
      
      def get_multi_lstm_params(vocab_size, num_hiddens, num_layers, device):
          """初始化多層LSTM參數"""
          num_inputs = vocab_size
          num_outputs = vocab_size
          
          def normal(shape):
              return torch.randn(size=shape, device=device) * 0.01
          
          def three(in_size, out_size):
              """返回門控機制的三組參數"""
              return (normal((in_size, out_size)),
                      normal((out_size, out_size)),
                      torch.zeros(out_size, device=device))
          
          params = []
          # 初始化各層參數
          for i in range(num_layers):
              # 第一層輸入為vocab_size,其他層輸入為上一層的隱藏層大小
              in_size = num_inputs if i == 0 else num_hiddens
              
              # 輸入門、遺忘門、輸出門、候選記憶元參數
              W_xi, W_hi, b_i = three(in_size, num_hiddens)
              W_xf, W_hf, b_f = three(in_size, num_hiddens)
              W_xo, W_ho, b_o = three(in_size, num_hiddens)
              W_xc, W_hc, b_c = three(in_size, num_hiddens)
              
              params.extend([W_xi, W_hi, b_i, W_xf, W_hf, b_f, 
                            W_xo, W_ho, b_o, W_xc, W_hc, b_c])
          
          # 輸出層參數(連接最后一層隱藏層到輸出)
          W_hq = normal((num_hiddens, num_outputs))
          b_q = torch.zeros(num_outputs, device=device)
          params.extend([W_hq, b_q])
          
          # 開啟梯度計算
          for param in params:
              param.requires_grad_(True)
          
          return params
      
      def init_multi_lstm_state(batch_size, num_hiddens, num_layers, device):
          """初始化多層LSTM的隱藏狀態和記憶元"""
          return [(torch.zeros((batch_size, num_hiddens), device=device),
                   torch.zeros((batch_size, num_hiddens), device=device)) 
                  for _ in range(num_layers)]
      
      def multi_lstm(inputs, state, params, num_layers):
          """多層LSTM前向傳播"""
          # 解析狀態:每層包含(H, C)
          layer_states = state
          outputs = []
          # 每層參數數量:12個參數(4個門×3組參數)
          per_layer_params = 12
          current_inputs = inputs
          
          # 逐層計算
          for layer in range(num_layers):
              layer_params = params[layer * per_layer_params : (layer + 1) * per_layer_params]
              H, C = layer_states[layer]
              layer_outputs = []
              
              # 時序步計算
              for X in current_inputs:
                  # 輸入門
                  I = torch.sigmoid((X @ layer_params[0]) + (H @ layer_params[1]) + layer_params[2])
                  # 遺忘門
                  F = torch.sigmoid((X @ layer_params[3]) + (H @ layer_params[4]) + layer_params[5])
                  # 輸出門
                  O = torch.sigmoid((X @ layer_params[6]) + (H @ layer_params[7]) + layer_params[8])
                  # 候選記憶元
                  C_tilda = torch.tanh((X @ layer_params[9]) + (H @ layer_params[10]) + layer_params[11])
                  # 更新記憶元
                  C = F * C + I * C_tilda
                  # 更新隱藏狀態
                  H = O * torch.tanh(C)
                  layer_outputs.append(H)
              
              # 當前層輸出作為下一層輸入
              current_inputs = layer_outputs
              # 更新該層狀態
              layer_states[layer] = (H, C)
          
          # 輸出層計算(使用最后一層的輸出)
          W_hq, b_q = params[-2], params[-1]
          final_outputs = [(H @ W_hq) + b_q for H in current_inputs]
          
          return torch.cat(final_outputs, dim=0), layer_states
      
      # 模型超參數
      vocab_size = len(vocab)
      num_hiddens = 256  # 每層隱藏單元數
      num_layers = 2     # 隱藏層數(可根據需要調整)
      device = d2l.try_gpu()
      num_epochs, lr = 500, 1  # 多層網絡可能需要調整學習率和迭代次數
      
      # 定義模型
      def model_fn(vocab_size, num_hiddens, device, num_layers):
          return d2l.RNNModelScratch(
              vocab_size, num_hiddens, device,
              lambda vs, nh, dev: get_multi_lstm_params(vs, nh, num_layers, dev),
              lambda bs, nh, dev: init_multi_lstm_state(bs, nh, num_layers, dev),
              lambda inputs, state, params: multi_lstm(inputs, state, params, num_layers)
          )
      
      model = model_fn(vocab_size, num_hiddens, device, num_layers)
      
      # 訓練模型
      d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)
      

      注意力機制

      Self-attention

      這里的自注意力機制是一對一的,即每一個向量vector輸入,就會輸出一個對應的標簽label

      image-20251022153232927

      使用全連接層完成輸出,但是我們要考慮前后文,怎么做呢,我們可以一次選定Window大小,可以連接上下文。但是如果Sequence過長,我們的Window設置很大的話,計算量將十分巨大,所以這個時候就用到了self-attention

      image-20251022153410900

      Self-attention一次將讀取全部的輸入,然后再進行輸出,同時這個也可以進行疊加,疊加多次再生成輸出。具體計算如下

      生成b1:

      1. a1與其他向量的關聯性α

      α計算方法:

      (1)dot-product:輸入向量分別乘兩個矩陣Wq和Wk,產生的q和k點乘得到α(最常用,用在transformer里面)

      (2)additive:得到的q與k相加通過激活函數activate function然后通過transform?得到α

      方法 公式 特點
      Dot-product α = q?·k 計算簡單,效率高,最常用
      Additive α = v?·tanh(W·[q;k]) 更靈活,可處理不同維度的q,k

      這里我們采用的是第一種方法。

      image-20251022153835292

      實際上自己也會和自己做關聯性,這樣做的意義是保留自身信息,如果不計算α1,1,輸出b1就會由其他向量決定,該操作就類似于人在做選擇時,既要傾聽他人建議,同時也要考慮自身的想法。

      image-20251022154713848

      然后用Softmax函數對α進行處理

      image-20251022155143184

      接下來再乘上通過a*Wv得到v,v與α相乘就得到了最終的b1

      image-20251022155247171

      如果a1,a2關聯性很強,那么b1就可能會比較接近b2。

      將輸入向量和QKV這些都視為矩陣,可以發現實際上只有三個參數是我們未知的,Wq、Wk和Wv

      image-20251022155444717

      多頭注意力機制

      多頭注意力機制與自注意機制類似,只是它有多個qkv

      image-20251022155623194

      我們這里計算依舊從前,不過我們計算bi1就只考慮ki,1和vi,1這些部分,計算bi2就只考慮qi2,ki2,vi2這部分,最終計算的時候將bi,1和bi,2視為一個矩陣進行計算,得到最終的bi。

      image-20251022155805143

      Transfrom

      Transfrom采用的結構是Sequence to Sequence,縮寫為Seq2Seq,如下圖所示

      image-20251022160359232

      Encoder

      encoder可以用rnn/cnn/self attention,編碼的目的就是,輸入一排向量,輸出一排等同數量的向量。

      image-20251022160559000

      Encoder結構具體可分為以下部分

      1、輸入層

      負責接收輸入序列,并將每個符號(如單詞)轉換為一個向量表示。這個向量表示通常被稱為詞嵌入(embedding),它是通過學習大量語料庫而得到的。

      2、位置編碼層(positonal encoding)

      由于Transformer模型采用自注意力機制,它不像RNN那樣天然地考慮輸入序列中詞的位置信息。因此,位置編碼層負責將每個詞的位置信息編碼為一個向量,并將其加到詞嵌入上。這樣,模型就能考慮到詞在序列中的位置。

      3、編碼器層堆疊(block)

      Transformer模型的Encoder部分通常由多個編碼器層堆疊而成。每個編碼器層都具有相同的結構block,并且它們之間通過殘差連接和規范化層進行連接。

      4、多頭自注意力子層(multi head attention)

      在每個編碼器層中,首先是一個多頭自注意力子層。這個子層負責計算輸入序列中每個詞與其他詞之間的相關性,并生成一個注意力權重分布。這個注意力權重分布用于計算一個加權和的向量表示,這個向量表示考慮了序列中其他詞對當前詞的影響。

      5、前饋全連接子層(feed forward)

      在每個編碼器層中,緊接著多頭自注意力子層的是一個前饋全連接子層。這個子層負責將多頭自注意力子層的輸出通過一個全連接神經網絡進行處理,以捕獲更復雜的特征表示。

      6、殘差連接和規范化層

      在每個子層之后,都有一個殘差連接和一個規范化層。殘差連接負責將子層的輸入與輸出相加,以避免梯度消失和表示瓶頸問題。規范化層則負責對子層的輸出進行規范化處理,以加速模型的收斂速度。

      Block結構如下圖所示

      image-20251022162059767

      其中,一個block包含了以下

      (1)self-attention
      (2)feed forward(在李宏毅的課件里稱為FC)
      (3)residual
      (4)norm
      

      self-attention子層中的residual connection(殘差連接)被用來添加輸入數據和輸出數據之間的直接連接(輸入和輸出相加一次),作用如下

      (1)避免梯度消失或梯度爆炸問題;

      (2)殘差連接可以使輸入數據和輸出數據的形狀保持不變,避免信息丟失,并且可以使模型的訓練速度和精度得到提高。

      得到residual后對其進行normalization(layer normalization,這個是計算一整個樣本的標準化方式)。

      norm完后,在全連接層層也進行residual。

      residual后,再norm,最后得到一個block的輸出。

      image-20251022162812925

      Decoder

      Decoder實際上與Encoder十分相像,遮擋中間的一部分,可以發現除了最后的Decoder走了線性層和Softmax,兩者在前面的操作幾乎無二。

      image-20251022163724683

      需要注意的是這里的多頭注意力機制是加了Masked的,這個是什么意思呢。因為是一個一個輸入的,比如當前要計算α2,我們還不知道a3和a4,那我們就只計算a1和a2,而不再考慮a3,a4。

      完整的結構

      image-20251022164744661

      中間這部分是一段多頭注意力子層,將Encoder的內容匯合進來結合之前的再傳到全連接層,最終經過線性層和Softmax進行輸出概率。

      posted @ 2025-10-19 17:12  quan9i  閱讀(15)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产精品天干天干综合网| 亚洲精品在线视频自拍| 人妻少妇精品系列一区二区| 亚洲全网成人资源在线观看| 久久精品国产再热青青青| 欧美老少配性行为| 国产色无码专区在线观看| 国产老妇伦国产熟女老妇高清| 国产成人欧美一区二区三区在线| 熟女系列丰满熟妇AV| 性欧美三级在线观看| 狠狠人妻久久久久久综合九色| 国产亚洲精品久久久久久无亚洲| 亚洲国产精品综合久久20| 亚洲成人av日韩在线| 插插射啊爱视频日a级| 无套内谢少妇毛片在线| 国产精品国产精品偷麻豆| 久久精品国产亚洲av电影| 冀州市| 国产福利酱国产一区二区| 国产精品粉嫩嫩在线观看| 国产18禁黄网站禁片免费视频| 老熟妇乱子交视频一区| 开心久久综合激情五月天| 热久在线免费观看视频| 香蕉亚洲欧洲在线一区| 精品久久一线二线三线区| 在线日韩一区二区| 蜜桃久久精品成人无码av| 亚洲综合一区二区精品导航| 2021最新国产精品网站| 亚洲AV乱码毛片在线播放| 综合区一区二区三区狠狠| 精品亚洲AⅤ无码午夜在线| 欧美黑吊大战白妞| 国产成人精品亚洲精品日日| 亚洲精品一区二区妖精| 女人香蕉久久毛毛片精品| 韩日午夜在线资源一区二区| 性色欲情网站iwww九文堂|