1- #!/usr/bin/env python
2- from __future__ import print_function
3- from itertools import count
4-
1+ import argparse
52import torch
63import torch .nn .functional as F
4+ import torch .optim as optim
5+ from torch .optim .lr_scheduler import StepLR
76
7+ # Polynomial degree and target weights/bias
88POLY_DEGREE = 4
99W_target = torch .randn (POLY_DEGREE , 1 ) * 5
1010b_target = torch .randn (1 ) * 5
1111
1212
13+ def parse_args ():
14+ """Command line arguments"""
15+ parser = argparse .ArgumentParser (description = 'Polynomial Regression Example' )
16+ parser .add_argument ('--batch-size' , type = int , default = 32 , metavar = 'N' ,
17+ help = 'input batch size for training (default: 32)' )
18+ parser .add_argument ('--epochs' , type = int , default = 100 , metavar = 'N' ,
19+ help = 'number of epochs to train (default: 100)' )
20+ parser .add_argument ('--lr' , type = float , default = 0.1 , metavar = 'LR' ,
21+ help = 'learning rate (default: 0.1)' )
22+ parser .add_argument ('--gamma' , type = float , default = 0.7 , metavar = 'M' ,
23+ help = 'Learning rate step gamma (default: 0.7)' )
24+ parser .add_argument ('--no-cuda' , action = 'store_true' , default = False ,
25+ help = 'disables CUDA training' )
26+ parser .add_argument ('--log-interval' , type = int , default = 10 , metavar = 'N' ,
27+ help = 'how many batches to wait before logging training status' )
28+ parser .add_argument ('--save-model' , action = 'store_true' , default = False ,
29+ help = 'For saving the current model' )
30+ parser .add_argument ('--dry-run' , action = 'store_true' , default = False ,
31+ help = 'quickly check a single pass' )
32+ parser .add_argument ('--seed' , type = int , default = 1 , metavar = 'S' ,
33+ help = 'random seed (default: 1)' )
34+ return parser .parse_args ()
35+
36+
1337def make_features (x ):
1438 """Builds features i.e. a matrix with columns [x, x^2, x^3, x^4]."""
1539 x = x .unsqueeze (1 )
16- return torch .cat ([x ** i for i in range (1 , POLY_DEGREE + 1 )], 1 )
40+ return torch .cat ([x ** i for i in range (1 , POLY_DEGREE + 1 )], 1 )
1741
1842
1943def f (x ):
20- """Approximated function."""
44+ """Approximated function. function f(x) = W_target * x + b_target """
2145 return x .mm (W_target ) + b_target .item ()
2246
2347
@@ -38,31 +62,86 @@ def get_batch(batch_size=32):
3862 return x , y
3963
4064
41- # Define model
42- fc = torch .nn .Linear (W_target .size (0 ), 1 )
65+ class PolyRegressor (torch .nn .Module ):
66+ """Define the model (simple linear regression)"""
67+ def __init__ (self ):
68+ super (PolyRegressor , self ).__init__ ()
69+ self .fc = torch .nn .Linear (POLY_DEGREE , 1 )
70+
71+ def forward (self , x ):
72+ return self .fc (x )
73+
74+
75+ def train (args , model , device , optimizer , epoch , log_interval = 10 ):
76+ """Training loop"""
77+ model .train ()
78+ for batch_idx in range (1 , args .epochs + 1 ):
79+ # Get a batch of data
80+ batch_x , batch_y = get_batch (args .batch_size )
81+ batch_x , batch_y = batch_x .to (device ), batch_y .to (device )
82+
83+ # Reset gradients
84+ optimizer .zero_grad ()
85+
86+ # Forward pass
87+ output = model (batch_x )
88+ loss = F .smooth_l1_loss (output , batch_y )
89+
90+ # Backward pass
91+ loss .backward ()
92+
93+ # Apply gradients
94+ optimizer .step ()
95+
96+ if batch_idx % log_interval == 0 :
97+ print (f'Epoch { epoch } Batch { batch_idx } /{ args .epochs } Loss: { loss .item ():.6f} ' )
98+
99+ # Dry run for a quick check
100+ if args .dry_run :
101+ break
102+
103+
104+ def test (model , device ):
105+ """Test function (in this case, we'll use it to print the learned function)"""
106+ model .eval ()
107+ model .to (device )
108+ with torch .no_grad ():
109+ print ('==> Learned function:' )
110+ print (poly_desc (model .fc .weight .view (- 1 ), model .fc .bias ))
111+ print ('==> Actual function:' )
112+ print (poly_desc (W_target .view (- 1 ), b_target ))
113+
114+
115+ def main ():
116+ args = parse_args ()
117+
118+ # Set the random seed
119+ torch .manual_seed (args .seed )
120+
121+ # Select the device (GPU/CPU)
122+ use_cuda = not args .no_cuda and torch .cuda .is_available ()
123+ device = torch .device ("cuda" if use_cuda else "cpu" )
43124
44- for batch_idx in count (1 ):
45- # Get data
46- batch_x , batch_y = get_batch ()
125+ # Initialize the model, optimizer and scheduler
126+ model = PolyRegressor ().to (device )
127+ optimizer = optim .SGD (model .parameters (), lr = args .lr )
128+ scheduler = StepLR (optimizer , step_size = 1 , gamma = args .gamma )
47129
48- # Reset gradients
49- fc .zero_grad ()
130+ # Training loop
131+ for epoch in range (1 , args .epochs + 1 ):
132+ train (args , model , device , optimizer , epoch , args .log_interval )
133+ scheduler .step ()
50134
51- # Forward pass
52- output = F .smooth_l1_loss (fc (batch_x ), batch_y )
53- loss = output .item ()
135+ # Print the learned function after each epoch
136+ test (model , device )
54137
55- # Backward pass
56- output . backward ( )
138+ if args . save_model :
139+ torch . save ( model . state_dict (), "polynomial_regressor.pt" )
57140
58- # Apply gradients
59- for param in fc . parameters () :
60- param . data . add_ ( - 0.1 * param . grad )
141+ print ( "Training complete." )
142+ if args . save_model :
143+ print ( "Model saved to polynomial_regressor.pt" )
61144
62- # Stop criterion
63- if loss < 1e-3 :
64- break
65145
66- print ('Loss: {:.6f} after {} batches' .format (loss , batch_idx ))
67- print ('==> Learned function:\t ' + poly_desc (fc .weight .view (- 1 ), fc .bias ))
68- print ('==> Actual function:\t ' + poly_desc (W_target .view (- 1 ), b_target ))
146+ if __name__ == '__main__' :
147+ main ()
0 commit comments