1
- #!/usr/bin/env python
2
- from __future__ import print_function
3
- from itertools import count
4
-
1
+ import argparse
5
2
import torch
6
3
import torch .nn .functional as F
4
+ import torch .optim as optim
5
+ from torch .optim .lr_scheduler import StepLR
7
6
7
+ # Polynomial degree and target weights/bias
8
8
POLY_DEGREE = 4
9
9
W_target = torch .randn (POLY_DEGREE , 1 ) * 5
10
10
b_target = torch .randn (1 ) * 5
11
11
12
12
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
+
13
37
def make_features (x ):
14
38
"""Builds features i.e. a matrix with columns [x, x^2, x^3, x^4]."""
15
39
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 )
17
41
18
42
19
43
def f (x ):
20
- """Approximated function."""
44
+ """Approximated function. function f(x) = W_target * x + b_target """
21
45
return x .mm (W_target ) + b_target .item ()
22
46
23
47
@@ -38,31 +62,86 @@ def get_batch(batch_size=32):
38
62
return x , y
39
63
40
64
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" )
43
124
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 )
47
129
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 ()
50
134
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 )
54
137
55
- # Backward pass
56
- output . backward ( )
138
+ if args . save_model :
139
+ torch . save ( model . state_dict (), "polynomial_regressor.pt" )
57
140
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" )
61
144
62
- # Stop criterion
63
- if loss < 1e-3 :
64
- break
65
145
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