Streszczenie AI
Konwolucyjne sieci neuronowe (CNN) wykorzystują zasady lokalnej łączności, współdzielenia wag i translacyjnej ekwiwariantności, co pozwala zachować strukturę danych i znacznie redukować liczbę parametrów w porównaniu z warstwami gęstymi. 1‑D i 2‑D sploty, w implementacjach głębokiego uczenia często interpretowane jako korelacje, są sterowane paddingiem, stride’em i dilacją, a ich złożoność i liczba parametrów można precyzyjnie obliczyć przy podanych wzorach; przejście do depthwise‑separable konwolucji redukuje operacje mnożenia do 8‑9‑krotkiego, a warstwy residualne z BatchNorm/ ReLU uzyskują stabilny przepływ gradientów w popularnych frameworkach. Dzięki temu architektury takie jak MobileNet, DeepLab czy UNet realizują wydajne segmentacje obrazów oraz analiza szeregów czasowych na platformach wbudowanych, zachowując deterministyczny profil pamięciowy i pełne wsparcie akceleratorów sprzętowych.
Spis treści:
- Matematyczne podstawy operacji konwolucji (1D i 2D)
- Parametry sterujące: Padding, Stride i Dilated Convolution
- Warstwy składowe sieci splotowych
- Ewolucja architektur: od LeNet do nowoczesnych motywów blokowych
- Analiza wymiarów tensorów i złożoność obliczeniowa
- Praktyczna implementacja: PyTorch vs. TensorFlow
- Zastosowania praktyczne i wytyczne inżynieryjne
- Źródła
Konwolucyjne sieci neuronowe (ang. Convolutional Neural Networks, CNN) stanowią fundament współczesnego przetwarzania sygnałów jedno- i wielowymiarowych w widzeniu komputerowym oraz analizie szeregów czasowych. Ich konstrukcja opiera się na trzech kluczowych zasadach: lokalnej łączności (ang. local connectivity), współdzieleniu wag (ang. weight sharing) oraz ekwiwariantności względem translacji (ang. translation equivariance). W przeciwieństwie do gęstych warstw liniowych (ang. fully-connected), w których każdy neuron wejściowy łączy się z każdym neuronem wyjściowym, operacja splotu pozwala zachować topologię danych przy drastycznym ograniczeniu liczby swobodnych parametrów.
Matematyczne podstawy operacji konwolucji (1D i 2D)
W ścisłym sensie matematycznym ciągła konwolucja (splot) dwóch funkcji f i g zdefiniowana jest jako całka:
(f * g)(t) = \int_{-\infty}^{\infty} f(\tau) g(t – \tau) \, d\tauW dyskretnych implementacjach stosowanych w uczeniu głębokim biblioteki obliczeniowe (np. cuDNN, PyTorch, TensorFlow) realizują w rzeczywistości operację wzajemnej korelacji (ang. cross-correlation), rezygnując z lustrzanego odbicia jądra (ang. kernel flipping). Różnica ta nie ma wpływu na proces optymalizacji, ponieważ wagi jądra są parametrami uczonymi gradientowo podczas propagacji wstecznej.
W przypadku dyskretnym 1D (stosowanym m.in. w sygnałach audio, analizie EKG lub modelowaniu sekwencji tekstowych) sygnał wejściowy x \in \mathbb{R}^L splotowany z filtrem w \in \mathbb{R}^K daje wyjście y \in \mathbb{R}^{L’}:
y[i] = (x * w)[i] = \sum_{k=0}^{K-1} x[i+k] \cdot w[k]Dla tensora dwuwymiarowego (2D) z wieloma kanałami wejściowymi, gdzie wejście ma postać X \in \mathbb{R}^{C_{\text{in}} \times H \times W}, a zestaw wag dla c_{\text{out}}-tego filtra stanowi jądro W_{c_{\text{out}}} \in \mathbb{R}^{C_{\text{in}} \times K_h \times K_w} wraz z obciążeniem b_{c_{\text{out}}} \in \mathbb{R}, pojedynczy punkt na mapie cech Y \in \mathbb{R}^{C_{\text{out}} \times H_{\text{out}} \times W_{\text{out}}} wyraża się wzorem:
Y[c_{\text{out}}, i, j] = b_{c_{\text{out}}} + \sum_{c_{\text{in}}=0}^{C_{\text{in}}-1} \sum_{m=0}^{K_h-1} \sum_{n=0}^{K_w-1} X[c_{\text{in}}, i+m, j+n] \cdot W[c_{\text{out}}, c_{\text{in}}, m, n]Wymiary wyjściowe mapy cech zależą bezpośrednio od parametrów siatki dyskretyzacji: paddingu (P), kroku (S), rozmiaru jądra (K) oraz dylatacji (D).
Parametry sterujące: Padding, Stride i Dilated Convolution
Kontrola przestrzennej propagacji sygnału przez warstwy splotowe wymaga precyzyjnego doboru hiperparametrów:
- Padding (P): Dopełnienie brzegów tensora dodatkowymi wartościami (najczęściej zerami, ang. zero-padding). Chroni ono przed przedwczesną utratą rozdzielczości przestrzennej na brzegach oraz zapobiega ucinaniu informacji brzeżnych. Wyróżnia się konfigurację valid (brak paddingu, P = 0) oraz same (dobór paddingu P = \lfloor K/2 \rfloor przy kroku S = 1, co zachowuje wyjściowe wymiary równe wejściowym).
- Stride (S): Krok przesunięcia jądra wzdłuż osi przestrzennych. Ustawienie S > 1 powoduje próbkowanie w dół (ang. strided downsampling), zastępując tradycyjne warstwy poolingowe bez wprowadzania nieparametrycznych operacji redukcji.
- Dilated (Atrous) Convolution (D): Splot z odstępami (dziurami) pomiędzy wagami filtra. Rozszerza pole recepcyjne (ang. receptive field) warstwy bez zwiększania liczby parametrów i bez utraty gęstej rozdzielczości przestrzennej. Efektywny rozmiar filtra K_{\text{eff}} wynosi K_{\text{eff}} = K + (K – 1)(D – 1). Znalazł kluczowe zastosowanie m.in. w architekturach DeepLab w zadaniach segmentacji semantycznej oraz w modelach sekwencyjnych WaveNet.
Ogólny wzór na przestrzenny wymiar wyjściowy dla danej osi ma postać:
H_{\text{out}} = \left\lfloor \frac{H_{\text{in}} + 2P – D(K – 1) – 1}{S} \right\rfloor + 1Warstwy składowe sieci splotowych
Stabilny przepływ gradientu i budowa nieliniowej reprezentacji wymagają zestawienia warstw splotowych z mechanizmami redukcji wymiarowości i normalizacji aktywacji.
- Warstwa konwolucyjna (Conv): Wyodrębnia lokalne reprezentacje cech poprzez zestaw uczonych filtrów reagujących na krawędzie, gradienty barwne, a w głębszych warstwach – na całe struktury semantyczne.
- Warstwy próbkowania (Pooling): Warstwy bezparametryczne, których celem jest redukcja wymiarów przestrzennych map cech, co ogranicza złożoność obliczeniową kolejnych bloków oraz zapewnia częściową niezmienniczość względem drobnych przesunięć. Najpowszechniejszy jest Max Pooling (wybór wartości maksymalnej w lokalnym oknie, zachowujący dominujące aktywacje) oraz Average Pooling (obliczanie średniej arytmetycznej, często stosowane globalnie jako Global Average Pooling na końcu sieci przed klasyfikatorem).
- Batch Normalization (BatchNorm): Technika normalizacji aktywacji wewnątrz mini-batcha wprowadzona przez Ioffe i Szegedy’ego. Dla tensora aktywacji 2D o kształcie (N, C, H, W) statystyki średniej \mu_c i wariancji \sigma_c^2 obliczane są osobno dla każdego kanału wzdłuż wymiarów (N, H, W). Warstwa wprowadza dwa uczone parametry dla każdego kanału: skalowanie \gamma i przesunięcie \beta. Drastycznie przyspiesza to zbieżność procesu optymalizacji, redukuje wrażliwość na inicjalizację wag oraz pełni rolę lekkiego regularyzatora.
Ewolucja architektur: od LeNet do nowoczesnych motywów blokowych
Rozwój sieci konwolucyjnych przeszedł drogę od prostych sekwencji warstw do zaawansowanych topologii wykorzystujących rezydualność i separację wymiarów.
Architektura | Rok | Kluczowe cechy konstrukcyjne | Wielkość filtrów | Rola / Ograniczenia |
|---|---|---|---|---|
| LeNet-5 | 1998 | Płytka sieć sekwencyjna (Conv-AvgPool-Conv-AvgPool-FC), aktywacje Sigmoid/Tanh | 5×5 | Prekursor klasyfikacji cyfr (MNIST); mała skalowalność do obrazów naturalnych |
| AlexNet | 2012 | Aktywacja ReLU, Local Response Normalization, Dropout, użycie 2x GPU | 11×11, 5×5, 3×3 | Przełom ImageNet; duży narzut pamięciowy ze względu na potężne warstwy FC |
| VGG (16/19) | 2014 | Jednolita topologia: stosy małych filtrów, podwajanie kanałów po poolingu | 3×3 | Faktoryzacja pola recepcyjnego (dwa filtry 3×3 dają zasięg 5×5 przy mniejszej liczbie wag); wysoki koszt obliczeniowy |
| ResNet | 2015 | Połączenia skrótowe (ang. skip connections, F(x) + x), eliminacja zanikającego gradientu | 3×3, 1×1 (Bottleneck) | Umożliwiła stabilne uczenie sieci o głębokości 50, 101, a nawet 152+ warstw |
| MobileNet (v1-v3) | 2017+ | Sploty rozdzielne głębokościowo (Depthwise Separable Convolutions), odwrócone bloki rezydualne | 3×3 DW, 1×1 PW | Projektowana pod urządzenia wbudowane/mobilne; radykalny spadek FLOPs |
| ConvNeXt | 2022 | Modernizacja klasycznego CNN z inspiracji Vision Transformers (ViT): jądra 7×7, LayerNorm, aktywacja GELU | 7×7 Depthwise | Przywrócenie konkurencyjności czystych modeli CNN względem architektur transformerowych |
Kluczową innowacją współczesnych sieci jest zastąpienie standardowego splotu 2D jego wariantem Depthwise Separable Convolution. Składa się on z dwóch etapów: splotu depthwise (pojedynczy filtr 2D aplikowany niezależnie dla każdego kanału wejściowego) oraz splotu pointwise (standardowy splot 1×1 agregujący informacje wzdłuż osi kanałów). Zabieg ten redukuje złożoność obliczeniową z K^2 \cdot C_{\text{in}} \cdot C_{\text{out}} do K^2 \cdot C_{\text{in}} + C_{\text{in}} \cdot C_{\text{out}}, co przy standardowym jądrze 3×3 oznacza około 8-krotne do 9-krotne zmniejszenie liczby operacji mnożenia i akumulacji.
Analiza wymiarów tensorów i złożoność obliczeniowa
W warstwie o parametrach: jądro K_h \times K_w, kanały wejściowe C_{\text{in}}, kanały wyjściowe C_{\text{out}} oraz rozmiar wyjścia H_{\text{out}} \times W_{\text{out}}, parametryzacja i koszt obliczeń kształtują się następująco:
- Liczba uczonych parametrów:
(gdzie +1 to wyraz wolny / bias na każdy kanał wyjściowy)
\text{MACs} \approx H_{\text{out}} \cdot W_{\text{out}} \cdot C_{\text{out}} \cdot (K_h \cdot K_w \cdot C_{\text{in}})
Liczba operacji zmiennoprzecinkowych (FLOPs) wynosi w przybliżeniu 2 \times \text{MACs}, z uwzględnieniem operacji dodawania i mnożenia. Oznacza to, że koszt obliczeniowy rośnie liniowo wraz z polem powierzchni wyjściowej mapy cech oraz kwadratowo względem szerokości kanałów przy symetrycznym skalowaniu C_{\text{in}} = C_{\text{out}}.
Praktyczna implementacja: PyTorch vs. TensorFlow
Poniżej przedstawiono porównanie implementacji modularnego bloku splotowego obejmującego splot 2D, normalizację wsadową, nieliniowość ReLU oraz rezydualne połączenie skrótowe w dwóch wiodących środowiskach uczenia maszynowego.
Wzorzec w środowisku PyTorch:
PyTorch przyjmuje konwencję formatu danych NCHW: (batch_size, channels, height, width).
import torch
import torch.nn as nn
class ResidualConvBlock(nn.Module):
def __init__(self, in_channels: int, out_channels: int, stride: int = 1):
super().__init__()
self.conv1 = nn.Conv2d(
in_channels, out_channels, kernel_size=3,
stride=stride, padding=1, bias=False
)
self.bn1 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(
out_channels, out_channels, kernel_size=3,
stride=1, padding=1, bias=False
)
self.bn2 = nn.BatchNorm2d(out_channels)
# Dopasowanie wymiarów przy zmianie liczby kanałów lub kroku
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != out_channels:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(out_channels)
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
residual = self.shortcut(x)
out = self.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
out += residual
return self.relu(out)
# Weryfikacja kształtu wejścia i wyjścia
x = torch.randn(8, 64, 56, 56) # NCHW
block = ResidualConvBlock(in_channels=64, out_channels=128, stride=2)
y = block(x)
print(f"PyTorch output shape: {y.shape}") # Zwraca: torch.Size([8, 128, 28, 28])
Wzorzec w środowisku TensorFlow / Keras:
TensorFlow domyślnie przetwarza dane w układzie NHWC: (batch_size, height, width, channels).
import tensorflow as tf
from tensorflow.keras import layers
class TFResidualConvBlock(layers.Layer):
def __init__(self, out_channels: int, stride: int = 1, **kwargs):
super().__init__(**kwargs)
self.out_channels = out_channels
self.stride = stride
def build(self, input_shape):
in_channels = input_shape[-1]
self.conv1 = layers.Conv2D(
self.out_channels, kernel_size=3, strides=self.stride,
padding="same", use_bias=False
)
self.bn1 = layers.BatchNormalization()
self.relu = layers.Activation("relu")
self.conv2 = layers.Conv2D(
self.out_channels, kernel_size=3, strides=1,
padding="same", use_bias=False
)
self.bn2 = layers.BatchNormalization()
if self.stride != 1 or in_channels != self.out_channels:
self.shortcut = tf.keras.Sequential([
layers.Conv2D(self.out_channels, kernel_size=1, strides=self.stride, use_bias=False),
layers.BatchNormalization()
])
else:
self.shortcut = lambda x: x
def call(self, inputs, training=False):
residual = self.shortcut(inputs)
x = self.conv1(inputs)
x = self.bn1(x, training=training)
x = self.relu(x)
x = self.conv2(x)
x = self.bn2(x, training=training)
return self.relu(x + residual)
# Weryfikacja kształtu wejścia i wyjścia
x_tf = tf.random.normal((8, 56, 56, 64)) # NHWC
tf_block = TFResidualConvBlock(out_channels=128, stride=2)
y_tf = tf_block(x_tf)
print(f"TensorFlow output shape: {y_tf.shape}") # Zwraca: (8, 28, 28, 128)
Zastosowania praktyczne i wytyczne inżynieryjne
Sieci splotowe pozostają wysoce wydajnym wyborem w architekturach produkcyjnych ze względu na deterministyczny profil pamięciowy i natywne wsparcie dla akceleratorów sprzętowych (Tensor Cores):
- Segmentacja obrazów w systemach brzegowych: Połączenie splotów depthwise separable z dylatacją pozwala na budowę kompaktowych sieci typu encoder-decoder (np. ENet, U-Net) zdolnych do działania na platformach wbudowanych w czasie rzeczywistym (>30 FPS).
- Przetwarzanie szeregów czasowych i telemetrii: Konwolucje 1D z rosnącą wykładniczo dylatacją (D = 1, 2, 4, 8, \dots) pozwalają na modelowanie długoterminowych zależności w sygnałach wibroakustycznych maszyn przemysłowych czy elektrokardiografii, oferując równoległe przetwarzanie wykluczające rekurencyjne wąskie gardła sieci LSTM.
- Wytyczna implementacyjna: Stosując
BatchNorm2dbezpośrednio poConv2d, należy zawsze wyłączyć wyraz wolny w warstwie splotowej (bias=False), ponieważ statystyka odejmowania średniej w procesie normalizacji całkowicie redukuje stały offset, oszczędzając parametry i operacje.





