Skip to content

Commit 7328303

Browse files
committed
Added tests for residual block and fixed bugs for it.
1 parent d62ce6b commit 7328303

6 files changed

Lines changed: 304 additions & 39 deletions

File tree

src/faith/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
from . import core

src/faith/train/blocks/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
from .residual import ResidualBlock
44
from .encoder import EncoderBlock, BlockBasedEncoder
55
from .decoder import DecoderBlock, BlockBasedDecoder
6-
from .base import BaseBlock, BlockUtils
6+
from .base import BaseConvBlock, BlockUtils
77

88
__all__ = [
99
"ResidualBlock",
1010
"EncoderBlock",
1111
"BlockBasedEncoder",
1212
"DecoderBlock",
1313
"BlockBasedDecoder",
14-
"BaseBlock",
14+
"BaseConvBlock",
1515
"BlockUtils",
1616
]

src/faith/train/blocks/base.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
import math
1414

1515

16-
class BaseBlock(nn.Module, ABC):
17-
"""Abstract base class for all neural network blocks.
16+
class BaseConvBlock(nn.Module, ABC):
17+
"""Abstract base class for all convolutional-based neural network blocks.
1818
1919
This class defines the common interface that all blocks should implement,
2020
ensuring consistency across different block types in the autoencoder
@@ -60,6 +60,21 @@ def __init__(
6060
raise ValueError(
6161
f"out_channels must be positive, got {out_channels}")
6262

63+
if not isinstance(in_channels, int):
64+
raise TypeError(
65+
f"in_channels must be an int, got {type(in_channels)}")
66+
67+
if not isinstance(out_channels, int):
68+
raise TypeError(
69+
f"out_channels must be an int, got {type(out_channels)}")
70+
71+
if isinstance(kernel_size, int) and kernel_size <= 0:
72+
raise ValueError(
73+
f"kernel_size must be positive, got {kernel_size}")
74+
if isinstance(kernel_size, tuple) and any(k <= 0 for k in kernel_size):
75+
raise ValueError(
76+
f"kernel_size must be positive, got {kernel_size}")
77+
6378
self.in_channels = in_channels
6479
self.out_channels = out_channels
6580
self.kernel_size = self._normalize_kernel_size(kernel_size)
@@ -82,11 +97,11 @@ def _calculate_padding(
8297
"""Calculate padding based on kernel size and padding specification."""
8398
if padding == 'auto':
8499
if isinstance(kernel_size, int):
85-
return (kernel_size // 2, kernel_size // 2)
100+
return kernel_size // 2, kernel_size // 2
86101
else:
87102
return tuple(k // 2 for k in kernel_size)
88103
elif isinstance(padding, int):
89-
return (padding, padding)
104+
return padding, padding
90105
else:
91106
return padding
92107

@@ -140,7 +155,7 @@ def __repr__(self) -> str:
140155
f"bias={self.bias})")
141156

142157

143-
class SequentialBlock(BaseBlock):
158+
class SequentialBlock(BaseConvBlock):
144159
"""Base class for blocks that apply operations sequentially.
145160
146161
This class provides common functionality for blocks that consist of
@@ -181,7 +196,7 @@ def add_operation(self, operation: nn.Module) -> None:
181196
self.operations.add_module(str(len(self.operations)), operation)
182197

183198

184-
class ConfigurableBlock(BaseBlock):
199+
class ConfigurableBlock(BaseConvBlock):
185200
"""
186201
Base class for blocks with extensive configuration options.
187202

src/faith/train/blocks/residual.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
import torch
88
import torch.nn as nn
99
from typing import Union, Any
10-
from .base import BaseBlock, WeightInitializer
10+
from .base import BaseConvBlock, WeightInitializer
1111

1212

13-
class ResidualBlock(BaseBlock):
13+
class ResidualBlock(BaseConvBlock):
1414
"""Residual convolutional block with batch normalization and ReLU.
1515
1616
This block implements a standard residual connection with two convolutional
@@ -27,9 +27,6 @@ class ResidualBlock(BaseBlock):
2727
Size of the convolving kernel.
2828
stride : int or tuple of int, default=1
2929
Stride of the convolution.
30-
padding : int, tuple of int, or str, default='auto'
31-
Padding added to all four sides of the input. If 'auto', padding is
32-
calculated to maintain spatial dimensions when stride=1.
3330
bias : bool, default=True
3431
If True, adds a learnable bias to the output.
3532
use_batch_norm : bool, default=True
@@ -78,7 +75,6 @@ def __init__(
7875
out_channels: int,
7976
kernel_size: Union[int, tuple[int, int]] = 3,
8077
stride: Union[int, tuple[int, int]] = 1,
81-
padding: Union[int, tuple[int, int], str] = 'auto',
8278
bias: bool = True,
8379
use_batch_norm: bool = True,
8480
activation: str = 'relu',
@@ -96,8 +92,6 @@ def __init__(
9692
Size of the convolving kernel.
9793
stride : int or tuple of int, default=1
9894
Stride of the convolution.
99-
padding : int, tuple of int, or str, default='auto'
100-
Padding specification.
10195
bias : bool, default=True
10296
Whether to use bias in convolutions.
10397
use_batch_norm : bool, default=True
@@ -110,9 +104,20 @@ def __init__(
110104
# Initialize base class
111105
super().__init__(in_channels, out_channels, kernel_size, bias)
112106

107+
if isinstance(stride, int) and stride < 1:
108+
raise ValueError(f"Stride must be a positive integer or tuple, "
109+
f"got {stride}")
110+
if isinstance(stride, tuple) and any(s < 1 for s in stride):
111+
raise ValueError(f"Stride must be a positive integer or tuple, "
112+
f"got {stride}")
113+
if (isinstance(stride, float) or isinstance(stride, tuple)
114+
and any(isinstance(s, float) for s in stride)):
115+
raise TypeError(f"Stride must be an integer or tuple, "
116+
f"got float {stride}")
117+
113118
# Normalize stride and padding
114119
self.stride = self._normalize_stride(stride)
115-
self.padding = self._calculate_padding(self.kernel_size, padding)
120+
self.padding = self._calculate_padding(self.kernel_size, "auto")
116121
self.use_batch_norm = use_batch_norm
117122
self.activation_name = activation
118123
self.init_method = init_method
@@ -239,8 +244,9 @@ def forward(self, x: torch.Tensor) -> torch.Tensor:
239244
Returns
240245
-------
241246
torch.Tensor
242-
Output tensor with shape (batch_size, out_channels, height', width')
243-
where height' and width' depend on stride.
247+
Output tensor with shape
248+
(batch_size, out_channels, height', width') where height'
249+
and 'width' depend on stride.
244250
"""
245251
# Store input for residual connection
246252
residual = x
@@ -350,4 +356,4 @@ def get_output_shape(
350356

351357
# Update channels
352358
batch_size, _, height, width = temp_shape
353-
return (batch_size, self.out_channels, height, width)
359+
return batch_size, self.out_channels, height, width

tests/test_train_blocks_base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import torch
2-
from src.faith.train.blocks import BaseBlock, BlockUtils
2+
from src.faith.train.blocks import BaseConvBlock, BlockUtils
33

44

55
# Example of how the base classes would be used
66

7-
class ExampleBlock(BaseBlock):
7+
class ExampleBlock(BaseConvBlock):
88
"""Example implementation of BaseBlock."""
99

1010
def __init__(self, in_channels: int, out_channels: int, **kwargs):

0 commit comments

Comments
 (0)