utils.py
ultralytics\nn\modules\utils.py
目錄
utils.py
1.所需的庫和模塊
2.def _get_clones(module, n):?
3.def bias_init_with_prob(prior_prob=0.01):?
4.def linear_init(module):?
5.def inverse_sigmoid(x, eps=1e-5):?
6.def multi_scale_deformable_attn_pytorch(value: torch.Tensor, value_spatial_shapes: torch.Tensor, sampling_locations: torch.Tensor, attention_weights: torch.Tensor,) -> torch.Tensor:?
1.所需的庫和模塊
# Ultralytics YOLO 🚀, AGPL-3.0 license
"""Module utils."""import copy
import mathimport numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.nn.init import uniform_# 這段代碼定義了一個名為 __all__ 的元組,它包含了一系列的類名。這個元組通常用于Python模塊中,以明確指出該模塊對外公開的接口或類。當某個模塊被導入時,如果使用了 from module import * 這樣的導入語句, __all__ 元組中列出的名稱將會被導入。
__all__ = "multi_scale_deformable_attn_pytorch", "inverse_sigmoid"
2.def _get_clones(module, n):?
# 這段代碼定義了一個名為 _get_clones 的類,它用于創建指定模塊( module )的多個副本,并返回這些副本組成的列表。這個函數通常在深度學習中用于復制相同的模塊多次,以構建序列模型或實現特定的網絡結構。
# 定義了一個名為 _get_clones 的函數,它接受兩個參數。
# 1.module :是一個神經網絡模塊。
# 2.n :是一個整數,表示要復制模塊的次數。
def _get_clones(module, n):# 從給定的模塊創建克隆模塊列表。"""Create a list of cloned modules from the given module."""# 這行代碼執行了實際的復制操作,并返回一個結果。# copy.deepcopy(module) :使用 copy 模塊的 deepcopy 函數來創建 module 的一個深拷貝。深拷貝意味著創建一個新的對象,并遞歸復制其所有元素,確保新對象和原始對象完全獨立。# for _ in range(n) :這是一個列表推導式,用于生成一個列表,其中包含 n 個 module 的深拷貝。 _ 是一個占位符,表示我們不關心循環的索引,只關心迭代的次數。# nn.ModuleList([...]) :將上述生成的列表包裝成一個 ModuleList 對象。 ModuleList 是PyTorch中的一個類,用于存儲一個模塊列表,并且可以像常規Python列表一樣進行索引和迭代,但它還具有一些額外的功能,比如在模型的參數中注冊每個模塊的參數。return nn.ModuleList([copy.deepcopy(module) for _ in range(n)])
# _get_clones 函數的作用是復制一個神經網絡模塊 n 次,并返回一個包含所有復制模塊的 ModuleList 。這在深度學習中很有用,比如在實現某些特定的網絡結構時,可能需要多個相同的模塊。通過這種方式,可以確保每個模塊都有獨立的參數,同時便于管理和使用。
3.def bias_init_with_prob(prior_prob=0.01):?
# 這段代碼定義了一個名為 bias_init_with_prob 的函數,它用于計算和返回一個初始化偏置值,這個值通常用于神經網絡中偏置項的初始化。
# 定義了一個名為 bias_init_with_prob 的函數,它接受一個參數。
# 1.prior_prob :是一個浮點數,默認值為 0.01 ,表示某個事件發生的先驗概率。
def bias_init_with_prob(prior_prob=0.01):# 根據給定的概率值初始化 conv/fc 偏差值。"""Initialize conv/fc bias value according to a given probability value."""# 計算并返回一個浮點數,這個數是根據 prior_prob 計算得到的初始化偏置值。# 1 - prior_prob :計算 prior_prob 的補數,即不發生該事件的概率。# (1 - prior_prob) / prior_prob :計算不發生該事件的概率與發生該事件的概率之比。# -np.log(...) :計算上述比值的負對數。這里使用的是自然對數 np.log ,它是 NumPy 庫中的一個函數,用于計算自然對數(以 e 為底的對數)。# float(...) :將計算結果轉換為浮點數,以確保返回值的類型是 float 。return float(-np.log((1 - prior_prob) / prior_prob)) # return bias_init
# bias_init_with_prob 函數根據給定的先驗概率 prior_prob 計算一個初始化偏置值。這個值通常用于初始化神經網絡中的偏置項,以便在訓練開始時對特定事件的發生概率進行調整。通過設置偏置項,可以影響網絡的輸出,使其更傾向于或更不傾向于激活某個神經元。這種方法在處理類別不平衡問題時特別有用,因為它可以幫助模型在訓練過程中更加關注少數類別。
4.def linear_init(module):?
# 這段代碼定義了一個名為 linear_init 的函數,它用于初始化線性層(例如全連接層)的權重和偏置。
# 定義了一個名為 linear_init 的函數,它接受一個參數。
# 1.module :指的是一個線性層模塊,比如 PyTorch 中的 nn.Linear 。
def linear_init(module):# 初始化線性模塊的權重和偏差。"""Initialize the weights and biases of a linear module."""# 計算了一個邊界值 bound ,用于初始化權重。# module.weight.shape[0] :獲取線性層權重矩陣的第一個維度的大小,即輸入特征的數量。# 1 / math.sqrt(...) :計算輸入特征數量的平方根的倒數,這個值將用作權重初始化的范圍的邊界。這是一種常見的權重初始化策略,稱為 Xavier/Glorot 初始化,它有助于保持激活函數輸出的方差在網絡的各層之間相對一致。bound = 1 / math.sqrt(module.weight.shape[0])# torch.nn.init.uniform_(tensor, a=0.0, b=1.0)# 在PyTorch中, nn.init.uniform_() 是一個用于初始化張量參數的函數,它將張量中的元素值從均勻分布中隨機采樣。這個函數通常用于初始化神經網絡中的權重和偏置參數。# 參數 :# tensor ( torch.Tensor ): 需要被初始化的張量。# a ( float , 可選): 均勻分布的下限,默認為0.0。# b ( float , 可選): 均勻分布的上限,默認為1.0。# 功能 :# nn.init.uniform_() 函數的主要功能是為張量提供一個隨機的初始值,這些值是從指定的均勻分布中采樣的。# 這種初始化方法可以幫助打破對稱性,使得神經網絡的權重在訓練開始時具有不同的值,從而有助于訓練過程的收斂。均勻初始化是一種常用的初始化方法,特別是在沒有特定先驗知識的情況下。# 使用均勻分布來初始化權重。# uniform_(...) :這是一個函數,用于將張量(Tensor)的元素值從均勻分布中隨機采樣。# module.weight :指定要初始化的權重張量。# -bound 和 bound :指定均勻分布的下限和上限,即權重的取值范圍。uniform_(module.weight, -bound, bound)# 檢查線性層是否有偏置項,并且偏置項是否不為 None 。 hasattr 函數用于檢查對象是否有指定的屬性, module.bias 檢查是否存在偏置項。if hasattr(module, "bias") and module.bias is not None:# 如果存在偏置項,使用與權重相同的均勻分布范圍來初始化偏置項。uniform_(module.bias, -bound, bound)
# linear_init 函數用于對線性層的權重和偏置進行初始化。權重使用 Xavier/Glorot 初始化策略,即從 [-bound, bound] 范圍內的均勻分布中采樣,其中 bound 是根據權重矩陣的輸入特征數量計算得到的。如果線性層有偏置項,偏置項也會被以相同的方式初始化。這種初始化方法有助于在訓練神經網絡時保持激活函數輸出的穩定性。
5.def inverse_sigmoid(x, eps=1e-5):?
# 這段代碼定義了一個名為 inverse_sigmoid 的函數,它用于計算輸入 x 的 Sigmoid 函數的逆函數。
# 定義了一個名為 inverse_sigmoid 的函數,它接受兩個參數。
# 1.x :是輸入值。
# 2.eps 是一個很小的數,用于防止除以零的錯誤,默認值為 1e-5 。
def inverse_sigmoid(x, eps=1e-5):# 計算張量的逆 sigmoid 函數。"""Calculate the inverse sigmoid function for a tensor."""# torch.clamp(input, min=None, max=None)# torch.clamp() 是 PyTorch 庫中的一個函數,用于將張量中的元素限制在指定的范圍內。如果元素超出了這個范圍,它們將被設置為范圍的上限或下限。# 參數 :# input :要進行裁剪的輸入張量。# min :元素的最小值。默認為 None ,表示不設置下界。# max :元素的最大值。默認為 None ,表示不設置上界。# 返回值 :# 返回一個新的張量,其中的元素被限制在 [min, max] 范圍內。# 注意事項 :# torch.clamp() 函數返回的是新張量,原始輸入張量不會被修改。# 如果需要在原地修改張量,可以使用 clamped_() 方法,例如 tensor.clamp_(0, 3) 。# torch.clamp() 可以用于多維張量,并且可以指定不同的 min 和 max 值用于不同的維度。# min 和 max 參數也可以是標量值,或者與輸入張量形狀相同的張量,用于對不同元素應用不同的限制。# 使用 clamp 方法將 x 的值限制在 [0, 1] 范圍內。這是因為 Sigmoid 函數的輸出值應該在這個區間內, clamp 方法確保即使輸入值超出這個范圍,函數也能正常工作。x = x.clamp(min=0, max=1)# 再次使用 clamp 方法,將 x 的值限制在 [eps, 1] 范圍內。這是為了防止在計算 x1 / x2 時, x 接近 0 導致除以一個非常小的數,從而產生數值不穩定。x1 = x.clamp(min=eps)# 計算 1 - x 并將結果限制在 [eps, 1] 范圍內。這是出于與 x1 相同的理由,確保在計算 x1 / x2 時, 1 - x 接近 0 也不會導致除以一個非常小的數。x2 = (1 - x).clamp(min=eps)# 計算 x1 除以 x2 的自然對數,并返回結果。這個計算實際上是 Sigmoid 函數的逆操作。Sigmoid 函數將任意實數映射到 (0, 1) 區間,其逆函數可以將 (0, 1) 區間內的值映射回原始的實數空間。return torch.log(x1 / x2)
# inverse_sigmoid 函數計算輸入 x 的 Sigmoid 逆值。它首先確保輸入值在 [0, 1] 區間內,然后計算 x 和 1 - x 的值,并將它們限制在一個非常小的正數 eps 以上,以避免數值計算中的不穩定。最后,它返回 x 和 1 - x 的比值的自然對數,這個值是 x 的 Sigmoid 逆值。這種逆 Sigmoid 轉換在某些機器學習任務中很有用,比如在處理經過 Sigmoid 函數的輸出時,需要將其轉換回原始的預測值。
6.def multi_scale_deformable_attn_pytorch(value: torch.Tensor, value_spatial_shapes: torch.Tensor, sampling_locations: torch.Tensor, attention_weights: torch.Tensor,) -> torch.Tensor:?
# 這段代碼定義了一個名為 multi_scale_deformable_attn_pytorch 的函數,它實現了多尺度可變形注意力機制(Multi-Scale Deformable Attention),這是一種在計算機視覺任務中,特別是在目標檢測和圖像分割中使用的高級注意力機制。
# 定義了函數 multi_scale_deformable_attn_pytorch ,它接受四個參數。
# 1.value :值特征圖。
# 2.value_spatial_shapes :不同尺度值特征圖的空間形狀。
# 3.sampling_locations :采樣位置。
# 4.attention_weights :注意力權重
# 返回一個 torch.Tensor 類型的輸出。
def multi_scale_deformable_attn_pytorch(value: torch.Tensor,value_spatial_shapes: torch.Tensor,sampling_locations: torch.Tensor,attention_weights: torch.Tensor,
) -> torch.Tensor:# 多尺度可變形注意力。"""Multiscale deformable attention.https://github.com/IDEA-Research/detrex/blob/main/detrex/layers/multi_scale_deform_attn.py"""# 從 value 張量中提取出 批量大小 bs 、 多頭注意力的頭數 num_heads 和 嵌入維度 embed_dims 。bs, _, num_heads, embed_dims = value.shape# 從 sampling_locations 張量中提取出 查詢的數量 num_queries 、 多頭注意力的頭數 num_heads 、 尺度級別 num_levels 和 每個查詢點的采樣點數 num_points 。_, num_queries, num_heads, num_levels, num_points, _ = sampling_locations.shape# 將 value 張量按照 value_spatial_shapes 中的形狀分割成多個子張量,每個子張量對應不同尺度的特征圖。value_list = value.split([H_ * W_ for H_, W_ in value_spatial_shapes], dim=1)# 這行代碼是將采樣位置從某種歸一化的坐標系轉換到 grid_sample 函數所需要的坐標系。在 PyTorch 中, grid_sample 函數期望的輸入坐標范圍是 [-1, 1] ,這對應于輸入特征圖的歸一化坐標,其中 -1 表示特征圖的最左邊(或最頂部), 1 表示最右邊(或最底部)。# sampling_locations :這是一個包含采樣位置的張量,其坐標可能是相對于特征圖的中心點歸一化的,范圍在 [0, 1] 之間。例如,如果 sampling_locations 中的值為 0 ,則表示特征圖的中心點; 0.5 表示特征圖的右邊緣(或下邊緣); -0.5 表示特征圖的左邊緣(或上邊緣)。# 2 * sampling_locations - 1 :這個表達式將 [0, 1] 范圍的坐標轉換為 [-1, 1] 范圍。具體來說,它首先將坐標乘以 2 ,這樣原來 [0, 1] 的范圍就變成了 [0, 2] ,然后減去 1 ,使得范圍最終變為 [-1, 1] 。# 這種轉換是必要的,因為 grid_sample 函數使用 [-1, 1] 范圍的坐標來確定采樣點在特征圖上的位置。通過這種方式,采樣點的坐標可以正確地映射到特征圖的邊界上,從而允許 grid_sample 函數進行雙線性插值并獲取正確的像素值。sampling_grids = 2 * sampling_locations - 1# 初始化一個空列表,用于存儲每個尺度的采樣值。sampling_value_list = []# 這段代碼是多尺度可變形注意力機制中的一部分,用于對每個尺度的特征圖進行采樣。# 開始一個循環,遍歷每個尺度的特征圖。 level 是當前尺度的索引, (H_, W_) 是當前尺度特征圖的高度和寬度。for level, (H_, W_) in enumerate(value_spatial_shapes):# bs, H_*W_, num_heads, embed_dims -># bs, H_*W_, num_heads*embed_dims -># bs, num_heads*embed_dims, H_*W_ -># bs*num_heads, embed_dims, H_, W_# 對當前尺度的特征圖進行一系列變換。# flatten(2) :將 value_list[level] 的最后兩個維度( num_heads 和 embed_dims )合并。# transpose(1, 2) :交換合并后的維度和 H_*W_ 維度的位置。# reshape(bs * num_heads, embed_dims, H_, W_) :將張量重塑為 (batch_size * num_heads, embed_dims, H_, W_) 的形狀,以適應 grid_sample 函數的輸入要求。value_l_ = value_list[level].flatten(2).transpose(1, 2).reshape(bs * num_heads, embed_dims, H_, W_)# bs, num_queries, num_heads, num_points, 2 -># bs, num_heads, num_queries, num_points, 2 -># bs*num_heads, num_queries, num_points, 2# 對當前尺度的采樣網格進行變換。# sampling_grids[:, :, :, level] :選擇當前尺度的采樣網格。# transpose(1, 2) :交換 num_heads 和 num_queries 維度。# flatten(0, 1) :將 batch_size 和 num_heads 維度合并,并與 num_queries 維度一起展平。sampling_grid_l_ = sampling_grids[:, :, :, level].transpose(1, 2).flatten(0, 1)# torch.nn.functional.grid_sample(input, grid, mode='bilinear', padding_mode='zeros', align_corners=None)# torch.nn.functional.grid_sample 是 PyTorch 中的一個函數,它用于根據給定的坐標網格對輸入張量進行采樣。這個函數常用于圖像變形、數據增強等任務。# 參數說明 :# input : 輸入張量,可以是 4D 張量(N, C, H, W)或 5D 張量(N, C, D, H, W),分別代表批量圖像或體積數據。# grid : 坐標網格張量,用于指定采樣位置。對于 4D 輸入, grid 的形狀為(N, H_out, W_out, 2),對于 5D 輸入, grid 的形狀為(N, D_out, H_out, W_out, 3)。# mode : 采樣模式,可以是 'bilinear'(雙線性插值)或 'nearest'(最近鄰插值)。# padding_mode : 填充模式,可以是 'zeros'(用 0 填充)或 'border'(用邊界值填充)。# align_corners : 布爾值,指示是否將網格坐標與輸入張量的角點對齊。如果設置為 True,則網格坐標被視為指向像素的角點;如果設置為 False,則網格坐標被視為指向像素之間的中心點。默認值為 None,此時與 align_corners=False 相同。# 功能描述 :# F.grid_sample 函數根據 grid 中的坐標網格對 input 張量進行采樣。坐標網格的值通常在 [-1, 1] 之間,(-1, -1) 表示輸入張量左上角的元素,(1, 1) 表示右下角的元素。該函數可以使用雙線性插值或最近鄰插值來計算采樣點的值。# 返回值 :# 返回采樣后的輸出張量,其形狀為(N, C, H_out, W_out)或(N, C, D_out, H_out, W_out),取決于輸入張量和網格的形狀。# bs*num_heads, embed_dims, num_queries, num_points# 使用 grid_sample 函數對當前尺度的特征圖 value_l_ 進行采樣,采樣位置由 sampling_grid_l_ 指定。采樣模式為雙線性插值( bilinear ),填充模式為零( zeros ),并且不使用對齊角( align_corners=False )。sampling_value_l_ = F.grid_sample(value_l_, sampling_grid_l_, mode="bilinear", padding_mode="zeros", align_corners=False)# 將當前尺度的采樣值 sampling_value_l_ 添加到列表 sampling_value_list 中。sampling_value_list.append(sampling_value_l_)# 這個循環對每個尺度的特征圖進行采樣,得到每個查詢點的采樣值。這些采樣值將用于后續的加權求和,以生成最終的注意力輸出。通過在每個尺度上進行采樣,模型能夠捕捉到不同尺度的特征信息,從而提高對空間關系的感知能力。# 這段代碼是多尺度可變形注意力機制中的最后幾步,用于計算最終的輸出。# (bs, num_queries, num_heads, num_levels, num_points) -># (bs, num_heads, num_queries, num_levels, num_points) -># (bs, num_heads, 1, num_queries, num_levels*num_points)# 對注意力權重進行轉置和重塑,以適應后續的計算。# attention_weights.transpose(1, 2) :將 attention_weights 張量進行轉置,交換 num_queries 和 num_heads 維度的位置。# reshape(bs * num_heads, 1, num_queries, num_levels * num_points) :將轉置后的張量重塑為新的維度,其中 bs * num_heads 是批量大小和頭數的乘積, 1 是一個額外的維度, num_queries 是查詢的數量, num_levels * num_points 是所有級別的采樣點總數。attention_weights = attention_weights.transpose(1, 2).reshape(bs * num_heads, 1, num_queries, num_levels * num_points)# 計算加權和,并將結果重塑為最終輸出的形狀。# torch.stack(sampling_value_list, dim=-2) :將 sampling_value_list 中的張量沿著倒數第二個維度堆疊起來,形成一個新維度。# flatten(-2) :將堆疊后的張量在倒數第二個維度上展平,即將 num_levels 和 num_points 合并。# * attention_weights :將展平后的采樣值張量與 attention_weights 張量相乘,執行逐元素的乘法。# .sum(-1) :沿著最后一個維度(即 num_levels * num_points )對乘積進行求和,將所有采樣點的貢獻聚合起來。# .view(bs, num_heads * embed_dims, num_queries) :將求和后的張量重塑為最終的輸出形狀,其中 bs 是批量大小, num_heads * embed_dims 是合并后的嵌入維度, num_queries 是查詢的數量。output = ((torch.stack(sampling_value_list, dim=-2).flatten(-2) * attention_weights).sum(-1).view(bs, num_heads * embed_dims, num_queries))# 將輸出張量進行轉置,并確保其在內存中是連續的,然后返回,形狀為 (batch_size, num_queries, num_heads * embed_dims) 。# output.transpose(1, 2) :將輸出張量再次轉置,交換 num_heads * embed_dims 和 num_queries 維度的位置。# .contiguous() :確保張量在內存中是連續存儲的,這對于某些 PyTorch 操作是必要的,特別是當張量需要被傳遞給 CUDA 時。return output.transpose(1, 2).contiguous()# 這段代碼將不同尺度的采樣值與對應的注意力權重相乘,然后對所有采樣點的貢獻進行求和,最后將結果重塑并轉置為最終的輸出張量。這個輸出張量包含了每個查詢在所有尺度上的加權特征表示,可以用于后續的網絡層。
# multi_scale_deformable_attn_pytorch 函數實現了多尺度可變形注意力機制,它通過在不同尺度的特征圖上采樣并加權,生成最終的輸出。這種機制能夠捕捉到不同尺度的特征信息,增強模型對空間關系的感知能力。