Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
OpenDAS
nni
Commits
ab2ed393
Unverified
Commit
ab2ed393
authored
Sep 05, 2021
by
Bill Wu
Committed by
GitHub
Sep 06, 2021
Browse files
[End-to-end demo] MobileNetV2 Compression (#4102)
parent
93c53503
Changes
14
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
2434 additions
and
0 deletions
+2434
-0
examples/model_compress/pruning/mobilenetv2_end2end/Compressing MobileNetV2 with NNI Pruners.ipynb
...v2_end2end/Compressing MobileNetV2 with NNI Pruners.ipynb
+1653
-0
examples/model_compress/pruning/mobilenetv2_end2end/final_performance.png
...ompress/pruning/mobilenetv2_end2end/final_performance.png
+0
-0
examples/model_compress/pruning/mobilenetv2_end2end/mobilenet.png
.../model_compress/pruning/mobilenetv2_end2end/mobilenet.png
+0
-0
examples/model_compress/pruning/mobilenetv2_end2end/prepare_data.sh
...odel_compress/pruning/mobilenetv2_end2end/prepare_data.sh
+32
-0
examples/model_compress/pruning/mobilenetv2_end2end/preprocess.py
.../model_compress/pruning/mobilenetv2_end2end/preprocess.py
+103
-0
examples/model_compress/pruning/mobilenetv2_end2end/pretrain.py
...es/model_compress/pruning/mobilenetv2_end2end/pretrain.py
+123
-0
examples/model_compress/pruning/mobilenetv2_end2end/pruning_experiments.py
...mpress/pruning/mobilenetv2_end2end/pruning_experiments.py
+375
-0
examples/model_compress/pruning/mobilenetv2_end2end/step1.png
...ples/model_compress/pruning/mobilenetv2_end2end/step1.png
+0
-0
examples/model_compress/pruning/mobilenetv2_end2end/step2.png
...ples/model_compress/pruning/mobilenetv2_end2end/step2.png
+0
-0
examples/model_compress/pruning/mobilenetv2_end2end/step3-1.png
...es/model_compress/pruning/mobilenetv2_end2end/step3-1.png
+0
-0
examples/model_compress/pruning/mobilenetv2_end2end/step3-2.png
...es/model_compress/pruning/mobilenetv2_end2end/step3-2.png
+0
-0
examples/model_compress/pruning/mobilenetv2_end2end/step4.png
...ples/model_compress/pruning/mobilenetv2_end2end/step4.png
+0
-0
examples/model_compress/pruning/mobilenetv2_end2end/test.py
examples/model_compress/pruning/mobilenetv2_end2end/test.py
+55
-0
examples/model_compress/pruning/mobilenetv2_end2end/utils.py
examples/model_compress/pruning/mobilenetv2_end2end/utils.py
+93
-0
No files found.
examples/model_compress/pruning/mobilenetv2_end2end/Compressing MobileNetV2 with NNI Pruners.ipynb
0 → 100644
View file @
ab2ed393
This diff is collapsed.
Click to expand it.
examples/model_compress/pruning/mobilenetv2_end2end/final_performance.png
0 → 100644
View file @
ab2ed393
78.5 KB
examples/model_compress/pruning/mobilenetv2_end2end/mobilenet.png
0 → 100644
View file @
ab2ed393
57.3 KB
examples/model_compress/pruning/mobilenetv2_end2end/prepare_data.sh
0 → 100755
View file @
ab2ed393
#!/bin/bash
# download and preprocess the Stanford Dogs dataset
mkdir
-p
data/stanford-dogs
# download raw data (images, annotations, and train-test split)
cd
data/stanford-dogs
if
[
!
-d
'./Images'
]
;
then
if
[
!
-f
'images.tar'
]
;
then
wget http://vision.stanford.edu/aditya86/ImageNetDogs/images.tar
fi
tar
-xvf
images.tar
fi
if
[
!
-d
'./Annotation'
]
;
then
if
[
!
-f
'annotation.tar'
]
;
then
wget http://vision.stanford.edu/aditya86/ImageNetDogs/annotation.tar
fi
tar
-xvf
annotation.tar
fi
if
[
!
-f
'lists.tar'
]
;
then
wget http://vision.stanford.edu/aditya86/ImageNetDogs/lists.tar
fi
tar
-xvf
lists.tar
cd
../..
# preprocess: train-valid-test splitting and image cropping
python preprocess.py
examples/model_compress/pruning/mobilenetv2_end2end/preprocess.py
0 → 100644
View file @
ab2ed393
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import
os
import
xml.etree.ElementTree
from
PIL
import
Image
import
numpy
as
np
from
sklearn.model_selection
import
train_test_split
from
scipy
import
io
ROOT_DIR
=
'./data/stanford-dogs/'
NUM_CATEGORIES
=
120
OUT_IMAGE_SIZE
=
(
224
,
224
)
RANDOM_SEED
=
42
# for splitting train and validation
TRAIN_RATIO
=
0.9
# train / (train + validation)
def
get_bounding_box
(
annotation_file
):
"""
Parse the annotation file and returns the bounding box information
Parameters
----------
annotation_file: path to the annotation XML file
Returns
-------
A dict containing bounding box information
"""
ret
=
{}
xml_root
=
xml
.
etree
.
ElementTree
.
parse
(
annotation_file
).
getroot
()
bounding_box
=
xml_root
.
findall
(
'object'
)[
0
].
findall
(
'bndbox'
)[
0
]
ret
[
'X_min'
]
=
int
(
bounding_box
.
findall
(
'xmin'
)[
0
].
text
)
ret
[
'X_max'
]
=
int
(
bounding_box
.
findall
(
'xmax'
)[
0
].
text
)
ret
[
'Y_min'
]
=
int
(
bounding_box
.
findall
(
'ymin'
)[
0
].
text
)
ret
[
'Y_max'
]
=
int
(
bounding_box
.
findall
(
'ymax'
)[
0
].
text
)
return
ret
def
main
(
root_dir
):
try
:
os
.
mkdir
(
root_dir
+
'Processed'
)
os
.
mkdir
(
root_dir
+
'Processed/train'
)
os
.
mkdir
(
root_dir
+
'Processed/valid'
)
os
.
mkdir
(
root_dir
+
'Processed/test'
)
except
:
print
(
'Directory already exists. Nothing done.'
)
exit
()
# load train test splits
train_metadata
=
io
.
loadmat
(
root_dir
+
'train_list.mat'
)
train_valid_file_list
=
[
x
[
0
][
0
]
for
x
in
train_metadata
[
'file_list'
]]
train_valid_annotation_list
=
[
x
[
0
][
0
]
for
x
in
train_metadata
[
'annotation_list'
]]
train_valid_labels
=
[
x
[
0
]
-
1
for
x
in
train_metadata
[
'labels'
]]
train_valid_lists
=
[
x
for
x
in
zip
(
train_valid_file_list
,
train_valid_annotation_list
,
train_valid_labels
)]
train_lists
,
valid_lists
=
train_test_split
(
train_valid_lists
,
train_size
=
TRAIN_RATIO
,
random_state
=
RANDOM_SEED
)
train_file_list
,
train_annotation_list
,
train_labels
=
zip
(
*
train_lists
)
valid_file_list
,
valid_annotation_list
,
valid_labels
=
zip
(
*
valid_lists
)
test_metadata
=
io
.
loadmat
(
root_dir
+
'test_list.mat'
)
test_file_list
=
[
x
[
0
][
0
]
for
x
in
test_metadata
[
'file_list'
]]
test_annotation_list
=
[
x
[
0
][
0
]
for
x
in
test_metadata
[
'annotation_list'
]]
test_labels
=
[
x
[
0
]
-
1
for
x
in
test_metadata
[
'labels'
]]
label2idx
=
{}
for
split
,
file_list
,
annotation_list
,
labels
in
zip
([
'train'
,
'valid'
,
'test'
],
[
train_file_list
,
valid_file_list
,
test_file_list
],
[
train_annotation_list
,
valid_annotation_list
,
test_annotation_list
],
[
train_labels
,
valid_labels
,
test_labels
]):
print
(
'Preprocessing {} set: {} cases'
.
format
(
split
,
len
(
file_list
)))
for
cur_file
,
cur_annotation
,
cur_label
in
zip
(
file_list
,
annotation_list
,
labels
):
label_name
=
cur_file
.
split
(
'/'
)[
0
].
split
(
'-'
)[
-
1
].
lower
()
if
label_name
not
in
label2idx
:
label2idx
[
label_name
]
=
cur_label
image
=
Image
.
open
(
root_dir
+
'/Images/'
+
cur_file
)
# cropping and reshape
annotation_file
=
root_dir
+
'/Annotation/'
+
cur_annotation
bounding_box
=
get_bounding_box
(
annotation_file
)
image
=
image
.
crop
([
bounding_box
[
'X_min'
],
bounding_box
[
'Y_min'
],
bounding_box
[
'X_max'
],
bounding_box
[
'Y_max'
]])
image
=
image
.
convert
(
'RGB'
)
image
=
image
.
resize
(
OUT_IMAGE_SIZE
)
# Normalize and save the instance
X
=
np
.
array
(
image
)
X
=
(
X
-
np
.
mean
(
X
,
axis
=
(
0
,
1
)))
/
np
.
std
(
X
,
axis
=
(
0
,
1
))
# normalize each channel separately
# image.save(root_dir + 'Processed/' + split + '/' + image_name)
np
.
save
(
root_dir
+
'Processed/'
+
split
+
'/'
+
cur_file
.
split
(
'/'
)[
-
1
].
replace
(
'.jpg'
,
'.npy'
),
{
'input'
:
X
,
'label'
:
cur_label
})
# save mapping from label name to index to a dict
with
open
(
ROOT_DIR
+
'/category_dict.tsv'
,
'w'
)
as
dict_f
:
final_dict_list
=
sorted
(
list
(
label2idx
.
items
()),
key
=
(
lambda
x
:
x
[
-
1
]))
for
label
,
index
in
final_dict_list
:
dict_f
.
write
(
'{}
\t
{}
\n
'
.
format
(
index
,
label
))
print
(
final_dict_list
)
if
__name__
==
'__main__'
:
main
(
ROOT_DIR
)
examples/model_compress/pruning/mobilenetv2_end2end/pretrain.py
0 → 100644
View file @
ab2ed393
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import
os
import
argparse
from
time
import
gmtime
,
strftime
import
torch
import
torch.nn
as
nn
from
torch.utils.data
import
DataLoader
from
tqdm
import
tqdm
import
numpy
as
np
from
utils
import
*
device
=
torch
.
device
(
"cuda"
if
torch
.
cuda
.
is_available
()
else
"cpu"
)
def
run_validation
(
model
,
valid_dataloader
):
model
.
eval
()
loss_func
=
nn
.
CrossEntropyLoss
()
acc_list
,
loss_list
=
[],
[]
with
torch
.
no_grad
():
for
i
,
(
inputs
,
labels
)
in
enumerate
(
tqdm
(
valid_dataloader
)):
inputs
,
labels
=
inputs
.
float
().
to
(
device
),
labels
.
to
(
device
)
preds
=
model
(
inputs
)
pred_idx
=
preds
.
max
(
1
).
indices
acc
=
(
pred_idx
==
labels
).
sum
().
item
()
/
labels
.
size
(
0
)
acc_list
.
append
(
acc
)
loss
=
loss_func
(
preds
,
labels
).
item
()
loss_list
.
append
(
loss
)
valid_loss
=
np
.
array
(
loss_list
).
mean
()
valid_acc
=
np
.
array
(
acc_list
).
mean
()
return
valid_loss
,
valid_acc
def
run_pretrain
(
args
):
print
(
args
)
torch
.
set_num_threads
(
args
.
n_workers
)
model_type
=
'mobilenet_v2_torchhub'
pretrained
=
True
# load imagenet weight
experiment_dir
=
'pretrained_{}'
.
format
(
model_type
)
if
args
.
experiment_dir
is
None
else
args
.
experiment_dir
os
.
mkdir
(
experiment_dir
)
checkpoint
=
None
input_size
=
224
n_classes
=
120
log
=
open
(
experiment_dir
+
'/pretrain.log'
,
'w'
)
model
=
create_model
(
model_type
=
model_type
,
pretrained
=
pretrained
,
n_classes
=
n_classes
,
input_size
=
input_size
,
checkpoint
=
checkpoint
)
model
=
model
.
to
(
device
)
print
(
model
)
# count_flops(model, device=device)
train_dataset
=
TrainDataset
(
'./data/stanford-dogs/Processed/train'
)
train_dataloader
=
DataLoader
(
train_dataset
,
batch_size
=
args
.
batch_size
,
shuffle
=
True
)
valid_dataset
=
EvalDataset
(
'./data/stanford-dogs/Processed/valid'
)
valid_dataloader
=
DataLoader
(
valid_dataset
,
batch_size
=
args
.
batch_size
,
shuffle
=
False
)
criterion
=
nn
.
CrossEntropyLoss
()
optimizer
=
torch
.
optim
.
SGD
(
model
.
parameters
(),
lr
=
args
.
learning_rate
,
momentum
=
0.9
,
weight_decay
=
args
.
weight_decay
)
best_valid_acc
=
0.0
for
epoch
in
range
(
args
.
n_epochs
):
print
(
'Start training epoch {}'
.
format
(
epoch
))
loss_list
=
[]
# train
model
.
train
()
for
i
,
(
inputs
,
labels
)
in
enumerate
(
tqdm
(
train_dataloader
)):
optimizer
.
zero_grad
()
inputs
,
labels
=
inputs
.
float
().
to
(
device
),
labels
.
to
(
device
)
preds
=
model
(
inputs
)
loss
=
criterion
(
preds
,
labels
)
loss_list
.
append
(
loss
.
item
())
loss
.
backward
()
optimizer
.
step
()
# validation
valid_loss
,
valid_acc
=
run_validation
(
model
,
valid_dataloader
)
train_loss
=
np
.
array
(
loss_list
).
mean
()
print
(
'Epoch {}: train loss {:.4f}, valid loss {:.4f}, valid acc {:.4f}'
.
format
(
epoch
,
train_loss
,
valid_loss
,
valid_acc
))
log
.
write
(
'Epoch {}: train loss {:.4f}, valid loss {:.4f}, valid acc {:.4f}
\n
'
.
format
(
epoch
,
train_loss
,
valid_loss
,
valid_acc
))
# save
if
valid_acc
>
best_valid_acc
:
best_valid_acc
=
valid_acc
torch
.
save
(
model
.
state_dict
(),
experiment_dir
+
'/checkpoint_best.pt'
)
log
.
close
()
def
parse_args
():
parser
=
argparse
.
ArgumentParser
(
description
=
'Example code for pruning MobileNetV2'
)
parser
.
add_argument
(
'--experiment_dir'
,
type
=
str
,
default
=
None
,
help
=
'directory containing the pretrained model'
)
parser
.
add_argument
(
'--checkpoint_name'
,
type
=
str
,
default
=
'checkpoint_best.pt'
,
help
=
'checkpoint of the pretrained model'
)
# finetuning parameters
parser
.
add_argument
(
'--n_workers'
,
type
=
int
,
default
=
16
,
help
=
'number of threads'
)
parser
.
add_argument
(
'--n_epochs'
,
type
=
int
,
default
=
180
,
help
=
'number of epochs to train the model'
)
parser
.
add_argument
(
'--learning_rate'
,
type
=
float
,
default
=
1e-4
)
parser
.
add_argument
(
'--weight_decay'
,
type
=
float
,
default
=
0.0
)
parser
.
add_argument
(
'--batch_size'
,
type
=
int
,
default
=
32
,
help
=
'input batch size for training and inference'
)
args
=
parser
.
parse_args
()
return
args
if
__name__
==
'__main__'
:
args
=
parse_args
()
run_pretrain
(
args
)
examples/model_compress/pruning/mobilenetv2_end2end/pruning_experiments.py
0 → 100644
View file @
ab2ed393
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import
os
import
argparse
import
copy
from
time
import
gmtime
,
strftime
import
torch
import
torch.nn
as
nn
import
torch.nn.functional
as
F
from
torch.utils.data
import
DataLoader
from
tqdm
import
tqdm
import
numpy
as
np
import
nni
from
nni.compression.pytorch
import
ModelSpeedup
from
nni.algorithms.compression.pytorch.pruning
import
(
LevelPruner
,
SlimPruner
,
FPGMPruner
,
TaylorFOWeightFilterPruner
,
L1FilterPruner
,
L2FilterPruner
,
AGPPruner
,
ActivationMeanRankFilterPruner
,
ActivationAPoZRankFilterPruner
)
from
utils
import
*
device
=
torch
.
device
(
"cuda"
if
torch
.
cuda
.
is_available
()
else
"cpu"
)
model_type
=
'mobilenet_v2_torchhub'
input_size
=
224
n_classes
=
120
pruner_type_to_class
=
{
'level'
:
LevelPruner
,
'l1'
:
L1FilterPruner
,
'l2'
:
L2FilterPruner
,
'slim'
:
SlimPruner
,
'fpgm'
:
FPGMPruner
,
'taylorfo'
:
TaylorFOWeightFilterPruner
,
'agp'
:
AGPPruner
,
'mean_activation'
:
ActivationMeanRankFilterPruner
,
'apoz'
:
ActivationAPoZRankFilterPruner
}
def
run_eval
(
model
,
dataloader
,
device
):
model
.
eval
()
loss_func
=
nn
.
CrossEntropyLoss
()
acc_list
,
loss_list
=
[],
[]
with
torch
.
no_grad
():
for
i
,
(
inputs
,
labels
)
in
enumerate
(
tqdm
(
dataloader
)):
inputs
,
labels
=
inputs
.
float
().
to
(
device
),
labels
.
to
(
device
)
preds
=
model
(
inputs
)
pred_idx
=
preds
.
max
(
1
).
indices
acc
=
(
pred_idx
==
labels
).
sum
().
item
()
/
labels
.
size
(
0
)
acc_list
.
append
(
acc
)
loss
=
loss_func
(
preds
,
labels
).
item
()
loss_list
.
append
(
loss
)
final_loss
=
np
.
array
(
loss_list
).
mean
()
final_acc
=
np
.
array
(
acc_list
).
mean
()
return
final_loss
,
final_acc
def
run_finetune
(
model
,
train_dataloader
,
valid_dataloader
,
device
,
n_epochs
=
2
,
learning_rate
=
1e-4
,
weight_decay
=
0.0
,
log
=
None
):
criterion
=
nn
.
CrossEntropyLoss
()
optimizer
=
torch
.
optim
.
Adam
(
model
.
parameters
(),
lr
=
learning_rate
,
weight_decay
=
weight_decay
)
best_valid_acc
=
0.0
best_model
=
None
for
epoch
in
range
(
n_epochs
):
print
(
'Start finetuning epoch {}'
.
format
(
epoch
))
loss_list
=
[]
# train
model
.
train
()
for
i
,
(
inputs
,
labels
)
in
enumerate
(
tqdm
(
train_dataloader
)):
optimizer
.
zero_grad
()
inputs
,
labels
=
inputs
.
float
().
to
(
device
),
labels
.
to
(
device
)
preds
=
model
(
inputs
)
loss
=
criterion
(
preds
,
labels
)
loss_list
.
append
(
loss
.
item
())
loss
.
backward
()
optimizer
.
step
()
# validation
valid_loss
,
valid_acc
=
run_eval
(
model
,
valid_dataloader
,
device
)
train_loss
=
np
.
array
(
loss_list
).
mean
()
print
(
'Epoch {}: train loss {:.4f}, valid loss {:.4f}, valid acc {:.4f}'
.
format
(
epoch
,
train_loss
,
valid_loss
,
valid_acc
))
if
log
is
not
None
:
log
.
write
(
'Epoch {}: train loss {:.4f}, valid loss {:.4f}, valid acc {:.4f}'
.
format
(
epoch
,
train_loss
,
valid_loss
,
valid_acc
))
if
valid_acc
>
best_valid_acc
:
best_valid_acc
=
valid_acc
best_model
=
copy
.
deepcopy
(
model
).
to
(
device
)
print
(
"Best validation accuracy: {}"
.
format
(
best_valid_acc
))
if
log
is
not
None
:
log
.
write
(
"Best validation accuracy: {}"
.
format
(
best_valid_acc
))
model
=
best_model
return
model
def
run_finetune_distillation
(
student_model
,
teacher_model
,
train_dataloader
,
valid_dataloader
,
device
,
alpha
,
temperature
,
n_epochs
=
2
,
learning_rate
=
1e-4
,
weight_decay
=
0.0
,
log
=
None
):
optimizer
=
torch
.
optim
.
Adam
(
student_model
.
parameters
(),
lr
=
learning_rate
,
weight_decay
=
weight_decay
)
# optimizer = torch.optim.SGD(student_model.parameters(), lr=learning_rate, momentum=0.9)
best_valid_acc
=
0.0
best_model
=
None
for
epoch
in
range
(
n_epochs
):
print
(
'Start finetuning with distillation epoch {}'
.
format
(
epoch
))
loss_list
=
[]
# train
student_model
.
train
()
for
i
,
(
inputs
,
labels
)
in
enumerate
(
tqdm
(
train_dataloader
)):
optimizer
.
zero_grad
()
inputs
,
labels
=
inputs
.
float
().
to
(
device
),
labels
.
to
(
device
)
with
torch
.
no_grad
():
teacher_preds
=
teacher_model
(
inputs
)
preds
=
student_model
(
inputs
)
soft_loss
=
nn
.
KLDivLoss
()(
F
.
log_softmax
(
preds
/
temperature
,
dim
=
1
),
F
.
softmax
(
teacher_preds
/
temperature
,
dim
=
1
))
hard_loss
=
F
.
cross_entropy
(
preds
,
labels
)
loss
=
soft_loss
*
(
alpha
*
temperature
*
temperature
)
+
hard_loss
*
(
1.
-
alpha
)
loss_list
.
append
(
loss
.
item
())
loss
.
backward
()
optimizer
.
step
()
# validation
valid_loss
,
valid_acc
=
run_eval
(
student_model
,
valid_dataloader
,
device
)
train_loss
=
np
.
array
(
loss_list
).
mean
()
print
(
'Epoch {}: train loss {:.4f}, valid loss {:.4f}, valid acc {:.4f}'
.
format
(
epoch
,
train_loss
,
valid_loss
,
valid_acc
))
if
log
is
not
None
:
log
.
write
(
'Epoch {}: train loss {:.4f}, valid loss {:.4f}, valid acc {:.4f}'
.
format
(
epoch
,
train_loss
,
valid_loss
,
valid_acc
))
if
valid_acc
>
best_valid_acc
:
best_valid_acc
=
valid_acc
best_model
=
copy
.
deepcopy
(
student_model
).
to
(
device
)
print
(
"Best validation accuracy: {}"
.
format
(
best_valid_acc
))
if
log
is
not
None
:
log
.
write
(
"Best validation accuracy: {}"
.
format
(
best_valid_acc
))
student_model
=
best_model
return
student_model
def
trainer_helper
(
model
,
criterion
,
optimizer
,
dataloader
,
device
):
print
(
"Running trainer in tuner"
)
for
epoch
in
range
(
1
):
model
.
train
()
for
i
,
(
inputs
,
labels
)
in
enumerate
(
tqdm
(
dataloader
)):
optimizer
.
zero_grad
()
inputs
,
labels
=
inputs
.
float
().
to
(
device
),
labels
.
to
(
device
)
preds
=
model
(
inputs
)
loss
=
criterion
(
preds
,
labels
)
loss
.
backward
()
optimizer
.
step
()
def
trainer_helper_with_distillation
(
model
,
teacher_model
,
alpha
,
temperature
,
optimizer
,
dataloader
,
device
):
print
(
"Running trainer in tuner"
)
for
epoch
in
range
(
1
):
model
.
train
()
for
i
,
(
inputs
,
labels
)
in
enumerate
(
tqdm
(
dataloader
)):
optimizer
.
zero_grad
()
inputs
,
labels
=
inputs
.
float
().
to
(
device
),
labels
.
to
(
device
)
with
torch
.
no_grad
():
teacher_preds
=
teacher_model
(
inputs
)
preds
=
model
(
inputs
)
soft_loss
=
nn
.
KLDivLoss
()(
F
.
log_softmax
(
preds
/
temperature
,
dim
=
1
),
F
.
softmax
(
teacher_preds
/
temperature
,
dim
=
1
))
hard_loss
=
F
.
cross_entropy
(
preds
,
labels
)
loss
=
soft_loss
*
(
alpha
*
temperature
*
temperature
)
+
hard_loss
*
(
1.
-
alpha
)
loss
.
backward
()
optimizer
.
step
()
def
parse_args
():
parser
=
argparse
.
ArgumentParser
(
description
=
'Example code for pruning MobileNetV2'
)
parser
.
add_argument
(
'--experiment_dir'
,
type
=
str
,
required
=
True
,
help
=
'directory containing the pretrained model'
)
parser
.
add_argument
(
'--checkpoint_name'
,
type
=
str
,
default
=
'checkpoint_best.pt'
,
help
=
'checkpoint of the pretrained model'
)
# pruner
parser
.
add_argument
(
'--pruning_mode'
,
type
=
str
,
default
=
'conv1andconv2'
,
choices
=
[
'conv0'
,
'conv1'
,
'conv2'
,
'conv1andconv2'
,
'all'
])
parser
.
add_argument
(
'--sparsity'
,
type
=
float
,
default
=
0.5
,
help
=
'target sparsity'
)
parser
.
add_argument
(
'--pruner_name'
,
type
=
str
,
default
=
'l1'
,
choices
=
[
'l1'
,
'l2'
,
'slim'
,
'agp'
,
'fpgm'
,
'mean_activation'
,
'apoz'
,
'taylorfo'
],
help
=
'pruner to use'
)
# for agp only
parser
.
add_argument
(
'--agp_pruning_alg'
,
default
=
'l1'
,
choices
=
[
'l1'
,
'l2'
,
'slim'
,
'fpgm'
,
'mean_activation'
,
'apoz'
,
'taylorfo'
],
help
=
'pruner to use for agp'
)
parser
.
add_argument
(
'--agp_n_iters'
,
type
=
int
,
default
=
64
,
help
=
'number of iterations for agp'
)
parser
.
add_argument
(
'--agp_n_epochs_per_iter'
,
type
=
int
,
default
=
1
,
help
=
'number of epochs per iteration for agp'
)
# speed-up
parser
.
add_argument
(
'--speed_up'
,
action
=
'store_true'
,
default
=
False
,
help
=
'Whether to speed-up the pruned model'
)
# finetuning parameters
parser
.
add_argument
(
'--n_workers'
,
type
=
int
,
default
=
16
,
help
=
'number of threads'
)
parser
.
add_argument
(
'--finetune_epochs'
,
type
=
int
,
default
=
180
,
help
=
'number of epochs to finetune the model'
)
parser
.
add_argument
(
'--learning_rate'
,
type
=
float
,
default
=
1e-4
)
parser
.
add_argument
(
'--weight_decay'
,
type
=
float
,
default
=
0.0
)
parser
.
add_argument
(
'--batch_size'
,
type
=
int
,
default
=
32
,
help
=
'input batch size for training and inference'
)
parser
.
add_argument
(
'--kd'
,
action
=
'store_true'
,
default
=
False
,
help
=
'Whether to use knowledge distillation'
)
parser
.
add_argument
(
'--alpha'
,
type
=
float
,
default
=
0.99
,
help
=
'Alpha for knowledge distillation loss'
)
parser
.
add_argument
(
'--temp'
,
type
=
float
,
default
=
8
,
help
=
'Temperature for knowledge distillation loss'
)
args
=
parser
.
parse_args
()
return
args
def
run_pruning
(
args
):
print
(
args
)
torch
.
set_num_threads
(
args
.
n_workers
)
device
=
torch
.
device
(
"cuda"
if
torch
.
cuda
.
is_available
()
else
"cpu"
)
log
=
open
(
args
.
experiment_dir
+
'/pruning_{}_{}_sparsity{}_{}.log'
.
format
(
args
.
pruner_name
,
args
.
pruning_mode
,
args
.
sparsity
,
strftime
(
"%Y%m%d%H%M"
,
gmtime
())),
'w'
)
train_dataset
=
TrainDataset
(
'./data/stanford-dogs/Processed/train'
)
train_dataloader
=
DataLoader
(
train_dataset
,
batch_size
=
args
.
batch_size
,
shuffle
=
True
)
train_dataset_for_pruner
=
EvalDataset
(
'./data/stanford-dogs/Processed/train'
)
train_dataloader_for_pruner
=
DataLoader
(
train_dataset
,
batch_size
=
args
.
batch_size
,
shuffle
=
False
)
valid_dataset
=
EvalDataset
(
'./data/stanford-dogs/Processed/valid'
)
valid_dataloader
=
DataLoader
(
valid_dataset
,
batch_size
=
args
.
batch_size
,
shuffle
=
False
)
test_dataset
=
EvalDataset
(
'./data/stanford-dogs/Processed/test'
)
test_dataloader
=
DataLoader
(
test_dataset
,
batch_size
=
args
.
batch_size
,
shuffle
=
False
)
model
=
create_model
(
model_type
=
model_type
,
pretrained
=
False
,
n_classes
=
n_classes
,
input_size
=
input_size
,
checkpoint
=
args
.
experiment_dir
+
'/'
+
args
.
checkpoint_name
)
model
=
model
.
to
(
device
)
teacher_model
=
None
if
args
.
kd
:
teacher_model
=
copy
.
deepcopy
(
model
)
# evaluation before pruning
# count_flops(model, log, device)
initial_loss
,
initial_acc
=
run_eval
(
model
,
test_dataloader
,
device
)
print
(
'Before Pruning:
\n
Loss: {}
\n
Accuracy: {}'
.
format
(
initial_loss
,
initial_acc
))
log
.
write
(
'Before Pruning:
\n
Loss: {}
\n
Accuracy: {}
\n
'
.
format
(
initial_loss
,
initial_acc
))
# set up config list and pruner
config_list
=
[]
if
'conv0'
in
args
.
pruning_mode
or
args
.
pruning_mode
==
'all'
:
if
args
.
pruner_name
==
'slim'
or
(
args
.
pruner_name
==
'agp'
and
args
.
agp_pruning_alg
==
'slim'
):
config_list
.
append
({
'op_names'
:
[
'features.{}.conv.0.1'
.
format
(
x
)
for
x
in
range
(
2
,
18
)],
'sparsity'
:
args
.
sparsity
})
else
:
config_list
.
append
({
'op_names'
:
[
'features.{}.conv.0.0'
.
format
(
x
)
for
x
in
range
(
2
,
18
)],
'sparsity'
:
args
.
sparsity
})
if
'conv1'
in
args
.
pruning_mode
or
args
.
pruning_mode
==
'all'
:
if
args
.
pruner_name
==
'slim'
or
(
args
.
pruner_name
==
'agp'
and
args
.
agp_pruning_alg
==
'slim'
):
config_list
.
append
({
'op_names'
:
[
'features.{}.conv.1.1'
.
format
(
x
)
for
x
in
range
(
2
,
18
)],
'sparsity'
:
args
.
sparsity
})
else
:
config_list
.
append
({
'op_names'
:
[
'features.{}.conv.1.0'
.
format
(
x
)
for
x
in
range
(
2
,
18
)],
'sparsity'
:
args
.
sparsity
})
if
'conv2'
in
args
.
pruning_mode
or
args
.
pruning_mode
==
'all'
:
if
args
.
pruner_name
==
'slim'
or
(
args
.
pruner_name
==
'agp'
and
args
.
agp_pruning_alg
==
'slim'
):
config_list
.
append
({
'op_names'
:
[
'features.{}.conv.3'
.
format
(
x
)
for
x
in
range
(
2
,
18
)],
'sparsity'
:
args
.
sparsity
})
else
:
config_list
.
append
({
'op_names'
:
[
'features.{}.conv.2'
.
format
(
x
)
for
x
in
range
(
2
,
18
)],
'sparsity'
:
args
.
sparsity
})
print
(
config_list
)
kwargs
=
{}
if
args
.
pruner_name
in
[
'slim'
,
'taylorfo'
,
'mean_activation'
,
'apoz'
,
'agp'
]:
def
trainer
(
model
,
optimizer
,
criterion
,
epoch
):
if
not
args
.
kd
:
return
trainer_helper
(
model
,
criterion
,
optimizer
,
train_dataloader
,
device
)
else
:
return
trainer_helper_with_distillation
(
model
,
teacher_model
,
args
.
alpha
,
args
.
temp
,
optimizer
,
train_dataloader
,
device
)
kwargs
=
{
'trainer'
:
trainer
,
'optimizer'
:
torch
.
optim
.
Adam
(
model
.
parameters
()),
'criterion'
:
nn
.
CrossEntropyLoss
()
}
if
args
.
pruner_name
==
'agp'
:
kwargs
[
'pruning_algorithm'
]
=
args
.
agp_pruning_alg
kwargs
[
'num_iterations'
]
=
args
.
agp_n_iters
kwargs
[
'epochs_per_iteration'
]
=
args
.
agp_n_epochs_per_iter
if
args
.
pruner_name
==
'slim'
:
kwargs
[
'sparsifying_training_epochs'
]
=
10
# pruning
pruner
=
pruner_type_to_class
[
args
.
pruner_name
](
model
,
config_list
,
**
kwargs
)
pruner
.
compress
()
pruner
.
export_model
(
args
.
experiment_dir
+
'/model_temp.pth'
,
args
.
experiment_dir
+
'./mask_temp.pth'
)
# model speedup
pruner
.
_unwrap_model
()
if
args
.
speed_up
:
dummy_input
=
torch
.
rand
(
1
,
3
,
224
,
224
).
to
(
device
)
ms
=
ModelSpeedup
(
model
,
dummy_input
,
args
.
experiment_dir
+
'./mask_temp.pth'
)
ms
.
speedup_model
()
print
(
model
)
count_flops
(
model
,
log
)
intermediate_loss
,
intermediate_acc
=
run_eval
(
model
,
test_dataloader
,
device
)
print
(
'Before Finetuning:
\n
Loss: {}
\n
Accuracy: {}'
.
format
(
intermediate_loss
,
intermediate_acc
))
log
.
write
(
'Before Finetuning:
\n
Loss: {}
\n
Accuracy: {}
\n
'
.
format
(
intermediate_loss
,
intermediate_acc
))
# finetuning
if
args
.
kd
:
model
=
run_finetune_distillation
(
model
,
teacher_model
,
train_dataloader
,
valid_dataloader
,
device
,
args
.
alpha
,
args
.
temp
,
n_epochs
=
args
.
finetune_epochs
,
learning_rate
=
args
.
learning_rate
,
weight_decay
=
args
.
weight_decay
)
else
:
model
=
run_finetune
(
model
,
train_dataloader
,
valid_dataloader
,
device
,
n_epochs
=
args
.
finetune_epochs
,
learning_rate
=
args
.
learning_rate
,
weight_decay
=
args
.
weight_decay
)
# final evaluation
final_loss
,
final_acc
=
run_eval
(
model
,
test_dataloader
,
device
)
print
(
'After Pruning:
\n
Loss: {}
\n
Accuracy: {}'
.
format
(
final_loss
,
final_acc
))
log
.
write
(
'After Pruning:
\n
Loss: {}
\n
Accuracy: {}'
.
format
(
final_loss
,
final_acc
))
# clean up
filePaths
=
[
args
.
experiment_dir
+
'/model_tmp.pth'
,
args
.
experiment_dir
+
'/mask_tmp.pth'
]
for
f
in
filePaths
:
if
os
.
path
.
exists
(
f
):
os
.
remove
(
f
)
log
.
close
()
if
__name__
==
'__main__'
:
args
=
parse_args
()
run_pruning
(
args
)
examples/model_compress/pruning/mobilenetv2_end2end/step1.png
0 → 100644
View file @
ab2ed393
49.8 KB
examples/model_compress/pruning/mobilenetv2_end2end/step2.png
0 → 100644
View file @
ab2ed393
73.3 KB
examples/model_compress/pruning/mobilenetv2_end2end/step3-1.png
0 → 100644
View file @
ab2ed393
63 KB
examples/model_compress/pruning/mobilenetv2_end2end/step3-2.png
0 → 100644
View file @
ab2ed393
96.6 KB
examples/model_compress/pruning/mobilenetv2_end2end/step4.png
0 → 100644
View file @
ab2ed393
72.4 KB
examples/model_compress/pruning/mobilenetv2_end2end/test.py
0 → 100644
View file @
ab2ed393
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import
os
import
torch
import
torch.nn
as
nn
from
torch.utils.data
import
DataLoader
from
tqdm
import
tqdm
import
numpy
as
np
from
utils
import
create_model
,
EvalDataset
,
count_flops
device
=
torch
.
device
(
"cuda"
if
torch
.
cuda
.
is_available
()
else
"cpu"
)
model_type
=
'mobilenet_v2_torchhub'
# 'mobilenet_v1' 'mobilenet_v2' 'mobilenet_v2_torchhub'
pretrained
=
False
# load imagenet weight (only for 'mobilenet_v2_torchhub')
checkpoint_dir
=
'./pretrained_{}/'
.
format
(
model_type
)
checkpoint
=
checkpoint_dir
+
'/checkpoint_best.pt'
# model checkpoint produced by pretrain.py
input_size
=
224
n_classes
=
120
batch_size
=
32
def
run_test
():
model
=
create_model
(
model_type
=
model_type
,
pretrained
=
pretrained
,
n_classes
=
n_classes
,
input_size
=
input_size
,
checkpoint
=
checkpoint
)
model
=
model
.
to
(
device
)
print
(
model
)
# count_flops(model, device=device)
test_dataset
=
EvalDataset
(
'./data/stanford-dogs/Processed/test'
)
test_dataloader
=
DataLoader
(
test_dataset
,
batch_size
=
batch_size
,
shuffle
=
False
)
model
.
eval
()
loss_func
=
nn
.
CrossEntropyLoss
()
acc_list
,
loss_list
=
[],
[]
with
torch
.
no_grad
():
for
i
,
(
inputs
,
labels
)
in
enumerate
(
tqdm
(
test_dataloader
)):
inputs
,
labels
=
inputs
.
float
().
to
(
device
),
labels
.
to
(
device
)
preds
=
model
(
inputs
)
pred_idx
=
preds
.
max
(
1
).
indices
acc
=
(
pred_idx
==
labels
).
sum
().
item
()
/
labels
.
size
(
0
)
acc_list
.
append
(
acc
)
loss
=
loss_func
(
preds
,
labels
).
item
()
loss_list
.
append
(
loss
)
final_loss
=
np
.
array
(
loss_list
).
mean
()
final_acc
=
np
.
array
(
acc_list
).
mean
()
print
(
'Test loss: {}
\n
Test accuracy: {}'
.
format
(
final_loss
,
final_acc
))
if
__name__
==
'__main__'
:
run_test
()
examples/model_compress/pruning/mobilenetv2_end2end/utils.py
0 → 100644
View file @
ab2ed393
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import
os
import
torch
from
torch.utils.data
import
Dataset
,
DataLoader
import
torchvision.transforms
as
transforms
import
numpy
as
np
from
nni.compression.pytorch.utils.counter
import
count_flops_params
import
sys
sys
.
path
.
append
(
'../../models'
)
from
mobilenet
import
MobileNet
from
mobilenet_v2
import
MobileNetV2
def
create_model
(
model_type
=
None
,
n_classes
=
120
,
input_size
=
224
,
checkpoint
=
None
,
pretrained
=
False
,
width_mult
=
1.
):
if
model_type
==
'mobilenet_v1'
:
model
=
MobileNet
(
n_class
=
n_classes
,
profile
=
'normal'
)
elif
model_type
==
'mobilenet_v2'
:
model
=
MobileNetV2
(
n_class
=
n_classes
,
input_size
=
input_size
,
width_mult
=
width_mult
)
elif
model_type
==
'mobilenet_v2_torchhub'
:
model
=
torch
.
hub
.
load
(
'pytorch/vision:v0.8.1'
,
'mobilenet_v2'
,
pretrained
=
pretrained
)
# model = torch.hub.load('pytorch/vision:v0.10.0', 'mobilenet_v2', pretrained=pretrained)
feature_size
=
model
.
classifier
[
1
].
weight
.
data
.
size
()[
1
]
replace_classifier
=
torch
.
nn
.
Linear
(
feature_size
,
n_classes
)
model
.
classifier
[
1
]
=
replace_classifier
elif
model_type
is
None
:
model
=
None
else
:
raise
RuntimeError
(
'Unknown model_type.'
)
if
checkpoint
is
not
None
:
model
.
load_state_dict
(
torch
.
load
(
checkpoint
))
return
model
def
get_dataloader
(
dataset_type
,
data_path
,
batch_size
=
32
,
shuffle
=
True
):
assert
dataset_type
in
[
'train'
,
'eval'
]
if
dataset_type
==
'train'
:
ds
=
TrainDataset
(
data_path
)
else
:
ds
=
EvalDataset
(
data_path
)
return
DataLoader
(
ds
,
batch_size
,
shuffle
=
shuffle
)
class
TrainDataset
(
Dataset
):
def
__init__
(
self
,
npy_dir
):
self
.
root_dir
=
npy_dir
self
.
case_names
=
[
self
.
root_dir
+
'/'
+
x
for
x
in
os
.
listdir
(
self
.
root_dir
)]
transform_set
=
[
transforms
.
Lambda
(
lambda
x
:
x
),
transforms
.
RandomRotation
(
30
),
transforms
.
ColorJitter
(),
transforms
.
RandomHorizontalFlip
(
p
=
1
)]
self
.
transform
=
transforms
.
RandomChoice
(
transform_set
)
def
__len__
(
self
):
return
len
(
self
.
case_names
)
def
__getitem__
(
self
,
index
):
instance
=
np
.
load
(
self
.
case_names
[
index
],
allow_pickle
=
True
).
item
()
x
=
instance
[
'input'
].
transpose
(
2
,
0
,
1
)
# (C, H, W)
x
=
torch
.
from_numpy
(
x
).
type
(
torch
.
float
)
# convert to Tensor to use torchvision.transforms
x
=
self
.
transform
(
x
)
return
x
,
instance
[
'label'
]
class
EvalDataset
(
Dataset
):
def
__init__
(
self
,
npy_dir
):
self
.
root_dir
=
npy_dir
self
.
case_names
=
[
self
.
root_dir
+
'/'
+
x
for
x
in
os
.
listdir
(
self
.
root_dir
)]
def
__len__
(
self
):
return
len
(
self
.
case_names
)
def
__getitem__
(
self
,
index
):
instance
=
np
.
load
(
self
.
case_names
[
index
],
allow_pickle
=
True
).
item
()
x
=
instance
[
'input'
].
transpose
(
2
,
0
,
1
)
x
=
torch
.
from_numpy
(
x
).
type
(
torch
.
float
)
return
x
,
instance
[
'label'
]
def
count_flops
(
model
,
log
=
None
,
device
=
None
):
dummy_input
=
torch
.
rand
([
1
,
3
,
256
,
256
])
if
device
is
not
None
:
dummy_input
=
dummy_input
.
to
(
device
)
flops
,
params
,
results
=
count_flops_params
(
model
,
dummy_input
)
print
(
f
"FLOPs:
{
flops
}
, params:
{
params
}
"
)
if
log
is
not
None
:
log
.
write
(
f
"FLOPs:
{
flops
}
, params:
{
params
}
\n
"
)
return
flops
,
params
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment