تم التحديث في Pytorch 1.5
يمكنك العثور على الرمز هنا
Pytorch عبارة عن أطر عمل للتعلم العميق مفتوحة المصدر توفر طريقة ذكية لإنشاء نماذج تعلم الآلة. حتى لو تم إعداد التوثيق بشكل جيد، ما زلت أرى أن معظم الأشخاص لا يكتبون التعليمات البرمجية بشكل جيد ومنظم في PyTorch.
سنرى اليوم كيفية استخدام العناصر الأساسية الثلاثة لـ PyTorch: Module, Sequential and ModuleList
. سنبدأ بمثال وسنعمل على تحسينه بشكل متكرر.
كل هذه الفئات الأربع موجودة في torch.nn
import torch . nn as nn
# nn.Module
# nn.Sequential
# nn.Module
الوحدة هي لبنة البناء الرئيسية، فهي تحدد الفئة الأساسية لجميع الشبكات العصبية ويجب عليك تصنيفها فرعيًا.
لنقم بإنشاء مصنف CNN كلاسيكي كمثال:
import torch . nn . functional as F
class MyCNNClassifier ( nn . Module ):
def __init__ ( self , in_c , n_classes ):
super (). __init__ ()
self . conv1 = nn . Conv2d ( in_c , 32 , kernel_size = 3 , stride = 1 , padding = 1 )
self . bn1 = nn . BatchNorm2d ( 32 )
self . conv2 = nn . Conv2d ( 32 , 64 , kernel_size = 3 , stride = 1 , padding = 1 )
self . bn2 = nn . BatchNorm2d ( 64 )
self . fc1 = nn . Linear ( 64 * 28 * 28 , 1024 )
self . fc2 = nn . Linear ( 1024 , n_classes )
def forward ( self , x ):
x = self . conv1 ( x )
x = self . bn1 ( x )
x = F . relu ( x )
x = self . conv2 ( x )
x = self . bn2 ( x )
x = F . relu ( x )
x = x . view ( x . size ( 0 ), - 1 ) # flat
x = self . fc1 ( x )
x = F . sigmoid ( x )
x = self . fc2 ( x )
return x
model = MyCNNClassifier ( 1 , 10 )
print ( model )
MyCNNClassifier(
(conv1): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(bn1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(fc1): Linear(in_features=50176, out_features=1024, bias=True)
(fc2): Linear(in_features=1024, out_features=10, bias=True)
)
هذا مصنف بسيط جدًا يحتوي على جزء تشفير يستخدم طبقتين مع تحويلات 3x3 + Batchnorm + Relu وجزء فك تشفير بطبقتين خطيتين. إذا لم تكن جديدًا على PyTorch، فربما تكون قد شاهدت هذا النوع من البرمجة من قبل، ولكن هناك مشكلتان.
إذا أردنا إضافة طبقة علينا أن نكتب مرة أخرى الكثير من التعليمات البرمجية في __init__
وفي وظيفة forward
. أيضًا، إذا كان لدينا بعض الكتل المشتركة التي نريد استخدامها في نموذج آخر، على سبيل المثال، 3x3 conv + Batchnorm + relu، فيجب علينا كتابتها مرة أخرى.
Sequential عبارة عن حاوية من الوحدات التي يمكن تكديسها معًا وتشغيلها في نفس الوقت.
يمكنك ملاحظة أنه يتعين علينا تخزين كل شيء في self
. يمكننا استخدام Sequential
لتحسين الكود الخاص بنا.
class MyCNNClassifier ( nn . Module ):
def __init__ ( self , in_c , n_classes ):
super (). __init__ ()
self . conv_block1 = nn . Sequential (
nn . Conv2d ( in_c , 32 , kernel_size = 3 , stride = 1 , padding = 1 ),
nn . BatchNorm2d ( 32 ),
nn . ReLU ()
)
self . conv_block2 = nn . Sequential (
nn . Conv2d ( 32 , 64 , kernel_size = 3 , stride = 1 , padding = 1 ),
nn . BatchNorm2d ( 64 ),
nn . ReLU ()
)
self . decoder = nn . Sequential (
nn . Linear ( 64 * 28 * 28 , 1024 ),
nn . Sigmoid (),
nn . Linear ( 1024 , n_classes )
)
def forward ( self , x ):
x = self . conv_block1 ( x )
x = self . conv_block2 ( x )
x = x . view ( x . size ( 0 ), - 1 ) # flat
x = self . decoder ( x )
return x
model = MyCNNClassifier ( 1 , 10 )
print ( model )
MyCNNClassifier(
(conv_block1): Sequential(
(0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
(conv_block2): Sequential(
(0): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
(decoder): Sequential(
(0): Linear(in_features=50176, out_features=1024, bias=True)
(1): Sigmoid()
(2): Linear(in_features=1024, out_features=10, bias=True)
)
)
أفضل بكثير أهو؟
هل لاحظت أن conv_block1
و conv_block2
يبدوان متماثلين تقريبًا؟ يمكننا إنشاء دالة تعيد nn.Sequential
حتى لتبسيط الكود!
def conv_block ( in_f , out_f , * args , ** kwargs ):
return nn . Sequential (
nn . Conv2d ( in_f , out_f , * args , ** kwargs ),
nn . BatchNorm2d ( out_f ),
nn . ReLU ()
)
ثم يمكننا فقط استدعاء هذه الوظيفة في الوحدة النمطية الخاصة بنا
class MyCNNClassifier ( nn . Module ):
def __init__ ( self , in_c , n_classes ):
super (). __init__ ()
self . conv_block1 = conv_block ( in_c , 32 , kernel_size = 3 , padding = 1 )
self . conv_block2 = conv_block ( 32 , 64 , kernel_size = 3 , padding = 1 )
self . decoder = nn . Sequential (
nn . Linear ( 64 * 28 * 28 , 1024 ),
nn . Sigmoid (),
nn . Linear ( 1024 , n_classes )
)
def forward ( self , x ):
x = self . conv_block1 ( x )
x = self . conv_block2 ( x )
x = x . view ( x . size ( 0 ), - 1 ) # flat
x = self . decoder ( x )
return x
model = MyCNNClassifier ( 1 , 10 )
print ( model )
MyCNNClassifier(
(conv_block1): Sequential(
(0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
(conv_block2): Sequential(
(0): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
(decoder): Sequential(
(0): Linear(in_features=50176, out_features=1024, bias=True)
(1): Sigmoid()
(2): Linear(in_features=1024, out_features=10, bias=True)
)
)
حتى أنظف! لا يزال conv_block1
و conv_block2
متماثلين تقريبًا! يمكننا دمجها باستخدام nn.Sequential
class MyCNNClassifier ( nn . Module ):
def __init__ ( self , in_c , n_classes ):
super (). __init__ ()
self . encoder = nn . Sequential (
conv_block ( in_c , 32 , kernel_size = 3 , padding = 1 ),
conv_block ( 32 , 64 , kernel_size = 3 , padding = 1 )
)
self . decoder = nn . Sequential (
nn . Linear ( 64 * 28 * 28 , 1024 ),
nn . Sigmoid (),
nn . Linear ( 1024 , n_classes )
)
def forward ( self , x ):
x = self . encoder ( x )
x = x . view ( x . size ( 0 ), - 1 ) # flat
x = self . decoder ( x )
return x
model = MyCNNClassifier ( 1 , 10 )
print ( model )
MyCNNClassifier(
(encoder): Sequential(
(0): Sequential(
(0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
(1): Sequential(
(0): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
)
(decoder): Sequential(
(0): Linear(in_features=50176, out_features=1024, bias=True)
(1): Sigmoid()
(2): Linear(in_features=1024, out_features=10, bias=True)
)
)
يحمل self.encoder
الآن كشك conv_block
. لقد قمنا بفصل المنطق الخاص بنموذجنا وتسهيل قراءته وإعادة استخدامه. يمكن استيراد وظيفة conv_block
الخاصة بنا واستخدامها في نموذج آخر.
ماذا لو تمكنا من إضافة طبقات جديدة في self.encoder
، فإن ترميزها بشكل ثابت ليس مناسبًا:
self . encoder = nn . Sequential (
conv_block ( in_c , 32 , kernel_size = 3 , padding = 1 ),
conv_block ( 32 , 64 , kernel_size = 3 , padding = 1 ),
conv_block ( 64 , 128 , kernel_size = 3 , padding = 1 ),
conv_block ( 128 , 256 , kernel_size = 3 , padding = 1 ),
)
هل سيكون من الرائع أن نتمكن من تحديد الأحجام كمصفوفة وإنشاء جميع الطبقات تلقائيًا دون كتابة كل واحدة منها؟ لحسن الحظ يمكننا إنشاء مصفوفة وتمريرها إلى Sequential
class MyCNNClassifier ( nn . Module ):
def __init__ ( self , in_c , n_classes ):
super (). __init__ ()
self . enc_sizes = [ in_c , 32 , 64 ]
conv_blocks = [ conv_block ( in_f , out_f , kernel_size = 3 , padding = 1 )
for in_f , out_f in zip ( self . enc_sizes , self . enc_sizes [ 1 :])]
self . encoder = nn . Sequential ( * conv_blocks )
self . decoder = nn . Sequential (
nn . Linear ( 64 * 28 * 28 , 1024 ),
nn . Sigmoid (),
nn . Linear ( 1024 , n_classes )
)
def forward ( self , x ):
x = self . encoder ( x )
x = x . view ( x . size ( 0 ), - 1 ) # flat
x = self . decoder ( x )
return x
model = MyCNNClassifier ( 1 , 10 )
print ( model )
MyCNNClassifier(
(encoder): Sequential(
(0): Sequential(
(0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
(1): Sequential(
(0): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
)
(decoder): Sequential(
(0): Linear(in_features=50176, out_features=1024, bias=True)
(1): Sigmoid()
(2): Linear(in_features=1024, out_features=10, bias=True)
)
)
دعونا كسرها. لقد أنشأنا مصفوفة self.enc_sizes
تحتوي على أحجام برنامج التشفير الخاص بنا. ثم نقوم بإنشاء مصفوفة conv_blocks
من خلال تكرار الأحجام. نظرًا لأننا يجب أن نعطي حجمًا وحجمًا كبيرًا لكل طبقة، فقد قمنا zip
مصفوفة الحجم مع نفسها عن طريق إزاحتها بمقدار واحد.
ولتوضيح الأمر فقط، ألقِ نظرة على المثال التالي:
sizes = [ 1 , 32 , 64 ]
for in_f , out_f in zip ( sizes , sizes [ 1 :]):
print ( in_f , out_f )
1 32
32 64
بعد ذلك، نظرًا لأن Sequential
لا يقبل القائمة، فإننا نقوم بتحليلها باستخدام عامل التشغيل *
.
تادا! الآن، إذا أردنا فقط إضافة حجم، فيمكننا بسهولة إضافة رقم جديد إلى القائمة. إنها ممارسة شائعة لجعل الحجم معلمة.
class MyCNNClassifier ( nn . Module ):
def __init__ ( self , in_c , enc_sizes , n_classes ):
super (). __init__ ()
self . enc_sizes = [ in_c , * enc_sizes ]
conv_blocks = [ conv_block ( in_f , out_f , kernel_size = 3 , padding = 1 )
for in_f , out_f in zip ( self . enc_sizes , self . enc_sizes [ 1 :])]
self . encoder = nn . Sequential ( * conv_blocks )
self . decoder = nn . Sequential (
nn . Linear ( 64 * 28 * 28 , 1024 ),
nn . Sigmoid (),
nn . Linear ( 1024 , n_classes )
)
def forward ( self , x ):
x = self . encoder ( x )
x = x . view ( x . size ( 0 ), - 1 ) # flat
x = self . decoder ( x )
return x
model = MyCNNClassifier ( 1 , [ 32 , 64 , 128 ], 10 )
print ( model )
MyCNNClassifier(
(encoder): Sequential(
(0): Sequential(
(0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
(1): Sequential(
(0): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
(2): Sequential(
(0): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
)
(decoder): Sequential(
(0): Linear(in_features=50176, out_features=1024, bias=True)
(1): Sigmoid()
(2): Linear(in_features=1024, out_features=10, bias=True)
)
)
يمكننا أن نفعل الشيء نفسه بالنسبة لجزء فك التشفير
def dec_block ( in_f , out_f ):
return nn . Sequential (
nn . Linear ( in_f , out_f ),
nn . Sigmoid ()
)
class MyCNNClassifier ( nn . Module ):
def __init__ ( self , in_c , enc_sizes , dec_sizes , n_classes ):
super (). __init__ ()
self . enc_sizes = [ in_c , * enc_sizes ]
self . dec_sizes = [ 64 * 28 * 28 , * dec_sizes ]
conv_blocks = [ conv_block ( in_f , out_f , kernel_size = 3 , padding = 1 )
for in_f , out_f in zip ( self . enc_sizes , self . enc_sizes [ 1 :])]
self . encoder = nn . Sequential ( * conv_blocks )
dec_blocks = [ dec_block ( in_f , out_f )
for in_f , out_f in zip ( self . dec_sizes , self . dec_sizes [ 1 :])]
self . decoder = nn . Sequential ( * dec_blocks )
self . last = nn . Linear ( self . dec_sizes [ - 1 ], n_classes )
def forward ( self , x ):
x = self . encoder ( x )
x = x . view ( x . size ( 0 ), - 1 ) # flat
x = self . decoder ( x )
return x
model = MyCNNClassifier ( 1 , [ 32 , 64 ], [ 1024 , 512 ], 10 )
print ( model )
MyCNNClassifier(
(encoder): Sequential(
(0): Sequential(
(0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
(1): Sequential(
(0): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
)
(decoder): Sequential(
(0): Sequential(
(0): Linear(in_features=50176, out_features=1024, bias=True)
(1): Sigmoid()
)
(1): Sequential(
(0): Linear(in_features=1024, out_features=512, bias=True)
(1): Sigmoid()
)
)
(last): Linear(in_features=512, out_features=10, bias=True)
)
اتبعنا نفس النمط، نقوم بإنشاء كتلة جديدة لجزء فك التشفير، خطي + سيني، ونمرر مصفوفة بالأحجام. كان علينا أن نضيف self.last
لأننا لا نريد تفعيل الإخراج
الآن، يمكننا حتى تقسيم نموذجنا إلى قسمين! التشفير + فك التشفير
class MyEncoder ( nn . Module ):
def __init__ ( self , enc_sizes ):
super (). __init__ ()
self . conv_blocks = nn . Sequential ( * [ conv_block ( in_f , out_f , kernel_size = 3 , padding = 1 )
for in_f , out_f in zip ( enc_sizes , enc_sizes [ 1 :])])
def forward ( self , x ):
return self . conv_blocks ( x )
class MyDecoder ( nn . Module ):
def __init__ ( self , dec_sizes , n_classes ):
super (). __init__ ()
self . dec_blocks = nn . Sequential ( * [ dec_block ( in_f , out_f )
for in_f , out_f in zip ( dec_sizes , dec_sizes [ 1 :])])
self . last = nn . Linear ( dec_sizes [ - 1 ], n_classes )
def forward ( self , x ):
return self . dec_blocks ()
class MyCNNClassifier ( nn . Module ):
def __init__ ( self , in_c , enc_sizes , dec_sizes , n_classes ):
super (). __init__ ()
self . enc_sizes = [ in_c , * enc_sizes ]
self . dec_sizes = [ self . enc_sizes [ - 1 ] * 28 * 28 , * dec_sizes ]
self . encoder = MyEncoder ( self . enc_sizes )
self . decoder = MyDecoder ( self . dec_sizes , n_classes )
def forward ( self , x ):
x = self . encoder ( x )
x = x . flatten ( 1 ) # flat
x = self . decoder ( x )
return x
model = MyCNNClassifier ( 1 , [ 32 , 64 ], [ 1024 , 512 ], 10 )
print ( model )
MyCNNClassifier(
(encoder): MyEncoder(
(conv_blocks): Sequential(
(0): Sequential(
(0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
(1): Sequential(
(0): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
)
)
(decoder): MyDecoder(
(dec_blocks): Sequential(
(0): Sequential(
(0): Linear(in_features=50176, out_features=1024, bias=True)
(1): Sigmoid()
)
(1): Sequential(
(0): Linear(in_features=1024, out_features=512, bias=True)
(1): Sigmoid()
)
)
(last): Linear(in_features=512, out_features=10, bias=True)
)
)
انتبه إلى أن MyEncoder
و MyDecoder
يمكن أن تكونا أيضًا دالتين تُرجعان nn.Sequential
. أفضل استخدام النمط الأول للنماذج والثاني لبنات البناء.
من خلال تقسيم وحدتنا إلى وحدات فرعية، يكون من الأسهل مشاركة الكود وتصحيح أخطائه واختباره .
يسمح لك ModuleList
بتخزين Module
كقائمة. يمكن أن يكون مفيدًا عندما تحتاج إلى التكرار عبر الطبقة وتخزين/استخدام بعض المعلومات، كما هو الحال في U-net.
والفرق الرئيسي بين Sequential
هو أن ModuleList
لا تحتوي على طريقة forward
لذا فإن الطبقات الداخلية غير متصلة. بافتراض أننا نحتاج إلى كل مخرجات كل طبقة في وحدة فك التشفير، يمكننا تخزينها عن طريق:
class MyModule ( nn . Module ):
def __init__ ( self , sizes ):
super (). __init__ ()
self . layers = nn . ModuleList ([ nn . Linear ( in_f , out_f ) for in_f , out_f in zip ( sizes , sizes [ 1 :])])
self . trace = []
def forward ( self , x ):
for layer in self . layers :
x = layer ( x )
self . trace . append ( x )
return x
model = MyModule ([ 1 , 16 , 32 ])
import torch
model ( torch . rand (( 4 , 1 )))
[ print ( trace . shape ) for trace in model . trace ]
torch.Size([4, 16])
torch.Size([4, 32])
[None, None]
ماذا لو أردنا التبديل إلى LearkyRelu
في conv_block
الخاص بنا؟ يمكننا استخدام ModuleDict
لإنشاء قاموس Module
وتبديل Module
ديناميكيًا عندما نريد
def conv_block ( in_f , out_f , activation = 'relu' , * args , ** kwargs ):
activations = nn . ModuleDict ([
[ 'lrelu' , nn . LeakyReLU ()],
[ 'relu' , nn . ReLU ()]
])
return nn . Sequential (
nn . Conv2d ( in_f , out_f , * args , ** kwargs ),
nn . BatchNorm2d ( out_f ),
activations [ activation ]
)
print ( conv_block ( 1 , 32 , 'lrelu' , kernel_size = 3 , padding = 1 ))
print ( conv_block ( 1 , 32 , 'relu' , kernel_size = 3 , padding = 1 ))
Sequential(
(0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): LeakyReLU(negative_slope=0.01)
)
Sequential(
(0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU()
)
دعونا نختتم كل شيء!
def conv_block ( in_f , out_f , activation = 'relu' , * args , ** kwargs ):
activations = nn . ModuleDict ([
[ 'lrelu' , nn . LeakyReLU ()],
[ 'relu' , nn . ReLU ()]
])
return nn . Sequential (
nn . Conv2d ( in_f , out_f , * args , ** kwargs ),
nn . BatchNorm2d ( out_f ),
activations [ activation ]
)
def dec_block ( in_f , out_f ):
return nn . Sequential (
nn . Linear ( in_f , out_f ),
nn . Sigmoid ()
)
class MyEncoder ( nn . Module ):
def __init__ ( self , enc_sizes , * args , ** kwargs ):
super (). __init__ ()
self . conv_blocks = nn . Sequential ( * [ conv_block ( in_f , out_f , kernel_size = 3 , padding = 1 , * args , ** kwargs )
for in_f , out_f in zip ( enc_sizes , enc_sizes [ 1 :])])
def forward ( self , x ):
return self . conv_blocks ( x )
class MyDecoder ( nn . Module ):
def __init__ ( self , dec_sizes , n_classes ):
super (). __init__ ()
self . dec_blocks = nn . Sequential ( * [ dec_block ( in_f , out_f )
for in_f , out_f in zip ( dec_sizes , dec_sizes [ 1 :])])
self . last = nn . Linear ( dec_sizes [ - 1 ], n_classes )
def forward ( self , x ):
return self . dec_blocks ()
class MyCNNClassifier ( nn . Module ):
def __init__ ( self , in_c , enc_sizes , dec_sizes , n_classes , activation = 'relu' ):
super (). __init__ ()
self . enc_sizes = [ in_c , * enc_sizes ]
self . dec_sizes = [ 32 * 28 * 28 , * dec_sizes ]
self . encoder = MyEncoder ( self . enc_sizes , activation = activation )
self . decoder = MyDecoder ( dec_sizes , n_classes )
def forward ( self , x ):
x = self . encoder ( x )
x = x . flatten ( 1 ) # flat
x = self . decoder ( x )
return x
model = MyCNNClassifier ( 1 , [ 32 , 64 ], [ 1024 , 512 ], 10 , activation = 'lrelu' )
print ( model )
MyCNNClassifier(
(encoder): MyEncoder(
(conv_blocks): Sequential(
(0): Sequential(
(0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): LeakyReLU(negative_slope=0.01)
)
(1): Sequential(
(0): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): LeakyReLU(negative_slope=0.01)
)
)
)
(decoder): MyDecoder(
(dec_blocks): Sequential(
(0): Sequential(
(0): Linear(in_features=1024, out_features=512, bias=True)
(1): Sigmoid()
)
)
(last): Linear(in_features=512, out_features=10, bias=True)
)
)
إذن باختصار.
Module
عندما يكون لديك كتلة كبيرة مكونة من عدة كتل أصغرSequential
عندما تريد إنشاء كتلة صغيرة من الطبقاتModuleList
عندما تحتاج إلى التكرار عبر بعض الطبقات أو الكتل البرمجية الإنشائية والقيام بشيء ماModuleDict
عندما تحتاج إلى تحديد بعض الكتل في النموذج الخاص بك، على سبيل المثال وظيفة التنشيطهذا كل الناس!
شكرا لك على القراءة