Adding fork of fastai- life is easier like this

This commit is contained in:
Jason Antic
2018-11-18 22:35:14 -08:00
parent 01f7021899
commit ec24c85325
61 changed files with 12426 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
weights/
+60
View File
@@ -0,0 +1,60 @@
Apache License, Version 2.0 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
View File
+80
View File
@@ -0,0 +1,80 @@
from .lm_rnn import *
class AdaptiveSoftmax(nn.Module):
def __init__(self, input_size, cutoff):
super().__init__()
self.input_size,self.cutoff = input_size,cutoff
self.output_size = cutoff[0] + len(cutoff) - 1
self.head = nn.Linear(input_size, self.output_size)
self.tail = nn.ModuleList()
for i in range(len(cutoff) - 1):
seq = nn.Sequential(nn.Linear(input_size, input_size // 4 ** i, False),
nn.Linear(input_size // 4 ** i, cutoff[i + 1] - cutoff[i], False))
self.tail.append(seq)
def reset(self):
nn.init.xavier_normal(self.head.weight)
for tail in self.tail:
nn.init.xavier_normal(tail[0].weight)
nn.init.xavier_normal(tail[1].weight)
def set_target(self, target):
self.id = []
for i in range(len(self.cutoff) - 1):
mask = target.ge(self.cutoff[i]).mul(target.lt(self.cutoff[i + 1]))
if mask.sum() > 0:
self.id.append(Variable(mask.float().nonzero().squeeze(1)))
else: self.id.append(None)
def forward(self, input):
output = [self.head(input)]
for i in range(len(self.id)):
if self.id[i] is not None:
output.append(self.tail[i](input.index_select(0, self.id[i])))
else: output.append(None)
return output
def log_prob(self, input):
lsm = nn.LogSoftmax().cuda()
head_out = self.head(input)
batch_size = head_out.size(0)
prob = torch.zeros(batch_size, self.cutoff[-1]).cuda()
lsm_head = lsm(head_out)
prob.narrow(1, 0, self.output_size).add_(lsm_head.narrow(1, 0, self.output_size).data)
for i in range(len(self.tail)):
pos = self.cutoff[i]
i_size = self.cutoff[i + 1] - pos
buffer = lsm_head.narrow(1, self.cutoff[0] + i, 1)
buffer = buffer.expand(batch_size, i_size)
lsm_tail = lsm(self.tail[i](input))
prob.narrow(1, pos, i_size).copy_(buffer.data).add_(lsm_tail.data)
return prob
class AdaptiveLoss(nn.Module):
def __init__(self, cutoff):
super().__init__()
self.cutoff = cutoff
self.criterions = nn.ModuleList([nn.CrossEntropyLoss(size_average=False) for i in self.cutoff])
def remap_target(self, target):
new_target = [target.clone()]
for i in range(len(self.cutoff) - 1):
mask = target.ge(self.cutoff[i]).mul(target.lt(self.cutoff[i + 1]))
new_target[0][mask] = self.cutoff[0] + i
if mask.sum() > 0: new_target.append(target[mask].add(-self.cutoff[i]))
else: new_target.append(None)
return new_target
def forward(self, input, target):
batch_size = input[0].size(0)
target = self.remap_target(target.data)
output = 0.0
for i in range(len(input)):
if input[i] is not None:
assert(target[i].min() >= 0 and target[i].max() <= input[i].size(1))
criterion = self.criterions[i]
output += criterion(input[i], Variable(target[i]))
output /= batch_size
return output
+224
View File
@@ -0,0 +1,224 @@
from .imports import *
from .torch_imports import *
from .dataset import *
from .learner import *
class PassthruDataset(Dataset):
def __init__(self,*args, is_reg=True, is_multi=False):
*xs,y=args
self.xs,self.y = xs,y
self.is_reg = is_reg
self.is_multi = is_multi
def __len__(self): return len(self.y)
def __getitem__(self, idx): return [o[idx] for o in self.xs] + [self.y[idx]]
@classmethod
def from_data_frame(cls, df, cols_x, col_y, is_reg=True, is_multi=False):
cols = [df[o] for o in cols_x+[col_y]]
return cls(*cols, is_reg=is_reg, is_multi=is_multi)
class ColumnarDataset(Dataset):
def __init__(self, cats, conts, y, is_reg, is_multi):
n = len(cats[0]) if cats else len(conts[0])
self.cats = np.stack(cats, 1).astype(np.int64) if cats else np.zeros((n,1))
self.conts = np.stack(conts, 1).astype(np.float32) if conts else np.zeros((n,1))
self.y = np.zeros((n,1)) if y is None else y
if is_reg:
self.y = self.y[:,None]
self.is_reg = is_reg
self.is_multi = is_multi
def __len__(self): return len(self.y)
def __getitem__(self, idx):
return [self.cats[idx], self.conts[idx], self.y[idx]]
@classmethod
def from_data_frames(cls, df_cat, df_cont, y=None, is_reg=True, is_multi=False):
cat_cols = [c.values for n,c in df_cat.items()]
cont_cols = [c.values for n,c in df_cont.items()]
return cls(cat_cols, cont_cols, y, is_reg, is_multi)
@classmethod
def from_data_frame(cls, df, cat_flds, y=None, is_reg=True, is_multi=False):
return cls.from_data_frames(df[cat_flds], df.drop(cat_flds, axis=1), y, is_reg, is_multi)
class ColumnarModelData(ModelData):
def __init__(self, path, trn_ds, val_ds, bs, test_ds=None, shuffle=True):
test_dl = DataLoader(test_ds, bs, shuffle=False, num_workers=1) if test_ds is not None else None
super().__init__(path, DataLoader(trn_ds, bs, shuffle=shuffle, num_workers=1),
DataLoader(val_ds, bs*2, shuffle=False, num_workers=1), test_dl)
@classmethod
def from_arrays(cls, path, val_idxs, xs, y, is_reg=True, is_multi=False, bs=64, test_xs=None, shuffle=True):
((val_xs, trn_xs), (val_y, trn_y)) = split_by_idx(val_idxs, xs, y)
test_ds = PassthruDataset(*(test_xs.T), [0] * len(test_xs), is_reg=is_reg, is_multi=is_multi) if test_xs is not None else None
return cls(path, PassthruDataset(*(trn_xs.T), trn_y, is_reg=is_reg, is_multi=is_multi),
PassthruDataset(*(val_xs.T), val_y, is_reg=is_reg, is_multi=is_multi),
bs=bs, shuffle=shuffle, test_ds=test_ds)
@classmethod
def from_data_frames(cls, path, trn_df, val_df, trn_y, val_y, cat_flds, bs, is_reg, is_multi, test_df=None, shuffle=True):
trn_ds = ColumnarDataset.from_data_frame(trn_df, cat_flds, trn_y, is_reg, is_multi)
val_ds = ColumnarDataset.from_data_frame(val_df, cat_flds, val_y, is_reg, is_multi)
test_ds = ColumnarDataset.from_data_frame(test_df, cat_flds, None, is_reg, is_multi) if test_df is not None else None
return cls(path, trn_ds, val_ds, bs, test_ds=test_ds, shuffle=shuffle)
@classmethod
def from_data_frame(cls, path, val_idxs, df, y, cat_flds, bs, is_reg=True, is_multi=False, test_df=None, shuffle=True):
((val_df, trn_df), (val_y, trn_y)) = split_by_idx(val_idxs, df, y)
return cls.from_data_frames(path, trn_df, val_df, trn_y, val_y, cat_flds, bs, is_reg, is_multi, test_df=test_df, shuffle=shuffle)
def get_learner(self, emb_szs, n_cont, emb_drop, out_sz, szs, drops,
y_range=None, use_bn=False, **kwargs):
model = MixedInputModel(emb_szs, n_cont, emb_drop, out_sz, szs, drops, y_range, use_bn, self.is_reg, self.is_multi)
return StructuredLearner(self, StructuredModel(to_gpu(model)), opt_fn=optim.Adam, **kwargs)
def emb_init(x):
x = x.weight.data
sc = 2/(x.size(1)+1)
x.uniform_(-sc,sc)
class MixedInputModel(nn.Module):
def __init__(self, emb_szs, n_cont, emb_drop, out_sz, szs, drops,
y_range=None, use_bn=False, is_reg=True, is_multi=False):
super().__init__()
self.embs = nn.ModuleList([nn.Embedding(c, s) for c,s in emb_szs])
for emb in self.embs: emb_init(emb)
n_emb = sum(e.embedding_dim for e in self.embs)
self.n_emb, self.n_cont=n_emb, n_cont
szs = [n_emb+n_cont] + szs
self.lins = nn.ModuleList([
nn.Linear(szs[i], szs[i+1]) for i in range(len(szs)-1)])
self.bns = nn.ModuleList([
nn.BatchNorm1d(sz) for sz in szs[1:]])
for o in self.lins: kaiming_normal(o.weight.data)
self.outp = nn.Linear(szs[-1], out_sz)
kaiming_normal(self.outp.weight.data)
self.emb_drop = nn.Dropout(emb_drop)
self.drops = nn.ModuleList([nn.Dropout(drop) for drop in drops])
self.bn = nn.BatchNorm1d(n_cont)
self.use_bn,self.y_range = use_bn,y_range
self.is_reg = is_reg
self.is_multi = is_multi
def forward(self, x_cat, x_cont):
if self.n_emb != 0:
x = [e(x_cat[:,i]) for i,e in enumerate(self.embs)]
x = torch.cat(x, 1)
x = self.emb_drop(x)
if self.n_cont != 0:
x2 = self.bn(x_cont)
x = torch.cat([x, x2], 1) if self.n_emb != 0 else x2
for l,d,b in zip(self.lins, self.drops, self.bns):
x = F.relu(l(x))
if self.use_bn: x = b(x)
x = d(x)
x = self.outp(x)
if not self.is_reg:
if self.is_multi:
x = F.sigmoid(x)
else:
x = F.log_softmax(x)
elif self.y_range:
x = F.sigmoid(x)
x = x*(self.y_range[1] - self.y_range[0])
x = x+self.y_range[0]
return x
class StructuredLearner(Learner):
def __init__(self, data, models, **kwargs):
super().__init__(data, models, **kwargs)
def _get_crit(self, data): return F.mse_loss if data.is_reg else F.binary_cross_entropy if data.is_multi else F.nll_loss
def summary(self):
x = [torch.ones(3, self.data.trn_ds.cats.shape[1], dtype=torch.int64), torch.rand(3, self.data.trn_ds.conts.shape[1])]
return model_summary(self.model, x)
class StructuredModel(BasicModel):
def get_layer_groups(self):
m=self.model
return [m.embs, children(m.lins)+children(m.bns), m.outp]
class CollabFilterDataset(Dataset):
def __init__(self, path, user_col, item_col, ratings):
self.ratings,self.path = ratings.values.astype(np.float32),path
self.n = len(ratings)
(self.users,self.user2idx,self.user_col,self.n_users) = self.proc_col(user_col)
(self.items,self.item2idx,self.item_col,self.n_items) = self.proc_col(item_col)
self.min_score,self.max_score = min(ratings),max(ratings)
self.cols = [self.user_col,self.item_col,self.ratings]
@classmethod
def from_data_frame(cls, path, df, user_name, item_name, rating_name):
return cls(path, df[user_name], df[item_name], df[rating_name])
@classmethod
def from_csv(cls, path, csv, user_name, item_name, rating_name):
df = pd.read_csv(os.path.join(path,csv))
return cls.from_data_frame(path, df, user_name, item_name, rating_name)
def proc_col(self,col):
uniq = col.unique()
name2idx = {o:i for i,o in enumerate(uniq)}
return (uniq, name2idx, np.array([name2idx[x] for x in col]), len(uniq))
def __len__(self): return self.n
def __getitem__(self, idx): return [o[idx] for o in self.cols]
def get_data(self, val_idxs, bs):
val, trn = zip(*split_by_idx(val_idxs, *self.cols))
return ColumnarModelData(self.path, PassthruDataset(*trn), PassthruDataset(*val), bs)
def get_model(self, n_factors):
model = EmbeddingDotBias(n_factors, self.n_users, self.n_items, self.min_score, self.max_score)
return CollabFilterModel(to_gpu(model))
def get_learner(self, n_factors, val_idxs, bs, **kwargs):
return CollabFilterLearner(self.get_data(val_idxs, bs), self.get_model(n_factors), **kwargs)
def get_emb(ni,nf):
e = nn.Embedding(ni, nf)
e.weight.data.uniform_(-0.05,0.05)
return e
class EmbeddingDotBias(nn.Module):
def __init__(self, n_factors, n_users, n_items, min_score, max_score):
super().__init__()
self.min_score,self.max_score = min_score,max_score
(self.u, self.i, self.ub, self.ib) = [get_emb(*o) for o in [
(n_users, n_factors), (n_items, n_factors), (n_users,1), (n_items,1)
]]
def forward(self, users, items):
um = self.u(users)* self.i(items)
res = um.sum(1) + self.ub(users).squeeze() + self.ib(items).squeeze()
return F.sigmoid(res) * (self.max_score-self.min_score) + self.min_score
class CollabFilterLearner(Learner):
def __init__(self, data, models, **kwargs):
super().__init__(data, models, **kwargs)
def _get_crit(self, data): return F.mse_loss
def summary(self): return model_summary(self.model, [torch.ones(3, dtype=torch.int64), torch.ones(3, dtype=torch.int64)])
class CollabFilterModel(BasicModel):
def get_layer_groups(self): return self.model
+209
View File
@@ -0,0 +1,209 @@
from .core import *
from .layers import *
from .learner import *
from .initializers import *
model_meta = {
resnet18:[8,6], resnet34:[8,6], resnet50:[8,6], resnet101:[8,6], resnet152:[8,6],
vgg16:[0,22], vgg19:[0,22],
resnext50:[8,6], resnext101:[8,6], resnext101_64:[8,6],
wrn:[8,6], inceptionresnet_2:[-2,9], inception_4:[-1,9],
dn121:[0,7], dn161:[0,7], dn169:[0,7], dn201:[0,7],
}
model_features = {inception_4: 3072, dn121: 2048, dn161: 4416,} # nasnetalarge: 4032*2}
class ConvnetBuilder():
"""Class representing a convolutional network.
Arguments:
f: a model creation function (e.g. resnet34, vgg16, etc)
c (int): size of the last layer
is_multi (bool): is multilabel classification?
(def here http://scikit-learn.org/stable/modules/multiclass.html)
is_reg (bool): is a regression?
ps (float or array of float): dropout parameters
xtra_fc (list of ints): list of hidden layers with # hidden neurons
xtra_cut (int): # layers earlier than default to cut the model, default is 0
custom_head : add custom model classes that are inherited from nn.modules at the end of the model
that is mentioned on Argument 'f'
"""
def __init__(self, f, c, is_multi, is_reg, ps=None, xtra_fc=None, xtra_cut=0, custom_head=None, pretrained=True):
self.f,self.c,self.is_multi,self.is_reg,self.xtra_cut = f,c,is_multi,is_reg,xtra_cut
if xtra_fc is None: xtra_fc = [512]
if ps is None: ps = [0.25]*len(xtra_fc) + [0.5]
self.ps,self.xtra_fc = ps,xtra_fc
if f in model_meta: cut,self.lr_cut = model_meta[f]
else: cut,self.lr_cut = 0,0
cut-=xtra_cut
layers = cut_model(f(pretrained), cut)
self.nf = model_features[f] if f in model_features else (num_features(layers)*2)
if not custom_head: layers += [AdaptiveConcatPool2d(), Flatten()]
self.top_model = nn.Sequential(*layers)
n_fc = len(self.xtra_fc)+1
if not isinstance(self.ps, list): self.ps = [self.ps]*n_fc
if custom_head: fc_layers = [custom_head]
else: fc_layers = self.get_fc_layers()
self.n_fc = len(fc_layers)
self.fc_model = to_gpu(nn.Sequential(*fc_layers))
if not custom_head: apply_init(self.fc_model, kaiming_normal)
self.model = to_gpu(nn.Sequential(*(layers+fc_layers)))
@property
def name(self): return f'{self.f.__name__}_{self.xtra_cut}'
def create_fc_layer(self, ni, nf, p, actn=None):
res=[nn.BatchNorm1d(num_features=ni)]
if p: res.append(nn.Dropout(p=p))
res.append(nn.Linear(in_features=ni, out_features=nf))
if actn: res.append(actn)
return res
def get_fc_layers(self):
res=[]
ni=self.nf
for i,nf in enumerate(self.xtra_fc):
res += self.create_fc_layer(ni, nf, p=self.ps[i], actn=nn.ReLU())
ni=nf
final_actn = nn.Sigmoid() if self.is_multi else nn.LogSoftmax()
if self.is_reg: final_actn = None
res += self.create_fc_layer(ni, self.c, p=self.ps[-1], actn=final_actn)
return res
def get_layer_groups(self, do_fc=False):
if do_fc:
return [self.fc_model]
idxs = [self.lr_cut]
c = children(self.top_model)
if len(c)==3: c = children(c[0])+c[1:]
lgs = list(split_by_idxs(c,idxs))
return lgs+[self.fc_model]
class ConvLearner(Learner):
"""
Class used to train a chosen supported covnet model. Eg. ResNet-34, etc.
Arguments:
data: training data for model
models: model architectures to base learner
precompute: bool to reuse precomputed activations
**kwargs: parameters from Learner() class
"""
def __init__(self, data, models, precompute=False, **kwargs):
self.precompute = False
super().__init__(data, models, **kwargs)
if hasattr(data, 'is_multi') and not data.is_reg and self.metrics is None:
self.metrics = [accuracy_thresh(0.5)] if self.data.is_multi else [accuracy]
if precompute: self.save_fc1()
self.freeze()
self.precompute = precompute
def _get_crit(self, data):
if not hasattr(data, 'is_multi'): return super()._get_crit(data)
return F.l1_loss if data.is_reg else F.binary_cross_entropy if data.is_multi else F.nll_loss
@classmethod
def pretrained(cls, f, data, ps=None, xtra_fc=None, xtra_cut=0, custom_head=None, precompute=False,
pretrained=True, **kwargs):
models = ConvnetBuilder(f, data.c, data.is_multi, data.is_reg,
ps=ps, xtra_fc=xtra_fc, xtra_cut=xtra_cut, custom_head=custom_head, pretrained=pretrained)
return cls(data, models, precompute, **kwargs)
@classmethod
def lsuv_learner(cls, f, data, ps=None, xtra_fc=None, xtra_cut=0, custom_head=None, precompute=False,
needed_std=1.0, std_tol=0.1, max_attempts=10, do_orthonorm=False, **kwargs):
models = ConvnetBuilder(f, data.c, data.is_multi, data.is_reg,
ps=ps, xtra_fc=xtra_fc, xtra_cut=xtra_cut, custom_head=custom_head, pretrained=False)
convlearn=cls(data, models, precompute, **kwargs)
convlearn.lsuv_init()
return convlearn
@property
def model(self): return self.models.fc_model if self.precompute else self.models.model
def half(self):
if self.fp16: return
self.fp16 = True
if type(self.model) != FP16: self.models.model = FP16(self.model)
if not isinstance(self.models.fc_model, FP16): self.models.fc_model = FP16(self.models.fc_model)
def float(self):
if not self.fp16: return
self.fp16 = False
if type(self.models.model) == FP16: self.models.model = self.model.module.float()
if type(self.models.fc_model) == FP16: self.models.fc_model = self.models.fc_model.module.float()
@property
def data(self): return self.fc_data if self.precompute else self.data_
def create_empty_bcolz(self, n, name):
return bcolz.carray(np.zeros((0,n), np.float32), chunklen=1, mode='w', rootdir=name)
def set_data(self, data, precompute=False):
super().set_data(data)
if precompute:
self.unfreeze()
self.save_fc1()
self.freeze()
self.precompute = True
else:
self.freeze()
def get_layer_groups(self):
return self.models.get_layer_groups(self.precompute)
def summary(self):
precompute = self.precompute
self.precompute = False
res = super().summary()
self.precompute = precompute
return res
def get_activations(self, force=False):
tmpl = f'_{self.models.name}_{self.data.sz}.bc'
# TODO: Somehow check that directory names haven't changed (e.g. added test set)
names = [os.path.join(self.tmp_path, p+tmpl) for p in ('x_act', 'x_act_val', 'x_act_test')]
if os.path.exists(names[0]) and not force:
self.activations = [bcolz.open(p) for p in names]
else:
self.activations = [self.create_empty_bcolz(self.models.nf,n) for n in names]
def save_fc1(self):
self.get_activations()
act, val_act, test_act = self.activations
m=self.models.top_model
if len(self.activations[0])!=len(self.data.trn_ds):
predict_to_bcolz(m, self.data.fix_dl, act)
if len(self.activations[1])!=len(self.data.val_ds):
predict_to_bcolz(m, self.data.val_dl, val_act)
if self.data.test_dl and (len(self.activations[2])!=len(self.data.test_ds)):
if self.data.test_dl: predict_to_bcolz(m, self.data.test_dl, test_act)
self.fc_data = ImageClassifierData.from_arrays(self.data.path,
(act, self.data.trn_y), (val_act, self.data.val_y), self.data.bs, classes=self.data.classes,
test = test_act if self.data.test_dl else None, num_workers=8)
def freeze(self):
""" Freeze all but the very last layer.
Make all layers untrainable (i.e. frozen) except for the last layer.
Returns:
None
"""
self.freeze_to(-1)
def unfreeze(self):
""" Unfreeze all layers.
Make all layers trainable by unfreezing. This will also set the `precompute` to `False` since we can
no longer pre-calculate the activation of frozen layers.
Returns:
None
"""
self.freeze_to(0)
self.precompute = False
+180
View File
@@ -0,0 +1,180 @@
from .imports import *
from .torch_imports import *
def sum_geom(a,r,n): return a*n if r==1 else math.ceil(a*(1-r**n)/(1-r))
def is_listy(x): return isinstance(x, (list,tuple))
def is_iter(x): return isinstance(x, collections.Iterable)
def map_over(x, f): return [f(o) for o in x] if is_listy(x) else f(x)
def map_none(x, f): return None if x is None else f(x)
def delistify(x): return x[0] if is_listy(x) else x
def listify(x, y):
if not is_iter(x): x=[x]
n = y if type(y)==int else len(y)
if len(x)==1: x = x * n
return x
conv_dict = {np.dtype('int8'): torch.LongTensor, np.dtype('int16'): torch.LongTensor,
np.dtype('int32'): torch.LongTensor, np.dtype('int64'): torch.LongTensor,
np.dtype('float32'): torch.FloatTensor, np.dtype('float64'): torch.FloatTensor}
def A(*a):
"""convert iterable object into numpy array"""
return np.array(a[0]) if len(a)==1 else [np.array(o) for o in a]
def T(a, half=False, cuda=True):
"""
Convert numpy array into a pytorch tensor.
if Cuda is available and USE_GPU=True, store resulting tensor in GPU.
"""
if not torch.is_tensor(a):
a = np.array(np.ascontiguousarray(a))
if a.dtype in (np.int8, np.int16, np.int32, np.int64):
a = torch.LongTensor(a.astype(np.int64))
elif a.dtype in (np.float32, np.float64):
a = torch.cuda.HalfTensor(a) if half else torch.FloatTensor(a)
else: raise NotImplementedError(a.dtype)
if cuda: a = to_gpu(a, async=True)
return a
def create_variable(x, volatile, requires_grad=False):
if type (x) != Variable:
if IS_TORCH_04: x = Variable(T(x), requires_grad=requires_grad)
else: x = Variable(T(x), requires_grad=requires_grad, volatile=volatile)
return x
def V_(x, requires_grad=False, volatile=False):
'''equivalent to create_variable, which creates a pytorch tensor'''
return create_variable(x, volatile=volatile, requires_grad=requires_grad)
def V(x, requires_grad=False, volatile=False):
'''creates a single or a list of pytorch tensors, depending on input x. '''
return map_over(x, lambda o: V_(o, requires_grad, volatile))
def VV_(x):
'''creates a volatile tensor, which does not require gradients. '''
return create_variable(x, True)
def VV(x):
'''creates a single or a list of pytorch tensors, depending on input x. '''
return map_over(x, VV_)
def to_np(v):
'''returns an np.array object given an input of np.array, list, tuple, torch variable or tensor.'''
if isinstance(v, (np.ndarray, np.generic)): return v
if isinstance(v, (list,tuple)): return [to_np(o) for o in v]
if isinstance(v, Variable): v=v.data
if isinstance(v, torch.cuda.HalfTensor): v=v.float()
return v.cpu().numpy()
IS_TORCH_04 = LooseVersion(torch.__version__) >= LooseVersion('0.4')
USE_GPU = torch.cuda.is_available()
def to_gpu(x, *args, **kwargs):
'''puts pytorch variable to gpu, if cuda is available and USE_GPU is set to true. '''
return x.cuda(*args, **kwargs) if USE_GPU else x
def noop(*args, **kwargs): return
def split_by_idxs(seq, idxs):
'''A generator that returns sequence pieces, seperated by indexes specified in idxs. '''
last = 0
for idx in idxs:
if not (-len(seq) <= idx < len(seq)):
raise KeyError(f'Idx {idx} is out-of-bounds')
yield seq[last:idx]
last = idx
yield seq[last:]
def trainable_params_(m):
'''Returns a list of trainable parameters in the model m. (i.e., those that require gradients.)'''
return [p for p in m.parameters() if p.requires_grad]
def chain_params(p):
if is_listy(p):
return list(chain(*[trainable_params_(o) for o in p]))
return trainable_params_(p)
def set_trainable_attr(m,b):
m.trainable=b
for p in m.parameters(): p.requires_grad=b
def apply_leaf(m, f):
c = children(m)
if isinstance(m, nn.Module): f(m)
if len(c)>0:
for l in c: apply_leaf(l,f)
def set_trainable(l, b):
apply_leaf(l, lambda m: set_trainable_attr(m,b))
def SGD_Momentum(momentum):
return lambda *args, **kwargs: optim.SGD(*args, momentum=momentum, **kwargs)
def one_hot(a,c): return np.eye(c)[a]
def partition(a, sz):
"""splits iterables a in equal parts of size sz"""
return [a[i:i+sz] for i in range(0, len(a), sz)]
def partition_by_cores(a):
return partition(a, len(a)//num_cpus() + 1)
def num_cpus():
try:
return len(os.sched_getaffinity(0))
except AttributeError:
return os.cpu_count()
class BasicModel():
def __init__(self,model,name='unnamed'): self.model,self.name = model,name
def get_layer_groups(self, do_fc=False): return children(self.model)
class SingleModel(BasicModel):
def get_layer_groups(self): return [self.model]
class SimpleNet(nn.Module):
def __init__(self, layers):
super().__init__()
self.layers = nn.ModuleList([
nn.Linear(layers[i], layers[i + 1]) for i in range(len(layers) - 1)])
def forward(self, x):
x = x.view(x.size(0), -1)
for l in self.layers:
l_x = l(x)
x = F.relu(l_x)
return F.log_softmax(l_x, dim=-1)
def save(fn, a):
"""Utility function that savess model, function, etc as pickle"""
pickle.dump(a, open(fn,'wb'))
def load(fn):
"""Utility function that loads model, function, etc as pickle"""
return pickle.load(open(fn,'rb'))
def load2(fn):
"""Utility funciton allowing model piclking across Python2 and Python3"""
return pickle.load(open(fn,'rb'), encoding='iso-8859-1')
def load_array(fname):
'''
Load array using bcolz, which is based on numpy, for fast array saving and loading operations.
https://github.com/Blosc/bcolz
'''
return bcolz.open(fname)[:]
def chunk_iter(iterable, chunk_size):
'''A generator that yields chunks of iterable, chunk_size at a time. '''
while True:
chunk = []
try:
for _ in range(chunk_size): chunk.append(next(iterable))
yield chunk
except StopIteration:
if chunk: yield chunk
break
def set_grad_enabled(mode): return torch.set_grad_enabled(mode) if IS_TORCH_04 else contextlib.suppress()
def no_grad_context(): return torch.no_grad() if IS_TORCH_04 else contextlib.suppress()
+90
View File
@@ -0,0 +1,90 @@
import torch, queue
from torch.utils.data.sampler import SequentialSampler, RandomSampler, BatchSampler
from .imports import *
from .core import *
import collections,sys,traceback,threading
string_classes = (str, bytes)
def get_tensor(batch, pin, half=False):
if isinstance(batch, (np.ndarray, np.generic)):
batch = T(batch, half=half, cuda=False).contiguous()
if pin: batch = batch.pin_memory()
return to_gpu(batch)
elif isinstance(batch, string_classes):
return batch
elif isinstance(batch, collections.Mapping):
return {k: get_tensor(sample, pin, half) for k, sample in batch.items()}
elif isinstance(batch, collections.Sequence):
return [get_tensor(sample, pin, half) for sample in batch]
raise TypeError(f"batch must contain numbers, dicts or lists; found {type(batch)}")
class DataLoader(object):
def __init__(self, dataset, batch_size=1, shuffle=False, sampler=None, batch_sampler=None, pad_idx=0,
num_workers=None, pin_memory=False, drop_last=False, pre_pad=True, half=False,
transpose=False, transpose_y=False):
self.dataset,self.batch_size,self.num_workers = dataset,batch_size,num_workers
self.pin_memory,self.drop_last,self.pre_pad = pin_memory,drop_last,pre_pad
self.transpose,self.transpose_y,self.pad_idx,self.half = transpose,transpose_y,pad_idx,half
if batch_sampler is not None:
if batch_size > 1 or shuffle or sampler is not None or drop_last:
raise ValueError('batch_sampler is mutually exclusive with '
'batch_size, shuffle, sampler, and drop_last')
if sampler is not None and shuffle:
raise ValueError('sampler is mutually exclusive with shuffle')
if batch_sampler is None:
if sampler is None:
sampler = RandomSampler(dataset) if shuffle else SequentialSampler(dataset)
batch_sampler = BatchSampler(sampler, batch_size, drop_last)
if num_workers is None:
self.num_workers = num_cpus()
self.sampler = sampler
self.batch_sampler = batch_sampler
def __len__(self): return len(self.batch_sampler)
def jag_stack(self, b):
if len(b[0].shape) not in (1,2): return np.stack(b)
ml = max(len(o) for o in b)
if min(len(o) for o in b)==ml: return np.stack(b)
res = np.zeros((len(b), ml), dtype=b[0].dtype) + self.pad_idx
for i,o in enumerate(b):
if self.pre_pad: res[i, -len(o):] = o
else: res[i, :len(o)] = o
return res
def np_collate(self, batch):
b = batch[0]
if isinstance(b, (np.ndarray, np.generic)): return self.jag_stack(batch)
elif isinstance(b, (int, float)): return np.array(batch)
elif isinstance(b, string_classes): return batch
elif isinstance(b, collections.Mapping):
return {key: self.np_collate([d[key] for d in batch]) for key in b}
elif isinstance(b, collections.Sequence):
return [self.np_collate(samples) for samples in zip(*batch)]
raise TypeError(("batch must contain numbers, dicts or lists; found {}".format(type(b))))
def get_batch(self, indices):
res = self.np_collate([self.dataset[i] for i in indices])
if self.transpose: res[0] = res[0].T
if self.transpose_y: res[1] = res[1].T
return res
def __iter__(self):
if self.num_workers==0:
for batch in map(self.get_batch, iter(self.batch_sampler)):
yield get_tensor(batch, self.pin_memory, self.half)
else:
with ThreadPoolExecutor(max_workers=self.num_workers) as e:
# avoid py3.6 issue where queue is infinite and can result in memory exhaustion
for c in chunk_iter(iter(self.batch_sampler), self.num_workers*10):
for batch in e.map(self.get_batch, c):
yield get_tensor(batch, self.pin_memory, self.half)
+529
View File
@@ -0,0 +1,529 @@
import csv
from .imports import *
from .torch_imports import *
from .core import *
from .transforms import *
from .layer_optimizer import *
from .dataloader import DataLoader
def get_cv_idxs(n, cv_idx=0, val_pct=0.2, seed=42):
""" Get a list of index values for Validation set from a dataset
Arguments:
n : int, Total number of elements in the data set.
cv_idx : int, starting index [idx_start = cv_idx*int(val_pct*n)]
val_pct : (int, float), validation set percentage
seed : seed value for RandomState
Returns:
list of indexes
"""
np.random.seed(seed)
n_val = int(val_pct*n)
idx_start = cv_idx*n_val
idxs = np.random.permutation(n)
return idxs[idx_start:idx_start+n_val]
def resize_img(fname, targ, path, new_path):
"""
Enlarge or shrink a single image to scale, such that the smaller of the height or width dimension is equal to targ.
"""
dest = os.path.join(path,new_path,str(targ),fname)
if os.path.exists(dest): return
im = Image.open(os.path.join(path, fname)).convert('RGB')
r,c = im.size
ratio = targ/min(r,c)
sz = (scale_to(r, ratio, targ), scale_to(c, ratio, targ))
os.makedirs(os.path.split(dest)[0], exist_ok=True)
im.resize(sz, Image.LINEAR).save(dest)
def resize_imgs(fnames, targ, path, new_path):
"""
Enlarge or shrink a set of images in the same directory to scale, such that the smaller of the height or width dimension is equal to targ.
Note:
-- This function is multithreaded for efficiency.
-- When destination file or folder already exist, function exists without raising an error.
"""
if not os.path.exists(os.path.join(path,new_path,str(targ),fnames[0])):
with ThreadPoolExecutor(8) as e:
ims = e.map(lambda x: resize_img(x, targ, path, new_path), fnames)
for x in tqdm(ims, total=len(fnames), leave=False): pass
return os.path.join(path,new_path,str(targ))
def read_dir(path, folder):
""" Returns a list of relative file paths to `path` for all files within `folder` """
full_path = os.path.join(path, folder)
fnames = glob(f"{full_path}/*.*")
directories = glob(f"{full_path}/*/")
if any(fnames):
return [os.path.relpath(f,path) for f in fnames]
elif any(directories):
raise FileNotFoundError("{} has subdirectories but contains no files. Is your directory structure is correct?".format(full_path))
else:
raise FileNotFoundError("{} folder doesn't exist or is empty".format(full_path))
def read_dirs(path, folder):
'''
Fetches name of all files in path in long form, and labels associated by extrapolation of directory names.
'''
lbls, fnames, all_lbls = [], [], []
full_path = os.path.join(path, folder)
for lbl in sorted(os.listdir(full_path)):
if lbl not in ('.ipynb_checkpoints','.DS_Store'):
all_lbls.append(lbl)
for fname in os.listdir(os.path.join(full_path, lbl)):
if fname not in ('.DS_Store'):
fnames.append(os.path.join(folder, lbl, fname))
lbls.append(lbl)
return fnames, lbls, all_lbls
def n_hot(ids, c):
'''
one hot encoding by index. Returns array of length c, where all entries are 0, except for the indecies in ids
'''
res = np.zeros((c,), dtype=np.float32)
res[ids] = 1
return res
def folder_source(path, folder):
"""
Returns the filenames and labels for a folder within a path
Returns:
-------
fnames: a list of the filenames within `folder`
all_lbls: a list of all of the labels in `folder`, where the # of labels is determined by the # of directories within `folder`
lbl_arr: a numpy array of the label indices in `all_lbls`
"""
fnames, lbls, all_lbls = read_dirs(path, folder)
lbl2idx = {lbl:idx for idx,lbl in enumerate(all_lbls)}
idxs = [lbl2idx[lbl] for lbl in lbls]
lbl_arr = np.array(idxs, dtype=int)
return fnames, lbl_arr, all_lbls
def parse_csv_labels(fn, skip_header=True, cat_separator = ' '):
"""Parse filenames and label sets from a CSV file.
This method expects that the csv file at path :fn: has two columns. If it
has a header, :skip_header: should be set to True. The labels in the
label set are expected to be space separated.
Arguments:
fn: Path to a CSV file.
skip_header: A boolean flag indicating whether to skip the header.
Returns:
a two-tuple of (
image filenames,
a dictionary of filenames and corresponding labels
)
.
:param cat_separator: the separator for the categories column
"""
df = pd.read_csv(fn, index_col=0, header=0 if skip_header else None, dtype=str)
fnames = df.index.values
df.iloc[:,0] = df.iloc[:,0].str.split(cat_separator)
return fnames, list(df.to_dict().values())[0]
def nhot_labels(label2idx, csv_labels, fnames, c):
all_idx = {k: n_hot([label2idx[o] for o in ([] if type(v) == float else v)], c)
for k,v in csv_labels.items()}
return np.stack([all_idx[o] for o in fnames])
def csv_source(folder, csv_file, skip_header=True, suffix='', continuous=False, cat_separator=' '):
fnames,csv_labels = parse_csv_labels(csv_file, skip_header, cat_separator)
return dict_source(folder, fnames, csv_labels, suffix, continuous)
def dict_source(folder, fnames, csv_labels, suffix='', continuous=False):
all_labels = sorted(list(set(p for o in csv_labels.values() for p in ([] if type(o) == float else o))))
full_names = [os.path.join(folder,str(fn)+suffix) for fn in fnames]
if continuous:
label_arr = np.array([np.array(csv_labels[i]).astype(np.float32)
for i in fnames])
else:
label2idx = {v:k for k,v in enumerate(all_labels)}
label_arr = nhot_labels(label2idx, csv_labels, fnames, len(all_labels))
is_single = np.all(label_arr.sum(axis=1)==1)
if is_single: label_arr = np.argmax(label_arr, axis=1)
return full_names, label_arr, all_labels
class BaseDataset(Dataset):
"""An abstract class representing a fastai dataset. Extends torch.utils.data.Dataset."""
def __init__(self, transform=None):
self.transform = transform
self.n = self.get_n()
self.c = self.get_c()
self.sz = self.get_sz()
def get1item(self, idx):
x,y = self.get_x(idx),self.get_y(idx)
return self.get(self.transform, x, y)
def __getitem__(self, idx):
if isinstance(idx,slice):
xs,ys = zip(*[self.get1item(i) for i in range(*idx.indices(self.n))])
return np.stack(xs),ys
return self.get1item(idx)
def __len__(self): return self.n
def get(self, tfm, x, y):
return (x,y) if tfm is None else tfm(x,y)
@abstractmethod
def get_n(self):
"""Return number of elements in the dataset == len(self)."""
raise NotImplementedError
@abstractmethod
def get_c(self):
"""Return number of classes in a dataset."""
raise NotImplementedError
@abstractmethod
def get_sz(self):
"""Return maximum size of an image in a dataset."""
raise NotImplementedError
@abstractmethod
def get_x(self, i):
"""Return i-th example (image, wav, etc)."""
raise NotImplementedError
@abstractmethod
def get_y(self, i):
"""Return i-th label."""
raise NotImplementedError
@property
def is_multi(self):
"""Returns true if this data set contains multiple labels per sample."""
return False
@property
def is_reg(self):
"""True if the data set is used to train regression models."""
return False
def open_image(fn):
""" Opens an image using OpenCV given the file path.
Arguments:
fn: the file path of the image
Returns:
The image in RGB format as numpy array of floats normalized to range between 0.0 - 1.0
"""
flags = cv2.IMREAD_UNCHANGED+cv2.IMREAD_ANYDEPTH+cv2.IMREAD_ANYCOLOR
if not os.path.exists(fn) and not str(fn).startswith("http"):
raise OSError('No such file or directory: {}'.format(fn))
elif os.path.isdir(fn) and not str(fn).startswith("http"):
raise OSError('Is a directory: {}'.format(fn))
else:
#res = np.array(Image.open(fn), dtype=np.float32)/255
#if len(res.shape)==2: res = np.repeat(res[...,None],3,2)
#return res
try:
if str(fn).startswith("http"):
req = urllib.urlopen(str(fn))
image = np.asarray(bytearray(req.read()), dtype="uint8")
im = cv2.imdecode(image, flags).astype(np.float32)/255
else:
im = cv2.imread(str(fn), flags).astype(np.float32)/255
if im is None: raise OSError(f'File not recognized by opencv: {fn}')
return cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
except Exception as e:
raise OSError('Error handling image at: {}'.format(fn)) from e
class FilesDataset(BaseDataset):
def __init__(self, fnames, transform, path):
self.path,self.fnames = path,fnames
super().__init__(transform)
def get_sz(self): return self.transform.sz
def get_x(self, i): return open_image(os.path.join(self.path, self.fnames[i]))
def get_n(self): return len(self.fnames)
def resize_imgs(self, targ, new_path):
dest = resize_imgs(self.fnames, targ, self.path, new_path)
return self.__class__(self.fnames, self.y, self.transform, dest)
def denorm(self,arr):
"""Reverse the normalization done to a batch of images.
Arguments:
arr: of shape/size (N,3,sz,sz)
"""
if type(arr) is not np.ndarray: arr = to_np(arr)
if len(arr.shape)==3: arr = arr[None]
return self.transform.denorm(np.rollaxis(arr,1,4))
class FilesArrayDataset(FilesDataset):
def __init__(self, fnames, y, transform, path):
self.y=y
assert(len(fnames)==len(y))
super().__init__(fnames, transform, path)
def get_y(self, i): return self.y[i]
def get_c(self):
return self.y.shape[1] if len(self.y.shape)>1 else 0
class FilesIndexArrayDataset(FilesArrayDataset):
def get_c(self): return int(self.y.max())+1
class FilesNhotArrayDataset(FilesArrayDataset):
@property
def is_multi(self): return True
class FilesIndexArrayRegressionDataset(FilesArrayDataset):
def is_reg(self): return True
class ArraysDataset(BaseDataset):
def __init__(self, x, y, transform):
self.x,self.y=x,y
assert(len(x)==len(y))
super().__init__(transform)
def get_x(self, i): return self.x[i]
def get_y(self, i): return self.y[i]
def get_n(self): return len(self.y)
def get_sz(self): return self.x.shape[1]
class ArraysIndexDataset(ArraysDataset):
def get_c(self): return int(self.y.max())+1
def get_y(self, i): return self.y[i]
class ArraysIndexRegressionDataset(ArraysIndexDataset):
def is_reg(self): return True
class ArraysNhotDataset(ArraysDataset):
def get_c(self): return self.y.shape[1]
@property
def is_multi(self): return True
class ModelData():
"""Encapsulates DataLoaders and Datasets for training, validation, test. Base class for fastai *Data classes."""
def __init__(self, path, trn_dl, val_dl, test_dl=None):
self.path,self.trn_dl,self.val_dl,self.test_dl = path,trn_dl,val_dl,test_dl
@classmethod
def from_dls(cls, path,trn_dl,val_dl,test_dl=None):
#trn_dl,val_dl = DataLoader(trn_dl),DataLoader(val_dl)
#if test_dl: test_dl = DataLoader(test_dl)
return cls(path, trn_dl, val_dl, test_dl)
@property
def is_reg(self): return self.trn_ds.is_reg
@property
def is_multi(self): return self.trn_ds.is_multi
@property
def trn_ds(self): return self.trn_dl.dataset
@property
def val_ds(self): return self.val_dl.dataset
@property
def test_ds(self): return self.test_dl.dataset
@property
def trn_y(self): return self.trn_ds.y
@property
def val_y(self): return self.val_ds.y
class ImageData(ModelData):
def __init__(self, path, datasets, bs, num_workers, classes):
trn_ds,val_ds,fix_ds,aug_ds,test_ds,test_aug_ds = datasets
self.path,self.bs,self.num_workers,self.classes = path,bs,num_workers,classes
self.trn_dl,self.val_dl,self.fix_dl,self.aug_dl,self.test_dl,self.test_aug_dl = [
self.get_dl(ds,shuf) for ds,shuf in [
(trn_ds,True),(val_ds,False),(fix_ds,False),(aug_ds,False),
(test_ds,False),(test_aug_ds,False)
]
]
def get_dl(self, ds, shuffle):
if ds is None: return None
return DataLoader(ds, batch_size=self.bs, shuffle=shuffle,
num_workers=self.num_workers, pin_memory=False)
@property
def sz(self): return self.trn_ds.sz
@property
def c(self): return self.trn_ds.c
def resized(self, dl, targ, new_path):
return dl.dataset.resize_imgs(targ,new_path) if dl else None
def resize(self, targ_sz, new_path='tmp'):
new_ds = []
dls = [self.trn_dl,self.val_dl,self.fix_dl,self.aug_dl]
if self.test_dl: dls += [self.test_dl, self.test_aug_dl]
else: dls += [None,None]
t = tqdm_notebook(dls)
for dl in t: new_ds.append(self.resized(dl, targ_sz, new_path))
t.close()
return self.__class__(new_ds[0].path, new_ds, self.bs, self.num_workers, self.classes)
@staticmethod
def get_ds(fn, trn, val, tfms, test=None, **kwargs):
res = [
fn(trn[0], trn[1], tfms[0], **kwargs), # train
fn(val[0], val[1], tfms[1], **kwargs), # val
fn(trn[0], trn[1], tfms[1], **kwargs), # fix
fn(val[0], val[1], tfms[0], **kwargs) # aug
]
if test is not None:
if isinstance(test, tuple):
test_lbls = test[1]
test = test[0]
else:
if len(trn[1].shape) == 1:
test_lbls = np.zeros((len(test),1))
else:
test_lbls = np.zeros((len(test),trn[1].shape[1]))
res += [
fn(test, test_lbls, tfms[1], **kwargs), # test
fn(test, test_lbls, tfms[0], **kwargs) # test_aug
]
else: res += [None,None]
return res
class ImageClassifierData(ImageData):
@classmethod
def from_arrays(cls, path, trn, val, bs=64, tfms=(None,None), classes=None, num_workers=4, test=None, continuous=False):
""" Read in images and their labels given as numpy arrays
Arguments:
path: a root path of the data (used for storing trained models, precomputed values, etc)
trn: a tuple of training data matrix and target label/classification array (e.g. `trn=(x,y)` where `x` has the
shape of `(5000, 784)` and `y` has the shape of `(5000,)`)
val: a tuple of validation data matrix and target label/classification array.
bs: batch size
tfms: transformations (for data augmentations). e.g. output of `tfms_from_model`
classes: a list of all labels/classifications
num_workers: a number of workers
test: a matrix of test data (the shape should match `trn[0]`)
Returns:
ImageClassifierData
"""
f = ArraysIndexRegressionDataset if continuous else ArraysIndexDataset
datasets = cls.get_ds(f, trn, val, tfms, test=test)
return cls(path, datasets, bs, num_workers, classes=classes)
@classmethod
def from_paths(cls, path, bs=64, tfms=(None,None), trn_name='train', val_name='valid', test_name=None, test_with_labels=False, num_workers=8):
""" Read in images and their labels given as sub-folder names
Arguments:
path: a root path of the data (used for storing trained models, precomputed values, etc)
bs: batch size
tfms: transformations (for data augmentations). e.g. output of `tfms_from_model`
trn_name: a name of the folder that contains training images.
val_name: a name of the folder that contains validation images.
test_name: a name of the folder that contains test images.
num_workers: number of workers
Returns:
ImageClassifierData
"""
assert not(tfms[0] is None or tfms[1] is None), "please provide transformations for your train and validation sets"
trn,val = [folder_source(path, o) for o in (trn_name, val_name)]
if test_name:
test = folder_source(path, test_name) if test_with_labels else read_dir(path, test_name)
else: test = None
datasets = cls.get_ds(FilesIndexArrayDataset, trn, val, tfms, path=path, test=test)
return cls(path, datasets, bs, num_workers, classes=trn[2])
@classmethod
def from_csv(cls, path, folder, csv_fname, bs=64, tfms=(None,None),
val_idxs=None, suffix='', test_name=None, continuous=False, skip_header=True, num_workers=8, cat_separator=' '):
""" Read in images and their labels given as a CSV file.
This method should be used when training image labels are given in an CSV file as opposed to
sub-directories with label names.
Arguments:
path: a root path of the data (used for storing trained models, precomputed values, etc)
folder: a name of the folder in which training images are contained.
csv_fname: a name of the CSV file which contains target labels.
bs: batch size
tfms: transformations (for data augmentations). e.g. output of `tfms_from_model`
val_idxs: index of images to be used for validation. e.g. output of `get_cv_idxs`.
If None, default arguments to get_cv_idxs are used.
suffix: suffix to add to image names in CSV file (sometimes CSV only contains the file name without file
extension e.g. '.jpg' - in which case, you can set suffix as '.jpg')
test_name: a name of the folder which contains test images.
continuous: TODO
skip_header: skip the first row of the CSV file.
num_workers: number of workers
cat_separator: Labels category separator
Returns:
ImageClassifierData
"""
assert not (tfms[0] is None or tfms[1] is None), "please provide transformations for your train and validation sets"
assert not (os.path.isabs(folder)), "folder needs to be a relative path"
fnames,y,classes = csv_source(folder, csv_fname, skip_header, suffix, continuous=continuous, cat_separator=cat_separator)
return cls.from_names_and_array(path, fnames, y, classes, val_idxs, test_name,
num_workers=num_workers, suffix=suffix, tfms=tfms, bs=bs, continuous=continuous)
@classmethod
def from_path_and_array(cls, path, folder, y, classes=None, val_idxs=None, test_name=None,
num_workers=8, tfms=(None,None), bs=64):
""" Read in images given a sub-folder and their labels given a numpy array
Arguments:
path: a root path of the data (used for storing trained models, precomputed values, etc)
folder: a name of the folder in which training images are contained.
y: numpy array which contains target labels ordered by filenames.
bs: batch size
tfms: transformations (for data augmentations). e.g. output of `tfms_from_model`
val_idxs: index of images to be used for validation. e.g. output of `get_cv_idxs`.
If None, default arguments to get_cv_idxs are used.
test_name: a name of the folder which contains test images.
num_workers: number of workers
Returns:
ImageClassifierData
"""
assert not (tfms[0] is None or tfms[1] is None), "please provide transformations for your train and validation sets"
assert not (os.path.isabs(folder)), "folder needs to be a relative path"
fnames = np.core.defchararray.add(f'{folder}/', sorted(os.listdir(f'{path}{folder}')))
return cls.from_names_and_array(path, fnames, y, classes, val_idxs, test_name,
num_workers=num_workers, tfms=tfms, bs=bs)
@classmethod
def from_names_and_array(cls, path, fnames, y, classes, val_idxs=None, test_name=None,
num_workers=8, suffix='', tfms=(None,None), bs=64, continuous=False):
val_idxs = get_cv_idxs(len(fnames)) if val_idxs is None else val_idxs
((val_fnames,trn_fnames),(val_y,trn_y)) = split_by_idx(val_idxs, np.array(fnames), y)
test_fnames = read_dir(path, test_name) if test_name else None
if continuous: f = FilesIndexArrayRegressionDataset
else:
f = FilesIndexArrayDataset if len(trn_y.shape)==1 else FilesNhotArrayDataset
datasets = cls.get_ds(f, (trn_fnames,trn_y), (val_fnames,val_y), tfms,
path=path, test=test_fnames)
return cls(path, datasets, bs, num_workers, classes=classes)
def split_by_idx(idxs, *a):
"""
Split each array passed as *a, to a pair of arrays like this (elements selected by idxs, the remaining elements)
This can be used to split multiple arrays containing training data to validation and training set.
:param idxs [int]: list of indexes selected
:param a list: list of np.array, each array should have same amount of elements in the first dimension
:return: list of tuples, each containing a split of corresponding array from *a.
First element of each tuple is an array composed from elements selected by idxs,
second element is an array of remaining elements.
"""
mask = np.zeros(len(a[0]),dtype=bool)
mask[np.array(idxs)] = True
return [(o[mask],o[~mask]) for o in a]
+37
View File
@@ -0,0 +1,37 @@
import collections
import itertools
from concurrent.futures import ThreadPoolExecutor
import time
class LazyThreadPoolExecutor(ThreadPoolExecutor):
def map(self, fn, *iterables, timeout=None, chunksize=1, prefetch=None):
"""
Collects iterables lazily, rather than immediately.
Docstring same as parent: https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Executor
Implmentation taken from this PR: https://github.com/python/cpython/pull/707
"""
if timeout is not None: end_time = timeout + time.time()
if prefetch is None: prefetch = self._max_workers
if prefetch < 0: raise ValueError("prefetch count may not be negative")
argsiter = zip(*iterables)
fs = collections.deque(self.submit(fn, *args) for args in itertools.islice(argsiter, self._max_workers+prefetch))
# Yield must be hidden in closure so that the futures are submitted before the first iterator value is required.
def result_iterator():
nonlocal argsiter
try:
while fs:
res = fs[0].result() if timeout is None else fs[0].result(end_time-time.time())
# Got a result, future needn't be cancelled
del fs[0]
# Dispatch next task before yielding to keep pipeline full
if argsiter:
try:
args = next(argsiter)
except StopIteration:
argsiter = None
else:
fs.append(self.submit(fn, *args))
yield res
finally:
for future in fs: future.cancel()
return result_iterator()
+73
View File
@@ -0,0 +1,73 @@
import torch
import torch.nn as nn
from .core import trainable_params_
from .torch_imports import *
IS_TORCH_04 = LooseVersion(torch.__version__) >= LooseVersion('0.4')
class FP16(nn.Module):
def __init__(self, module):
super().__init__()
self.module = batchnorm_to_fp32(module.half())
def forward(self, input):
if is_float(input): input = input.half()
return self.module(input)
def load_state_dict(self, *inputs, **kwargs):
self.module.load_state_dict(*inputs, **kwargs)
def state_dict(self, *inputs, **kwargs):
return self.module.state_dict(*inputs, **kwargs)
def __getitem__(self, idx):
return self.module[idx]
def is_float(tensor):
if IS_TORCH_04: return tensor.is_floating_point()
if isinstance(tensor, Variable): tensor = tensor.data
return isinstance(tensor, torch.cuda.FloatTensor)
def batchnorm_to_fp32(module):
'''
BatchNorm layers to have parameters in single precision.
Find all layers and convert them back to float. This can't
be done with built in .apply as that function will apply
fn to all modules, parameters, and buffers. Thus we wouldn't
be able to guard the float conversion based on the module type.
'''
if isinstance(module, nn.modules.batchnorm._BatchNorm):
module.float()
for child in module.children():
batchnorm_to_fp32(child)
return module
def copy_model_to_fp32(m, optim):
""" Creates a fp32 copy of model parameters and sets optimizer parameters
"""
fp32_params = [m_param.clone().type(torch.cuda.FloatTensor).detach() for m_param in trainable_params_(m)]
optim_groups = [group['params'] for group in optim.param_groups]
iter_fp32_params = iter(fp32_params)
for group_params in optim_groups:
for i in range(len(group_params)):
if not group_params[i].requires_grad: continue # only update trainable_params_
fp32_param = next(iter_fp32_params)
assert(fp32_param.shape == group_params[i].shape)
fp32_param.requires_grad = group_params[i].requires_grad
group_params[i] = fp32_param
return fp32_params
def copy_fp32_to_model(m, fp32_params):
m_params = trainable_params_(m)
assert(len(m_params) == len(fp32_params))
for fp32_param, m_param in zip(fp32_params, m_params):
m_param.data.copy_(fp32_param.data)
def update_fp32_grads(fp32_params, m):
m_params = trainable_params_(m)
assert(len(m_params) == len(fp32_params))
for fp32_param, m_param in zip(fp32_params, m_params):
if fp32_param.grad is None:
fp32_param.grad = nn.Parameter(fp32_param.data.new().resize_(*fp32_param.data.size()))
fp32_param.grad.data.copy_(m_param.grad.data)
+60
View File
@@ -0,0 +1,60 @@
from IPython.lib.deepreload import reload as dreload
import PIL, os, numpy as np, math, collections, threading, json, bcolz, random, scipy, cv2
import pandas as pd, pickle, sys, itertools, string, sys, re, datetime, time, shutil, copy
import seaborn as sns, matplotlib
import IPython, graphviz, sklearn_pandas, sklearn, warnings, pdb
import contextlib
from abc import abstractmethod
from glob import glob, iglob
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
from itertools import chain
from functools import partial
from collections import Iterable, Counter, OrderedDict
from isoweek import Week
from pandas_summary import DataFrameSummary
from IPython.lib.display import FileLink
from PIL import Image, ImageEnhance, ImageOps
from sklearn import metrics, ensemble, preprocessing
from operator import itemgetter, attrgetter
from pathlib import Path
from distutils.version import LooseVersion
from matplotlib import pyplot as plt, rcParams, animation
from ipywidgets import interact, interactive, fixed, widgets
matplotlib.rc('animation', html='html5')
np.set_printoptions(precision=5, linewidth=110, suppress=True)
from ipykernel.kernelapp import IPKernelApp
def in_notebook(): return IPKernelApp.initialized()
def in_ipynb():
try:
#cls = get_ipython().__class__.__name__
#return cls == 'ZMQInteractiveShell'
return False
except NameError:
return False
import tqdm as tq
from tqdm import tqdm_notebook, tnrange
def clear_tqdm():
inst = getattr(tq.tqdm, '_instances', None)
if not inst: return
try:
for i in range(len(inst)): inst.pop().close()
except Exception:
pass
if in_notebook():
def tqdm(*args, **kwargs):
clear_tqdm()
return tq.tqdm(*args, file=sys.stdout, **kwargs)
def trange(*args, **kwargs):
clear_tqdm()
return tq.trange(*args, file=sys.stdout, **kwargs)
else:
from tqdm import tqdm, trange
tnrange=trange
tqdm_notebook=tqdm
+12
View File
@@ -0,0 +1,12 @@
from .imports import *
from .torch_imports import *
def cond_init(m, init_fn):
if not isinstance(m, (nn.BatchNorm1d,nn.BatchNorm2d,nn.BatchNorm3d)):
if hasattr(m, 'weight'): init_fn(m.weight)
if hasattr(m, 'bias'): m.bias.data.fill_(0.)
def apply_init(m, init_fn):
m.apply(lambda x: cond_init(x, init_fn))
+22
View File
@@ -0,0 +1,22 @@
from .imports import *
from .torch_imports import *
import gzip
from urllib.request import urlretrieve
from tqdm import tqdm
class TqdmUpTo(tqdm):
def update_to(self, b=1, bsize=1, tsize=None):
if tsize is not None: self.total = tsize
self.update(b * bsize - self.n)
def get_data(url, filename):
if not os.path.exists(filename):
dirname = os.path.dirname(filename)
if not os.path.exists(dirname):
os.makedirs(dirname)
with TqdmUpTo(unit='B', unit_scale=True, miniters=1, desc=url.split('/')[-1]) as t:
urlretrieve(url, filename, reporthook=t.update_to)
+91
View File
@@ -0,0 +1,91 @@
from .imports import *
from .torch_imports import *
from .core import *
def opt_params(parm, lr, wd):
return {'params': chain_params(parm), 'lr':lr, 'weight_decay':wd}
class LayerOptimizer():
def __init__(self, opt_fn, layer_groups, lrs, wds=None):
if not isinstance(layer_groups, (list,tuple)): layer_groups=[layer_groups]
if not isinstance(lrs, Iterable): lrs=[lrs]
if len(lrs)==1: lrs=lrs*len(layer_groups)
if wds is None: wds=0.
if not isinstance(wds, Iterable): wds=[wds]
if len(wds)==1: wds=wds*len(layer_groups)
self.layer_groups,self.lrs,self.wds = layer_groups,lrs,wds
self.opt = opt_fn(self.opt_params())
def opt_params(self):
assert(len(self.layer_groups) == len(self.lrs))
assert(len(self.layer_groups) == len(self.wds))
params = list(zip(self.layer_groups,self.lrs,self.wds))
return [opt_params(*p) for p in params]
@property
def lr(self): return self.lrs[-1]
@property
def mom(self):
if 'betas' in self.opt.param_groups[0]:
return self.opt.param_groups[0]['betas'][0]
else:
return self.opt.param_groups[0]['momentum']
def set_lrs(self, lrs):
if not isinstance(lrs, Iterable): lrs=[lrs]
if len(lrs)==1: lrs=lrs*len(self.layer_groups)
set_lrs(self.opt, lrs)
self.lrs=lrs
def set_wds_out(self, wds):
if not isinstance(wds, Iterable): wds=[wds]
if len(wds)==1: wds=wds*len(self.layer_groups)
set_wds_out(self.opt, wds)
set_wds(self.opt, [0] * len(self.layer_groups))
self.wds=wds
def set_wds(self, wds):
if not isinstance(wds, Iterable): wds=[wds]
if len(wds)==1: wds=wds*len(self.layer_groups)
set_wds(self.opt, wds)
set_wds_out(self.opt, [0] * len(self.layer_groups))
self.wds=wds
def set_mom(self,momentum):
if 'betas' in self.opt.param_groups[0]:
for pg in self.opt.param_groups: pg['betas'] = (momentum, pg['betas'][1])
else:
for pg in self.opt.param_groups: pg['momentum'] = momentum
def set_beta(self,beta):
if 'betas' in self.opt.param_groups[0]:
for pg in self.opt.param_groups: pg['betas'] = (pg['betas'][0],beta)
elif 'alpha' in self.opt.param_groups[0]:
for pg in self.opt.param_groups: pg['alpha'] = beta
def set_opt_fn(self, opt_fn):
if type(self.opt) != type(opt_fn(self.opt_params())):
self.opt = opt_fn(self.opt_params())
def zip_strict_(l, r):
assert(len(l) == len(r))
return zip(l, r)
def set_lrs(opt, lrs):
if not isinstance(lrs, Iterable): lrs=[lrs]
if len(lrs)==1: lrs=lrs*len(opt.param_groups)
for pg,lr in zip_strict_(opt.param_groups,lrs): pg['lr'] = lr
def set_wds_out(opt, wds):
if not isinstance(wds, Iterable): wds=[wds]
if len(wds)==1: wds=wds*len(opt.param_groups)
assert(len(opt.param_groups) == len(wds))
for pg,wd in zip_strict_(opt.param_groups,wds): pg['wd'] = wd
def set_wds(opt, wds):
if not isinstance(wds, Iterable): wds=[wds]
if len(wds)==1: wds=wds*len(opt.param_groups)
assert(len(opt.param_groups) == len(wds))
for pg,wd in zip_strict_(opt.param_groups,wds): pg['weight_decay'] = wd
+19
View File
@@ -0,0 +1,19 @@
import torch
from torch import nn
class AdaptiveConcatPool2d(nn.Module):
def __init__(self, sz=None):
super().__init__()
sz = sz or (1,1)
self.ap = nn.AdaptiveAvgPool2d(sz)
self.mp = nn.AdaptiveMaxPool2d(sz)
def forward(self, x): return torch.cat([self.mp(x), self.ap(x)], 1)
class Lambda(nn.Module):
def __init__(self, f): super().__init__(); self.f=f
def forward(self, x): return self.f(x)
class Flatten(nn.Module):
def __init__(self): super().__init__()
def forward(self, x): return x.view(x.size(0), -1)
+458
View File
@@ -0,0 +1,458 @@
from .imports import *
from .torch_imports import *
from .core import *
from .transforms import *
from .model import *
from .dataset import *
from .sgdr import *
from .layer_optimizer import *
from .layers import *
from .metrics import *
from .losses import *
from .swa import *
from .fp16 import *
from .lsuv_initializer import apply_lsuv_init
import time
class Learner():
def __init__(self, data, models, opt_fn=None, tmp_name='tmp', models_name='models', metrics=None, clip=None, crit=None):
"""
Combines a ModelData object with a nn.Module object, such that you can train that
module.
data (ModelData): An instance of ModelData.
models(module): chosen neural architecture for solving a supported problem.
opt_fn(function): optimizer function, uses SGD with Momentum of .9 if none.
tmp_name(str): output name of the directory containing temporary files from training process
models_name(str): output name of the directory containing the trained model
metrics(list): array of functions for evaluating a desired metric. Eg. accuracy.
clip(float): gradient clip chosen to limit the change in the gradient to prevent exploding gradients Eg. .3
"""
self.data_,self.models,self.metrics = data,models,metrics
self.sched=None
self.wd_sched = None
self.clip = None
self.opt_fn = opt_fn or SGD_Momentum(0.9)
self.tmp_path = tmp_name if os.path.isabs(tmp_name) else os.path.join(self.data.path, tmp_name)
self.models_path = models_name if os.path.isabs(models_name) else os.path.join(self.data.path, models_name)
os.makedirs(self.tmp_path, exist_ok=True)
os.makedirs(self.models_path, exist_ok=True)
self.crit = crit if crit else self._get_crit(data)
self.reg_fn = None
self.fp16 = False
@classmethod
def from_model_data(cls, m, data, **kwargs):
self = cls(data, BasicModel(to_gpu(m)), **kwargs)
self.unfreeze()
return self
def __getitem__(self,i): return self.children[i]
@property
def children(self): return children(self.model)
@property
def model(self): return self.models.model
@property
def data(self): return self.data_
def summary(self): return model_summary(self.model, [torch.rand(3, 3, self.data.sz,self.data.sz)])
def __repr__(self): return self.model.__repr__()
def lsuv_init(self, needed_std=1.0, std_tol=0.1, max_attempts=10, do_orthonorm=False):
x = V(next(iter(self.data.trn_dl))[0])
self.models.model=apply_lsuv_init(self.model, x, needed_std=needed_std, std_tol=std_tol,
max_attempts=max_attempts, do_orthonorm=do_orthonorm,
cuda=USE_GPU and torch.cuda.is_available())
def set_bn_freeze(self, m, do_freeze):
if hasattr(m, 'running_mean'): m.bn_freeze = do_freeze
def bn_freeze(self, do_freeze):
apply_leaf(self.model, lambda m: self.set_bn_freeze(m, do_freeze))
def freeze_to(self, n):
c=self.get_layer_groups()
for l in c: set_trainable(l, False)
for l in c[n:]: set_trainable(l, True)
def freeze_all_but(self, n):
c=self.get_layer_groups()
for l in c: set_trainable(l, False)
set_trainable(c[n], True)
def freeze_groups(self, groups):
c = self.get_layer_groups()
self.unfreeze()
for g in groups:
set_trainable(c[g], False)
def unfreeze_groups(self, groups):
c = self.get_layer_groups()
for g in groups:
set_trainable(c[g], True)
def unfreeze(self): self.freeze_to(0)
def get_model_path(self, name): return os.path.join(self.models_path,name)+'.h5'
def save(self, name):
save_model(self.model, self.get_model_path(name))
if hasattr(self, 'swa_model'): save_model(self.swa_model, self.get_model_path(name)[:-3]+'-swa.h5')
def load(self, name):
load_model(self.model, self.get_model_path(name))
if hasattr(self, 'swa_model'): load_model(self.swa_model, self.get_model_path(name)[:-3]+'-swa.h5')
def set_data(self, data): self.data_ = data
def get_cycle_end(self, name):
if name is None: return None
return lambda sched, cycle: self.save_cycle(name, cycle)
def save_cycle(self, name, cycle): self.save(f'{name}_cyc_{cycle}')
def load_cycle(self, name, cycle): self.load(f'{name}_cyc_{cycle}')
def half(self):
if self.fp16: return
self.fp16 = True
if type(self.model) != FP16: self.models.model = FP16(self.model)
def float(self):
if not self.fp16: return
self.fp16 = False
if type(self.model) == FP16: self.models.model = self.model.module
self.model.float()
def fit_gen(self, model, data, layer_opt, n_cycle, cycle_len=None, cycle_mult=1, cycle_save_name=None, best_save_name=None,
use_clr=None, use_clr_beta=None, metrics=None, callbacks=None, use_wd_sched=False, norm_wds=False,
wds_sched_mult=None, use_swa=False, swa_start=1, swa_eval_freq=5, **kwargs):
"""Method does some preparation before finally delegating to the 'fit' method for
fitting the model. Namely, if cycle_len is defined, it adds a 'Cosine Annealing'
scheduler for varying the learning rate across iterations.
Method also computes the total number of epochs to fit based on provided 'cycle_len',
'cycle_mult', and 'n_cycle' parameters.
Args:
model (Learner): Any neural architecture for solving a supported problem.
Eg. ResNet-34, RNN_Learner etc.
data (ModelData): An instance of ModelData.
layer_opt (LayerOptimizer): An instance of the LayerOptimizer class
n_cycle (int): number of cycles
cycle_len (int): number of cycles before lr is reset to the initial value.
E.g if cycle_len = 3, then the lr is varied between a maximum
and minimum value over 3 epochs.
cycle_mult (int): additional parameter for influencing how the lr resets over
the cycles. For an intuitive explanation, please see
https://github.com/fastai/fastai/blob/master/courses/dl1/lesson1.ipynb
cycle_save_name (str): use to save the weights at end of each cycle
best_save_name (str): use to save weights of best model during training.
metrics (function): some function for evaluating a desired metric. Eg. accuracy.
callbacks (list(Callback)): callbacks to apply during the training.
use_wd_sched (bool, optional): set to True to enable weight regularization using
the technique mentioned in https://arxiv.org/abs/1711.05101. When this is True
alone (see below), the regularization is detached from gradient update and
applied directly to the weights.
norm_wds (bool, optional): when this is set to True along with use_wd_sched, the
regularization factor is normalized with each training cycle.
wds_sched_mult (function, optional): when this is provided along with use_wd_sched
as True, the value computed by this function is multiplied with the regularization
strength. This function is passed the WeightDecaySchedule object. And example
function that can be passed is:
f = lambda x: np.array(x.layer_opt.lrs) / x.init_lrs
use_swa (bool, optional): when this is set to True, it will enable the use of
Stochastic Weight Averaging (https://arxiv.org/abs/1803.05407). The learner will
include an additional model (in the swa_model attribute) for keeping track of the
average weights as described in the paper. All testing of this technique so far has
been in image classification, so use in other contexts is not guaranteed to work.
swa_start (int, optional): if use_swa is set to True, then this determines the epoch
to start keeping track of the average weights. It is 1-indexed per the paper's
conventions.
swa_eval_freq (int, optional): if use_swa is set to True, this determines the frequency
at which to evaluate the performance of the swa_model. This evaluation can be costly
for models using BatchNorm (requiring a full pass through the data), which is why the
default is not to evaluate after each epoch.
Returns:
None
"""
if callbacks is None: callbacks=[]
if metrics is None: metrics=self.metrics
if use_wd_sched:
# This needs to come before CosAnneal() because we need to read the initial learning rate from
# layer_opt.lrs - but CosAnneal() alters the layer_opt.lrs value initially (divides by 100)
if np.sum(layer_opt.wds) == 0:
print('fit() warning: use_wd_sched is set to True, but weight decay(s) passed are 0. Use wds to '
'pass weight decay values.')
batch_per_epoch = len(data.trn_dl)
cl = cycle_len if cycle_len else 1
self.wd_sched = WeightDecaySchedule(layer_opt, batch_per_epoch, cl, cycle_mult, n_cycle,
norm_wds, wds_sched_mult)
callbacks += [self.wd_sched]
if use_clr is not None:
clr_div,cut_div = use_clr[:2]
moms = use_clr[2:] if len(use_clr) > 2 else None
cycle_end = self.get_cycle_end(cycle_save_name)
self.sched = CircularLR(layer_opt, len(data.trn_dl)*cycle_len, on_cycle_end=cycle_end, div=clr_div, cut_div=cut_div,
momentums=moms)
elif use_clr_beta is not None:
div,pct = use_clr_beta[:2]
moms = use_clr_beta[2:] if len(use_clr_beta) > 3 else None
cycle_end = self.get_cycle_end(cycle_save_name)
self.sched = CircularLR_beta(layer_opt, len(data.trn_dl)*cycle_len, on_cycle_end=cycle_end, div=div,
pct=pct, momentums=moms)
elif cycle_len:
cycle_end = self.get_cycle_end(cycle_save_name)
cycle_batches = len(data.trn_dl)*cycle_len
self.sched = CosAnneal(layer_opt, cycle_batches, on_cycle_end=cycle_end, cycle_mult=cycle_mult)
elif not self.sched: self.sched=LossRecorder(layer_opt)
callbacks+=[self.sched]
if best_save_name is not None:
callbacks+=[SaveBestModel(self, layer_opt, metrics, best_save_name)]
if use_swa:
# make a copy of the model to track average weights
self.swa_model = copy.deepcopy(model)
callbacks+=[SWA(model, self.swa_model, swa_start)]
n_epoch = int(sum_geom(cycle_len if cycle_len else 1, cycle_mult, n_cycle))
return fit(model, data, n_epoch, layer_opt.opt, self.crit,
metrics=metrics, callbacks=callbacks, reg_fn=self.reg_fn, clip=self.clip, fp16=self.fp16,
swa_model=self.swa_model if use_swa else None, swa_start=swa_start,
swa_eval_freq=swa_eval_freq, **kwargs)
def get_layer_groups(self): return self.models.get_layer_groups()
def get_layer_opt(self, lrs, wds):
"""Method returns an instance of the LayerOptimizer class, which
allows for setting differential learning rates for different
parts of the model.
An example of how a model maybe differentiated into different parts
for application of differential learning rates and weight decays is
seen in ../.../courses/dl1/fastai/conv_learner.py, using the dict
'model_meta'. Currently, this seems supported only for convolutional
networks such as VGG-19, ResNet-XX etc.
Args:
lrs (float or list(float)): learning rate(s) for the model
wds (float or list(float)): weight decay parameter(s).
Returns:
An instance of a LayerOptimizer
"""
return LayerOptimizer(self.opt_fn, self.get_layer_groups(), lrs, wds)
def fit(self, lrs, n_cycle, wds=None, **kwargs):
"""Method gets an instance of LayerOptimizer and delegates to self.fit_gen(..)
Note that one can specify a list of learning rates which, when appropriately
defined, will be applied to different segments of an architecture. This seems
mostly relevant to ImageNet-trained models, where we want to alter the layers
closest to the images by much smaller amounts.
Likewise, a single or list of weight decay parameters can be specified, which
if appropriate for a model, will apply variable weight decay parameters to
different segments of the model.
Args:
lrs (float or list(float)): learning rate for the model
n_cycle (int): number of cycles (or iterations) to fit the model for
wds (float or list(float)): weight decay parameter(s).
kwargs: other arguments
Returns:
None
"""
self.sched = None
layer_opt = self.get_layer_opt(lrs, wds)
return self.fit_gen(self.model, self.data, layer_opt, n_cycle, **kwargs)
def warm_up(self, lr, wds=None):
layer_opt = self.get_layer_opt(lr/4, wds)
self.sched = LR_Finder(layer_opt, len(self.data.trn_dl), lr, linear=True)
return self.fit_gen(self.model, self.data, layer_opt, 1)
def lr_find(self, start_lr=1e-5, end_lr=10, wds=None, linear=False, **kwargs):
"""Helps you find an optimal learning rate for a model.
It uses the technique developed in the 2015 paper
`Cyclical Learning Rates for Training Neural Networks`, where
we simply keep increasing the learning rate from a very small value,
until the loss starts decreasing.
Args:
start_lr (float/numpy array) : Passing in a numpy array allows you
to specify learning rates for a learner's layer_groups
end_lr (float) : The maximum learning rate to try.
wds (iterable/float)
Examples:
As training moves us closer to the optimal weights for a model,
the optimal learning rate will be smaller. We can take advantage of
that knowledge and provide lr_find() with a starting learning rate
1000x smaller than the model's current learning rate as such:
>> learn.lr_find(lr/1000)
>> lrs = np.array([ 1e-4, 1e-3, 1e-2 ])
>> learn.lr_find(lrs / 1000)
Notes:
lr_find() may finish before going through each batch of examples if
the loss decreases enough.
.. _Cyclical Learning Rates for Training Neural Networks:
http://arxiv.org/abs/1506.01186
"""
self.save('tmp')
layer_opt = self.get_layer_opt(start_lr, wds)
self.sched = LR_Finder(layer_opt, len(self.data.trn_dl), end_lr, linear=linear)
self.fit_gen(self.model, self.data, layer_opt, 1, **kwargs)
self.load('tmp')
def lr_find2(self, start_lr=1e-5, end_lr=10, num_it = 100, wds=None, linear=False, stop_dv=True, **kwargs):
"""A variant of lr_find() that helps find the best learning rate. It doesn't do
an epoch but a fixed num of iterations (which may be more or less than an epoch
depending on your data).
At each step, it computes the validation loss and the metrics on the next
batch of the validation data, so it's slower than lr_find().
Args:
start_lr (float/numpy array) : Passing in a numpy array allows you
to specify learning rates for a learner's layer_groups
end_lr (float) : The maximum learning rate to try.
num_it : the number of iterations you want it to run
wds (iterable/float)
stop_dv : stops (or not) when the losses starts to explode.
"""
self.save('tmp')
layer_opt = self.get_layer_opt(start_lr, wds)
self.sched = LR_Finder2(layer_opt, num_it, end_lr, linear=linear, metrics=self.metrics, stop_dv=stop_dv)
self.fit_gen(self.model, self.data, layer_opt, num_it//len(self.data.trn_dl) + 1, all_val=True, **kwargs)
self.load('tmp')
def predict(self, is_test=False, use_swa=False):
dl = self.data.test_dl if is_test else self.data.val_dl
m = self.swa_model if use_swa else self.model
return predict(m, dl)
def predict_with_targs(self, is_test=False, use_swa=False):
dl = self.data.test_dl if is_test else self.data.val_dl
m = self.swa_model if use_swa else self.model
return predict_with_targs(m, dl)
def predict_dl(self, dl): return predict_with_targs(self.model, dl)[0]
def predict_array(self, arr):
self.model.eval()
return to_np(self.model(to_gpu(V(T(arr)))))
def TTA(self, n_aug=4, is_test=False):
""" Predict with Test Time Augmentation (TTA)
Additional to the original test/validation images, apply image augmentation to them
(just like for training images) and calculate the mean of predictions. The intent
is to increase the accuracy of predictions by examining the images using multiple
perspectives.
Args:
n_aug: a number of augmentation images to use per original image
is_test: indicate to use test images; otherwise use validation images
Returns:
(tuple): a tuple containing:
log predictions (numpy.ndarray): log predictions (i.e. `np.exp(log_preds)` will return probabilities)
targs (numpy.ndarray): target values when `is_test==False`; zeros otherwise.
"""
dl1 = self.data.test_dl if is_test else self.data.val_dl
dl2 = self.data.test_aug_dl if is_test else self.data.aug_dl
preds1,targs = predict_with_targs(self.model, dl1)
preds1 = [preds1]*math.ceil(n_aug/4)
preds2 = [predict_with_targs(self.model, dl2)[0] for i in tqdm(range(n_aug), leave=False)]
return np.stack(preds1+preds2), targs
def fit_opt_sched(self, phases, cycle_save_name=None, best_save_name=None, stop_div=False, data_list=None, callbacks=None,
cut = None, use_swa=False, swa_start=1, swa_eval_freq=5, **kwargs):
"""Wraps us the content of phases to send them to model.fit(..)
This will split the training in several parts, each with their own learning rates/
wds/momentums/optimizer detailed in phases.
Additionaly we can add a list of different data objets in data_list to train
on different datasets (to change the size for instance) for each of these groups.
Args:
phases: a list of TrainingPhase objects
stop_div: when True, stops the training if the loss goes too high
data_list: a list of different Data objects.
kwargs: other arguments
use_swa (bool, optional): when this is set to True, it will enable the use of
Stochastic Weight Averaging (https://arxiv.org/abs/1803.05407). The learner will
include an additional model (in the swa_model attribute) for keeping track of the
average weights as described in the paper. All testing of this technique so far has
been in image classification, so use in other contexts is not guaranteed to work.
swa_start (int, optional): if use_swa is set to True, then this determines the epoch
to start keeping track of the average weights. It is 1-indexed per the paper's
conventions.
swa_eval_freq (int, optional): if use_swa is set to True, this determines the frequency
at which to evaluate the performance of the swa_model. This evaluation can be costly
for models using BatchNorm (requiring a full pass through the data), which is why the
default is not to evaluate after each epoch.
Returns:
None
"""
if data_list is None: data_list=[]
if callbacks is None: callbacks=[]
layer_opt = LayerOptimizer(phases[0].opt_fn, self.get_layer_groups(), 1e-2, phases[0].wds)
if len(data_list) == 0: nb_batches = [len(self.data.trn_dl)] * len(phases)
else: nb_batches = [len(data.trn_dl) for data in data_list]
self.sched = OptimScheduler(layer_opt, phases, nb_batches, stop_div)
callbacks.append(self.sched)
metrics = self.metrics
if best_save_name is not None:
callbacks+=[SaveBestModel(self, layer_opt, metrics, best_save_name)]
if use_swa:
# make a copy of the model to track average weights
self.swa_model = copy.deepcopy(self.model)
callbacks+=[SWA(self.model, self.swa_model, swa_start)]
n_epochs = [phase.epochs for phase in phases] if cut is None else cut
if len(data_list)==0: data_list = [self.data]
return fit(self.model, data_list, n_epochs,layer_opt, self.crit,
metrics=metrics, callbacks=callbacks, reg_fn=self.reg_fn, clip=self.clip, fp16=self.fp16,
swa_model=self.swa_model if use_swa else None, swa_start=swa_start,
swa_eval_freq=swa_eval_freq, **kwargs)
def _get_crit(self, data): return F.mse_loss
+247
View File
@@ -0,0 +1,247 @@
import warnings
from .imports import *
from .torch_imports import *
from .rnn_reg import LockedDropout,WeightDrop,EmbeddingDropout
from .model import Stepper
from .core import set_grad_enabled
IS_TORCH_04 = LooseVersion(torch.__version__) >= LooseVersion('0.4')
def seq2seq_reg(output, xtra, loss, alpha=0, beta=0):
hs,dropped_hs = xtra
if alpha: # Activation Regularization
loss = loss + (alpha * dropped_hs[-1].pow(2).mean()).sum()
if beta: # Temporal Activation Regularization (slowness)
h = hs[-1]
if len(h)>1: loss = loss + (beta * (h[1:] - h[:-1]).pow(2).mean()).sum()
return loss
def repackage_var(h):
"""Wraps h in new Variables, to detach them from their history."""
if IS_TORCH_04: return h.detach() if type(h) == torch.Tensor else tuple(repackage_var(v) for v in h)
else: return Variable(h.data) if type(h) == Variable else tuple(repackage_var(v) for v in h)
class RNN_Encoder(nn.Module):
"""A custom RNN encoder network that uses
- an embedding matrix to encode input,
- a stack of LSTM or QRNN layers to drive the network, and
- variational dropouts in the embedding and LSTM/QRNN layers
The architecture for this network was inspired by the work done in
"Regularizing and Optimizing LSTM Language Models".
(https://arxiv.org/pdf/1708.02182.pdf)
"""
initrange=0.1
def __init__(self, ntoken, emb_sz, n_hid, n_layers, pad_token, bidir=False,
dropouth=0.3, dropouti=0.65, dropoute=0.1, wdrop=0.5, qrnn=False):
""" Default constructor for the RNN_Encoder class
Args:
bs (int): batch size of input data
ntoken (int): number of vocabulary (or tokens) in the source dataset
emb_sz (int): the embedding size to use to encode each token
n_hid (int): number of hidden activation per LSTM layer
n_layers (int): number of LSTM layers to use in the architecture
pad_token (int): the int value used for padding text.
dropouth (float): dropout to apply to the activations going from one LSTM layer to another
dropouti (float): dropout to apply to the input layer.
dropoute (float): dropout to apply to the embedding layer.
wdrop (float): dropout used for a LSTM's internal (or hidden) recurrent weights.
Returns:
None
"""
super().__init__()
self.ndir = 2 if bidir else 1
self.bs, self.qrnn = 1, qrnn
self.encoder = nn.Embedding(ntoken, emb_sz, padding_idx=pad_token)
self.encoder_with_dropout = EmbeddingDropout(self.encoder)
if self.qrnn:
#Using QRNN requires cupy: https://github.com/cupy/cupy
from .torchqrnn.qrnn import QRNNLayer
self.rnns = [QRNNLayer(emb_sz if l == 0 else n_hid, (n_hid if l != n_layers - 1 else emb_sz)//self.ndir,
save_prev_x=True, zoneout=0, window=2 if l == 0 else 1, output_gate=True) for l in range(n_layers)]
if wdrop:
for rnn in self.rnns:
rnn.linear = WeightDrop(rnn.linear, wdrop, weights=['weight'])
else:
self.rnns = [nn.LSTM(emb_sz if l == 0 else n_hid, (n_hid if l != n_layers - 1 else emb_sz)//self.ndir,
1, bidirectional=bidir) for l in range(n_layers)]
if wdrop: self.rnns = [WeightDrop(rnn, wdrop) for rnn in self.rnns]
self.rnns = torch.nn.ModuleList(self.rnns)
self.encoder.weight.data.uniform_(-self.initrange, self.initrange)
self.emb_sz,self.n_hid,self.n_layers,self.dropoute = emb_sz,n_hid,n_layers,dropoute
self.dropouti = LockedDropout(dropouti)
self.dropouths = nn.ModuleList([LockedDropout(dropouth) for l in range(n_layers)])
def forward(self, input):
""" Invoked during the forward propagation of the RNN_Encoder module.
Args:
input (Tensor): input of shape (sentence length x batch_size)
Returns:
raw_outputs (tuple(list (Tensor), list(Tensor)): list of tensors evaluated from each RNN layer without using
dropouth, list of tensors evaluated from each RNN layer using dropouth,
"""
sl,bs = input.size()
if bs!=self.bs:
self.bs=bs
self.reset()
with set_grad_enabled(self.training):
emb = self.encoder_with_dropout(input, dropout=self.dropoute if self.training else 0)
emb = self.dropouti(emb)
raw_output = emb
new_hidden,raw_outputs,outputs = [],[],[]
for l, (rnn,drop) in enumerate(zip(self.rnns, self.dropouths)):
current_input = raw_output
with warnings.catch_warnings():
warnings.simplefilter("ignore")
raw_output, new_h = rnn(raw_output, self.hidden[l])
new_hidden.append(new_h)
raw_outputs.append(raw_output)
if l != self.n_layers - 1: raw_output = drop(raw_output)
outputs.append(raw_output)
self.hidden = repackage_var(new_hidden)
return raw_outputs, outputs
def one_hidden(self, l):
nh = (self.n_hid if l != self.n_layers - 1 else self.emb_sz)//self.ndir
if IS_TORCH_04: return Variable(self.weights.new(self.ndir, self.bs, nh).zero_())
else: return Variable(self.weights.new(self.ndir, self.bs, nh).zero_(), volatile=not self.training)
def reset(self):
if self.qrnn: [r.reset() for r in self.rnns]
self.weights = next(self.parameters()).data
if self.qrnn: self.hidden = [self.one_hidden(l) for l in range(self.n_layers)]
else: self.hidden = [(self.one_hidden(l), self.one_hidden(l)) for l in range(self.n_layers)]
class MultiBatchRNN(RNN_Encoder):
def __init__(self, bptt, max_seq, *args, **kwargs):
self.max_seq,self.bptt = max_seq,bptt
super().__init__(*args, **kwargs)
def concat(self, arrs):
return [torch.cat([l[si] for l in arrs]) for si in range(len(arrs[0]))]
def forward(self, input):
sl,bs = input.size()
for l in self.hidden:
for h in l: h.data.zero_()
raw_outputs, outputs = [],[]
for i in range(0, sl, self.bptt):
r, o = super().forward(input[i: min(i+self.bptt, sl)])
if i>(sl-self.max_seq):
raw_outputs.append(r)
outputs.append(o)
return self.concat(raw_outputs), self.concat(outputs)
class LinearDecoder(nn.Module):
initrange=0.1
def __init__(self, n_out, n_hid, dropout, tie_encoder=None, bias=False):
super().__init__()
self.decoder = nn.Linear(n_hid, n_out, bias=bias)
self.decoder.weight.data.uniform_(-self.initrange, self.initrange)
self.dropout = LockedDropout(dropout)
if bias: self.decoder.bias.data.zero_()
if tie_encoder: self.decoder.weight = tie_encoder.weight
def forward(self, input):
raw_outputs, outputs = input
output = self.dropout(outputs[-1])
decoded = self.decoder(output.view(output.size(0)*output.size(1), output.size(2)))
result = decoded.view(-1, decoded.size(1))
return result, raw_outputs, outputs
class LinearBlock(nn.Module):
def __init__(self, ni, nf, drop):
super().__init__()
self.lin = nn.Linear(ni, nf)
self.drop = nn.Dropout(drop)
self.bn = nn.BatchNorm1d(ni)
def forward(self, x): return self.lin(self.drop(self.bn(x)))
class PoolingLinearClassifier(nn.Module):
def __init__(self, layers, drops):
super().__init__()
self.layers = nn.ModuleList([
LinearBlock(layers[i], layers[i + 1], drops[i]) for i in range(len(layers) - 1)])
def pool(self, x, bs, is_max):
f = F.adaptive_max_pool1d if is_max else F.adaptive_avg_pool1d
return f(x.permute(1,2,0), (1,)).view(bs,-1)
def forward(self, input):
raw_outputs, outputs = input
output = outputs[-1]
sl,bs,_ = output.size()
avgpool = self.pool(output, bs, False)
mxpool = self.pool(output, bs, True)
x = torch.cat([output[-1], mxpool, avgpool], 1)
for l in self.layers:
l_x = l(x)
x = F.relu(l_x)
return l_x, raw_outputs, outputs
class SequentialRNN(nn.Sequential):
def reset(self):
for c in self.children():
if hasattr(c, 'reset'): c.reset()
def get_language_model(n_tok, emb_sz, n_hid, n_layers, pad_token,
dropout=0.4, dropouth=0.3, dropouti=0.5, dropoute=0.1, wdrop=0.5, tie_weights=True, qrnn=False, bias=False):
"""Returns a SequentialRNN model.
A RNN_Encoder layer is instantiated using the parameters provided.
This is followed by the creation of a LinearDecoder layer.
Also by default (i.e. tie_weights = True), the embedding matrix used in the RNN_Encoder
is used to instantiate the weights for the LinearDecoder layer.
The SequentialRNN layer is the native torch's Sequential wrapper that puts the RNN_Encoder and
LinearDecoder layers sequentially in the model.
Args:
n_tok (int): number of unique vocabulary words (or tokens) in the source dataset
emb_sz (int): the embedding size to use to encode each token
n_hid (int): number of hidden activation per LSTM layer
n_layers (int): number of LSTM layers to use in the architecture
pad_token (int): the int value used for padding text.
dropouth (float): dropout to apply to the activations going from one LSTM layer to another
dropouti (float): dropout to apply to the input layer.
dropoute (float): dropout to apply to the embedding layer.
wdrop (float): dropout used for a LSTM's internal (or hidden) recurrent weights.
tie_weights (bool): decide if the weights of the embedding matrix in the RNN encoder should be tied to the
weights of the LinearDecoder layer.
qrnn (bool): decide if the model is composed of LSTMS (False) or QRNNs (True).
bias (bool): decide if the decoder should have a bias layer or not.
Returns:
A SequentialRNN model
"""
rnn_enc = RNN_Encoder(n_tok, emb_sz, n_hid=n_hid, n_layers=n_layers, pad_token=pad_token,
dropouth=dropouth, dropouti=dropouti, dropoute=dropoute, wdrop=wdrop, qrnn=qrnn)
enc = rnn_enc.encoder if tie_weights else None
return SequentialRNN(rnn_enc, LinearDecoder(n_tok, emb_sz, dropout, tie_encoder=enc, bias=bias))
def get_rnn_classifier(bptt, max_seq, n_class, n_tok, emb_sz, n_hid, n_layers, pad_token, layers, drops, bidir=False,
dropouth=0.3, dropouti=0.5, dropoute=0.1, wdrop=0.5, qrnn=False):
rnn_enc = MultiBatchRNN(bptt, max_seq, n_tok, emb_sz, n_hid, n_layers, pad_token=pad_token, bidir=bidir,
dropouth=dropouth, dropouti=dropouti, dropoute=dropoute, wdrop=wdrop, qrnn=qrnn)
return SequentialRNN(rnn_enc, PoolingLinearClassifier(layers, drops))
get_rnn_classifer=get_rnn_classifier
+12
View File
@@ -0,0 +1,12 @@
from .imports import *
from .torch_imports import *
def fbeta_torch(y_true, y_pred, beta, threshold, eps=1e-9):
y_pred = (y_pred.float() > threshold).float()
y_true = y_true.float()
tp = (y_pred * y_true).sum(dim=1)
precision = tp / (y_pred.sum(dim=1)+eps)
recall = tp / (y_true.sum(dim=1)+eps)
return torch.mean(
precision*recall / (precision*(beta**2)+recall+eps) * (1+beta**2))
+157
View File
@@ -0,0 +1,157 @@
"""
From https://github.com/ducha-aiki/LSUV-pytorch
Copyright (C) 2017, Dmytro Mishkin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import numpy as np
import torch
import torch.nn.init
import torch.nn as nn
gg = {}
gg['hook_position'] = 0
gg['total_fc_conv_layers'] = 0
gg['done_counter'] = -1
gg['hook'] = None
gg['act_dict'] = {}
gg['counter_to_apply_correction'] = 0
gg['correction_needed'] = False
gg['current_coef'] = 1.0
# Orthonorm init code is taked from Lasagne
# https://github.com/Lasagne/Lasagne/blob/master/lasagne/init.py
def svd_orthonormal(w):
shape = w.shape
if len(shape) < 2:
raise RuntimeError("Only shapes of length 2 or more are supported.")
flat_shape = (shape[0], np.prod(shape[1:]))
a = np.random.normal(0.0, 1.0, flat_shape)#w;
u, _, v = np.linalg.svd(a, full_matrices=False)
q = u if u.shape == flat_shape else v
q = q.reshape(shape)
return q.astype(np.float32)
def store_activations(self, input, output):
gg['act_dict'] = output.data.cpu().numpy();
return
def add_current_hook(m):
if gg['hook'] is not None:
return
if (isinstance(m, nn.Conv2d)) or (isinstance(m, nn.Linear)):
if gg['hook_position'] > gg['done_counter']:
gg['hook'] = m.register_forward_hook(store_activations)
else:
gg['hook_position'] += 1
return
def count_conv_fc_layers(m):
if (isinstance(m, nn.Conv2d)) or (isinstance(m, nn.Linear)):
gg['total_fc_conv_layers'] +=1
return
def remove_hooks(hooks):
for h in hooks:
h.remove()
return
def orthogonal_weights_init(m):
if isinstance(m, nn.Conv2d) or isinstance(m, nn.Linear):
if hasattr(m, 'weight_v'):
w_ortho = svd_orthonormal(m.weight_v.data.cpu().numpy())
m.weight_v.data = torch.from_numpy(w_ortho)
try:
nn.init.constant(m.bias, 0)
except:
pass
else:
w_ortho = svd_orthonormal(m.weight.data.cpu().numpy())
m.weight.data = torch.from_numpy(w_ortho)
try:
nn.init.constant(m.bias, 0)
except:
pass
return
def apply_weights_correction(m):
if gg['hook'] is None:
return
if not gg['correction_needed']:
return
if (isinstance(m, nn.Conv2d)) or (isinstance(m, nn.Linear)):
if gg['counter_to_apply_correction'] < gg['hook_position']:
gg['counter_to_apply_correction'] += 1
else:
if hasattr(m, 'weight_g'):
m.weight_g.data *= float(gg['current_coef'])
gg['correction_needed'] = False
else:
m.weight.data *= gg['current_coef']
gg['correction_needed'] = False
return
return
def apply_lsuv_init(model, data, needed_std=1.0, std_tol=0.1, max_attempts=10, do_orthonorm=True, cuda=True):
model.eval();
if cuda:
model=model.cuda()
data=data.cuda()
else:
model=model.cpu()
data=data.cpu()
model.apply(count_conv_fc_layers)
if do_orthonorm:
model.apply(orthogonal_weights_init)
if cuda:
model=model.cuda()
for layer_idx in range(gg['total_fc_conv_layers']):
model.apply(add_current_hook)
out = model(data)
current_std = gg['act_dict'].std()
attempts = 0
while (np.abs(current_std - needed_std) > std_tol):
gg['current_coef'] = needed_std / (current_std + 1e-8);
gg['correction_needed'] = True
model.apply(apply_weights_correction)
if cuda:
model=model.cuda()
out = model(data)
current_std = gg['act_dict'].std()
attempts+=1
if attempts > max_attempts:
print(f'Cannot converge in {max_attempts} iterations')
break
if gg['hook'] is not None:
gg['hook'].remove()
gg['done_counter']+=1
gg['counter_to_apply_correction'] = 0
gg['hook_position'] = 0
gg['hook'] = None
if not cuda:
model=model.cpu()
return model
+45
View File
@@ -0,0 +1,45 @@
from .imports import *
from .torch_imports import *
def accuracy_np(preds, targs):
preds = np.argmax(preds, 1)
return (preds==targs).mean()
def accuracy(preds, targs):
preds = torch.max(preds, dim=1)[1]
return (preds==targs).float().mean()
def accuracy_thresh(thresh):
return lambda preds,targs: accuracy_multi(preds, targs, thresh)
def accuracy_multi(preds, targs, thresh):
return ((preds>thresh).float()==targs).float().mean()
def accuracy_multi_np(preds, targs, thresh):
return ((preds>thresh)==targs).mean()
def recall(preds, targs, thresh=0.5):
pred_pos = preds > thresh
tpos = torch.mul((targs.byte() == pred_pos), targs.byte())
return tpos.sum()/targs.sum()
def precision(preds, targs, thresh=0.5):
pred_pos = preds > thresh
tpos = torch.mul((targs.byte() == pred_pos), targs.byte())
return tpos.sum()/pred_pos.sum()
def fbeta(preds, targs, beta, thresh=0.5):
"""Calculates the F-beta score (the weighted harmonic mean of precision and recall).
This is the micro averaged version where the true positives, false negatives and
false positives are calculated globally (as opposed to on a per label basis).
beta == 1 places equal weight on precision and recall, b < 1 emphasizes precision and
beta > 1 favors recall.
"""
assert beta > 0, 'beta needs to be greater than 0'
beta2 = beta ** 2
rec = recall(preds, targs, thresh)
prec = precision(preds, targs, thresh)
return (1 + beta2) * prec * rec / (beta2 * prec + rec)
def f1(preds, targs, thresh=0.5): return fbeta(preds, targs, 1, thresh)
+302
View File
@@ -0,0 +1,302 @@
from .imports import *
from .torch_imports import *
from .core import *
from .layer_optimizer import *
from .swa import *
from .fp16 import *
IS_TORCH_04 = LooseVersion(torch.__version__) >= LooseVersion('0.4')
def cut_model(m, cut):
return list(m.children())[:cut] if cut else [m]
def predict_to_bcolz(m, gen, arr, workers=4):
arr.trim(len(arr))
lock=threading.Lock()
m.eval()
for x,*_ in tqdm(gen):
y = to_np(m(VV(x)).data)
with lock:
arr.append(y)
arr.flush()
def num_features(m):
c=children(m)
if len(c)==0: return None
for l in reversed(c):
if hasattr(l, 'num_features'): return l.num_features
res = num_features(l)
if res is not None: return res
def torch_item(x): return x.item() if hasattr(x,'item') else x[0]
class Stepper():
def __init__(self, m, opt, crit, clip=0, reg_fn=None, fp16=False, loss_scale=1):
self.m,self.opt,self.crit,self.clip,self.reg_fn = m,opt,crit,clip,reg_fn
self.fp16 = fp16
self.reset(True)
if self.fp16: self.fp32_params = copy_model_to_fp32(m, opt)
self.loss_scale = loss_scale
def reset(self, train=True):
if train: apply_leaf(self.m, set_train_mode)
else: self.m.eval()
if hasattr(self.m, 'reset'):
self.m.reset()
if self.fp16: self.fp32_params = copy_model_to_fp32(self.m, self.opt)
def step(self, xs, y, epoch):
xtra = []
output = self.m(*xs)
if isinstance(output,tuple): output,*xtra = output
if self.fp16: self.m.zero_grad()
else: self.opt.zero_grad()
loss = raw_loss = self.crit(output, y)
if self.loss_scale != 1: assert(self.fp16); loss = loss*self.loss_scale
if self.reg_fn: loss = self.reg_fn(output, xtra, raw_loss)
loss.backward()
if self.fp16: update_fp32_grads(self.fp32_params, self.m)
if self.loss_scale != 1:
for param in self.fp32_params: param.grad.data.div_(self.loss_scale)
if self.clip: # Gradient clipping
if IS_TORCH_04: nn.utils.clip_grad_norm_(trainable_params_(self.m), self.clip)
else: nn.utils.clip_grad_norm(trainable_params_(self.m), self.clip)
if 'wd' in self.opt.param_groups[0] and self.opt.param_groups[0]['wd'] != 0:
#Weight decay out of the loss. After the gradient computation but before the step.
for group in self.opt.param_groups:
lr, wd = group['lr'], group['wd']
for p in group['params']:
if p.grad is not None: p.data = p.data.add(-wd * lr, p.data)
self.opt.step()
if self.fp16:
copy_fp32_to_model(self.m, self.fp32_params)
torch.cuda.synchronize()
return torch_item(raw_loss.data)
def evaluate(self, xs, y):
preds = self.m(*xs)
if isinstance(preds,tuple): preds=preds[0]
return preds, self.crit(preds, y)
def set_train_mode(m):
if (hasattr(m, 'running_mean') and (getattr(m,'bn_freeze',False)
or not getattr(m,'trainable',False))): m.eval()
elif (getattr(m,'drop_freeze',False) and hasattr(m, 'p')
and ('drop' in type(m).__name__.lower())): m.eval()
else: m.train()
def fit(model, data, n_epochs, opt, crit, metrics=None, callbacks=None, stepper=Stepper,
swa_model=None, swa_start=None, swa_eval_freq=None, visualize=False, **kwargs):
""" Fits a model
Arguments:
model (model): any pytorch module
net = to_gpu(net)
data (ModelData): see ModelData class and subclasses (can be a list)
opts: an optimizer. Example: optim.Adam.
If n_epochs is a list, it needs to be the layer_optimizer to get the optimizer as it changes.
n_epochs(int or list): number of epochs (or list of number of epochs)
crit: loss function to optimize. Example: F.cross_entropy
"""
seq_first = kwargs.pop('seq_first', False)
all_val = kwargs.pop('all_val', False)
get_ep_vals = kwargs.pop('get_ep_vals', False)
metrics = metrics or []
callbacks = callbacks or []
avg_mom=0.98
batch_num,avg_loss=0,0.
for cb in callbacks: cb.on_train_begin()
names = ["epoch", "trn_loss", "val_loss"] + [f.__name__ for f in metrics]
if swa_model is not None:
swa_names = ['swa_loss'] + [f'swa_{f.__name__}' for f in metrics]
names += swa_names
# will use this to call evaluate later
swa_stepper = stepper(swa_model, None, crit, **kwargs)
layout = "{!s:10} " * len(names)
if not isinstance(n_epochs, Iterable): n_epochs=[n_epochs]
if not isinstance(data, Iterable): data = [data]
if len(data) == 1: data = data * len(n_epochs)
for cb in callbacks: cb.on_phase_begin()
model_stepper = stepper(model, opt.opt if hasattr(opt,'opt') else opt, crit, **kwargs)
ep_vals = collections.OrderedDict()
tot_epochs = int(np.ceil(np.array(n_epochs).sum()))
cnt_phases = np.array([ep * len(dat.trn_dl) for (ep,dat) in zip(n_epochs,data)]).cumsum()
phase = 0
for epoch in tnrange(tot_epochs, desc='Epoch'):
if phase >= len(n_epochs): break #Sometimes cumulated errors make this append.
model_stepper.reset(True)
cur_data = data[phase]
if hasattr(cur_data, 'trn_sampler'): cur_data.trn_sampler.set_epoch(epoch)
if hasattr(cur_data, 'val_sampler'): cur_data.val_sampler.set_epoch(epoch)
num_batch = len(cur_data.trn_dl)
t = tqdm(iter(cur_data.trn_dl), leave=False, total=num_batch, miniters=0)
if all_val: val_iter = IterBatch(cur_data.val_dl)
for (*x,y) in t:
batch_num += 1
for cb in callbacks: cb.on_batch_begin()
loss = model_stepper.step(V(x),V(y), epoch)
avg_loss = avg_loss * avg_mom + loss * (1-avg_mom)
debias_loss = avg_loss / (1 - avg_mom**batch_num)
t.set_postfix(loss=debias_loss, refresh=False)
stop=False
los = debias_loss if not all_val else [debias_loss] + validate_next(model_stepper,metrics, val_iter)
for cb in callbacks: stop = stop or cb.on_batch_end(los)
if stop: return
if batch_num >= cnt_phases[phase]:
for cb in callbacks: cb.on_phase_end()
phase += 1
if phase >= len(n_epochs):
t.close()
break
for cb in callbacks: cb.on_phase_begin()
if isinstance(opt, LayerOptimizer): model_stepper.opt = opt.opt
if cur_data != data[phase]:
t.close()
break
if not all_val:
vals = validate(model_stepper, cur_data.val_dl, metrics, seq_first=seq_first)
stop=False
for cb in callbacks: stop = stop or cb.on_epoch_end(vals)
if swa_model is not None:
if (epoch + 1) >= swa_start and ((epoch + 1 - swa_start) % swa_eval_freq == 0 or epoch == tot_epochs - 1):
fix_batchnorm(swa_model, cur_data.trn_dl)
swa_vals = validate(swa_stepper, cur_data.val_dl, metrics)
vals += swa_vals
if epoch > 0:
print_stats(epoch, [debias_loss] + vals, visualize, prev_val)
else:
print(layout.format(*names))
print_stats(epoch, [debias_loss] + vals, visualize)
prev_val = [debias_loss] + vals
ep_vals = append_stats(ep_vals, epoch, [debias_loss] + vals)
if stop: break
for cb in callbacks: cb.on_train_end()
if get_ep_vals: return vals, ep_vals
else: return vals
def append_stats(ep_vals, epoch, values, decimals=6):
ep_vals[epoch]=list(np.round(values, decimals))
return ep_vals
def print_stats(epoch, values, visualize, prev_val=[], decimals=6):
layout = "{!s:^10}" + " {!s:10}" * len(values)
values = [epoch] + list(np.round(values, decimals))
sym = ""
if visualize:
if epoch == 0: pass
elif values[1] > prev_val[0] and values[2] > prev_val[1]: sym = " △ △"
elif values[1] > prev_val[0] and values[2] < prev_val[1]: sym = " △ ▼"
elif values[1] < prev_val[0] and values[2] > prev_val[1]: sym = " ▼ △"
elif values[1] < prev_val[0] and values[2] < prev_val[1]: sym = " ▼ ▼"
print(layout.format(*values) + sym)
class IterBatch():
def __init__(self, dl):
self.idx = 0
self.dl = dl
self.iter = iter(dl)
def __iter__(self): return self
def next(self):
res = next(self.iter)
self.idx += 1
if self.idx == len(self.dl):
self.iter = iter(self.dl)
self.idx=0
return res
def validate_next(stepper, metrics, val_iter):
"""Computes the loss on the next minibatch of the validation set."""
stepper.reset(False)
with no_grad_context():
(*x,y) = val_iter.next()
preds,l = stepper.evaluate(VV(x), VV(y))
res = [delistify(to_np(l))]
res += [f(preds.data,y) for f in metrics]
stepper.reset(True)
return res
def batch_sz(x, seq_first=False):
if is_listy(x): x = x[0]
return x.shape[1 if seq_first else 0]
def validate(stepper, dl, metrics, seq_first=False):
batch_cnts,loss,res = [],[],[]
stepper.reset(False)
with no_grad_context():
for (*x,y) in iter(dl):
preds, l = stepper.evaluate(VV(x), VV(y))
batch_cnts.append(batch_sz(x, seq_first=seq_first))
loss.append(to_np(l))
res.append([f(preds.data, y) for f in metrics])
return [np.average(loss, 0, weights=batch_cnts)] + list(np.average(np.stack(res), 0, weights=batch_cnts))
def get_prediction(x):
if is_listy(x): x=x[0]
return x.data
def predict(m, dl):
preda,_ = predict_with_targs_(m, dl)
return np.concatenate(preda)
def predict_batch(m, x):
m.eval()
if hasattr(m, 'reset'): m.reset()
return m(VV(x))
def predict_with_targs_(m, dl):
m.eval()
if hasattr(m, 'reset'): m.reset()
res = []
for *x,y in iter(dl): res.append([get_prediction(to_np(m(*VV(x)))),to_np(y)])
return zip(*res)
def predict_with_targs(m, dl):
preda,targa = predict_with_targs_(m, dl)
return np.concatenate(preda), np.concatenate(targa)
# From https://github.com/ncullen93/torchsample
def model_summary(m, inputs):
def register_hook(module):
def hook(module, input, output):
class_name = str(module.__class__).split('.')[-1].split("'")[0]
module_idx = len(summary)
m_key = '%s-%i' % (class_name, module_idx+1)
summary[m_key] = OrderedDict()
summary[m_key]['input_shape'] = list(input[0].size())
summary[m_key]['input_shape'][0] = -1
if is_listy(output):
summary[m_key]['output_shape'] = [[-1] + list(o.size())[1:] for o in output]
else:
summary[m_key]['output_shape'] = list(output.size())
summary[m_key]['output_shape'][0] = -1
params = 0
if hasattr(module, 'weight'):
params += torch.prod(torch.LongTensor(list(module.weight.size())))
summary[m_key]['trainable'] = module.weight.requires_grad
if hasattr(module, 'bias') and module.bias is not None:
params += torch.prod(torch.LongTensor(list(module.bias.size())))
summary[m_key]['nb_params'] = params
if (not isinstance(module, nn.Sequential) and
not isinstance(module, nn.ModuleList) and
not (module == m)):
hooks.append(module.register_forward_hook(hook))
summary = OrderedDict()
hooks = []
m.apply(register_hook)
xs = [to_gpu(Variable(x)) for x in inputs]
m(*xs)
for h in hooks: h.remove()
return summary
+5
View File
@@ -0,0 +1,5 @@
*.png
*.tar
checkpoint*
log*
wgts/
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
python main.py --lr=0.1
python main.py --resume --lr=0.01
python main.py --resume --lr=0.001
+283
View File
@@ -0,0 +1,283 @@
from __future__ import division
from senet import *
import os, sys, shutil, time, random
import argparse
import torch
import torch.backends.cudnn as cudnn
import torchvision.datasets as dset
import torchvision.transforms as transforms
from utils import AverageMeter, RecorderMeter, time_string, convert_secs2time
parser = argparse.ArgumentParser(description='Trains ResNeXt on CIFAR or ImageNet', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--data_path', default='./data', type=str, help='Path to dataset')
parser.add_argument('--dataset', default='cifar10', type=str, choices=['cifar10', 'cifar100', 'imagenet', 'svhn', 'stl10'], help='Choose between Cifar10/100 and ImageNet.')
# Optimization options
parser.add_argument('--epochs', type=int, default=300, help='Number of epochs to train.')
parser.add_argument('--batch_size', type=int, default=64, help='Batch size.')
parser.add_argument('--learning_rate', type=float, default=0.05, help='The Learning Rate.')
parser.add_argument('--momentum', type=float, default=0.9, help='Momentum.')
parser.add_argument('--decay', type=float, default=0.0005, help='Weight decay (L2 penalty).')
parser.add_argument('--schedule', type=int, nargs='+', default=[150, 225], help='Decrease learning rate at these epochs.')
parser.add_argument('--gammas', type=float, nargs='+', default=[0.1, 0.1], help='LR is multiplied by gamma on schedule, number of gammas should be equal to schedule')
# Checkpoints
parser.add_argument('--print_freq', default=200, type=int, metavar='N', help='print frequency (default: 200)')
parser.add_argument('--save_path', type=str, default='./', help='Folder to save checkpoints and log.')
parser.add_argument('--resume', default='', type=str, metavar='PATH', help='path to latest checkpoint (default: none)')
parser.add_argument('--start_epoch', default=0, type=int, metavar='N', help='manual epoch number (useful on restarts)')
parser.add_argument('--evaluate', dest='evaluate', action='store_true', help='evaluate model on validation set')
# Acceleration
parser.add_argument('--workers', type=int, default=2, help='number of data loading workers (default: 2)')
# random seed
parser.add_argument('--manualSeed', type=int, help='manual seed')
args = parser.parse_args()
args.use_cuda = torch.cuda.is_available()
torch.cuda.set_device(0)
if args.manualSeed is None: args.manualSeed = random.randint(1, 10000)
random.seed(args.manualSeed)
torch.manual_seed(args.manualSeed)
if args.use_cuda: torch.cuda.manual_seed_all(args.manualSeed)
cudnn.benchmark = True
def main():
if not os.path.isdir(args.save_path): os.makedirs(args.save_path)
log = open(os.path.join(args.save_path, 'log_seed_{}.txt'.format(args.manualSeed)), 'w')
print_log('save path : {}'.format(args.save_path), log)
state = {k: v for k, v in args._get_kwargs()}
print_log(state, log)
print_log("Random Seed: {}".format(args.manualSeed), log)
print_log("python version : {}".format(sys.version.replace('\n', ' ')), log)
print_log("torch version : {}".format(torch.__version__), log)
print_log("cudnn version : {}".format(torch.backends.cudnn.version()), log)
# Init dataset
if not os.path.isdir(args.data_path):
os.makedirs(args.data_path)
if args.dataset == 'cifar10':
mean = [x / 255 for x in [125.3, 123.0, 113.9]]
std = [x / 255 for x in [63.0, 62.1, 66.7]]
elif args.dataset == 'cifar100':
mean = [x / 255 for x in [129.3, 124.1, 112.4]]
std = [x / 255 for x in [68.2, 65.4, 70.4]]
else:
assert False, "Unknow dataset : {}".format(args.dataset)
train_transform = transforms.Compose(
[transforms.RandomHorizontalFlip(), transforms.RandomCrop(32, padding=4), transforms.ToTensor(),
transforms.Normalize(mean, std)])
test_transform = transforms.Compose(
[transforms.ToTensor(), transforms.Normalize(mean, std)])
if args.dataset == 'cifar10':
train_data = dset.CIFAR10(args.data_path, train=True, transform=train_transform, download=True)
test_data = dset.CIFAR10(args.data_path, train=False, transform=test_transform, download=True)
num_classes = 10
elif args.dataset == 'cifar100':
train_data = dset.CIFAR100(args.data_path, train=True, transform=train_transform, download=True)
test_data = dset.CIFAR100(args.data_path, train=False, transform=test_transform, download=True)
num_classes = 100
elif args.dataset == 'svhn':
train_data = dset.SVHN(args.data_path, split='train', transform=train_transform, download=True)
test_data = dset.SVHN(args.data_path, split='test', transform=test_transform, download=True)
num_classes = 10
elif args.dataset == 'stl10':
train_data = dset.STL10(args.data_path, split='train', transform=train_transform, download=True)
test_data = dset.STL10(args.data_path, split='test', transform=test_transform, download=True)
num_classes = 10
elif args.dataset == 'imagenet':
assert False, 'Do not finish imagenet code'
else:
assert False, 'Do not support dataset : {}'.format(args.dataset)
train_loader = torch.utils.data.DataLoader(train_data, batch_size=args.batch_size, shuffle=True,
num_workers=args.workers, pin_memory=True)
test_loader = torch.utils.data.DataLoader(test_data, batch_size=args.batch_size, shuffle=False,
num_workers=args.workers, pin_memory=True)
# Init model, criterion, and optimizer
#net = models.__dict__[args.arch](num_classes).cuda()
net = SENet34()
# define loss function (criterion) and optimizer
criterion = F.nll_loss
optimizer = torch.optim.SGD(net.parameters(), state['learning_rate'], momentum=state['momentum'],
weight_decay=state['decay'], nesterov=True)
if args.use_cuda: net.cuda()
recorder = RecorderMeter(args.epochs)
# optionally resume from a checkpoint
if args.resume:
if os.path.isfile(args.resume):
print_log("=> loading checkpoint '{}'".format(args.resume), log)
checkpoint = torch.load(args.resume)
recorder = checkpoint['recorder']
args.start_epoch = checkpoint['epoch']
net.load_state_dict(checkpoint['state_dict'])
optimizer.load_state_dict(checkpoint['optimizer'])
print_log("=> loaded checkpoint '{}' (epoch {})" .format(args.resume, checkpoint['epoch']), log)
else:
print_log("=> no checkpoint found at '{}'".format(args.resume), log)
else:
print_log("=> do not use any checkpoint for model", log)
if args.evaluate:
validate(test_loader, net, criterion, log)
return
# Main loop
start_time = time.time()
epoch_time = AverageMeter()
for epoch in range(args.start_epoch, args.epochs):
current_learning_rate = adjust_learning_rate(optimizer, epoch, args.gammas, args.schedule)
need_hour, need_mins, need_secs = convert_secs2time(epoch_time.avg * (args.epochs-epoch))
need_time = '[Need: {:02d}:{:02d}:{:02d}]'.format(need_hour, need_mins, need_secs)
print_log('\n==>>{:s} [Epoch={:03d}/{:03d}] {:s} [learning_rate={:6.4f}]'.format(time_string(), epoch, args.epochs, need_time, current_learning_rate) \
+ ' [Best : Accuracy={:.2f}, Error={:.2f}]'.format(recorder.max_accuracy(False), 100-recorder.max_accuracy(False)), log)
# train for one epoch
train_acc, train_los = train(train_loader, net, criterion, optimizer, epoch, log)
# evaluate on validation set
val_acc, val_los = validate(test_loader, net, criterion, log)
is_best = recorder.update(epoch, train_los, train_acc, val_los, val_acc)
save_checkpoint({
'epoch': epoch + 1,
'state_dict': net.state_dict(),
'recorder': recorder,
'optimizer' : optimizer.state_dict(),
}, is_best, args.save_path, 'checkpoint.pth.tar')
# measure elapsed time
epoch_time.update(time.time() - start_time)
start_time = time.time()
recorder.plot_curve( os.path.join(args.save_path, 'curve.png') )
log.close()
# train function (forward, backward, update)
def train(train_loader, model, criterion, optimizer, epoch, log):
batch_time = AverageMeter()
data_time = AverageMeter()
losses = AverageMeter()
top1 = AverageMeter()
top5 = AverageMeter()
# switch to train mode
model.train()
end = time.time()
for i, (input, target) in enumerate(train_loader):
# measure data loading time
data_time.update(time.time() - end)
if args.use_cuda:
target = target.cuda(async=True)
input = input.cuda()
input_var = torch.autograd.Variable(input)
target_var = torch.autograd.Variable(target)
# compute output
output = model(input_var)
loss = criterion(output, target_var)
# measure accuracy and record loss
prec1, prec5 = accuracy(output.data, target, topk=(1, 5))
losses.update(loss.data[0], input.size(0))
top1.update(prec1[0], input.size(0))
top5.update(prec5[0], input.size(0))
# compute gradient and do SGD step
optimizer.zero_grad()
loss.backward()
optimizer.step()
# measure elapsed time
batch_time.update(time.time() - end)
end = time.time()
print_log(' Epoch: [{:03d}][{:03d}/{:03d}] '
'Time {batch_time.val:.3f} ({batch_time.avg:.3f}) '
'Data {data_time.val:.3f} ({data_time.avg:.3f}) '
'Loss {loss.val:.4f} ({loss.avg:.4f}) '
'Prec@1 {top1.val:.3f} ({top1.avg:.3f}) '
'Prec@5 {top5.val:.3f} ({top5.avg:.3f}) '.format(
epoch, i, len(train_loader), batch_time=batch_time,
data_time=data_time, loss=losses, top1=top1, top5=top5) + time_string(), log)
return top1.avg, losses.avg
def validate(val_loader, model, criterion, log):
losses = AverageMeter()
top1 = AverageMeter()
top5 = AverageMeter()
# switch to evaluate mode
model.eval()
for i, (input, target) in enumerate(val_loader):
if args.use_cuda:
target = target.cuda(async=True)
input = input.cuda()
input_var = torch.autograd.Variable(input, volatile=True)
target_var = torch.autograd.Variable(target, volatile=True)
# compute output
output = model(input_var)
loss = criterion(output, target_var)
# measure accuracy and record loss
prec1, prec5 = accuracy(output.data, target, topk=(1, 5))
losses.update(loss.data[0], input.size(0))
top1.update(prec1[0], input.size(0))
top5.update(prec5[0], input.size(0))
print_log(' **Test** Prec@1 {top1.avg:.3f} Prec@5 {top5.avg:.3f} Error@1 {error1:.3f}'.format(top1=top1, top5=top5, error1=100-top1.avg), log)
return top1.avg, losses.avg
def print_log(print_string, log):
print("{}".format(print_string))
log.write('{}\n'.format(print_string))
log.flush()
def save_checkpoint(state, is_best, save_path, filename):
filename = os.path.join(save_path, filename)
torch.save(state, filename)
if is_best:
bestname = os.path.join(save_path, 'model_best.pth.tar')
shutil.copyfile(filename, bestname)
def adjust_learning_rate(optimizer, epoch, gammas, schedule):
"""Sets the learning rate to the initial LR decayed by 10 every 30 epochs"""
lr = args.learning_rate
assert len(gammas) == len(schedule), "length of gammas and schedule should be equal"
for (gamma, step) in zip(gammas, schedule):
if (epoch >= step):
lr = lr * gamma
else:
break
for param_group in optimizer.param_groups:
param_group['lr'] = lr
return lr
def accuracy(output, target, topk=(1,)):
"""Computes the precision@k for the specified values of k"""
maxk = max(topk)
batch_size = target.size(0)
_, pred = output.topk(maxk, 1, True, True)
pred = pred.t()
correct = pred.eq(target.view(1, -1).expand_as(pred))
res = []
for k in topk:
correct_k = correct[:k].view(-1).float().sum(0)
res.append(correct_k.mul_(100.0 / batch_size))
return res
if __name__ == '__main__':
main()
+145
View File
@@ -0,0 +1,145 @@
'''Train CIFAR10 with PyTorch.'''
from __future__ import print_function
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import torch.backends.cudnn as cudnn
import torchvision
import torchvision.transforms as transforms
import os
import argparse
from senet import *
from utils import progress_bar
from torch.autograd import Variable
parser = argparse.ArgumentParser(description='PyTorch CIFAR10 Training')
parser.add_argument('--lr', default=0.1, type=float, help='learning rate')
parser.add_argument('--resume', '-r', action='store_true', help='resume from checkpoint')
args = parser.parse_args()
use_cuda = torch.cuda.is_available()
torch.cuda.set_device(3)
best_acc = 0 # best test accuracy
start_epoch = 0 # start from epoch 0 or last checkpoint epoch
# Data
print('==> Preparing data..')
transform_train = transforms.Compose([
transforms.RandomCrop(32, padding=4),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
])
transform_test = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=4)
testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test)
testloader = torch.utils.data.DataLoader(testset, batch_size=100, shuffle=False, num_workers=4)
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# Model
if args.resume:
# Load checkpoint.
print('==> Resuming from checkpoint..')
assert os.path.isdir('checkpoint'), 'Error: no checkpoint directory found!'
checkpoint = torch.load('./checkpoint/ckpt.t7')
net = checkpoint['net']
best_acc = checkpoint['acc']
start_epoch = checkpoint['epoch']
else:
print('==> Building model..')
# net = VGG('VGG19')
# net = ResNet18()
# net = PreActResNet18()
# net = GoogLeNet()
# net = DenseNet121()
# net = ResNeXt29_2x64d()
# net = MobileNet()
# net = DPN92()
# net = ShuffleNetG2()
net = SENet18()
if use_cuda:
net.cuda()
#net = torch.nn.DataParallel(net, device_ids=(0,3))
#net = torch.nn.DataParallel(net, device_ids=range(torch.cuda.device_count()))
cudnn.benchmark = True
criterion = F.nll_loss
optimizer = optim.SGD(net.parameters(), lr=args.lr, momentum=0.9, weight_decay=5e-4)
# Training
def train(epoch):
print('\nEpoch: %d' % epoch)
net.train()
train_loss = 0
correct = 0
total = 0
for batch_idx, (inputs, targets) in enumerate(trainloader):
if use_cuda: inputs, targets = inputs.cuda(), targets.cuda()
optimizer.zero_grad()
inputs, targets = Variable(inputs), Variable(targets)
outputs = net(inputs)
loss = criterion(outputs, targets)
loss.backward()
optimizer.step()
train_loss += loss.data[0]
_, predicted = torch.max(outputs.data, 1)
total += targets.size(0)
correct += predicted.eq(targets.data).cpu().sum()
progress_bar(batch_idx, len(trainloader), 'Loss: %.3f | Acc: %.3f%% (%d/%d)'
% (train_loss/(batch_idx+1), 100.*correct/total, correct, total))
def test(epoch):
global best_acc
net.eval()
test_loss = 0
correct = 0
total = 0
for batch_idx, (inputs, targets) in enumerate(testloader):
if use_cuda: inputs, targets = inputs.cuda(), targets.cuda()
inputs, targets = Variable(inputs, volatile=True), Variable(targets)
outputs = net(inputs)
loss = criterion(outputs, targets)
test_loss += loss.data[0]
_, predicted = torch.max(outputs.data, 1)
total += targets.size(0)
correct += predicted.eq(targets.data).cpu().sum()
progress_bar(batch_idx, len(testloader), 'Loss: %.3f | Acc: %.3f%% (%d/%d)'
% (test_loss/(batch_idx+1), 100.*correct/total, correct, total))
# Save checkpoint.
acc = 100.*correct/total
if acc > best_acc:
print('Saving..')
state = {
'net': net,
'acc': acc,
'epoch': epoch,
}
if not os.path.isdir('checkpoint'):
os.mkdir('checkpoint')
torch.save(state, './checkpoint/ckpt.t7')
best_acc = acc
for epoch in range(start_epoch, start_epoch+100):
train(epoch)
test(epoch)
+102
View File
@@ -0,0 +1,102 @@
'''Pre-activation ResNet in PyTorch.
Reference:
[1] Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun
Identity Mappings in Deep Residual Networks. arXiv:1603.05027
'''
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
class PreActBlock(nn.Module):
'''Pre-activation version of the BasicBlock.'''
expansion = 1
def __init__(self, in_planes, planes, stride=1):
super(PreActBlock, self).__init__()
self.bn1 = nn.BatchNorm2d(in_planes)
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
if stride != 1 or in_planes != self.expansion*planes:
self.shortcut = nn.Sequential(
nn.Conv2d(in_planes, self.expansion*planes, kernel_size=1, stride=stride, bias=False)
)
def forward(self, x):
out = F.relu(self.bn1(x))
shortcut = self.shortcut(out) if hasattr(self, 'shortcut') else x
out = self.conv1(out)
out = self.conv2(F.relu(self.bn2(out)))
out += shortcut
return out
class PreActBottleneck(nn.Module):
'''Pre-activation version of the original Bottleneck module.'''
expansion = 4
def __init__(self, in_planes, planes, stride=1):
super(PreActBottleneck, self).__init__()
self.bn1 = nn.BatchNorm2d(in_planes)
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=1, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn3 = nn.BatchNorm2d(planes)
self.conv3 = nn.Conv2d(planes, self.expansion*planes, kernel_size=1, bias=False)
if stride != 1 or in_planes != self.expansion*planes:
self.shortcut = nn.Sequential(
nn.Conv2d(in_planes, self.expansion*planes, kernel_size=1, stride=stride, bias=False)
)
def forward(self, x):
out = F.relu(self.bn1(x))
shortcut = self.shortcut(out) if hasattr(self, 'shortcut') else x
out = self.conv1(out)
out = self.conv2(F.relu(self.bn2(out)))
out = self.conv3(F.relu(self.bn3(out)))
out += shortcut
return out
class PreActResNet(nn.Module):
def __init__(self, block, num_blocks, num_classes=10):
super(PreActResNet, self).__init__()
self.in_planes = 64
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1)
self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2)
self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)
self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)
self.linear = nn.Linear(512*block.expansion, num_classes)
def _make_layer(self, block, planes, num_blocks, stride):
strides = [stride] + [1]*(num_blocks-1)
layers = []
for stride in strides:
layers.append(block(self.in_planes, planes, stride))
self.in_planes = planes * block.expansion
return nn.Sequential(*layers)
def forward(self, x):
out = self.conv1(x)
out = self.layer1(out)
out = self.layer2(out)
out = self.layer3(out)
out = self.layer4(out)
out = F.adaptive_max_pool2d(out, 1)
out = out.view(out.size(0), -1)
return F.log_softmax(self.linear(out))
def PreActResNet18(): return PreActResNet(PreActBlock, [2,2,2,2])
def PreActResNet34(): return PreActResNet(PreActBlock, [3,4,6,3])
def PreActResNet50(): return PreActResNet(PreActBottleneck, [3,4,6,3])
def PreActResNet101(): return PreActResNet(PreActBottleneck, [3,4,23,3])
def PreActResNet152(): return PreActResNet(PreActBottleneck, [3,8,36,3])
+119
View File
@@ -0,0 +1,119 @@
import torch.nn as nn
import torch.nn.functional as F
from torch.nn import init
import math
class ResNeXtBottleneck(nn.Module):
expansion = 4
"""
RexNeXt bottleneck type C (https://github.com/facebookresearch/ResNeXt/blob/master/models/resnext.lua)
"""
def __init__(self, inplanes, planes, cardinality, base_width, stride=1, downsample=None):
super(ResNeXtBottleneck, self).__init__()
self.downsample = downsample
D = int(math.floor(planes * (base_width/64.0)))
C = cardinality
self.conv_reduce = nn.Conv2d(inplanes, D*C, kernel_size=1, stride=1, padding=0, bias=False)
self.bn_reduce = nn.BatchNorm2d(D*C)
self.conv_conv = nn.Conv2d(D*C, D*C, kernel_size=3, stride=stride, padding=1, groups=cardinality, bias=False)
self.bn = nn.BatchNorm2d(D*C)
self.conv_expand = nn.Conv2d(D*C, planes*4, kernel_size=1, stride=1, padding=0, bias=False)
self.bn_expand = nn.BatchNorm2d(planes*4)
def forward(self, x):
residual = x
bottleneck = self.conv_reduce(x)
bottleneck = F.relu(self.bn_reduce(bottleneck), inplace=True)
bottleneck = self.conv_conv(bottleneck)
bottleneck = F.relu(self.bn(bottleneck), inplace=True)
bottleneck = self.conv_expand(bottleneck)
bottleneck = self.bn_expand(bottleneck)
if self.downsample is not None: residual = self.downsample(x)
return F.relu(residual + bottleneck, inplace=True)
class CifarResNeXt(nn.Module):
"""
ResNext optimized for the Cifar dataset, as specified in
https://arxiv.org/pdf/1611.05431.pdf
"""
def __init__(self, block, depth, cardinality, base_width, num_classes):
super(CifarResNeXt, self).__init__()
# Model type specifies number of layers for CIFAR-10 and CIFAR-100 model
assert (depth - 2) % 9 == 0, 'depth should be one of 29, 38, 47, 56, 101'
self.layer_blocks = (depth - 2) // 9
self.cardinality,self.base_width,self.num_classes,self.block = cardinality,base_width,num_classes,block
self.conv_1_3x3 = nn.Conv2d(3, 64, 3, 1, 1, bias=False)
self.bn_1 = nn.BatchNorm2d(64)
self.inplanes = 64
self.stage_1 = self._make_layer(64 , 1)
self.stage_2 = self._make_layer(128, 2)
self.stage_3 = self._make_layer(256, 2)
self.avgpool = nn.AdaptiveAvgPool2d((1,1))
self.classifier = nn.Linear(256*block.expansion, num_classes)
for m in self.modules():
if isinstance(m, nn.Conv2d):
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
m.weight.data.normal_(0, math.sqrt(2. / n))
elif isinstance(m, nn.BatchNorm2d):
m.weight.data.fill_(1)
m.bias.data.zero_()
elif isinstance(m, nn.Linear):
init.kaiming_normal(m.weight)
m.bias.data.zero_()
def _make_layer(self, planes, stride=1):
downsample = None
exp_planes = planes * self.block.expansion
if stride != 1 or self.inplanes != exp_planes:
downsample = nn.Sequential(
nn.Conv2d(self.inplanes, exp_planes, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(exp_planes),
)
layers = []
layers.append(self.block(self.inplanes, planes, self.cardinality, self.base_width, stride, downsample))
self.inplanes = exp_planes
for i in range(1, self.layer_blocks):
layers.append(self.block(self.inplanes, planes, self.cardinality, self.base_width))
return nn.Sequential(*layers)
def forward(self, x):
x = self.conv_1_3x3(x)
x = F.relu(self.bn_1(x), inplace=True)
x = self.stage_1(x)
x = self.stage_2(x)
x = self.stage_3(x)
x = self.avgpool(x)
x = x.view(x.size(0), -1)
return F.log_softmax(self.classifier(x))
def resnext29_16_64(num_classes=10):
"""Constructs a ResNeXt-29, 16*64d model for CIFAR-10 (by default)
Args:
num_classes (uint): number of classes
"""
model = CifarResNeXt(ResNeXtBottleneck, 29, 16, 64, num_classes)
return model
def resnext29_8_64(num_classes=10):
"""Constructs a ResNeXt-29, 8*64d model for CIFAR-10 (by default)
Args:
num_classes (uint): number of classes
"""
model = CifarResNeXt(ResNeXtBottleneck, 29, 8, 64, num_classes)
return model
+116
View File
@@ -0,0 +1,116 @@
'''SENet in PyTorch.
SENet is the winner of ImageNet-2017 (https://arxiv.org/abs/1709.01507).
'''
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
class BasicBlock(nn.Module):
def __init__(self, in_planes, planes, stride=1):
super(BasicBlock, self).__init__()
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.shortcut = nn.Sequential()
if stride != 1 or in_planes != planes:
self.shortcut = nn.Sequential(
nn.Conv2d(in_planes, planes, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(planes)
)
# SE layers
self.fc1 = nn.Conv2d(planes, planes//16, kernel_size=1) # Use nn.Conv2d instead of nn.Linear
self.fc2 = nn.Conv2d(planes//16, planes, kernel_size=1)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
# Squeeze
w = F.avg_pool2d(out, out.size(2))
w = F.relu(self.fc1(w))
w = F.sigmoid(self.fc2(w))
# Excitation
out = out * w # New broadcasting feature from v0.2!
out += self.shortcut(x)
out = F.relu(out)
return out
class PreActBlock(nn.Module):
def __init__(self, in_planes, planes, stride=1):
super(PreActBlock, self).__init__()
self.bn1 = nn.BatchNorm2d(in_planes)
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
if stride != 1 or in_planes != planes:
self.shortcut = nn.Sequential(
nn.Conv2d(in_planes, planes, kernel_size=1, stride=stride, bias=False)
)
# SE layers
self.fc1 = nn.Conv2d(planes, planes//16, kernel_size=1)
self.fc2 = nn.Conv2d(planes//16, planes, kernel_size=1)
def forward(self, x):
out = F.relu(self.bn1(x))
shortcut = self.shortcut(out) if hasattr(self, 'shortcut') else x
out = self.conv1(out)
out = self.conv2(F.relu(self.bn2(out)))
# Squeeze
w = F.avg_pool2d(out, out.size(2))
w = F.relu(self.fc1(w))
w = F.sigmoid(self.fc2(w))
# Excitation
out = out * w
out += shortcut
return out
class SENet(nn.Module):
def __init__(self, block, num_blocks, num_classes=10):
super(SENet, self).__init__()
self.in_planes = 64
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1)
self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2)
self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)
self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)
self.linear = nn.Linear(512, num_classes)
def _make_layer(self, block, planes, num_blocks, stride):
strides = [stride] + [1]*(num_blocks-1)
layers = []
for stride in strides:
layers.append(block(self.in_planes, planes, stride))
self.in_planes = planes
return nn.Sequential(*layers)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.layer1(out)
out = self.layer2(out)
out = self.layer3(out)
out = self.layer4(out)
out = F.adaptive_max_pool2d(out, 1)
out = out.view(out.size(0), -1)
out = F.log_softmax(self.linear(out))
return out
def SENet18(): return SENet(PreActBlock, [2,2,2,2])
def SENet34(): return SENet(PreActBlock, [3,4,6,3])
+114
View File
@@ -0,0 +1,114 @@
import os, sys, time
import numpy as np
import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plt
class AverageMeter(object):
"""Computes and stores the average and current value"""
def __init__(self):
self.reset()
def reset(self):
self.val = 0
self.avg = 0
self.sum = 0
self.count = 0
def update(self, val, n=1):
self.val = val
self.sum += val * n
self.count += n
self.avg = self.sum / self.count
class RecorderMeter(object):
"""Computes and stores the minimum loss value and its epoch index"""
def __init__(self, total_epoch):
self.reset(total_epoch)
def reset(self, total_epoch):
assert total_epoch > 0
self.total_epoch = total_epoch
self.current_epoch = 0
self.epoch_losses = np.zeros((self.total_epoch, 2), dtype=np.float32) # [epoch, train/val]
self.epoch_losses = self.epoch_losses - 1
self.epoch_accuracy= np.zeros((self.total_epoch, 2), dtype=np.float32) # [epoch, train/val]
self.epoch_accuracy= self.epoch_accuracy
def update(self, idx, train_loss, train_acc, val_loss, val_acc):
assert idx >= 0 and idx < self.total_epoch, 'total_epoch : {} , but update with the {} index'.format(self.total_epoch, idx)
self.epoch_losses [idx, 0] = train_loss
self.epoch_losses [idx, 1] = val_loss
self.epoch_accuracy[idx, 0] = train_acc
self.epoch_accuracy[idx, 1] = val_acc
self.current_epoch = idx + 1
return self.max_accuracy(False) == val_acc
def max_accuracy(self, istrain):
if self.current_epoch <= 0: return 0
if istrain: return self.epoch_accuracy[:self.current_epoch, 0].max()
else: return self.epoch_accuracy[:self.current_epoch, 1].max()
def plot_curve(self, save_path):
title = 'the accuracy/loss curve of train/val'
dpi = 80
width, height = 1200, 800
legend_fontsize = 10
scale_distance = 48.8
figsize = width / float(dpi), height / float(dpi)
fig = plt.figure(figsize=figsize)
x_axis = np.array([i for i in range(self.total_epoch)]) # epochs
y_axis = np.zeros(self.total_epoch)
plt.xlim(0, self.total_epoch)
plt.ylim(0, 100)
interval_y = 5
interval_x = 5
plt.xticks(np.arange(0, self.total_epoch + interval_x, interval_x))
plt.yticks(np.arange(0, 100 + interval_y, interval_y))
plt.grid()
plt.title(title, fontsize=20)
plt.xlabel('the training epoch', fontsize=16)
plt.ylabel('accuracy', fontsize=16)
y_axis[:] = self.epoch_accuracy[:, 0]
plt.plot(x_axis, y_axis, color='g', linestyle='-', label='train-accuracy', lw=2)
plt.legend(loc=4, fontsize=legend_fontsize)
y_axis[:] = self.epoch_accuracy[:, 1]
plt.plot(x_axis, y_axis, color='y', linestyle='-', label='valid-accuracy', lw=2)
plt.legend(loc=4, fontsize=legend_fontsize)
y_axis[:] = self.epoch_losses[:, 0]
plt.plot(x_axis, y_axis*50, color='g', linestyle=':', label='train-loss-x50', lw=2)
plt.legend(loc=4, fontsize=legend_fontsize)
y_axis[:] = self.epoch_losses[:, 1]
plt.plot(x_axis, y_axis*50, color='y', linestyle=':', label='valid-loss-x50', lw=2)
plt.legend(loc=4, fontsize=legend_fontsize)
if save_path is not None:
fig.savefig(save_path, dpi=dpi, bbox_inches='tight')
print ('---- save figure {} into {}'.format(title, save_path))
plt.close(fig)
def time_string():
ISOTIMEFORMAT='%Y-%m-%d %X'
string = '[{}]'.format(time.strftime( ISOTIMEFORMAT, time.gmtime(time.time()) ))
return string
def convert_secs2time(epoch_time):
need_hour = int(epoch_time / 3600)
need_mins = int((epoch_time - 3600*need_hour) / 60)
need_secs = int(epoch_time - 3600*need_hour - 60*need_mins)
return need_hour, need_mins, need_secs
def time_file_str():
ISOTIMEFORMAT='%Y-%m-%d'
string = '{}'.format(time.strftime( ISOTIMEFORMAT, time.gmtime(time.time()) ))
return string + '-{}'.format(random.randint(1, 10000))
+124
View File
@@ -0,0 +1,124 @@
'''Some helper functions for PyTorch, including:
- get_mean_and_std: calculate the mean and std value of dataset.
- msr_init: net parameter initialization.
- progress_bar: progress bar mimic xlua.progress.
'''
import os
import sys
import time
import math
import torch.nn as nn
import torch.nn.init as init
def get_mean_and_std(dataset):
'''Compute the mean and std value of dataset.'''
dataloader = torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=True, num_workers=2)
mean = torch.zeros(3)
std = torch.zeros(3)
print('==> Computing mean and std..')
for inputs, targets in dataloader:
for i in range(3):
mean[i] += inputs[:,i,:,:].mean()
std[i] += inputs[:,i,:,:].std()
mean.div_(len(dataset))
std.div_(len(dataset))
return mean, std
def init_params(net):
'''Init layer parameters.'''
for m in net.modules():
if isinstance(m, nn.Conv2d):
init.kaiming_normal(m.weight, mode='fan_out')
if m.bias:
init.constant(m.bias, 0)
elif isinstance(m, nn.BatchNorm2d):
init.constant(m.weight, 1)
init.constant(m.bias, 0)
elif isinstance(m, nn.Linear):
init.normal(m.weight, std=1e-3)
if m.bias:
init.constant(m.bias, 0)
_, term_width = os.popen('stty size', 'r').read().split()
term_width = int(term_width)
TOTAL_BAR_LENGTH = 65.
last_time = time.time()
begin_time = last_time
def progress_bar(current, total, msg=None):
global last_time, begin_time
if current == 0:
begin_time = time.time() # Reset for new bar.
cur_len = int(TOTAL_BAR_LENGTH*current/total)
rest_len = int(TOTAL_BAR_LENGTH - cur_len) - 1
sys.stdout.write(' [')
for i in range(cur_len):
sys.stdout.write('=')
sys.stdout.write('>')
for i in range(rest_len):
sys.stdout.write('.')
sys.stdout.write(']')
cur_time = time.time()
step_time = cur_time - last_time
last_time = cur_time
tot_time = cur_time - begin_time
L = []
L.append(' Step: %s' % format_time(step_time))
L.append(' | Tot: %s' % format_time(tot_time))
if msg:
L.append(' | ' + msg)
msg = ''.join(L)
sys.stdout.write(msg)
for i in range(term_width-int(TOTAL_BAR_LENGTH)-len(msg)-3):
sys.stdout.write(' ')
# Go back to the center of the bar.
for i in range(term_width-int(TOTAL_BAR_LENGTH/2)+2):
sys.stdout.write('\b')
sys.stdout.write(' %d/%d ' % (current+1, total))
if current < total-1:
sys.stdout.write('\r')
else:
sys.stdout.write('\n')
sys.stdout.flush()
def format_time(seconds):
days = int(seconds / 3600/24)
seconds = seconds - days*3600*24
hours = int(seconds / 3600)
seconds = seconds - hours*3600
minutes = int(seconds / 60)
seconds = seconds - minutes*60
secondsf = int(seconds)
seconds = seconds - secondsf
millis = int(seconds*1000)
f = ''
i = 1
if days > 0:
f += str(days) + 'D'
i += 1
if hours > 0 and i <= 2:
f += str(hours) + 'h'
i += 1
if minutes > 0 and i <= 2:
f += str(minutes) + 'm'
i += 1
if secondsf > 0 and i <= 2:
f += str(secondsf) + 's'
i += 1
if millis > 0 and i <= 2:
f += str(millis) + 'ms'
i += 1
if f == '':
f = '0ms'
return f
+69
View File
@@ -0,0 +1,69 @@
# Cifar10 Wideresnet for Dawn Submission
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
from ...layers import *
def conv_2d(ni, nf, ks, stride): return nn.Conv2d(ni, nf, kernel_size=ks, stride=stride, padding=ks//2, bias=False)
def bn(ni, init_zero=False):
m = nn.BatchNorm2d(ni)
m.weight.data.fill_(0 if init_zero else 1)
m.bias.data.zero_()
return m
def bn_relu_conv(ni, nf, ks, stride, init_zero=False):
bn_initzero = bn(ni, init_zero=init_zero)
return nn.Sequential(bn_initzero, nn.ReLU(inplace=True), conv_2d(ni, nf, ks, stride))
def noop(x): return x
class BasicBlock(nn.Module):
def __init__(self, ni, nf, stride, drop_p=0.0):
super().__init__()
self.bn = nn.BatchNorm2d(ni)
self.conv1 = conv_2d(ni, nf, 3, stride)
self.conv2 = bn_relu_conv(nf, nf, 3, 1)
self.drop = nn.Dropout(drop_p, inplace=True) if drop_p else None
self.shortcut = conv_2d(ni, nf, 1, stride) if ni != nf else noop
def forward(self, x):
x2 = F.relu(self.bn(x), inplace=True)
r = self.shortcut(x2)
x = self.conv1(x2)
if self.drop: x = self.drop(x)
x = self.conv2(x) * 0.2
return x.add_(r)
def _make_group(N, ni, nf, block, stride, drop_p):
return [block(ni if i == 0 else nf, nf, stride if i == 0 else 1, drop_p) for i in range(N)]
class WideResNet(nn.Module):
def __init__(self, num_groups, N, num_classes, k=1, drop_p=0.0, start_nf=16):
super().__init__()
n_channels = [start_nf]
for i in range(num_groups): n_channels.append(start_nf*(2**i)*k)
layers = [conv_2d(3, n_channels[0], 3, 1)] # conv1
for i in range(num_groups):
layers += _make_group(N, n_channels[i], n_channels[i+1], BasicBlock, (1 if i==0 else 2), drop_p)
layers += [nn.BatchNorm2d(n_channels[3]), nn.ReLU(inplace=True), nn.AdaptiveAvgPool2d(1),
Flatten(), nn.Linear(n_channels[3], num_classes)]
self.features = nn.Sequential(*layers)
def forward(self, x): return self.features(x)
def wrn_22(): return WideResNet(num_groups=3, N=3, num_classes=10, k=6, drop_p=0.)
def wrn_22_k8(): return WideResNet(num_groups=3, N=3, num_classes=10, k=8, drop_p=0.)
def wrn_22_k10(): return WideResNet(num_groups=3, N=3, num_classes=10, k=10, drop_p=0.)
def wrn_22_k8_p2(): return WideResNet(num_groups=3, N=3, num_classes=10, k=8, drop_p=0.2)
def wrn_28(): return WideResNet(num_groups=3, N=4, num_classes=10, k=6, drop_p=0.)
def wrn_28_k8(): return WideResNet(num_groups=3, N=4, num_classes=10, k=8, drop_p=0.)
def wrn_28_k8_p2(): return WideResNet(num_groups=3, N=4, num_classes=10, k=8, drop_p=0.2)
def wrn_28_p2(): return WideResNet(num_groups=3, N=4, num_classes=10, k=6, drop_p=0.2)
+286
View File
@@ -0,0 +1,286 @@
from __future__ import print_function
import argparse
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
from torch.utils.serialization import load_lua
import numpy as np
import os
import math
from functools import reduce
class LambdaBase(nn.Sequential):
def __init__(self, fn, *args):
super(LambdaBase, self).__init__(*args)
self.lambda_func = fn
def forward_prepare(self, input):
output = []
for module in self._modules.values():
output.append(module(input))
return output if output else input
class Lambda(LambdaBase):
def forward(self, input):
return self.lambda_func(self.forward_prepare(input))
class LambdaMap(LambdaBase):
def forward(self, input):
# result is Variables list [Variable1, Variable2, ...]
return list(map(self.lambda_func,self.forward_prepare(input)))
class LambdaReduce(LambdaBase):
def forward(self, input):
# result is a Variable
return reduce(self.lambda_func,self.forward_prepare(input))
def copy_param(m,n):
if m.weight is not None: n.weight.data.copy_(m.weight)
if m.bias is not None: n.bias.data.copy_(m.bias)
if hasattr(n,'running_mean'): n.running_mean.copy_(m.running_mean)
if hasattr(n,'running_var'): n.running_var.copy_(m.running_var)
def add_submodule(seq, *args):
for n in args:
seq.add_module(str(len(seq._modules)),n)
def lua_recursive_model(module,seq):
for m in module.modules:
name = type(m).__name__
real = m
if name == 'TorchObject':
name = m._typename.replace('cudnn.','')
m = m._obj
if name == 'SpatialConvolution':
if not hasattr(m,'groups'): m.groups=1
n = nn.Conv2d(m.nInputPlane,m.nOutputPlane,(m.kW,m.kH),(m.dW,m.dH),(m.padW,m.padH),1,m.groups,bias=(m.bias is not None))
copy_param(m,n)
add_submodule(seq,n)
elif name == 'SpatialBatchNormalization':
n = nn.BatchNorm2d(m.running_mean.size(0), m.eps, m.momentum, m.affine)
copy_param(m,n)
add_submodule(seq,n)
elif name == 'ReLU':
n = nn.ReLU()
add_submodule(seq,n)
elif name == 'SpatialMaxPooling':
n = nn.MaxPool2d((m.kW,m.kH),(m.dW,m.dH),(m.padW,m.padH),ceil_mode=m.ceil_mode)
add_submodule(seq,n)
elif name == 'SpatialAveragePooling':
n = nn.AvgPool2d((m.kW,m.kH),(m.dW,m.dH),(m.padW,m.padH),ceil_mode=m.ceil_mode)
add_submodule(seq,n)
elif name == 'SpatialUpSamplingNearest':
n = nn.UpsamplingNearest2d(scale_factor=m.scale_factor)
add_submodule(seq,n)
elif name == 'View':
n = Lambda(lambda x: x.view(x.size(0),-1))
add_submodule(seq,n)
elif name == 'Linear':
# Linear in pytorch only accept 2D input
n1 = Lambda(lambda x: x.view(1,-1) if 1==len(x.size()) else x )
n2 = nn.Linear(m.weight.size(1),m.weight.size(0),bias=(m.bias is not None))
copy_param(m,n2)
n = nn.Sequential(n1,n2)
add_submodule(seq,n)
elif name == 'Dropout':
m.inplace = False
n = nn.Dropout(m.p)
add_submodule(seq,n)
elif name == 'SoftMax':
n = nn.Softmax()
add_submodule(seq,n)
elif name == 'Identity':
n = Lambda(lambda x: x) # do nothing
add_submodule(seq,n)
elif name == 'SpatialFullConvolution':
n = nn.ConvTranspose2d(m.nInputPlane,m.nOutputPlane,(m.kW,m.kH),(m.dW,m.dH),(m.padW,m.padH))
add_submodule(seq,n)
elif name == 'SpatialReplicationPadding':
n = nn.ReplicationPad2d((m.pad_l,m.pad_r,m.pad_t,m.pad_b))
add_submodule(seq,n)
elif name == 'SpatialReflectionPadding':
n = nn.ReflectionPad2d((m.pad_l,m.pad_r,m.pad_t,m.pad_b))
add_submodule(seq,n)
elif name == 'Copy':
n = Lambda(lambda x: x) # do nothing
add_submodule(seq,n)
elif name == 'Narrow':
n = Lambda(lambda x,a=(m.dimension,m.index,m.length): x.narrow(*a))
add_submodule(seq,n)
elif name == 'SpatialCrossMapLRN':
lrn = torch.legacy.nn.SpatialCrossMapLRN(m.size,m.alpha,m.beta,m.k)
n = Lambda(lambda x,lrn=lrn: Variable(lrn.forward(x.data)))
add_submodule(seq,n)
elif name == 'Sequential':
n = nn.Sequential()
lua_recursive_model(m,n)
add_submodule(seq,n)
elif name == 'ConcatTable': # output is list
n = LambdaMap(lambda x: x)
lua_recursive_model(m,n)
add_submodule(seq,n)
elif name == 'CAddTable': # input is list
n = LambdaReduce(lambda x,y: x+y)
add_submodule(seq,n)
elif name == 'Concat':
dim = m.dimension
n = LambdaReduce(lambda x,y,dim=dim: torch.cat((x,y),dim))
lua_recursive_model(m,n)
add_submodule(seq,n)
elif name == 'TorchObject':
print('Not Implement',name,real._typename)
else:
print('Not Implement',name)
def lua_recursive_source(module):
s = []
for m in module.modules:
name = type(m).__name__
real = m
if name == 'TorchObject':
name = m._typename.replace('cudnn.','')
m = m._obj
if name == 'SpatialConvolution':
if not hasattr(m,'groups'): m.groups=1
s += ['nn.Conv2d({},{},{},{},{},{},{},bias={}),#Conv2d'.format(m.nInputPlane,
m.nOutputPlane,(m.kW,m.kH),(m.dW,m.dH),(m.padW,m.padH),1,m.groups,m.bias is not None)]
elif name == 'SpatialBatchNormalization':
s += ['nn.BatchNorm2d({},{},{},{}),#BatchNorm2d'.format(m.running_mean.size(0), m.eps, m.momentum, m.affine)]
elif name == 'ReLU':
s += ['nn.ReLU()']
elif name == 'SpatialMaxPooling':
s += ['nn.MaxPool2d({},{},{},ceil_mode={}),#MaxPool2d'.format((m.kW,m.kH),(m.dW,m.dH),(m.padW,m.padH),m.ceil_mode)]
elif name == 'SpatialAveragePooling':
s += ['nn.AvgPool2d({},{},{},ceil_mode={}),#AvgPool2d'.format((m.kW,m.kH),(m.dW,m.dH),(m.padW,m.padH),m.ceil_mode)]
elif name == 'SpatialUpSamplingNearest':
s += ['nn.UpsamplingNearest2d(scale_factor={})'.format(m.scale_factor)]
elif name == 'View':
s += ['Lambda(lambda x: x.view(x.size(0),-1)), # View']
elif name == 'Linear':
s1 = 'Lambda(lambda x: x.view(1,-1) if 1==len(x.size()) else x )'
s2 = 'nn.Linear({},{},bias={})'.format(m.weight.size(1),m.weight.size(0),(m.bias is not None))
s += ['nn.Sequential({},{}),#Linear'.format(s1,s2)]
elif name == 'Dropout':
s += ['nn.Dropout({})'.format(m.p)]
elif name == 'SoftMax':
s += ['nn.Softmax()']
elif name == 'Identity':
s += ['Lambda(lambda x: x), # Identity']
elif name == 'SpatialFullConvolution':
s += ['nn.ConvTranspose2d({},{},{},{},{})'.format(m.nInputPlane,
m.nOutputPlane,(m.kW,m.kH),(m.dW,m.dH),(m.padW,m.padH))]
elif name == 'SpatialReplicationPadding':
s += ['nn.ReplicationPad2d({})'.format((m.pad_l,m.pad_r,m.pad_t,m.pad_b))]
elif name == 'SpatialReflectionPadding':
s += ['nn.ReflectionPad2d({})'.format((m.pad_l,m.pad_r,m.pad_t,m.pad_b))]
elif name == 'Copy':
s += ['Lambda(lambda x: x), # Copy']
elif name == 'Narrow':
s += ['Lambda(lambda x,a={}: x.narrow(*a))'.format((m.dimension,m.index,m.length))]
elif name == 'SpatialCrossMapLRN':
lrn = 'torch.legacy.nn.SpatialCrossMapLRN(*{})'.format((m.size,m.alpha,m.beta,m.k))
s += ['Lambda(lambda x,lrn={}: Variable(lrn.forward(x.data)))'.format(lrn)]
elif name == 'Sequential':
s += ['nn.Sequential( # Sequential']
s += lua_recursive_source(m)
s += [')']
elif name == 'ConcatTable':
s += ['LambdaMap(lambda x: x, # ConcatTable']
s += lua_recursive_source(m)
s += [')']
elif name == 'CAddTable':
s += ['LambdaReduce(lambda x,y: x+y), # CAddTable']
elif name == 'Concat':
dim = m.dimension
s += ['LambdaReduce(lambda x,y,dim={}: torch.cat((x,y),dim), # Concat'.format(m.dimension)]
s += lua_recursive_source(m)
s += [')']
else:
s += '# ' + name + ' Not Implement,\n'
s = map(lambda x: '\t{}'.format(x),s)
return s
def simplify_source(s):
s = map(lambda x: x.replace(',(1, 1),(0, 0),1,1,bias=True),#Conv2d',')'),s)
s = map(lambda x: x.replace(',(0, 0),1,1,bias=True),#Conv2d',')'),s)
s = map(lambda x: x.replace(',1,1,bias=True),#Conv2d',')'),s)
s = map(lambda x: x.replace(',bias=True),#Conv2d',')'),s)
s = map(lambda x: x.replace('),#Conv2d',')'),s)
s = map(lambda x: x.replace(',1e-05,0.1,True),#BatchNorm2d',')'),s)
s = map(lambda x: x.replace('),#BatchNorm2d',')'),s)
s = map(lambda x: x.replace(',(0, 0),ceil_mode=False),#MaxPool2d',')'),s)
s = map(lambda x: x.replace(',ceil_mode=False),#MaxPool2d',')'),s)
s = map(lambda x: x.replace('),#MaxPool2d',')'),s)
s = map(lambda x: x.replace(',(0, 0),ceil_mode=False),#AvgPool2d',')'),s)
s = map(lambda x: x.replace(',ceil_mode=False),#AvgPool2d',')'),s)
s = map(lambda x: x.replace(',bias=True)),#Linear',')), # Linear'),s)
s = map(lambda x: x.replace(')),#Linear',')), # Linear'),s)
s = map(lambda x: '{},\n'.format(x),s)
s = map(lambda x: x[1:],s)
s = reduce(lambda x,y: x+y, s)
return s
def torch_to_pytorch(t7_filename,outputname=None):
model = load_lua(t7_filename,unknown_classes=True)
if type(model).__name__=='hashable_uniq_dict': model=model.model
model.gradInput = None
slist = lua_recursive_source(torch.legacy.nn.Sequential().add(model))
s = simplify_source(slist)
header = '''
import torch
import torch.nn as nn
from torch.autograd import Variable
from functools import reduce
class LambdaBase(nn.Sequential):
def __init__(self, fn, *args):
super(LambdaBase, self).__init__(*args)
self.lambda_func = fn
def forward_prepare(self, input):
output = []
for module in self._modules.values():
output.append(module(input))
return output if output else input
class Lambda(LambdaBase):
def forward(self, input):
return self.lambda_func(self.forward_prepare(input))
class LambdaMap(LambdaBase):
def forward(self, input):
return list(map(self.lambda_func,self.forward_prepare(input)))
class LambdaReduce(LambdaBase):
def forward(self, input):
return reduce(self.lambda_func,self.forward_prepare(input))
'''
varname = t7_filename.replace('.t7','').replace('.','_').replace('-','_')
s = '{}\n\n{} = {}'.format(header,varname,s[:-2])
if outputname is None: outputname=varname
with open(outputname+'.py', "w") as pyfile:
pyfile.write(s)
n = nn.Sequential()
lua_recursive_model(model,n)
torch.save(n.state_dict(),outputname+'.pth')
parser = argparse.ArgumentParser(description='Convert torch t7 model to pytorch')
parser.add_argument('--model','-m', type=str, required=True,
help='torch model file in t7 format')
parser.add_argument('--output', '-o', type=str, default=None,
help='output file name prefix, xxx.py xxx.pth')
args = parser.parse_args()
torch_to_pytorch(args.model,args.output)
+51
View File
@@ -0,0 +1,51 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
from .layers import *
class ConvBN(nn.Module):
"convolutional layer then batchnorm"
def __init__(self, ch_in, ch_out, kernel_size = 3, stride=1, padding=0):
super().__init__()
self.conv = nn.Conv2d(ch_in, ch_out, kernel_size=kernel_size, stride=stride, padding=padding, bias=False)
self.bn = nn.BatchNorm2d(ch_out, momentum=0.01)
self.relu = nn.LeakyReLU(0.1, inplace=True)
def forward(self, x): return self.relu(self.bn(self.conv(x)))
class DarknetBlock(nn.Module):
def __init__(self, ch_in):
super().__init__()
ch_hid = ch_in//2
self.conv1 = ConvBN(ch_in, ch_hid, kernel_size=1, stride=1, padding=0)
self.conv2 = ConvBN(ch_hid, ch_in, kernel_size=3, stride=1, padding=1)
def forward(self, x): return self.conv2(self.conv1(x)) + x
class Darknet(nn.Module):
"Replicates the darknet classifier from the YOLOv3 paper (table 1)"
def make_group_layer(self, ch_in, num_blocks, stride=1):
layers = [ConvBN(ch_in,ch_in*2,stride=stride)]
for i in range(num_blocks): layers.append(DarknetBlock(ch_in*2))
return layers
def __init__(self, num_blocks, num_classes=1000, start_nf=32):
super().__init__()
nf = start_nf
layers = [ConvBN(3, nf, kernel_size=3, stride=1, padding=1)]
for i,nb in enumerate(num_blocks):
layers += self.make_group_layer(nf, nb, stride=(1 if i==1 else 2))
nf *= 2
layers += [nn.AdaptiveAvgPool2d(1), Flatten(), nn.Linear(nf, num_classes)]
self.layers = nn.Sequential(*layers)
def forward(self, x): return self.layers(x)
def darknet_53(num_classes=1000): return Darknet([1,2,8,8,4], num_classes)
def darknet_small(num_classes=1000): return Darknet([1,2,4,8,4], num_classes)
def darknet_mini(num_classes=1000): return Darknet([1,2,4,4,2], num_classes, start_nf=24)
def darknet_mini2(num_classes=1000): return Darknet([1,2,8,8,4], num_classes, start_nf=16)
def darknet_mini3(num_classes=1000): return Darknet([1,2,4,4], num_classes)
+227
View File
@@ -0,0 +1,227 @@
import torch.nn as nn
import math
import torch.utils.model_zoo as model_zoo
from ..layers import *
model_urls = {
'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth',
'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth',
'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth',
'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth',
'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth',
}
def conv3x3(in_planes, out_planes, stride=1):
"3x3 convolution with padding"
return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
padding=1, bias=False)
def bn1(planes):
m = nn.BatchNorm1d(planes)
m.weight.data.fill_(1)
m.bias.data.zero_()
return m
def bn(planes, init_zero=False):
m = nn.BatchNorm2d(planes)
m.weight.data.fill_(0 if init_zero else 1)
m.bias.data.zero_()
return m
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, inplanes, planes, stride=1, downsample=None):
super().__init__()
self.conv1 = conv3x3(inplanes, planes, stride)
self.bn1 = bn(planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = conv3x3(planes, planes)
self.bn2 = bn(planes)
self.downsample = downsample
self.stride = stride
def forward(self, x):
residual = x
if self.downsample is not None: residual = self.downsample(x)
out = self.conv1(x)
out = self.relu(out)
out = self.bn1(out)
out = self.conv2(out)
out += residual
out = self.relu(out)
out = self.bn2(out)
return out
class BottleneckFinal(nn.Module):
expansion = 4
def __init__(self, inplanes, planes, stride=1, downsample=None):
super().__init__()
self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
self.bn1 = bn(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
padding=1, bias=False)
self.bn2 = bn(planes)
self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
self.bn3 = bn(planes * 4)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride
def forward(self, x):
residual = x
if self.downsample is not None: residual = self.downsample(x)
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out += residual
out = self.bn3(out)
out = self.relu(out)
return out
class BottleneckZero(nn.Module):
expansion = 4
def __init__(self, inplanes, planes, stride=1, downsample=None):
super().__init__()
self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
self.bn1 = bn(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
padding=1, bias=False)
self.bn2 = bn(planes)
self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
self.bn3 = bn(planes * 4, init_zero=True)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride
def forward(self, x):
residual = x
if self.downsample is not None: residual = self.downsample(x)
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
out += residual
out = self.relu(out)
return out
class Bottleneck(nn.Module):
expansion = 4
def __init__(self, inplanes, planes, stride=1, downsample=None):
super().__init__()
self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
self.bn1 = bn(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
padding=1, bias=False)
self.bn2 = bn(planes)
self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
self.bn3 = bn(planes * 4)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride
def forward(self, x):
residual = x
if self.downsample is not None: residual = self.downsample(x)
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
out += residual
out = self.relu(out)
return out
class ResNet(nn.Module):
def __init__(self, block, layers, num_classes=1000, k=1, vgg_head=False):
super().__init__()
self.inplanes = 64
features = [nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
, bn(64) , nn.ReLU(inplace=True) , nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
, self._make_layer(block, int(64*k), layers[0])
, self._make_layer(block, int(128*k), layers[1], stride=2)
, self._make_layer(block, int(256*k), layers[2], stride=2)
, self._make_layer(block, int(512*k), layers[3], stride=2)]
out_sz = int(512*k) * block.expansion
if vgg_head:
features += [nn.AdaptiveAvgPool2d(3), Flatten()
, nn.Linear(out_sz*3*3, 4096), nn.ReLU(inplace=True), bn1(4096), nn.Dropout(0.25)
, nn.Linear(4096, 4096), nn.ReLU(inplace=True), bn1(4096), nn.Dropout(0.25)
, nn.Linear(4096, num_classes)]
else: features += [nn.AdaptiveAvgPool2d(1), Flatten(), nn.Linear(out_sz, num_classes)]
self.features = nn.Sequential(*features)
for m in self.modules():
if isinstance(m, nn.Conv2d):
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
m.weight.data.normal_(0, math.sqrt(2. / n))
def _make_layer(self, block, planes, blocks, stride=1):
downsample = None
if stride != 1 or self.inplanes != planes * block.expansion:
downsample = nn.Sequential(
nn.Conv2d(self.inplanes, planes * block.expansion,
kernel_size=1, stride=stride, bias=False),
bn(planes * block.expansion),
)
layers = []
layers.append(block(self.inplanes, planes, stride, downsample))
self.inplanes = planes * block.expansion
for i in range(1, blocks): layers.append(block(self.inplanes, planes))
return nn.Sequential(*layers)
def forward(self, x): return self.features(x)
def load(model, pre, name):
if pretrained: model.load_state_dict(model_zoo.load_url(model_urls[name]))
return model
def fa_resnet18(pretrained=False, **kwargs): return load(ResNet(BasicBlock, [2, 2, 2, 2], **kwargs), pretrained, 'resnet18')
def fa_resnet34(pretrained=False, **kwargs): return load(ResNet(BasicBlock, [3, 4, 6, 3], **kwargs), pretrained, 'resnet34')
def fa_resnet50(pretrained=False, **kwargs): return load(ResNet(Bottleneck, [3, 4, 6, 3], **kwargs), pretrained, 'resnet50')
def fa_resnet101(pretrained=False, **kwargs): return load(ResNet(Bottleneck, [3, 4, 23, 3], **kwargs), pretrained, 'resnet101')
def fa_resnet152(pretrained=False, **kwargs): return load(ResNet(Bottleneck, [3, 8, 36, 3], **kwargs), pretrained, 'resnet152')
def bnf_resnet50 (): return ResNet(BottleneckFinal, [3, 4, 6, 3])
def bnz_resnet50 (): return ResNet(BottleneckZero, [3, 4, 6, 3])
def w5_resnet50 (): return ResNet(Bottleneck, [2, 3, 3, 2], k=1.5)
def w25_resnet50(): return ResNet(Bottleneck, [3, 4, 4, 3], k=1.25)
def w125_resnet50(): return ResNet(Bottleneck,[3, 4, 6, 3], k=1.125)
def vgg_resnet50(): return ResNet(Bottleneck, [3, 4, 6, 3], vgg_head=True)
+358
View File
@@ -0,0 +1,358 @@
import torch
import torch.nn as nn
import torch.utils.model_zoo as model_zoo
import os
import sys
model_urls = {
'imagenet': 'http://data.lip6.fr/cadene/pretrainedmodels/inceptionresnetv2-520b38e4.pth'
}
class BasicConv2d(nn.Module):
def __init__(self, in_planes, out_planes, kernel_size, stride, padding=0):
super(BasicConv2d, self).__init__()
self.conv = nn.Conv2d(in_planes, out_planes,
kernel_size=kernel_size, stride=stride,
padding=padding, bias=False) # verify bias false
self.bn = nn.BatchNorm2d(out_planes,
eps=0.001, # value found in tensorflow
momentum=0.1, # default pytorch value
affine=True)
self.relu = nn.ReLU(inplace=False)
def forward(self, x):
x = self.conv(x)
x = self.bn(x)
x = self.relu(x)
return x
class Mixed_5b(nn.Module):
def __init__(self):
super(Mixed_5b, self).__init__()
self.branch0 = BasicConv2d(192, 96, kernel_size=1, stride=1)
self.branch1 = nn.Sequential(
BasicConv2d(192, 48, kernel_size=1, stride=1),
BasicConv2d(48, 64, kernel_size=5, stride=1, padding=2)
)
self.branch2 = nn.Sequential(
BasicConv2d(192, 64, kernel_size=1, stride=1),
BasicConv2d(64, 96, kernel_size=3, stride=1, padding=1),
BasicConv2d(96, 96, kernel_size=3, stride=1, padding=1)
)
self.branch3 = nn.Sequential(
nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False),
BasicConv2d(192, 64, kernel_size=1, stride=1)
)
def forward(self, x):
x0 = self.branch0(x)
x1 = self.branch1(x)
x2 = self.branch2(x)
x3 = self.branch3(x)
out = torch.cat((x0, x1, x2, x3), 1)
return out
class Block35(nn.Module):
def __init__(self, scale=1.0):
super(Block35, self).__init__()
self.scale = scale
self.branch0 = BasicConv2d(320, 32, kernel_size=1, stride=1)
self.branch1 = nn.Sequential(
BasicConv2d(320, 32, kernel_size=1, stride=1),
BasicConv2d(32, 32, kernel_size=3, stride=1, padding=1)
)
self.branch2 = nn.Sequential(
BasicConv2d(320, 32, kernel_size=1, stride=1),
BasicConv2d(32, 48, kernel_size=3, stride=1, padding=1),
BasicConv2d(48, 64, kernel_size=3, stride=1, padding=1)
)
self.conv2d = nn.Conv2d(128, 320, kernel_size=1, stride=1)
self.relu = nn.ReLU(inplace=False)
def forward(self, x):
x0 = self.branch0(x)
x1 = self.branch1(x)
x2 = self.branch2(x)
out = torch.cat((x0, x1, x2), 1)
out = self.conv2d(out)
out = out * self.scale + x
out = self.relu(out)
return out
class Mixed_6a(nn.Module):
def __init__(self):
super(Mixed_6a, self).__init__()
self.branch0 = BasicConv2d(320, 384, kernel_size=3, stride=2)
self.branch1 = nn.Sequential(
BasicConv2d(320, 256, kernel_size=1, stride=1),
BasicConv2d(256, 256, kernel_size=3, stride=1, padding=1),
BasicConv2d(256, 384, kernel_size=3, stride=2)
)
self.branch2 = nn.MaxPool2d(3, stride=2)
def forward(self, x):
x0 = self.branch0(x)
x1 = self.branch1(x)
x2 = self.branch2(x)
out = torch.cat((x0, x1, x2), 1)
return out
class Block17(nn.Module):
def __init__(self, scale=1.0):
super(Block17, self).__init__()
self.scale = scale
self.branch0 = BasicConv2d(1088, 192, kernel_size=1, stride=1)
self.branch1 = nn.Sequential(
BasicConv2d(1088, 128, kernel_size=1, stride=1),
BasicConv2d(128, 160, kernel_size=(1,7), stride=1, padding=(0,3)),
BasicConv2d(160, 192, kernel_size=(7,1), stride=1, padding=(3,0))
)
self.conv2d = nn.Conv2d(384, 1088, kernel_size=1, stride=1)
self.relu = nn.ReLU(inplace=False)
def forward(self, x):
x0 = self.branch0(x)
x1 = self.branch1(x)
out = torch.cat((x0, x1), 1)
out = self.conv2d(out)
out = out * self.scale + x
out = self.relu(out)
return out
class Mixed_7a(nn.Module):
def __init__(self):
super(Mixed_7a, self).__init__()
self.branch0 = nn.Sequential(
BasicConv2d(1088, 256, kernel_size=1, stride=1),
BasicConv2d(256, 384, kernel_size=3, stride=2)
)
self.branch1 = nn.Sequential(
BasicConv2d(1088, 256, kernel_size=1, stride=1),
BasicConv2d(256, 288, kernel_size=3, stride=2)
)
self.branch2 = nn.Sequential(
BasicConv2d(1088, 256, kernel_size=1, stride=1),
BasicConv2d(256, 288, kernel_size=3, stride=1, padding=1),
BasicConv2d(288, 320, kernel_size=3, stride=2)
)
self.branch3 = nn.MaxPool2d(3, stride=2)
def forward(self, x):
x0 = self.branch0(x)
x1 = self.branch1(x)
x2 = self.branch2(x)
x3 = self.branch3(x)
out = torch.cat((x0, x1, x2, x3), 1)
return out
class Block8(nn.Module):
def __init__(self, scale=1.0, noReLU=False):
super(Block8, self).__init__()
self.scale = scale
self.noReLU = noReLU
self.branch0 = BasicConv2d(2080, 192, kernel_size=1, stride=1)
self.branch1 = nn.Sequential(
BasicConv2d(2080, 192, kernel_size=1, stride=1),
BasicConv2d(192, 224, kernel_size=(1,3), stride=1, padding=(0,1)),
BasicConv2d(224, 256, kernel_size=(3,1), stride=1, padding=(1,0))
)
self.conv2d = nn.Conv2d(448, 2080, kernel_size=1, stride=1)
if not self.noReLU:
self.relu = nn.ReLU(inplace=False)
def forward(self, x):
x0 = self.branch0(x)
x1 = self.branch1(x)
out = torch.cat((x0, x1), 1)
out = self.conv2d(out)
out = out * self.scale + x
if not self.noReLU:
out = self.relu(out)
return out
class InceptionResnetV2(nn.Module):
def __init__(self, num_classes=1001):
super(InceptionResnetV2, self).__init__()
# Special attributs
self.input_space = None
self.input_size = (299, 299, 3)
self.mean = None
self.std = None
# Modules
self.conv2d_1a = BasicConv2d(3, 32, kernel_size=3, stride=2)
self.conv2d_2a = BasicConv2d(32, 32, kernel_size=3, stride=1)
self.conv2d_2b = BasicConv2d(32, 64, kernel_size=3, stride=1, padding=1)
self.maxpool_3a = nn.MaxPool2d(3, stride=2)
self.conv2d_3b = BasicConv2d(64, 80, kernel_size=1, stride=1)
self.conv2d_4a = BasicConv2d(80, 192, kernel_size=3, stride=1)
self.maxpool_5a = nn.MaxPool2d(3, stride=2)
self.mixed_5b = Mixed_5b()
self.repeat = nn.Sequential(
Block35(scale=0.17),
Block35(scale=0.17),
Block35(scale=0.17),
Block35(scale=0.17),
Block35(scale=0.17),
Block35(scale=0.17),
Block35(scale=0.17),
Block35(scale=0.17),
Block35(scale=0.17),
Block35(scale=0.17)
)
self.mixed_6a = Mixed_6a()
self.repeat_1 = nn.Sequential(
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10),
Block17(scale=0.10)
)
self.mixed_7a = Mixed_7a()
self.repeat_2 = nn.Sequential(
Block8(scale=0.20),
Block8(scale=0.20),
Block8(scale=0.20),
Block8(scale=0.20),
Block8(scale=0.20),
Block8(scale=0.20),
Block8(scale=0.20),
Block8(scale=0.20),
Block8(scale=0.20)
)
self.block8 = Block8(noReLU=True)
self.conv2d_7b = BasicConv2d(2080, 1536, kernel_size=1, stride=1)
self.avgpool_1a = nn.AvgPool2d(8, count_include_pad=False)
self.last_linear = nn.Linear(1536, num_classes)
def features(self, input):
x = self.conv2d_1a(input)
x = self.conv2d_2a(x)
x = self.conv2d_2b(x)
x = self.maxpool_3a(x)
x = self.conv2d_3b(x)
x = self.conv2d_4a(x)
x = self.maxpool_5a(x)
x = self.mixed_5b(x)
x = self.repeat(x)
x = self.mixed_6a(x)
x = self.repeat_1(x)
x = self.mixed_7a(x)
x = self.repeat_2(x)
x = self.block8(x)
x = self.conv2d_7b(x)
return x
def logits(self, features):
x = self.avgpool_1a(features)
x = x.view(x.size(0), -1)
x = self.last_linear(x)
return x
def forward(self, input):
x = self.features(input)
x = self.logits(x)
return x
def inceptionresnetv2(num_classes=1000, pretrained='imagenet'):
r"""InceptionResNetV2 model architecture from the
`"InceptionV4, Inception-ResNet..." <https://arxiv.org/abs/1602.07261>`_ paper.
"""
if pretrained:
settings = pretrained_settings['inceptionresnetv2'][pretrained]
assert num_classes == settings['num_classes'], \
"num_classes should be {}, but is {}".format(settings['num_classes'], num_classes)
# both 'imagenet'&'imagenet+background' are loaded from same parameters
model = InceptionResNetV2(num_classes=1001)
model.load_state_dict(model_zoo.load_url(settings['url']))
if pretrained == 'imagenet':
new_last_linear = nn.Linear(1536, 1000)
new_last_linear.weight.data = model.last_linear.weight.data[1:]
new_last_linear.bias.data = model.last_linear.bias.data[1:]
model.last_linear = new_last_linear
model.input_space = settings['input_space']
model.input_size = settings['input_size']
model.input_range = settings['input_range']
model.mean = settings['mean']
model.std = settings['std']
else:
model = InceptionResNetV2(num_classes=num_classes)
return model
'''
TEST
Run this code with:
```
cd $HOME/pretrained-models.pytorch
python -m pretrainedmodels.inceptionresnetv2
```
'''
if __name__ == '__main__':
assert inceptionresnetv2(num_classes=10, pretrained=None)
print('success')
assert inceptionresnetv2(num_classes=1000, pretrained='imagenet')
print('success')
assert inceptionresnetv2(num_classes=1001, pretrained='imagenet+background')
print('success')
# fail
assert inceptionresnetv2(num_classes=1001, pretrained='imagenet')
+442
View File
@@ -0,0 +1,442 @@
import torch
import torch.nn as nn
import torch.utils.model_zoo as model_zoo
import os
import sys
model_urls = {
'imagenet': 'https://s3.amazonaws.com/pytorch/models/inceptionv4-58153ba9.pth'
}
class BasicConv2d(nn.Module):
def __init__(self, in_planes, out_planes, kernel_size, stride, padding=0):
super(BasicConv2d, self).__init__()
self.conv = nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, padding=padding, bias=False) # verify bias false
self.bn = nn.BatchNorm2d(out_planes)
self.relu = nn.ReLU(inplace=True)
def forward(self, x):
x = self.conv(x)
x = self.bn(x)
x = self.relu(x)
return x
class Mixed_3a(nn.Module):
def __init__(self):
super(Mixed_3a, self).__init__()
self.maxpool = nn.MaxPool2d(3, stride=2)
self.conv = BasicConv2d(64, 96, kernel_size=3, stride=2)
def forward(self, x):
x0 = self.maxpool(x)
x1 = self.conv(x)
out = torch.cat((x0, x1), 1)
return out
class Mixed_4a(nn.Module):
def __init__(self):
super(Mixed_4a, self).__init__()
self.block0 = nn.Sequential(
BasicConv2d(160, 64, kernel_size=1, stride=1),
BasicConv2d(64, 96, kernel_size=3, stride=1)
)
self.block1 = nn.Sequential(
BasicConv2d(160, 64, kernel_size=1, stride=1),
BasicConv2d(64, 64, kernel_size=(1,7), stride=1, padding=(0,3)),
BasicConv2d(64, 64, kernel_size=(7,1), stride=1, padding=(3,0)),
BasicConv2d(64, 96, kernel_size=(3,3), stride=1)
)
def forward(self, x):
x0 = self.block0(x)
x1 = self.block1(x)
out = torch.cat((x0, x1), 1)
return out
class Mixed_5a(nn.Module):
def __init__(self):
super(Mixed_5a, self).__init__()
self.conv = BasicConv2d(192, 192, kernel_size=3, stride=2)
self.maxpool = nn.MaxPool2d(3, stride=2)
def forward(self, x):
x0 = self.conv(x)
x1 = self.maxpool(x)
out = torch.cat((x0, x1), 1)
return out
class Inception_A(nn.Module):
def __init__(self):
super(Inception_A, self).__init__()
self.block0 = BasicConv2d(384, 96, kernel_size=1, stride=1)
self.block1 = nn.Sequential(
BasicConv2d(384, 64, kernel_size=1, stride=1),
BasicConv2d(64, 96, kernel_size=3, stride=1, padding=1)
)
self.block2 = nn.Sequential(
BasicConv2d(384, 64, kernel_size=1, stride=1),
BasicConv2d(64, 96, kernel_size=3, stride=1, padding=1),
BasicConv2d(96, 96, kernel_size=3, stride=1, padding=1)
)
self.block3 = nn.Sequential(
nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False),
BasicConv2d(384, 96, kernel_size=1, stride=1)
)
def forward(self, x):
x0 = self.block0(x)
x1 = self.block1(x)
x2 = self.block2(x)
x3 = self.block3(x)
out = torch.cat((x0, x1, x2, x3), 1)
return out
class Reduction_A(nn.Module):
def __init__(self):
super(Reduction_A, self).__init__()
self.block0 = BasicConv2d(384, 384, kernel_size=3, stride=2)
self.block1 = nn.Sequential(
BasicConv2d(384, 192, kernel_size=1, stride=1),
BasicConv2d(192, 224, kernel_size=3, stride=1, padding=1),
BasicConv2d(224, 256, kernel_size=3, stride=2)
)
self.block2 = nn.MaxPool2d(3, stride=2)
def forward(self, x):
x0 = self.block0(x)
x1 = self.block1(x)
x2 = self.block2(x)
out = torch.cat((x0, x1, x2), 1)
return out
class Inception_B(nn.Module):
def __init__(self):
super(Inception_B, self).__init__()
self.block0 = BasicConv2d(1024, 384, kernel_size=1, stride=1)
self.block1 = nn.Sequential(
BasicConv2d(1024, 192, kernel_size=1, stride=1),
BasicConv2d(192, 224, kernel_size=(1,7), stride=1, padding=(0,3)),
BasicConv2d(224, 256, kernel_size=(7,1), stride=1, padding=(3,0))
)
self.block2 = nn.Sequential(
BasicConv2d(1024, 192, kernel_size=1, stride=1),
BasicConv2d(192, 192, kernel_size=(7,1), stride=1, padding=(3,0)),
BasicConv2d(192, 224, kernel_size=(1,7), stride=1, padding=(0,3)),
BasicConv2d(224, 224, kernel_size=(7,1), stride=1, padding=(3,0)),
BasicConv2d(224, 256, kernel_size=(1,7), stride=1, padding=(0,3))
)
self.block3 = nn.Sequential(
nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False),
BasicConv2d(1024, 128, kernel_size=1, stride=1)
)
def forward(self, x):
x0 = self.block0(x)
x1 = self.block1(x)
x2 = self.block2(x)
x3 = self.block3(x)
out = torch.cat((x0, x1, x2, x3), 1)
return out
class Reduction_B(nn.Module):
def __init__(self):
super(Reduction_B, self).__init__()
self.block0 = nn.Sequential(
BasicConv2d(1024, 192, kernel_size=1, stride=1),
BasicConv2d(192, 192, kernel_size=3, stride=2)
)
self.block1 = nn.Sequential(
BasicConv2d(1024, 256, kernel_size=1, stride=1),
BasicConv2d(256, 256, kernel_size=(1,7), stride=1, padding=(0,3)),
BasicConv2d(256, 320, kernel_size=(7,1), stride=1, padding=(3,0)),
BasicConv2d(320, 320, kernel_size=3, stride=2)
)
self.block2 = nn.MaxPool2d(3, stride=2)
def forward(self, x):
x0 = self.block0(x)
x1 = self.block1(x)
x2 = self.block2(x)
out = torch.cat((x0, x1, x2), 1)
return out
class Inception_C(nn.Module):
def __init__(self):
super(Inception_C, self).__init__()
self.block0 = BasicConv2d(1536, 256, kernel_size=1, stride=1)
self.block1_0 = BasicConv2d(1536, 384, kernel_size=1, stride=1)
self.block1_1a = BasicConv2d(384, 256, kernel_size=(1,3), stride=1, padding=(0,1))
self.block1_1b = BasicConv2d(384, 256, kernel_size=(3,1), stride=1, padding=(1,0))
self.block2_0 = BasicConv2d(1536, 384, kernel_size=1, stride=1)
self.block2_1 = BasicConv2d(384, 448, kernel_size=(3,1), stride=1, padding=(1,0))
self.block2_2 = BasicConv2d(448, 512, kernel_size=(1,3), stride=1, padding=(0,1))
self.block2_3a = BasicConv2d(512, 256, kernel_size=(1,3), stride=1, padding=(0,1))
self.block2_3b = BasicConv2d(512, 256, kernel_size=(3,1), stride=1, padding=(1,0))
self.block3 = nn.Sequential(
nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False),
BasicConv2d(1536, 256, kernel_size=1, stride=1)
)
def forward(self, x):
x0 = self.block0(x)
x1_0 = self.block1_0(x)
x1_1a = self.block1_1a(x1_0)
x1_1b = self.block1_1b(x1_0)
x1 = torch.cat((x1_1a, x1_1b), 1)
x2_0 = self.block2_0(x)
x2_1 = self.block2_1(x2_0)
x2_2 = self.block2_2(x2_1)
x2_3a = self.block2_3a(x2_2)
x2_3b = self.block2_3b(x2_2)
x2 = torch.cat((x2_3a, x2_3b), 1)
x3 = self.block3(x)
out = torch.cat((x0, x1, x2, x3), 1)
return out
class InceptionV4(nn.Module):
def __init__(self, num_classes=1001):
super(InceptionV4, self).__init__()
self.features = nn.Sequential(
BasicConv2d(3, 32, kernel_size=3, stride=2),
BasicConv2d(32, 32, kernel_size=3, stride=1),
BasicConv2d(32, 64, kernel_size=3, stride=1, padding=1),
Mixed_3a(),
Mixed_4a(),
Mixed_5a(),
Inception_A(),
Inception_A(),
Inception_A(),
Inception_A(),
Reduction_A(), # Mixed_6a
Inception_B(),
Inception_B(),
Inception_B(),
Inception_B(),
Inception_B(),
Inception_B(),
Inception_B(),
Reduction_B(), # Mixed_7a
Inception_C(),
Inception_C(),
Inception_C(),
nn.AdaptiveAvgPool2d((1,1))
)
self.classif = nn.Linear(1536, num_classes)
def forward(self, x):
x = self.features(x)
x = x.view(x.size(0), -1)
x = self.classif(x)
return x
def inceptionv4(pretrained=True):
r"""InceptionV4 model architecture from the
`"Inception-v4, Inception-ResNet..." <https://arxiv.org/abs/1602.07261>`_ paper.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
"""
model = InceptionV4()
if pretrained:
model.load_state_dict(model_zoo.load_url(model_urls['imagenet']))
return model
######################################################################
## Load parameters from HDF5 to Dict
######################################################################
def load_conv2d(state_dict, name_pth, name_tf):
h5f = h5py.File('dump/InceptionV4/'+name_tf+'.h5', 'r')
state_dict[name_pth+'.conv.weight'] = torch.from_numpy(h5f['weights'][()]).permute(3, 2, 0, 1)
out_planes = state_dict[name_pth+'.conv.weight'].size(0)
state_dict[name_pth+'.bn.weight'] = torch.ones(out_planes)
state_dict[name_pth+'.bn.bias'] = torch.from_numpy(h5f['beta'][()])
state_dict[name_pth+'.bn.running_mean'] = torch.from_numpy(h5f['mean'][()])
state_dict[name_pth+'.bn.running_var'] = torch.from_numpy(h5f['var'][()])
h5f.close()
def load_linear(state_dict, name_pth, name_tf):
h5f = h5py.File('dump/InceptionV4/'+name_tf+'.h5', 'r')
state_dict[name_pth+'.weight'] = torch.from_numpy(h5f['weights'][()]).t()
state_dict[name_pth+'.bias'] = torch.from_numpy(h5f['biases'][()])
h5f.close()
def load_mixed_4a_7a(state_dict, name_pth, name_tf):
load_conv2d(state_dict, name_pth+'.branch0.0', name_tf+'/Branch_0/Conv2d_0a_1x1')
load_conv2d(state_dict, name_pth+'.branch0.1', name_tf+'/Branch_0/Conv2d_1a_3x3')
load_conv2d(state_dict, name_pth+'.branch1.0', name_tf+'/Branch_1/Conv2d_0a_1x1')
load_conv2d(state_dict, name_pth+'.branch1.1', name_tf+'/Branch_1/Conv2d_0b_1x7')
load_conv2d(state_dict, name_pth+'.branch1.2', name_tf+'/Branch_1/Conv2d_0c_7x1')
load_conv2d(state_dict, name_pth+'.branch1.3', name_tf+'/Branch_1/Conv2d_1a_3x3')
def load_mixed_5(state_dict, name_pth, name_tf):
load_conv2d(state_dict, name_pth+'.branch0', name_tf+'/Branch_0/Conv2d_0a_1x1')
load_conv2d(state_dict, name_pth+'.branch1.0', name_tf+'/Branch_1/Conv2d_0a_1x1')
load_conv2d(state_dict, name_pth+'.branch1.1', name_tf+'/Branch_1/Conv2d_0b_3x3')
load_conv2d(state_dict, name_pth+'.branch2.0', name_tf+'/Branch_2/Conv2d_0a_1x1')
load_conv2d(state_dict, name_pth+'.branch2.1', name_tf+'/Branch_2/Conv2d_0b_3x3')
load_conv2d(state_dict, name_pth+'.branch2.2', name_tf+'/Branch_2/Conv2d_0c_3x3')
load_conv2d(state_dict, name_pth+'.branch3.1', name_tf+'/Branch_3/Conv2d_0b_1x1')
def load_mixed_6(state_dict, name_pth, name_tf):
load_conv2d(state_dict, name_pth+'.branch0', name_tf+'/Branch_0/Conv2d_0a_1x1')
load_conv2d(state_dict, name_pth+'.branch1.0', name_tf+'/Branch_1/Conv2d_0a_1x1')
load_conv2d(state_dict, name_pth+'.branch1.1', name_tf+'/Branch_1/Conv2d_0b_1x7')
load_conv2d(state_dict, name_pth+'.branch1.2', name_tf+'/Branch_1/Conv2d_0c_7x1')
load_conv2d(state_dict, name_pth+'.branch2.0', name_tf+'/Branch_2/Conv2d_0a_1x1')
load_conv2d(state_dict, name_pth+'.branch2.1', name_tf+'/Branch_2/Conv2d_0b_7x1')
load_conv2d(state_dict, name_pth+'.branch2.2', name_tf+'/Branch_2/Conv2d_0c_1x7')
load_conv2d(state_dict, name_pth+'.branch2.3', name_tf+'/Branch_2/Conv2d_0d_7x1')
load_conv2d(state_dict, name_pth+'.branch2.4', name_tf+'/Branch_2/Conv2d_0e_1x7')
load_conv2d(state_dict, name_pth+'.branch3.1', name_tf+'/Branch_3/Conv2d_0b_1x1')
def load_mixed_7(state_dict, name_pth, name_tf):
load_conv2d(state_dict, name_pth+'.branch0', name_tf+'/Branch_0/Conv2d_0a_1x1')
load_conv2d(state_dict, name_pth+'.branch1_0', name_tf+'/Branch_1/Conv2d_0a_1x1')
load_conv2d(state_dict, name_pth+'.branch1_1a', name_tf+'/Branch_1/Conv2d_0b_1x3')
load_conv2d(state_dict, name_pth+'.branch1_1b', name_tf+'/Branch_1/Conv2d_0c_3x1')
load_conv2d(state_dict, name_pth+'.branch2_0', name_tf+'/Branch_2/Conv2d_0a_1x1')
load_conv2d(state_dict, name_pth+'.branch2_1', name_tf+'/Branch_2/Conv2d_0b_3x1')
load_conv2d(state_dict, name_pth+'.branch2_2', name_tf+'/Branch_2/Conv2d_0c_1x3')
load_conv2d(state_dict, name_pth+'.branch2_3a', name_tf+'/Branch_2/Conv2d_0d_1x3')
load_conv2d(state_dict, name_pth+'.branch2_3b', name_tf+'/Branch_2/Conv2d_0e_3x1')
load_conv2d(state_dict, name_pth+'.branch3.1', name_tf+'/Branch_3/Conv2d_0b_1x1')
def load():
state_dict={}
load_conv2d(state_dict, name_pth='features.0', name_tf='Conv2d_1a_3x3')
load_conv2d(state_dict, name_pth='features.1', name_tf='Conv2d_2a_3x3')
load_conv2d(state_dict, name_pth='features.2', name_tf='Conv2d_2b_3x3')
load_conv2d(state_dict, name_pth='features.3.conv', name_tf='Mixed_3a/Branch_1/Conv2d_0a_3x3')
load_mixed_4a_7a(state_dict, name_pth='features.4', name_tf='Mixed_4a')
load_conv2d(state_dict, name_pth='features.5.conv', name_tf='Mixed_5a/Branch_0/Conv2d_1a_3x3')
load_mixed_5(state_dict, name_pth='features.6', name_tf='Mixed_5b')
load_mixed_5(state_dict, name_pth='features.7', name_tf='Mixed_5c')
load_mixed_5(state_dict, name_pth='features.8', name_tf='Mixed_5d')
load_mixed_5(state_dict, name_pth='features.9', name_tf='Mixed_5e')
load_conv2d(state_dict, name_pth='features.10.branch0', name_tf='Mixed_6a/Branch_0/Conv2d_1a_3x3')
load_conv2d(state_dict, name_pth='features.10.branch1.0', name_tf='Mixed_6a/Branch_1/Conv2d_0a_1x1')
load_conv2d(state_dict, name_pth='features.10.branch1.1', name_tf='Mixed_6a/Branch_1/Conv2d_0b_3x3')
load_conv2d(state_dict, name_pth='features.10.branch1.2', name_tf='Mixed_6a/Branch_1/Conv2d_1a_3x3')
load_mixed_6(state_dict, name_pth='features.11', name_tf='Mixed_6b')
load_mixed_6(state_dict, name_pth='features.12', name_tf='Mixed_6c')
load_mixed_6(state_dict, name_pth='features.13', name_tf='Mixed_6d')
load_mixed_6(state_dict, name_pth='features.14', name_tf='Mixed_6e')
load_mixed_6(state_dict, name_pth='features.15', name_tf='Mixed_6f')
load_mixed_6(state_dict, name_pth='features.16', name_tf='Mixed_6g')
load_mixed_6(state_dict, name_pth='features.17', name_tf='Mixed_6h')
load_mixed_4a_7a(state_dict, name_pth='features.18', name_tf='Mixed_7a')
load_mixed_7(state_dict, name_pth='features.19', name_tf='Mixed_7b')
load_mixed_7(state_dict, name_pth='features.20', name_tf='Mixed_7c')
load_mixed_7(state_dict, name_pth='features.21', name_tf='Mixed_7d')
load_linear(state_dict, name_pth='classif', name_tf='Logits')
return state_dict
######################################################################
## Test
######################################################################
def test(model):
model.eval()
from scipy import misc
img = misc.imread('lena_299.png')
inputs = torch.zeros(1,299,299,3)
inputs[0] = torch.from_numpy(img)
inputs.transpose_(1,3)
inputs.transpose_(2,3)
# 1, 3, 299, 299
outputs = model.forward(torch.autograd.Variable(inputs))
h5f = h5py.File('dump/InceptionV4/Logits.h5', 'r')
outputs_tf = torch.from_numpy(h5f['out'][()])
h5f.close()
outputs = torch.nn.functional.softmax(outputs)
print(torch.dist(outputs.data, outputs_tf))
return outputs
def test_conv2d(module, name):
#global output_tf
h5f = h5py.File('dump/InceptionV4/'+name+'.h5', 'r')
output_tf = torch.from_numpy(h5f['relu_out'][()])
output_tf.transpose_(1,3)
output_tf.transpose_(2,3)
h5f.close()
def test_dist(self, input, output):
print(name, torch.dist(output.data, output_tf))
module.register_forward_hook(test_dist)
def test_mixed_4a_7a(module, name):
test_conv2d(module.branch0[0], name+'/Branch_0/Conv2d_0a_1x1')
test_conv2d(module.branch0[1], name+'/Branch_0/Conv2d_1a_3x3')
test_conv2d(module.branch1[0], name+'/Branch_1/Conv2d_0a_1x1')
test_conv2d(module.branch1[1], name+'/Branch_1/Conv2d_0b_1x7')
test_conv2d(module.branch1[2], name+'/Branch_1/Conv2d_0c_7x1')
test_conv2d(module.branch1[3], name+'/Branch_1/Conv2d_1a_3x3')
######################################################################
## Main
######################################################################
if __name__ == "__main__":
import h5py
model = InceptionV4()
state_dict = load()
model.load_state_dict(state_dict)
# test_conv2d(model.features[0], 'Conv2d_1a_3x3')
# test_conv2d(model.features[1], 'Conv2d_2a_3x3')
# test_conv2d(model.features[2], 'Conv2d_2b_3x3')
# test_conv2d(model.features[3].conv, 'Mixed_3a/Branch_1/Conv2d_0a_3x3')
# test_mixed_4a_7a(model.features[4], 'Mixed_4a')
os.system('mkdir -p save')
torch.save(model, 'save/inceptionv4.pth')
torch.save(state_dict, 'save/inceptionv4_state.pth')
outputs = test(model)
+620
View File
@@ -0,0 +1,620 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.model_zoo as model_zoo
from torch.autograd import Variable
pretrained_settings = {
'nasnetalarge': {
'imagenet': {
'url': 'http://data.lip6.fr/cadene/pretrainedmodels/nasnetalarge-a1897284.pth',
'input_space': 'RGB',
'input_size': [3, 331, 331], # resize 354
'input_range': [0, 1],
'mean': [0.5, 0.5, 0.5],
'std': [0.5, 0.5, 0.5],
'num_classes': 1000
},
'imagenet+background': {
'url': 'http://data.lip6.fr/cadene/pretrainedmodels/nasnetalarge-a1897284.pth',
'input_space': 'RGB',
'input_size': [3, 331, 331], # resize 354
'input_range': [0, 1],
'mean': [0.5, 0.5, 0.5],
'std': [0.5, 0.5, 0.5],
'num_classes': 1001
}
}
}
class MaxPoolPad(nn.Module):
def __init__(self):
super(MaxPoolPad, self).__init__()
self.pad = nn.ZeroPad2d((1, 0, 1, 0))
self.pool = nn.MaxPool2d(3, stride=2, padding=1)
def forward(self, x):
x = self.pad(x)
x = self.pool(x)
x = x[:, :, 1:, 1:]
return x
class AvgPoolPad(nn.Module):
def __init__(self, stride=2, padding=1):
super(AvgPoolPad, self).__init__()
self.pad = nn.ZeroPad2d((1, 0, 1, 0))
self.pool = nn.AvgPool2d(3, stride=stride, padding=padding, count_include_pad=False)
def forward(self, x):
x = self.pad(x)
x = self.pool(x)
x = x[:, :, 1:, 1:]
return x
class SeparableConv2d(nn.Module):
def __init__(self, in_channels, out_channels, dw_kernel, dw_stride, dw_padding, bias=False):
super(SeparableConv2d, self).__init__()
self.depthwise_conv2d = nn.Conv2d(in_channels, in_channels, dw_kernel,
stride=dw_stride,
padding=dw_padding,
bias=bias,
groups=in_channels)
self.pointwise_conv2d = nn.Conv2d(in_channels, out_channels, 1, stride=1, bias=bias)
def forward(self, x):
x = self.depthwise_conv2d(x)
x = self.pointwise_conv2d(x)
return x
class BranchSeparables(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride, padding, bias=False):
super(BranchSeparables, self).__init__()
self.relu = nn.ReLU()
self.separable_1 = SeparableConv2d(in_channels, in_channels, kernel_size, stride, padding, bias=bias)
self.bn_sep_1 = nn.BatchNorm2d(in_channels, eps=0.001, momentum=0.1, affine=True)
self.relu1 = nn.ReLU()
self.separable_2 = SeparableConv2d(in_channels, out_channels, kernel_size, 1, padding, bias=bias)
self.bn_sep_2 = nn.BatchNorm2d(out_channels, eps=0.001, momentum=0.1, affine=True)
def forward(self, x):
x = self.relu(x)
x = self.separable_1(x)
x = self.bn_sep_1(x)
x = self.relu1(x)
x = self.separable_2(x)
x = self.bn_sep_2(x)
return x
class BranchSeparablesStem(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride, padding, bias=False):
super(BranchSeparablesStem, self).__init__()
self.relu = nn.ReLU()
self.separable_1 = SeparableConv2d(in_channels, out_channels, kernel_size, stride, padding, bias=bias)
self.bn_sep_1 = nn.BatchNorm2d(out_channels, eps=0.001, momentum=0.1, affine=True)
self.relu1 = nn.ReLU()
self.separable_2 = SeparableConv2d(out_channels, out_channels, kernel_size, 1, padding, bias=bias)
self.bn_sep_2 = nn.BatchNorm2d(out_channels, eps=0.001, momentum=0.1, affine=True)
def forward(self, x):
x = self.relu(x)
x = self.separable_1(x)
x = self.bn_sep_1(x)
x = self.relu1(x)
x = self.separable_2(x)
x = self.bn_sep_2(x)
return x
class BranchSeparablesReduction(BranchSeparables):
def __init__(self, in_channels, out_channels, kernel_size, stride, padding, z_padding=1, bias=False):
BranchSeparables.__init__(self, in_channels, out_channels, kernel_size, stride, padding, bias)
self.padding = nn.ZeroPad2d((z_padding, 0, z_padding, 0))
def forward(self, x):
x = self.relu(x)
x = self.padding(x)
x = self.separable_1(x)
x = x[:, :, 1:, 1:].contiguous()
x = self.bn_sep_1(x)
x = self.relu1(x)
x = self.separable_2(x)
x = self.bn_sep_2(x)
return x
class CellStem0(nn.Module):
def __init__(self):
super(CellStem0, self).__init__()
self.conv_1x1 = nn.Sequential()
self.conv_1x1.add_module('relu', nn.ReLU())
self.conv_1x1.add_module('conv', nn.Conv2d(96, 42, 1, stride=1, bias=False))
self.conv_1x1.add_module('bn', nn.BatchNorm2d(42, eps=0.001, momentum=0.1, affine=True))
self.comb_iter_0_left = BranchSeparables(42, 42, 5, 2, 2)
self.comb_iter_0_right = BranchSeparablesStem(96, 42, 7, 2, 3, bias=False)
self.comb_iter_1_left = nn.MaxPool2d(3, stride=2, padding=1)
self.comb_iter_1_right = BranchSeparablesStem(96, 42, 7, 2, 3, bias=False)
self.comb_iter_2_left = nn.AvgPool2d(3, stride=2, padding=1, count_include_pad=False)
self.comb_iter_2_right = BranchSeparablesStem(96, 42, 5, 2, 2, bias=False)
self.comb_iter_3_right = nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False)
self.comb_iter_4_left = BranchSeparables(42, 42, 3, 1, 1, bias=False)
self.comb_iter_4_right = nn.MaxPool2d(3, stride=2, padding=1)
def forward(self, x):
x1 = self.conv_1x1(x)
x_comb_iter_0_left = self.comb_iter_0_left(x1)
x_comb_iter_0_right = self.comb_iter_0_right(x)
x_comb_iter_0 = x_comb_iter_0_left + x_comb_iter_0_right
x_comb_iter_1_left = self.comb_iter_1_left(x1)
x_comb_iter_1_right = self.comb_iter_1_right(x)
x_comb_iter_1 = x_comb_iter_1_left + x_comb_iter_1_right
x_comb_iter_2_left = self.comb_iter_2_left(x1)
x_comb_iter_2_right = self.comb_iter_2_right(x)
x_comb_iter_2 = x_comb_iter_2_left + x_comb_iter_2_right
x_comb_iter_3_right = self.comb_iter_3_right(x_comb_iter_0)
x_comb_iter_3 = x_comb_iter_3_right + x_comb_iter_1
x_comb_iter_4_left = self.comb_iter_4_left(x_comb_iter_0)
x_comb_iter_4_right = self.comb_iter_4_right(x1)
x_comb_iter_4 = x_comb_iter_4_left + x_comb_iter_4_right
x_out = torch.cat([x_comb_iter_1, x_comb_iter_2, x_comb_iter_3, x_comb_iter_4], 1)
return x_out
class CellStem1(nn.Module):
def __init__(self):
super(CellStem1, self).__init__()
self.conv_1x1 = nn.Sequential()
self.conv_1x1.add_module('relu', nn.ReLU())
self.conv_1x1.add_module('conv', nn.Conv2d(168, 84, 1, stride=1, bias=False))
self.conv_1x1.add_module('bn', nn.BatchNorm2d(84, eps=0.001, momentum=0.1, affine=True))
self.relu = nn.ReLU()
self.path_1 = nn.Sequential()
self.path_1.add_module('avgpool', nn.AvgPool2d(1, stride=2, count_include_pad=False))
self.path_1.add_module('conv', nn.Conv2d(96, 42, 1, stride=1, bias=False))
self.path_2 = nn.ModuleList()
self.path_2.add_module('pad', nn.ZeroPad2d((0, 1, 0, 1)))
self.path_2.add_module('avgpool', nn.AvgPool2d(1, stride=2, count_include_pad=False))
self.path_2.add_module('conv', nn.Conv2d(96, 42, 1, stride=1, bias=False))
self.final_path_bn = nn.BatchNorm2d(84, eps=0.001, momentum=0.1, affine=True)
self.comb_iter_0_left = BranchSeparables(84, 84, 5, 2, 2, bias=False)
self.comb_iter_0_right = BranchSeparables(84, 84, 7, 2, 3, bias=False)
self.comb_iter_1_left = nn.MaxPool2d(3, stride=2, padding=1)
self.comb_iter_1_right = BranchSeparables(84, 84, 7, 2, 3, bias=False)
self.comb_iter_2_left = nn.AvgPool2d(3, stride=2, padding=1, count_include_pad=False)
self.comb_iter_2_right = BranchSeparables(84, 84, 5, 2, 2, bias=False)
self.comb_iter_3_right = nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False)
self.comb_iter_4_left = BranchSeparables(84, 84, 3, 1, 1, bias=False)
self.comb_iter_4_right = nn.MaxPool2d(3, stride=2, padding=1)
def forward(self, x_conv0, x_stem_0):
x_left = self.conv_1x1(x_stem_0)
x_relu = self.relu(x_conv0)
# path 1
x_path1 = self.path_1(x_relu)
# path 2
x_path2 = self.path_2.pad(x_relu)
x_path2 = x_path2[:, :, 1:, 1:]
x_path2 = self.path_2.avgpool(x_path2)
x_path2 = self.path_2.conv(x_path2)
# final path
x_right = self.final_path_bn(torch.cat([x_path1, x_path2], 1))
x_comb_iter_0_left = self.comb_iter_0_left(x_left)
x_comb_iter_0_right = self.comb_iter_0_right(x_right)
x_comb_iter_0 = x_comb_iter_0_left + x_comb_iter_0_right
x_comb_iter_1_left = self.comb_iter_1_left(x_left)
x_comb_iter_1_right = self.comb_iter_1_right(x_right)
x_comb_iter_1 = x_comb_iter_1_left + x_comb_iter_1_right
x_comb_iter_2_left = self.comb_iter_2_left(x_left)
x_comb_iter_2_right = self.comb_iter_2_right(x_right)
x_comb_iter_2 = x_comb_iter_2_left + x_comb_iter_2_right
x_comb_iter_3_right = self.comb_iter_3_right(x_comb_iter_0)
x_comb_iter_3 = x_comb_iter_3_right + x_comb_iter_1
x_comb_iter_4_left = self.comb_iter_4_left(x_comb_iter_0)
x_comb_iter_4_right = self.comb_iter_4_right(x_left)
x_comb_iter_4 = x_comb_iter_4_left + x_comb_iter_4_right
x_out = torch.cat([x_comb_iter_1, x_comb_iter_2, x_comb_iter_3, x_comb_iter_4], 1)
return x_out
class FirstCell(nn.Module):
def __init__(self, in_channels_left, out_channels_left, in_channels_right, out_channels_right):
super(FirstCell, self).__init__()
self.conv_1x1 = nn.Sequential()
self.conv_1x1.add_module('relu', nn.ReLU())
self.conv_1x1.add_module('conv', nn.Conv2d(in_channels_right, out_channels_right, 1, stride=1, bias=False))
self.conv_1x1.add_module('bn', nn.BatchNorm2d(out_channels_right, eps=0.001, momentum=0.1, affine=True))
self.relu = nn.ReLU()
self.path_1 = nn.Sequential()
self.path_1.add_module('avgpool', nn.AvgPool2d(1, stride=2, count_include_pad=False))
self.path_1.add_module('conv', nn.Conv2d(in_channels_left, out_channels_left, 1, stride=1, bias=False))
self.path_2 = nn.ModuleList()
self.path_2.add_module('pad', nn.ZeroPad2d((0, 1, 0, 1)))
self.path_2.add_module('avgpool', nn.AvgPool2d(1, stride=2, count_include_pad=False))
self.path_2.add_module('conv', nn.Conv2d(in_channels_left, out_channels_left, 1, stride=1, bias=False))
self.final_path_bn = nn.BatchNorm2d(out_channels_left * 2, eps=0.001, momentum=0.1, affine=True)
self.comb_iter_0_left = BranchSeparables(out_channels_right, out_channels_right, 5, 1, 2, bias=False)
self.comb_iter_0_right = BranchSeparables(out_channels_right, out_channels_right, 3, 1, 1, bias=False)
self.comb_iter_1_left = BranchSeparables(out_channels_right, out_channels_right, 5, 1, 2, bias=False)
self.comb_iter_1_right = BranchSeparables(out_channels_right, out_channels_right, 3, 1, 1, bias=False)
self.comb_iter_2_left = nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False)
self.comb_iter_3_left = nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False)
self.comb_iter_3_right = nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False)
self.comb_iter_4_left = BranchSeparables(out_channels_right, out_channels_right, 3, 1, 1, bias=False)
def forward(self, x, x_prev):
x_relu = self.relu(x_prev)
# path 1
x_path1 = self.path_1(x_relu)
# path 2
x_path2 = self.path_2.pad(x_relu)
x_path2 = x_path2[:, :, 1:, 1:]
x_path2 = self.path_2.avgpool(x_path2)
x_path2 = self.path_2.conv(x_path2)
# final path
x_left = self.final_path_bn(torch.cat([x_path1, x_path2], 1))
x_right = self.conv_1x1(x)
x_comb_iter_0_left = self.comb_iter_0_left(x_right)
x_comb_iter_0_right = self.comb_iter_0_right(x_left)
x_comb_iter_0 = x_comb_iter_0_left + x_comb_iter_0_right
x_comb_iter_1_left = self.comb_iter_1_left(x_left)
x_comb_iter_1_right = self.comb_iter_1_right(x_left)
x_comb_iter_1 = x_comb_iter_1_left + x_comb_iter_1_right
x_comb_iter_2_left = self.comb_iter_2_left(x_right)
x_comb_iter_2 = x_comb_iter_2_left + x_left
x_comb_iter_3_left = self.comb_iter_3_left(x_left)
x_comb_iter_3_right = self.comb_iter_3_right(x_left)
x_comb_iter_3 = x_comb_iter_3_left + x_comb_iter_3_right
x_comb_iter_4_left = self.comb_iter_4_left(x_right)
x_comb_iter_4 = x_comb_iter_4_left + x_right
x_out = torch.cat([x_left, x_comb_iter_0, x_comb_iter_1, x_comb_iter_2, x_comb_iter_3, x_comb_iter_4], 1)
return x_out
class NormalCell(nn.Module):
def __init__(self, in_channels_left, out_channels_left, in_channels_right, out_channels_right):
super(NormalCell, self).__init__()
self.conv_prev_1x1 = nn.Sequential()
self.conv_prev_1x1.add_module('relu', nn.ReLU())
self.conv_prev_1x1.add_module('conv', nn.Conv2d(in_channels_left, out_channels_left, 1, stride=1, bias=False))
self.conv_prev_1x1.add_module('bn', nn.BatchNorm2d(out_channels_left, eps=0.001, momentum=0.1, affine=True))
self.conv_1x1 = nn.Sequential()
self.conv_1x1.add_module('relu', nn.ReLU())
self.conv_1x1.add_module('conv', nn.Conv2d(in_channels_right, out_channels_right, 1, stride=1, bias=False))
self.conv_1x1.add_module('bn', nn.BatchNorm2d(out_channels_right, eps=0.001, momentum=0.1, affine=True))
self.comb_iter_0_left = BranchSeparables(out_channels_right, out_channels_right, 5, 1, 2, bias=False)
self.comb_iter_0_right = BranchSeparables(out_channels_left, out_channels_left, 3, 1, 1, bias=False)
self.comb_iter_1_left = BranchSeparables(out_channels_left, out_channels_left, 5, 1, 2, bias=False)
self.comb_iter_1_right = BranchSeparables(out_channels_left, out_channels_left, 3, 1, 1, bias=False)
self.comb_iter_2_left = nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False)
self.comb_iter_3_left = nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False)
self.comb_iter_3_right = nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False)
self.comb_iter_4_left = BranchSeparables(out_channels_right, out_channels_right, 3, 1, 1, bias=False)
def forward(self, x, x_prev):
x_left = self.conv_prev_1x1(x_prev)
x_right = self.conv_1x1(x)
x_comb_iter_0_left = self.comb_iter_0_left(x_right)
x_comb_iter_0_right = self.comb_iter_0_right(x_left)
x_comb_iter_0 = x_comb_iter_0_left + x_comb_iter_0_right
x_comb_iter_1_left = self.comb_iter_1_left(x_left)
x_comb_iter_1_right = self.comb_iter_1_right(x_left)
x_comb_iter_1 = x_comb_iter_1_left + x_comb_iter_1_right
x_comb_iter_2_left = self.comb_iter_2_left(x_right)
x_comb_iter_2 = x_comb_iter_2_left + x_left
x_comb_iter_3_left = self.comb_iter_3_left(x_left)
x_comb_iter_3_right = self.comb_iter_3_right(x_left)
x_comb_iter_3 = x_comb_iter_3_left + x_comb_iter_3_right
x_comb_iter_4_left = self.comb_iter_4_left(x_right)
x_comb_iter_4 = x_comb_iter_4_left + x_right
x_out = torch.cat([x_left, x_comb_iter_0, x_comb_iter_1, x_comb_iter_2, x_comb_iter_3, x_comb_iter_4], 1)
return x_out
class ReductionCell0(nn.Module):
def __init__(self, in_channels_left, out_channels_left, in_channels_right, out_channels_right):
super(ReductionCell0, self).__init__()
self.conv_prev_1x1 = nn.Sequential()
self.conv_prev_1x1.add_module('relu', nn.ReLU())
self.conv_prev_1x1.add_module('conv', nn.Conv2d(in_channels_left, out_channels_left, 1, stride=1, bias=False))
self.conv_prev_1x1.add_module('bn', nn.BatchNorm2d(out_channels_left, eps=0.001, momentum=0.1, affine=True))
self.conv_1x1 = nn.Sequential()
self.conv_1x1.add_module('relu', nn.ReLU())
self.conv_1x1.add_module('conv', nn.Conv2d(in_channels_right, out_channels_right, 1, stride=1, bias=False))
self.conv_1x1.add_module('bn', nn.BatchNorm2d(out_channels_right, eps=0.001, momentum=0.1, affine=True))
self.comb_iter_0_left = BranchSeparablesReduction(out_channels_right, out_channels_right, 5, 2, 2, bias=False)
self.comb_iter_0_right = BranchSeparablesReduction(out_channels_right, out_channels_right, 7, 2, 3, bias=False)
self.comb_iter_1_left = MaxPoolPad()
self.comb_iter_1_right = BranchSeparablesReduction(out_channels_right, out_channels_right, 7, 2, 3, bias=False)
self.comb_iter_2_left = AvgPoolPad()
self.comb_iter_2_right = BranchSeparablesReduction(out_channels_right, out_channels_right, 5, 2, 2, bias=False)
self.comb_iter_3_right = nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False)
self.comb_iter_4_left = BranchSeparablesReduction(out_channels_right, out_channels_right, 3, 1, 1, bias=False)
self.comb_iter_4_right = MaxPoolPad()
def forward(self, x, x_prev):
x_left = self.conv_prev_1x1(x_prev)
x_right = self.conv_1x1(x)
x_comb_iter_0_left = self.comb_iter_0_left(x_right)
x_comb_iter_0_right = self.comb_iter_0_right(x_left)
x_comb_iter_0 = x_comb_iter_0_left + x_comb_iter_0_right
x_comb_iter_1_left = self.comb_iter_1_left(x_right)
x_comb_iter_1_right = self.comb_iter_1_right(x_left)
x_comb_iter_1 = x_comb_iter_1_left + x_comb_iter_1_right
x_comb_iter_2_left = self.comb_iter_2_left(x_right)
x_comb_iter_2_right = self.comb_iter_2_right(x_left)
x_comb_iter_2 = x_comb_iter_2_left + x_comb_iter_2_right
x_comb_iter_3_right = self.comb_iter_3_right(x_comb_iter_0)
x_comb_iter_3 = x_comb_iter_3_right + x_comb_iter_1
x_comb_iter_4_left = self.comb_iter_4_left(x_comb_iter_0)
x_comb_iter_4_right = self.comb_iter_4_right(x_right)
x_comb_iter_4 = x_comb_iter_4_left + x_comb_iter_4_right
x_out = torch.cat([x_comb_iter_1, x_comb_iter_2, x_comb_iter_3, x_comb_iter_4], 1)
return x_out
class ReductionCell1(nn.Module):
def __init__(self, in_channels_left, out_channels_left, in_channels_right, out_channels_right):
super(ReductionCell1, self).__init__()
self.conv_prev_1x1 = nn.Sequential()
self.conv_prev_1x1.add_module('relu', nn.ReLU())
self.conv_prev_1x1.add_module('conv', nn.Conv2d(in_channels_left, out_channels_left, 1, stride=1, bias=False))
self.conv_prev_1x1.add_module('bn', nn.BatchNorm2d(out_channels_left, eps=0.001, momentum=0.1, affine=True))
self.conv_1x1 = nn.Sequential()
self.conv_1x1.add_module('relu', nn.ReLU())
self.conv_1x1.add_module('conv', nn.Conv2d(in_channels_right, out_channels_right, 1, stride=1, bias=False))
self.conv_1x1.add_module('bn', nn.BatchNorm2d(out_channels_right, eps=0.001, momentum=0.1, affine=True))
self.comb_iter_0_left = BranchSeparables(out_channels_right, out_channels_right, 5, 2, 2, bias=False)
self.comb_iter_0_right = BranchSeparables(out_channels_right, out_channels_right, 7, 2, 3, bias=False)
self.comb_iter_1_left = nn.MaxPool2d(3, stride=2, padding=1)
self.comb_iter_1_right = BranchSeparables(out_channels_right, out_channels_right, 7, 2, 3, bias=False)
self.comb_iter_2_left = nn.AvgPool2d(3, stride=2, padding=1, count_include_pad=False)
self.comb_iter_2_right = BranchSeparables(out_channels_right, out_channels_right, 5, 2, 2, bias=False)
self.comb_iter_3_right = nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False)
self.comb_iter_4_left = BranchSeparables(out_channels_right, out_channels_right, 3, 1, 1, bias=False)
self.comb_iter_4_right = nn.MaxPool2d(3, stride=2, padding=1)
def forward(self, x, x_prev):
x_left = self.conv_prev_1x1(x_prev)
x_right = self.conv_1x1(x)
x_comb_iter_0_left = self.comb_iter_0_left(x_right)
x_comb_iter_0_right = self.comb_iter_0_right(x_left)
x_comb_iter_0 = x_comb_iter_0_left + x_comb_iter_0_right
x_comb_iter_1_left = self.comb_iter_1_left(x_right)
x_comb_iter_1_right = self.comb_iter_1_right(x_left)
x_comb_iter_1 = x_comb_iter_1_left + x_comb_iter_1_right
x_comb_iter_2_left = self.comb_iter_2_left(x_right)
x_comb_iter_2_right = self.comb_iter_2_right(x_left)
x_comb_iter_2 = x_comb_iter_2_left + x_comb_iter_2_right
x_comb_iter_3_right = self.comb_iter_3_right(x_comb_iter_0)
x_comb_iter_3 = x_comb_iter_3_right + x_comb_iter_1
x_comb_iter_4_left = self.comb_iter_4_left(x_comb_iter_0)
x_comb_iter_4_right = self.comb_iter_4_right(x_right)
x_comb_iter_4 = x_comb_iter_4_left + x_comb_iter_4_right
x_out = torch.cat([x_comb_iter_1, x_comb_iter_2, x_comb_iter_3, x_comb_iter_4], 1)
return x_out
class NASNetALarge(nn.Module):
def __init__(self, use_classifier=False, num_classes=1001):
super(NASNetALarge, self).__init__()
self.use_classifier,self.num_classes = use_classifier,num_classes
self.conv0 = nn.Sequential()
self.conv0.add_module('conv', nn.Conv2d(in_channels=3, out_channels=96, kernel_size=3, padding=0, stride=2,
bias=False))
self.conv0.add_module('bn', nn.BatchNorm2d(96, eps=0.001, momentum=0.1, affine=True))
self.cell_stem_0 = CellStem0()
self.cell_stem_1 = CellStem1()
self.cell_0 = FirstCell(in_channels_left=168, out_channels_left=84,
in_channels_right=336, out_channels_right=168)
self.cell_1 = NormalCell(in_channels_left=336, out_channels_left=168,
in_channels_right=1008, out_channels_right=168)
self.cell_2 = NormalCell(in_channels_left=1008, out_channels_left=168,
in_channels_right=1008, out_channels_right=168)
self.cell_3 = NormalCell(in_channels_left=1008, out_channels_left=168,
in_channels_right=1008, out_channels_right=168)
self.cell_4 = NormalCell(in_channels_left=1008, out_channels_left=168,
in_channels_right=1008, out_channels_right=168)
self.cell_5 = NormalCell(in_channels_left=1008, out_channels_left=168,
in_channels_right=1008, out_channels_right=168)
self.reduction_cell_0 = ReductionCell0(in_channels_left=1008, out_channels_left=336,
in_channels_right=1008, out_channels_right=336)
self.cell_6 = FirstCell(in_channels_left=1008, out_channels_left=168,
in_channels_right=1344, out_channels_right=336)
self.cell_7 = NormalCell(in_channels_left=1344, out_channels_left=336,
in_channels_right=2016, out_channels_right=336)
self.cell_8 = NormalCell(in_channels_left=2016, out_channels_left=336,
in_channels_right=2016, out_channels_right=336)
self.cell_9 = NormalCell(in_channels_left=2016, out_channels_left=336,
in_channels_right=2016, out_channels_right=336)
self.cell_10 = NormalCell(in_channels_left=2016, out_channels_left=336,
in_channels_right=2016, out_channels_right=336)
self.cell_11 = NormalCell(in_channels_left=2016, out_channels_left=336,
in_channels_right=2016, out_channels_right=336)
self.reduction_cell_1 = ReductionCell1(in_channels_left=2016, out_channels_left=672,
in_channels_right=2016, out_channels_right=672)
self.cell_12 = FirstCell(in_channels_left=2016, out_channels_left=336,
in_channels_right=2688, out_channels_right=672)
self.cell_13 = NormalCell(in_channels_left=2688, out_channels_left=672,
in_channels_right=4032, out_channels_right=672)
self.cell_14 = NormalCell(in_channels_left=4032, out_channels_left=672,
in_channels_right=4032, out_channels_right=672)
self.cell_15 = NormalCell(in_channels_left=4032, out_channels_left=672,
in_channels_right=4032, out_channels_right=672)
self.cell_16 = NormalCell(in_channels_left=4032, out_channels_left=672,
in_channels_right=4032, out_channels_right=672)
self.cell_17 = NormalCell(in_channels_left=4032, out_channels_left=672,
in_channels_right=4032, out_channels_right=672)
self.relu = nn.ReLU()
self.dropout = nn.Dropout()
self.last_linear = nn.Linear(4032, self.num_classes)
def features(self, x):
x_conv0 = self.conv0(x)
x_stem_0 = self.cell_stem_0(x_conv0)
x_stem_1 = self.cell_stem_1(x_conv0, x_stem_0)
x_cell_0 = self.cell_0(x_stem_1, x_stem_0)
x_cell_1 = self.cell_1(x_cell_0, x_stem_1)
x_cell_2 = self.cell_2(x_cell_1, x_cell_0)
x_cell_3 = self.cell_3(x_cell_2, x_cell_1)
x_cell_4 = self.cell_4(x_cell_3, x_cell_2)
x_cell_5 = self.cell_5(x_cell_4, x_cell_3)
x_reduction_cell_0 = self.reduction_cell_0(x_cell_5, x_cell_4)
x_cell_6 = self.cell_6(x_reduction_cell_0, x_cell_4)
x_cell_7 = self.cell_7(x_cell_6, x_reduction_cell_0)
x_cell_8 = self.cell_8(x_cell_7, x_cell_6)
x_cell_9 = self.cell_9(x_cell_8, x_cell_7)
x_cell_10 = self.cell_10(x_cell_9, x_cell_8)
x_cell_11 = self.cell_11(x_cell_10, x_cell_9)
x_reduction_cell_1 = self.reduction_cell_1(x_cell_11, x_cell_10)
x_cell_12 = self.cell_12(x_reduction_cell_1, x_cell_10)
x_cell_13 = self.cell_13(x_cell_12, x_reduction_cell_1)
x_cell_14 = self.cell_14(x_cell_13, x_cell_12)
x_cell_15 = self.cell_15(x_cell_14, x_cell_13)
x_cell_16 = self.cell_16(x_cell_15, x_cell_14)
x_cell_17 = self.cell_17(x_cell_16, x_cell_15)
return self.relu(x_cell_17)
def classifier(self, x):
x = F.adaptive_max_pool2d(x, 1)
x = x.view(x.size(0), -1)
x = self.dropout(x)
return F.log_softmax(self.linear(x))
def forward(self, x):
x = self.features(x)
if self.use_classifier: x = self.classifier(x)
return x
def nasnetalarge(num_classes=1000, pretrained='imagenet'):
r"""NASNetALarge model architecture from the
`"NASNet" <https://arxiv.org/abs/1707.07012>`_ paper.
"""
if pretrained:
settings = pretrained_settings['nasnetalarge'][pretrained]
assert num_classes == settings['num_classes'], \
"num_classes should be {}, but is {}".format(settings['num_classes'], num_classes)
# both 'imagenet'&'imagenet+background' are loaded from same parameters
model = NASNetALarge(num_classes=1001)
model.load_state_dict(model_zoo.load_url(settings['url']))
if pretrained == 'imagenet':
new_last_linear = nn.Linear(model.last_linear.in_features, 1000)
new_last_linear.weight.data = model.last_linear.weight.data[1:]
new_last_linear.bias.data = model.last_linear.bias.data[1:]
model.last_linear = new_last_linear
model.input_space = settings['input_space']
model.input_size = settings['input_size']
model.input_range = settings['input_range']
model.mean = settings['mean']
model.std = settings['std']
else:
model = NASNetALarge(num_classes=num_classes)
return model
+217
View File
@@ -0,0 +1,217 @@
import torch.nn as nn
import math
import torch.utils.model_zoo as model_zoo
from ..layers import *
__all__ = ['vgg_resnet50']
model_urls = {
'vgg_resnet50': 'https://download.pytorch.org/models/vggresnet.pth',
}
def conv(ni, nf, ks=3, stride=1):
return nn.Conv2d(ni, nf, kernel_size=ks, stride=stride, padding=ks//2, bias=False)
def bn1(planes):
m = nn.BatchNorm1d(planes)
m.weight.data.fill_(1)
m.bias.data.zero_()
return m
def bn(planes, init_zero=False):
m = nn.BatchNorm2d(planes)
m.weight.data.fill_(0 if init_zero else 1)
m.bias.data.zero_()
return m
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, inplanes, planes, stride=1, downsample=None):
super().__init__()
self.conv1 = conv(inplanes, planes, stride=stride)
self.bn1 = bn(planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = conv(planes, planes)
self.bn2 = bn(planes)
self.downsample = downsample
self.stride = stride
def forward(self, x):
residual = x
if self.downsample is not None: residual = self.downsample(x)
out = self.conv1(x)
out = self.relu(out)
out = self.bn1(out)
out = self.conv2(out)
out = residual + out
out = self.relu(out)
out = self.bn2(out)
return out
class BottleneckFinal(nn.Module):
expansion = 4
def __init__(self, inplanes, planes, stride=1, downsample=None):
super().__init__()
self.conv1 = conv(inplanes, planes, ks=1)
self.bn1 = bn(planes)
self.conv2 = conv(planes, planes, stride=stride)
self.bn2 = bn(planes)
self.conv3 = conv(planes, planes*4, ks=1)
self.bn3 = bn(planes * 4)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride
def forward(self, x):
residual = x
if self.downsample is not None: residual = self.downsample(x)
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = residual + out
out = self.bn3(out)
out = self.relu(out)
return out
class BottleneckZero(nn.Module):
expansion = 4
def __init__(self, inplanes, planes, stride=1, downsample=None):
super().__init__()
self.conv1 = conv(inplanes, planes, ks=1)
self.bn1 = bn(planes)
self.conv2 = conv(planes, planes, stride=stride)
self.bn2 = bn(planes)
self.conv3 = conv(planes, planes*4, ks=1)
self.bn3 = bn(planes * 4, init_zero=True)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride
def forward(self, x):
residual = x
if self.downsample is not None: residual = self.downsample(x)
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
out = residual + out
out = self.relu(out)
return out
class Bottleneck(nn.Module):
expansion = 4
def __init__(self, inplanes, planes, stride=1, downsample=None):
super().__init__()
self.conv1 = conv(inplanes, planes, ks=1)
self.bn1 = bn(planes)
self.conv2 = conv(planes, planes, stride=stride)
self.bn2 = bn(planes)
self.conv3 = conv(planes, planes*4, ks=1)
self.bn3 = bn(planes * 4)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride
def forward(self, x):
residual = x
if self.downsample is not None: residual = self.downsample(x)
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
out = residual + out
out = self.relu(out)
return out
class ResNet(nn.Module):
def __init__(self, block, layers, num_classes=1000, k=1, vgg_head=False):
super().__init__()
self.inplanes = 64
features = [conv(3, 64, ks=7, stride=2)
, bn(64) , nn.ReLU(inplace=True) , nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
, self._make_layer(block, int(64*k), layers[0])
, self._make_layer(block, int(128*k), layers[1], stride=2)
, self._make_layer(block, int(256*k), layers[2], stride=2)
, self._make_layer(block, int(512*k), layers[3], stride=2)]
out_sz = int(512*k) * block.expansion
if vgg_head:
features += [nn.AdaptiveAvgPool2d(3), Flatten()
, nn.Linear(out_sz*3*3, 4096), nn.ReLU(inplace=True), bn1(4096), nn.Dropout(0.25)
, nn.Linear(4096, 4096), nn.ReLU(inplace=True), bn1(4096), nn.Dropout(0.25)
, nn.Linear(4096, num_classes)]
else: features += [nn.AdaptiveAvgPool2d(1), Flatten(), nn.Linear(out_sz, num_classes)]
self.features = nn.Sequential(*features)
for m in self.modules():
if isinstance(m, nn.Conv2d):
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
m.weight.data.normal_(0, math.sqrt(2. / n))
def _make_layer(self, block, planes, blocks, stride=1):
downsample = None
if stride != 1 or self.inplanes != planes * block.expansion:
downsample = nn.Sequential(
conv(self.inplanes, planes*block.expansion, ks=1, stride=stride),
bn(planes * block.expansion),
)
layers = []
layers.append(block(self.inplanes, planes, stride, downsample))
self.inplanes = planes * block.expansion
for i in range(1, blocks): layers.append(block(self.inplanes, planes))
return nn.Sequential(*layers)
def forward(self, x): return self.features(x)
def bnf_resnet50 (): return ResNet(BottleneckFinal, [3, 4, 6, 3])
def bnz_resnet50 (): return ResNet(BottleneckZero, [3, 4, 6, 3])
def w5_resnet50 (): return ResNet(Bottleneck, [2, 3, 3, 2], k=1.5)
def w25_resnet50(): return ResNet(Bottleneck, [3, 4, 4, 3], k=1.25)
def w125_resnet50(): return ResNet(Bottleneck, [3, 4, 6, 3], k=1.125)
def vgg_resnet34(): return ResNet(BasicBlock, [3, 4, 6, 3], vgg_head=True)
def vgg_resnet50(pretrained=False):
model = ResNet(Bottleneck, [3, 4, 6, 3], vgg_head=True)
if pretrained: model.load_state_dict(torch.load('/home/jhoward/.torch/models/vgg_resnet50.pth'))
return model
+686
View File
@@ -0,0 +1,686 @@
import torch
import torch.nn as nn
from torch.autograd import Variable
from functools import reduce
class LambdaBase(nn.Sequential):
def __init__(self, fn, *args):
super(LambdaBase, self).__init__(*args)
self.lambda_func = fn
def forward_prepare(self, input):
output = []
for module in self._modules.values():
output.append(module(input))
return output if output else input
class Lambda(LambdaBase):
def forward(self, input):
return self.lambda_func(self.forward_prepare(input))
class LambdaMap(LambdaBase):
def forward(self, input):
return list(map(self.lambda_func,self.forward_prepare(input)))
class LambdaReduce(LambdaBase):
def forward(self, input):
return reduce(self.lambda_func,self.forward_prepare(input))
def resnext_101_32x4d(): return nn.Sequential( # Sequential,
nn.Conv2d(3,64,(7, 7),(2, 2),(3, 3),1,1,bias=False),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d((3, 3),(2, 2),(1, 1)),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(64,128,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.Conv2d(128,128,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
),
nn.Conv2d(128,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
nn.Sequential( # Sequential,
nn.Conv2d(64,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(256,128,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.Conv2d(128,128,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
),
nn.Conv2d(128,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(256,128,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.Conv2d(128,128,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
),
nn.Conv2d(128,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(256,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(2, 2),(1, 1),1,32,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
),
nn.Conv2d(256,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
nn.Sequential( # Sequential,
nn.Conv2d(256,512,(1, 1),(2, 2),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(512,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
),
nn.Conv2d(256,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(512,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
),
nn.Conv2d(256,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(512,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
),
nn.Conv2d(256,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(512,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(2, 2),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
nn.Sequential( # Sequential,
nn.Conv2d(512,1024,(1, 1),(2, 2),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(2, 2),(1, 1),1,32,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
nn.Sequential( # Sequential,
nn.Conv2d(1024,2048,(1, 1),(2, 2),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(2048,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(2048,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.AvgPool2d((7, 7),(1, 1)),
Lambda(lambda x: x.view(x.size(0),-1)), # View,
nn.Sequential(Lambda(lambda x: x.view(1,-1) if 1==len(x.size()) else x ),nn.Linear(2048,1000)), # Linear,
)
+686
View File
@@ -0,0 +1,686 @@
import torch
import torch.nn as nn
from torch.autograd import Variable
from functools import reduce
class LambdaBase(nn.Sequential):
def __init__(self, fn, *args):
super(LambdaBase, self).__init__(*args)
self.lambda_func = fn
def forward_prepare(self, input):
output = []
for module in self._modules.values():
output.append(module(input))
return output if output else input
class Lambda(LambdaBase):
def forward(self, input):
return self.lambda_func(self.forward_prepare(input))
class LambdaMap(LambdaBase):
def forward(self, input):
return list(map(self.lambda_func,self.forward_prepare(input)))
class LambdaReduce(LambdaBase):
def forward(self, input):
return reduce(self.lambda_func,self.forward_prepare(input))
def resnext_101_64x4d(): return nn.Sequential( # Sequential,
nn.Conv2d(3,64,(7, 7),(2, 2),(3, 3),1,1,bias=False),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d((3, 3),(2, 2),(1, 1)),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(64,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
),
nn.Conv2d(256,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
nn.Sequential( # Sequential,
nn.Conv2d(64,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(256,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
),
nn.Conv2d(256,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(256,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
),
nn.Conv2d(256,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(256,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(2, 2),(1, 1),1,64,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
nn.Sequential( # Sequential,
nn.Conv2d(256,512,(1, 1),(2, 2),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(512,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(512,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(512,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(2, 2),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
nn.Sequential( # Sequential,
nn.Conv2d(512,1024,(1, 1),(2, 2),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
nn.ReLU(),
nn.Conv2d(2048,2048,(3, 3),(2, 2),(1, 1),1,64,bias=False),
nn.BatchNorm2d(2048),
nn.ReLU(),
),
nn.Conv2d(2048,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
nn.Sequential( # Sequential,
nn.Conv2d(1024,2048,(1, 1),(2, 2),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(2048,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
nn.ReLU(),
nn.Conv2d(2048,2048,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(2048),
nn.ReLU(),
),
nn.Conv2d(2048,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(2048,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
nn.ReLU(),
nn.Conv2d(2048,2048,(3, 3),(1, 1),(1, 1),1,64,bias=False),
nn.BatchNorm2d(2048),
nn.ReLU(),
),
nn.Conv2d(2048,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.AvgPool2d((7, 7),(1, 1)),
Lambda(lambda x: x.view(x.size(0),-1)), # View,
nn.Sequential(Lambda(lambda x: x.view(1,-1) if 1==len(x.size()) else x ),nn.Linear(2048,1000)), # Linear,
)
+363
View File
@@ -0,0 +1,363 @@
import torch
import torch.nn as nn
from torch.autograd import Variable
from functools import reduce
class LambdaBase(nn.Sequential):
def __init__(self, fn, *args):
super(LambdaBase, self).__init__(*args)
self.lambda_func = fn
def forward_prepare(self, input):
output = []
for module in self._modules.values():
output.append(module(input))
return output if output else input
class Lambda(LambdaBase):
def forward(self, input):
return self.lambda_func(self.forward_prepare(input))
class LambdaMap(LambdaBase):
def forward(self, input):
return list(map(self.lambda_func,self.forward_prepare(input)))
class LambdaReduce(LambdaBase):
def forward(self, input):
return reduce(self.lambda_func,self.forward_prepare(input))
def resnext_50_32x4d(): return nn.Sequential( # Sequential,
nn.Conv2d(3,64,(7, 7),(2, 2),(3, 3),1,1,bias=False),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d((3, 3),(2, 2),(1, 1)),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(64,128,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.Conv2d(128,128,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
),
nn.Conv2d(128,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
nn.Sequential( # Sequential,
nn.Conv2d(64,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(256,128,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.Conv2d(128,128,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
),
nn.Conv2d(128,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(256,128,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.Conv2d(128,128,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
),
nn.Conv2d(128,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(256,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(2, 2),(1, 1),1,32,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
),
nn.Conv2d(256,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
nn.Sequential( # Sequential,
nn.Conv2d(256,512,(1, 1),(2, 2),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(512,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
),
nn.Conv2d(256,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(512,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
),
nn.Conv2d(256,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(512,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
),
nn.Conv2d(256,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(512,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(2, 2),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
nn.Sequential( # Sequential,
nn.Conv2d(512,1024,(1, 1),(2, 2),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(2, 2),(1, 1),1,32,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
nn.Sequential( # Sequential,
nn.Conv2d(1024,2048,(1, 1),(2, 2),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(2048,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
nn.Conv2d(2048,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,32,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
),
nn.Conv2d(1024,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.AdaptiveAvgPool2d(1),
Lambda(lambda x: x.view(x.size(0),-1)), # View,
nn.Sequential(Lambda(lambda x: x.view(1,-1) if 1==len(x.size()) else x ),nn.Linear(2048,1000)), # Linear,
)
+445
View File
@@ -0,0 +1,445 @@
'''
File name: senet.py
Squeeze-and-Excitation Networks (SeNet) implementation for fast.ai/pytorch with pretrained model
Credit https://github.com/hujie-frank/SENet
SENet is the winner of ImageNet-2017 (https://arxiv.org/pdf/1709.01507.pdf).
'''
from collections import OrderedDict
import math
import torch.utils.model_zoo as model_zoo
from ..layers import *
import torch.nn as nn
from torch.utils import model_zoo
__all__ = ['SENet', 'senet154', 'se_resnet50', 'se_resnet101', 'se_resnet152',
'se_resnext50_32x4d', 'se_resnext101_32x4d']
pretrained_settings = {
'senet154': {
'imagenet': {
'url': 'http://data.lip6.fr/cadene/pretrainedmodels/senet154-c7b49a05.pth',
'input_space': 'RGB',
'input_size': [3, 224, 224],
'input_range': [0, 1],
'mean': [0.485, 0.456, 0.406],
'std': [0.229, 0.224, 0.225],
'num_classes': 1000
}
},
'se_resnet50': {
'imagenet': {
'url': 'http://data.lip6.fr/cadene/pretrainedmodels/se_resnet50-ce0d4300.pth',
'input_space': 'RGB',
'input_size': [3, 224, 224],
'input_range': [0, 1],
'mean': [0.485, 0.456, 0.406],
'std': [0.229, 0.224, 0.225],
'num_classes': 1000
}
},
'se_resnet101': {
'imagenet': {
'url': 'http://data.lip6.fr/cadene/pretrainedmodels/se_resnet101-7e38fcc6.pth',
'input_space': 'RGB',
'input_size': [3, 224, 224],
'input_range': [0, 1],
'mean': [0.485, 0.456, 0.406],
'std': [0.229, 0.224, 0.225],
'num_classes': 1000
}
},
'se_resnet152': {
'imagenet': {
'url': 'http://data.lip6.fr/cadene/pretrainedmodels/se_resnet152-d17c99b7.pth',
'input_space': 'RGB',
'input_size': [3, 224, 224],
'input_range': [0, 1],
'mean': [0.485, 0.456, 0.406],
'std': [0.229, 0.224, 0.225],
'num_classes': 1000
}
},
'se_resnext50_32x4d': {
'imagenet': {
'url': 'http://data.lip6.fr/cadene/pretrainedmodels/se_resnext50_32x4d-a260b3a4.pth',
'input_space': 'RGB',
'input_size': [3, 224, 224],
'input_range': [0, 1],
'mean': [0.485, 0.456, 0.406],
'std': [0.229, 0.224, 0.225],
'num_classes': 1000
}
},
'se_resnext101_32x4d': {
'imagenet': {
'url': 'http://data.lip6.fr/cadene/pretrainedmodels/se_resnext101_32x4d-3b2fe3d8.pth',
'input_space': 'RGB',
'input_size': [3, 224, 224],
'input_range': [0, 1],
'mean': [0.485, 0.456, 0.406],
'std': [0.229, 0.224, 0.225],
'num_classes': 1000
}
},
}
class SEModule(nn.Module):
def __init__(self, channels, reduction):
super(SEModule, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.fc1 = nn.Conv2d(channels, channels // reduction, kernel_size=1,
padding=0)
self.relu = nn.ReLU(inplace=True)
self.fc2 = nn.Conv2d(channels // reduction, channels, kernel_size=1,
padding=0)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
module_input = x
x = self.avg_pool(x)
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
x = self.sigmoid(x)
return module_input * x
class Bottleneck(nn.Module):
"""
Base class for bottlenecks that implements `forward()` method.
"""
def forward(self, x):
residual = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
if self.downsample is not None:
residual = self.downsample(x)
out = self.se_module(out) + residual
out = self.relu(out)
return out
class SEBottleneck(Bottleneck):
"""
Bottleneck for SENet154.
"""
expansion = 4
def __init__(self, inplanes, planes, groups, reduction, stride=1,
downsample=None):
super(SEBottleneck, self).__init__()
self.conv1 = nn.Conv2d(inplanes, planes * 2, kernel_size=1, bias=False)
self.bn1 = nn.BatchNorm2d(planes * 2)
self.conv2 = nn.Conv2d(planes * 2, planes * 4, kernel_size=3,
stride=stride, padding=1, groups=groups,
bias=False)
self.bn2 = nn.BatchNorm2d(planes * 4)
self.conv3 = nn.Conv2d(planes * 4, planes * 4, kernel_size=1,
bias=False)
self.bn3 = nn.BatchNorm2d(planes * 4)
self.relu = nn.ReLU(inplace=True)
self.se_module = SEModule(planes * 4, reduction=reduction)
self.downsample = downsample
self.stride = stride
class SEResNetBottleneck(Bottleneck):
"""
ResNet bottleneck with a Squeeze-and-Excitation module. It follows Caffe
implementation and uses `stride=stride` in `conv1` and not in `conv2`
(the latter is used in the torchvision implementation of ResNet).
"""
expansion = 4
def __init__(self, inplanes, planes, groups, reduction, stride=1,
downsample=None):
super(SEResNetBottleneck, self).__init__()
self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False,
stride=stride)
self.bn1 = nn.BatchNorm2d(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, padding=1,
groups=groups, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
self.bn3 = nn.BatchNorm2d(planes * 4)
self.relu = nn.ReLU(inplace=True)
self.se_module = SEModule(planes * 4, reduction=reduction)
self.downsample = downsample
self.stride = stride
class SEResNeXtBottleneck(Bottleneck):
"""
ResNeXt bottleneck type C with a Squeeze-and-Excitation module.
"""
expansion = 4
def __init__(self, inplanes, planes, groups, reduction, stride=1,
downsample=None, base_width=4):
super(SEResNeXtBottleneck, self).__init__()
width = math.floor(planes * (base_width / 64)) * groups
self.conv1 = nn.Conv2d(inplanes, width, kernel_size=1, bias=False,
stride=1)
self.bn1 = nn.BatchNorm2d(width)
self.conv2 = nn.Conv2d(width, width, kernel_size=3, stride=stride,
padding=1, groups=groups, bias=False)
self.bn2 = nn.BatchNorm2d(width)
self.conv3 = nn.Conv2d(width, planes * 4, kernel_size=1, bias=False)
self.bn3 = nn.BatchNorm2d(planes * 4)
self.relu = nn.ReLU(inplace=True)
self.se_module = SEModule(planes * 4, reduction=reduction)
self.downsample = downsample
self.stride = stride
class SENet(nn.Module):
def __init__(self, block, layers, groups, reduction, dropout_p=0.2,
inplanes=128, input_3x3=True, downsample_kernel_size=3,
downsample_padding=1, num_classes=1000):
"""
Parameters
----------
block (nn.Module): Bottleneck class.
- For SENet154: SEBottleneck
- For SE-ResNet models: SEResNetBottleneck
- For SE-ResNeXt models: SEResNeXtBottleneck
layers (list of ints): Number of residual blocks for 4 layers of the
network (layer1...layer4).
groups (int): Number of groups for the 3x3 convolution in each
bottleneck block.
- For SENet154: 64
- For SE-ResNet models: 1
- For SE-ResNeXt models: 32
reduction (int): Reduction ratio for Squeeze-and-Excitation modules.
- For all models: 16
dropout_p (float or None): Drop probability for the Dropout layer.
If `None` the Dropout layer is not used.
- For SENet154: 0.2
- For SE-ResNet models: None
- For SE-ResNeXt models: None
inplanes (int): Number of input channels for layer1.
- For SENet154: 128
- For SE-ResNet models: 64
- For SE-ResNeXt models: 64
input_3x3 (bool): If `True`, use three 3x3 convolutions instead of
a single 7x7 convolution in layer0.
- For SENet154: True
- For SE-ResNet models: False
- For SE-ResNeXt models: False
downsample_kernel_size (int): Kernel size for downsampling convolutions
in layer2, layer3 and layer4.
- For SENet154: 3
- For SE-ResNet models: 1
- For SE-ResNeXt models: 1
downsample_padding (int): Padding for downsampling convolutions in
layer2, layer3 and layer4.
- For SENet154: 1
- For SE-ResNet models: 0
- For SE-ResNeXt models: 0
num_classes (int): Number of outputs in `last_linear` layer.
- For all models: 1000
"""
super(SENet, self).__init__()
self.inplanes = inplanes
if input_3x3:
layer0_modules = [
('conv1', nn.Conv2d(3, 64, 3, stride=2, padding=1,
bias=False)),
('bn1', nn.BatchNorm2d(64)),
('relu1', nn.ReLU(inplace=True)),
('conv2', nn.Conv2d(64, 64, 3, stride=1, padding=1,
bias=False)),
('bn2', nn.BatchNorm2d(64)),
('relu2', nn.ReLU(inplace=True)),
('conv3', nn.Conv2d(64, inplanes, 3, stride=1, padding=1,
bias=False)),
('bn3', nn.BatchNorm2d(inplanes)),
('relu3', nn.ReLU(inplace=True)),
]
else:
layer0_modules = [
('conv1', nn.Conv2d(3, inplanes, kernel_size=7, stride=2,
padding=3, bias=False)),
('bn1', nn.BatchNorm2d(inplanes)),
('relu1', nn.ReLU(inplace=True)),
]
# To preserve compatibility with Caffe weights `ceil_mode=True`
# is used instead of `padding=1`.
layer0_modules.append(('pool', nn.MaxPool2d(3, stride=2,
ceil_mode=True)))
self.layer0 = nn.Sequential(OrderedDict(layer0_modules))
self.layer1 = self._make_layer(
block,
planes=64,
blocks=layers[0],
groups=groups,
reduction=reduction,
downsample_kernel_size=1,
downsample_padding=0
)
self.layer2 = self._make_layer(
block,
planes=128,
blocks=layers[1],
stride=2,
groups=groups,
reduction=reduction,
downsample_kernel_size=downsample_kernel_size,
downsample_padding=downsample_padding
)
self.layer3 = self._make_layer(
block,
planes=256,
blocks=layers[2],
stride=2,
groups=groups,
reduction=reduction,
downsample_kernel_size=downsample_kernel_size,
downsample_padding=downsample_padding
)
self.layer4 = self._make_layer(
block,
planes=512,
blocks=layers[3],
stride=2,
groups=groups,
reduction=reduction,
downsample_kernel_size=downsample_kernel_size,
downsample_padding=downsample_padding
)
self.avg_pool = nn.AvgPool2d(7, stride=1)
self.dropout = nn.Dropout(dropout_p) if dropout_p is not None else None
self.last_linear = nn.Linear(512 * block.expansion, num_classes)
def _make_layer(self, block, planes, blocks, groups, reduction, stride=1,
downsample_kernel_size=1, downsample_padding=0):
downsample = None
if stride != 1 or self.inplanes != planes * block.expansion:
downsample = nn.Sequential(
nn.Conv2d(self.inplanes, planes * block.expansion,
kernel_size=downsample_kernel_size, stride=stride,
padding=downsample_padding, bias=False),
nn.BatchNorm2d(planes * block.expansion),
)
layers = []
layers.append(block(self.inplanes, planes, groups, reduction, stride,
downsample))
self.inplanes = planes * block.expansion
for i in range(1, blocks):
layers.append(block(self.inplanes, planes, groups, reduction))
return nn.Sequential(*layers)
def features(self, x):
x = self.layer0(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
return x
def logits(self, x):
x = self.avg_pool(x)
if self.dropout is not None:
x = self.dropout(x)
x = x.view(x.size(0), -1)
x = self.last_linear(x)
return x
def forward(self, x):
x = self.features(x)
x = self.logits(x)
return x
def initialize_pretrained_model(model, num_classes, settings):
assert num_classes == settings['num_classes'], \
'num_classes should be {}, but is {}'.format(
settings['num_classes'], num_classes)
model.load_state_dict(model_zoo.load_url(settings['url']))
model.input_space = settings['input_space']
model.input_size = settings['input_size']
model.input_range = settings['input_range']
model.mean = settings['mean']
model.std = settings['std']
def senet154(num_classes=1000, pretrained='imagenet'):
model = SENet(SEBottleneck, [3, 8, 36, 3], groups=64, reduction=16,
dropout_p=0.2, num_classes=num_classes)
if pretrained is not None:
settings = pretrained_settings['senet154'][pretrained]
initialize_pretrained_model(model, num_classes, settings)
return model
def se_resnet50(num_classes=1000, pretrained='imagenet'):
model = SENet(SEResNetBottleneck, [3, 4, 6, 3], groups=1, reduction=16,
dropout_p=None, inplanes=64, input_3x3=False,
downsample_kernel_size=1, downsample_padding=0,
num_classes=num_classes)
if pretrained is not None:
settings = pretrained_settings['se_resnet50'][pretrained]
initialize_pretrained_model(model, num_classes, settings)
return model
def se_resnet101(num_classes=1000, pretrained='imagenet'):
model = SENet(SEResNetBottleneck, [3, 4, 23, 3], groups=1, reduction=16,
dropout_p=None, inplanes=64, input_3x3=False,
downsample_kernel_size=1, downsample_padding=0,
num_classes=num_classes)
if pretrained is not None:
settings = pretrained_settings['se_resnet101'][pretrained]
initialize_pretrained_model(model, num_classes, settings)
return model
def se_resnet152(num_classes=1000, pretrained='imagenet'):
model = SENet(SEResNetBottleneck, [3, 8, 36, 3], groups=1, reduction=16,
dropout_p=None, inplanes=64, input_3x3=False,
downsample_kernel_size=1, downsample_padding=0,
num_classes=num_classes)
if pretrained is not None:
settings = pretrained_settings['se_resnet152'][pretrained]
initialize_pretrained_model(model, num_classes, settings)
return model
def se_resnext50_32x4d(num_classes=1000, pretrained='imagenet'):
model = SENet(SEResNeXtBottleneck, [3, 4, 6, 3], groups=32, reduction=16,
dropout_p=None, inplanes=64, input_3x3=False,
downsample_kernel_size=1, downsample_padding=0,
num_classes=num_classes)
if pretrained is not None:
settings = pretrained_settings['se_resnext50_32x4d'][pretrained]
initialize_pretrained_model(model, num_classes, settings)
return model
def se_resnext101_32x4d(num_classes=1000, pretrained='imagenet'):
model = SENet(SEResNeXtBottleneck, [3, 4, 23, 3], groups=32, reduction=16,
dropout_p=None, inplanes=64, input_3x3=False,
downsample_kernel_size=1, downsample_padding=0,
num_classes=num_classes)
if pretrained is not None:
settings = pretrained_settings['se_resnext101_32x4d'][pretrained]
initialize_pretrained_model(model, num_classes, settings)
return model
+128
View File
@@ -0,0 +1,128 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
import numpy as np
def get_sfs_idxs(sfs, last=True):
"""
Return the saved feature indexes that will be concatenated
Inputs:
sfs (list): saved features by hook function, in other words intermediate activations
last (bool): whether to concatenate only last different activation, or all from the encoder model
"""
if last:
feature_szs = [sfs_feats.features.size()[-1] for sfs_feats in sfs]
sfs_idxs = list(np.where(np.array(feature_szs[:-1]) != np.array(feature_szs[1:]))[0])
if feature_szs[0] != feature_szs[1]: sfs_idxs = [0] + sfs_idxs
else: sfs_idxs = list(range(len(sfs)))
return sfs_idxs
def conv_bn_relu(in_c, out_c, kernel_size, stride, padding):
return [
nn.Conv2d(in_c, out_c, kernel_size=kernel_size, stride=stride, padding=padding),
nn.ReLU(),
nn.BatchNorm2d(out_c)]
class UnetBlock(nn.Module):
#TODO: ADAPT KERNEL SIZE, STRIDE AND PADDING SO THAT ANY SIZE DECAY WILL BE SUPPORTED
def __init__(self, up_in_c, x_in_c):
super().__init__()
self.upconv = nn.ConvTranspose2d(up_in_c, up_in_c // 2, 2, 2) # H, W -> 2H, 2W
self.conv1 = nn.Conv2d(x_in_c + up_in_c // 2, (x_in_c + up_in_c // 2) // 2, 3, 1, 1)
self.conv2 = nn.Conv2d((x_in_c + up_in_c // 2) // 2, (x_in_c + up_in_c // 2) // 2, 3, 1, 1)
self.bn = nn.BatchNorm2d((x_in_c + up_in_c // 2) // 2)
def forward(self, up_in, x_in):
up_out = self.upconv(up_in)
cat_x = torch.cat([up_out, x_in], dim=1)
x = F.relu(self.conv1(cat_x))
x = F.relu(self.conv2(x))
return self.bn(x)
class SaveFeatures():
""" Extract pretrained activations"""
features=None
def __init__(self, m): self.hook = m.register_forward_hook(self.hook_fn)
def hook_fn(self, module, input, output): self.features = output
def remove(self): self.hook.remove()
class DynamicUnet(nn.Module):
"""
A dynamic implementation of Unet architecture, because calculating connections
and channels suck!. When an encoder is passed, this network will
automatically construct a decoder after the first single forward pass for any
given encoder architecture.
Decoder part is heavily based on the original Unet paper:
https://arxiv.org/abs/1505.04597.
Inputs:
encoder(nn.Module): Preferably a pretrained model, such as VGG or ResNet
last (bool): Whether to concat only last activation just before a size change
n_classes (int): Number of classes to output in final step of decoder
Important Note: If architecture directly reduces the dimension of an image as soon as the
first forward pass then output size will not be same as the input size, e.g. ResNet.
In order to resolve this problem architecture will add an additional extra conv transpose
layer. Also, currently Dynamic Unet expects size change to be H,W -> H/2, W/2. This is
not a problem for state-of-the-art architectures as they follow this pattern but it should
be changed for custom encoders that might have a different size decay.
"""
def __init__(self, encoder, last=True, n_classes=3):
super().__init__()
self.encoder = encoder
self.n_children = len(list(encoder.children()))
self.sfs = [SaveFeatures(encoder[i]) for i in range(self.n_children)]
self.last = last
self.n_classes = n_classes
def forward(self, x):
# get imsize
imsize = x.size()[-2:]
# encoder output
x = F.relu(self.encoder(x))
# initialize sfs_idxs, sfs_szs, middle_in_c and middle_conv only once
if not hasattr(self, 'middle_conv'):
self.sfs_szs = [sfs_feats.features.size() for sfs_feats in self.sfs]
self.sfs_idxs = get_sfs_idxs(self.sfs, self.last)
middle_in_c = self.sfs_szs[-1][1]
middle_conv = nn.Sequential(*conv_bn_relu(middle_in_c, middle_in_c * 2, 3, 1, 1),
*conv_bn_relu(middle_in_c * 2, middle_in_c, 3, 1, 1))
self.middle_conv = middle_conv
# middle conv
x = self.middle_conv(x)
# initialize upmodel, extra_block and 1x1 final conv
if not hasattr(self, 'upmodel'):
x_copy = Variable(x.data, requires_grad=False)
upmodel = []
for idx in self.sfs_idxs[::-1]:
up_in_c, x_in_c = int(x_copy.size()[1]), int(self.sfs_szs[idx][1])
unet_block = UnetBlock(up_in_c, x_in_c)
upmodel.append(unet_block)
x_copy = unet_block(x_copy, self.sfs[idx].features)
self.upmodel = nn.Sequential(*upmodel)
if imsize != self.sfs_szs[0][-2:]:
extra_in_c = self.upmodel[-1].conv2.out_channels
self.extra_block = nn.ConvTranspose2d(extra_in_c, extra_in_c, 2, 2)
final_in_c = self.upmodel[-1].conv2.out_channels
self.final_conv = nn.Conv2d(final_in_c, self.n_classes, 1)
# run upsample
for block, idx in zip(self.upmodel, self.sfs_idxs[::-1]):
x = block(x, self.sfs[idx].features)
if hasattr(self, 'extra_block'):
x = self.extra_block(x)
out = self.final_conv(x)
return out
+78
View File
@@ -0,0 +1,78 @@
# https://github.com/uoguelph-mlrg/Cutout
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
class BasicBlock(nn.Module):
def __init__(self, in_planes, out_planes, stride, dropRate=0.0):
super().__init__()
self.bn1 = nn.BatchNorm2d(in_planes)
self.relu1 = nn.ReLU(inplace=True)
self.conv1 = nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_planes)
self.relu2 = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(out_planes, out_planes, kernel_size=3, stride=1,
padding=1, bias=False)
self.droprate = dropRate
self.equalInOut = (in_planes == out_planes)
self.convShortcut = (not self.equalInOut) and nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride,
padding=0, bias=False) or None
def forward(self, x):
if not self.equalInOut: x = self.relu1(self.bn1(x))
else: out = self.relu1(self.bn1(x))
out = self.relu2(self.bn2(self.conv1(out if self.equalInOut else x)))
if self.droprate > 0:
out = F.dropout(out, p=self.droprate, training=self.training)
out = self.conv2(out)
return torch.add(x if self.equalInOut else self.convShortcut(x), out)
class NetworkBlock(nn.Module):
def __init__(self, nb_layers, in_planes, out_planes, block, stride, dropRate=0.0):
super().__init__()
self.layer = self._make_layer(block, in_planes, out_planes, nb_layers, stride, dropRate)
def _make_layer(self, block, in_planes, out_planes, nb_layers, stride, dropRate):
layers = []
for i in range(nb_layers):
layers.append(block(i == 0 and in_planes or out_planes, out_planes, i == 0 and stride or 1, dropRate))
return nn.Sequential(*layers)
def forward(self, x): return self.layer(x)
class WideResNet(nn.Module):
def __init__(self, depth, num_classes, widen_factor=1, dropRate=0.0):
super().__init__()
nChannels = [16, 16*widen_factor, 32*widen_factor, 64*widen_factor]
assert((depth - 4) % 6 == 0)
n = (depth - 4) // 6
block = BasicBlock
# 1st conv before any network block
self.conv1 = nn.Conv2d(3, nChannels[0], kernel_size=3, stride=1,
padding=1, bias=False)
self.block1 = NetworkBlock(n, nChannels[0], nChannels[1], block, 1, dropRate)
self.block2 = NetworkBlock(n, nChannels[1], nChannels[2], block, 2, dropRate)
self.block3 = NetworkBlock(n, nChannels[2], nChannels[3], block, 2, dropRate)
self.bn1 = nn.BatchNorm2d(nChannels[3])
self.relu = nn.ReLU(inplace=True)
self.fc = nn.Linear(nChannels[3], num_classes)
self.nChannels = nChannels[3]
for m in self.modules():
if isinstance(m, nn.Conv2d):
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
m.weight.data.normal_(0, math.sqrt(2. / n))
elif isinstance(m, nn.BatchNorm2d):
m.weight.data.fill_(1)
m.bias.data.zero_()
elif isinstance(m, nn.Linear): m.bias.data.zero_()
def forward(self, x):
out = self.conv1(x)
out = self.block1(out)
out = self.block2(out)
out = self.block3(out)
out = self.relu(self.bn1(out))
out = F.adaptive_avg_pool2d(out, 1)
out = out.view(-1, self.nChannels)
return self.fc(out)
+331
View File
@@ -0,0 +1,331 @@
import torch
import torch.nn as nn
from torch.autograd import Variable
from functools import reduce
class LambdaBase(nn.Sequential):
def __init__(self, fn, *args):
super(LambdaBase, self).__init__(*args)
self.lambda_func = fn
def forward_prepare(self, input):
output = []
for module in self._modules.values():
output.append(module(input))
return output if output else input
class Lambda(LambdaBase):
def forward(self, input):
return self.lambda_func(self.forward_prepare(input))
class LambdaMap(LambdaBase):
def forward(self, input):
return list(map(self.lambda_func,self.forward_prepare(input)))
class LambdaReduce(LambdaBase):
def forward(self, input):
return reduce(self.lambda_func,self.forward_prepare(input))
def wrn_50_2f(): return nn.Sequential( # Sequential,
nn.Conv2d(3,64,(7, 7),(2, 2),(3, 3),1,1,bias=False),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d((3, 3),(2, 2),(1, 1)),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(64,128,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.Conv2d(128,128,(3, 3),(1, 1),(1, 1),1,1,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.Conv2d(128,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
nn.Sequential( # Sequential,
nn.Conv2d(64,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(256,128,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.Conv2d(128,128,(3, 3),(1, 1),(1, 1),1,1,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.Conv2d(128,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(256,128,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.Conv2d(128,128,(3, 3),(1, 1),(1, 1),1,1,bias=False),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.Conv2d(128,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(256,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(2, 2),(1, 1),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
nn.Sequential( # Sequential,
nn.Conv2d(256,512,(1, 1),(2, 2),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(512,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(1, 1),(1, 1),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(512,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(1, 1),(1, 1),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(512,256,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(1, 1),(1, 1),1,1,bias=False),
nn.BatchNorm2d(256),
nn.ReLU(),
nn.Conv2d(256,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(512,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(2, 2),(1, 1),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
nn.Sequential( # Sequential,
nn.Conv2d(512,1024,(1, 1),(2, 2),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(1024,512,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,512,(3, 3),(1, 1),(1, 1),1,1,bias=False),
nn.BatchNorm2d(512),
nn.ReLU(),
nn.Conv2d(512,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.Sequential( # Sequential,
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(1024,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(2, 2),(1, 1),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
nn.Sequential( # Sequential,
nn.Conv2d(1024,2048,(1, 1),(2, 2),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(2048,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
nn.Sequential( # Sequential,
LambdaMap(lambda x: x, # ConcatTable,
nn.Sequential( # Sequential,
nn.Conv2d(2048,1024,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,1024,(3, 3),(1, 1),(1, 1),1,1,bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(),
nn.Conv2d(1024,2048,(1, 1),(1, 1),(0, 0),1,1,bias=False),
nn.BatchNorm2d(2048),
),
Lambda(lambda x: x), # Identity,
),
LambdaReduce(lambda x,y: x+y), # CAddTable,
nn.ReLU(),
),
),
nn.AvgPool2d((7, 7),(1, 1)),
Lambda(lambda x: x.view(x.size(0),-1)), # View,
nn.Sequential(Lambda(lambda x: x.view(1,-1) if 1==len(x.size()) else x ),nn.Linear(2048,1000)), # Linear,
)
+368
View File
@@ -0,0 +1,368 @@
from .imports import *
from .torch_imports import *
from .core import *
from .model import *
from .dataset import *
from .learner import *
from .text import *
from .lm_rnn import *
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.model_selection import train_test_split
from torchtext.datasets import language_modeling
class DotProdNB(nn.Module):
def __init__(self, nf, ny, w_adj=0.4, r_adj=10):
super().__init__()
self.w_adj,self.r_adj = w_adj,r_adj
self.w = nn.Embedding(nf+1, 1, padding_idx=0)
self.w.weight.data.uniform_(-0.1,0.1)
self.r = nn.Embedding(nf+1, ny)
def forward(self, feat_idx, feat_cnt, sz):
w = self.w(feat_idx)
r = self.r(feat_idx)
x = ((w+self.w_adj)*r/self.r_adj).sum(1)
return F.softmax(x)
class SimpleNB(nn.Module):
def __init__(self, nf, ny):
super().__init__()
self.r = nn.Embedding(nf+1, ny, padding_idx=0)
self.b = nn.Parameter(torch.zeros(ny,))
def forward(self, feat_idx, feat_cnt, sz):
r = self.r(feat_idx)
x = r.sum(1)+self.b
return F.softmax(x)
class BOW_Learner(Learner):
def __init__(self, data, models, **kwargs):
super().__init__(data, models, **kwargs)
def _get_crit(self, data): return F.l1_loss
def calc_pr(y_i, x, y, b):
idx = np.argwhere((y==y_i)==b)
ct = x[idx[:,0]].sum(0)+1
tot = ((y==y_i)==b).sum()+1
return ct/tot
def calc_r(y_i, x, y):
return np.log(calc_pr(y_i, x, y, True) / calc_pr(y_i, x, y, False))
class BOW_Dataset(Dataset):
def __init__(self, bow, y, max_len):
self.bow,self.max_len = bow,max_len
self.c = int(y.max())+1
self.n,self.vocab_size = bow.shape
self.y = one_hot(y,self.c).astype(np.float32)
x = self.bow.sign()
self.r = np.stack([calc_r(i, x, y).A1 for i in range(self.c)]).T
def __getitem__(self, i):
row = self.bow.getrow(i)
num_row_entries = row.indices.shape[0]
indices = (row.indices + 1).astype(np.int64)
data = (row.data).astype(np.int64)
if num_row_entries < self.max_len:
# If short, pad
indices = np.pad(indices, (self.max_len - num_row_entries, 0), mode='constant')
data = np.pad(data, (self.max_len - num_row_entries, 0), mode='constant')
else:
# If long, truncate
indices, data = indices[-self.max_len:], data[-self.max_len:]
return indices, data, min(self.max_len, num_row_entries), self.y[i]
def __len__(self): return len(self.bow.indptr)-1
class TextClassifierData(ModelData):
@property
def c(self): return self.trn_ds.c
@property
def r(self):
return torch.Tensor(np.concatenate([np.zeros((1,self.c)), self.trn_ds.r]))
def get_model(self, f, **kwargs):
m = to_gpu(f(self.trn_ds.vocab_size, self.c, **kwargs))
m.r.weight.data = to_gpu(self.r)
m.r.weight.requires_grad = False
model = BasicModel(m)
return BOW_Learner(self, model, metrics=[accuracy_thresh(0.5)], opt_fn=optim.Adam)
def dotprod_nb_learner(self, **kwargs): return self.get_model(DotProdNB, **kwargs)
def nb_learner(self, **kwargs): return self.get_model(SimpleNB, **kwargs)
@classmethod
def from_bow(cls, trn_bow, trn_y, val_bow, val_y, sl):
trn_ds = BOW_Dataset(trn_bow, trn_y, sl)
val_ds = BOW_Dataset(val_bow, val_y, sl)
trn_dl = DataLoader(trn_ds, 64, True)
val_dl = DataLoader(val_ds, 64, False)
return cls('.', trn_dl, val_dl)
def flip_tensor(x, dim):
xsize = x.size()
dim = x.dim() + dim if dim < 0 else dim
x = x.view(-1, *xsize[dim:])
x = x.view(x.size(0), x.size(1), -1)[:, getattr(torch.arange(x.size(1)-1,
-1, -1), ('cpu','cuda')[x.is_cuda])().long(), :]
return x.view(xsize)
class LanguageModelLoader():
def __init__(self, ds, bs, bptt, backwards=False):
self.bs,self.bptt,self.backwards = bs,bptt,backwards
text = sum([o.text for o in ds], [])
fld = ds.fields['text']
nums = fld.numericalize([text],device=None if torch.cuda.is_available() else -1)
self.data = self.batchify(nums)
self.i,self.iter = 0,0
self.n = len(self.data)
def __iter__(self):
self.i,self.iter = 0,0
return self
def __len__(self): return self.n // self.bptt - 1
def __next__(self):
if self.i >= self.n-1 or self.iter>=len(self): raise StopIteration
bptt = self.bptt if np.random.random() < 0.95 else self.bptt / 2.
seq_len = max(5, int(np.random.normal(bptt, 5)))
res = self.get_batch(self.i, seq_len)
self.i += seq_len
self.iter += 1
return res
def batchify(self, data):
nb = data.size(0) // self.bs
data = data[:nb*self.bs]
data = data.view(self.bs, -1).t().contiguous()
if self.backwards: data=flip_tensor(data, 0)
return to_gpu(data)
def get_batch(self, i, seq_len):
source = self.data
seq_len = min(seq_len, len(source) - 1 - i)
return source[i:i+seq_len], source[i+1:i+1+seq_len].view(-1)
class RNN_Learner(Learner):
def __init__(self, data, models, **kwargs):
super().__init__(data, models, **kwargs)
def _get_crit(self, data): return F.cross_entropy
def save_encoder(self, name): save_model(self.model[0], self.get_model_path(name))
def load_encoder(self, name): load_model(self.model[0], self.get_model_path(name))
class ConcatTextDataset(torchtext.data.Dataset):
def __init__(self, path, text_field, newline_eos=True, encoding='utf-8', **kwargs):
fields = [('text', text_field)]
text = []
if os.path.isdir(path): paths=glob(f'{path}/*.*')
else: paths=[path]
for p in paths:
for line in open(p, encoding=encoding): text += text_field.preprocess(line)
if newline_eos: text.append('<eos>')
examples = [torchtext.data.Example.fromlist([text], fields)]
super().__init__(examples, fields, **kwargs)
class ConcatTextDatasetFromDataFrames(torchtext.data.Dataset):
def __init__(self, df, text_field, col, newline_eos=True, **kwargs):
fields = [('text', text_field)]
text = []
text += text_field.preprocess(df[col].str.cat(sep=' <eos> '))
if (newline_eos): text.append('<eos>')
examples = [torchtext.data.Example.fromlist([text], fields)]
super().__init__(examples, fields, **kwargs)
@classmethod
def splits(cls, train_df=None, val_df=None, test_df=None, keep_nones=False, **kwargs):
res = (
cls(train_df, **kwargs),
cls(val_df, **kwargs),
map_none(test_df, partial(cls, **kwargs))) # not required
return res if keep_nones else tuple(d for d in res if d is not None)
class LanguageModelData():
"""
This class provides the entry point for dealing with supported NLP tasks.
Usage:
1. Use one of the factory constructors (from_dataframes, from_text_files) to
obtain an instance of the class.
2. Use the get_model method to return a RNN_Learner instance (a network suited
for NLP tasks), then proceed with training.
Example:
>> TEXT = data.Field(lower=True, tokenize=spacy_tok)
>> FILES = dict(train=TRN_PATH, validation=VAL_PATH, test=VAL_PATH)
>> md = LanguageModelData.from_text_files(PATH, TEXT, **FILES, bs=64, bptt=70, min_freq=10)
>> em_sz = 200 # size of each embedding vector
>> nh = 500 # number of hidden activations per layer
>> nl = 3 # number of layers
>> opt_fn = partial(optim.Adam, betas=(0.7, 0.99))
>> learner = md.get_model(opt_fn, em_sz, nh, nl,
dropouti=0.05, dropout=0.05, wdrop=0.1, dropoute=0.02, dropouth=0.05)
>> learner.reg_fn = seq2seq_reg
>> learner.clip=0.3
>> learner.fit(3e-3, 4, wds=1e-6, cycle_len=1, cycle_mult=2)
"""
def __init__(self, path, field, trn_ds, val_ds, test_ds, bs, bptt, backwards=False, **kwargs):
""" Constructor for the class. An important thing that happens here is
that the field's "build_vocab" method is invoked, which builds the vocabulary
for this NLP model.
Also, three instances of the LanguageModelLoader are constructed; one each
for training data (self.trn_dl), validation data (self.val_dl), and the
testing data (self.test_dl)
Args:
path (str): testing path
field (Field): torchtext field object
trn_ds (Dataset): training dataset
val_ds (Dataset): validation dataset
test_ds (Dataset): testing dataset
bs (int): batch size
bptt (int): back propagation through time
kwargs: other arguments
"""
self.bs = bs
self.path = path
self.trn_ds = trn_ds; self.val_ds = val_ds; self.test_ds = test_ds
if not hasattr(field, 'vocab'): field.build_vocab(self.trn_ds, **kwargs)
self.pad_idx = field.vocab.stoi[field.pad_token]
self.nt = len(field.vocab)
factory = lambda ds: LanguageModelLoader(ds, bs, bptt, backwards=backwards)
self.trn_dl = factory(self.trn_ds)
self.val_dl = factory(self.val_ds)
self.test_dl = map_none(self.test_ds, factory) # not required
def get_model(self, opt_fn, emb_sz, n_hid, n_layers, **kwargs):
""" Method returns a RNN_Learner object, that wraps an instance of the RNN_Encoder module.
Args:
opt_fn (Optimizer): the torch optimizer function to use
emb_sz (int): embedding size
n_hid (int): number of hidden inputs
n_layers (int): number of hidden layers
kwargs: other arguments
Returns:
An instance of the RNN_Learner class.
"""
m = get_language_model(self.nt, emb_sz, n_hid, n_layers, self.pad_idx, **kwargs)
model = SingleModel(to_gpu(m))
return RNN_Learner(self, model, opt_fn=opt_fn)
@classmethod
def from_dataframes(cls, path, field, col, train_df, val_df, test_df=None, bs=64, bptt=70, **kwargs):
trn_ds, val_ds, test_ds = ConcatTextDatasetFromDataFrames.splits(
text_field=field, col=col, train_df=train_df, val_df=val_df, test_df=test_df, keep_nones=True)
return cls(path, field, trn_ds, val_ds, test_ds, bs, bptt, **kwargs)
@classmethod
def from_text_files(cls, path, field, train, validation, test=None, bs=64, bptt=70, **kwargs):
""" Method used to instantiate a LanguageModelData object that can be used for a
supported nlp task.
Args:
path (str): the absolute path in which temporary model data will be saved
field (Field): torchtext field
train (str): file location of the training data
validation (str): file location of the validation data
test (str): file location of the testing data
bs (int): batch size to use
bptt (int): back propagation through time hyper-parameter
kwargs: other arguments
Returns:
a LanguageModelData instance, which most importantly, provides us the datasets for training,
validation, and testing
Note:
The train, validation, and test path can be pointed to any file (or folder) that contains a valid
text corpus.
"""
trn_ds, val_ds, test_ds = ConcatTextDataset.splits(
path, text_field=field, train=train, validation=validation, test=test)
return cls(path, field, trn_ds, val_ds, test_ds, bs, bptt, **kwargs)
class TextDataLoader():
def __init__(self, src, x_fld, y_fld):
self.src,self.x_fld,self.y_fld = src,x_fld,y_fld
def __len__(self): return len(self.src)
def __iter__(self):
it = iter(self.src)
for i in range(len(self)):
b = next(it)
yield getattr(b, self.x_fld).data, getattr(b, self.y_fld).data
class TextModel(BasicModel):
def get_layer_groups(self):
m = self.model[0]
return [(m.encoder, m.dropouti), *zip(m.rnns, m.dropouths), (self.model[1])]
class TextData(ModelData):
def create_td(self, it): return TextDataLoader(it, self.text_fld, self.label_fld)
@classmethod
def from_splits(cls, path, splits, bs, text_name='text', label_name='label'):
text_fld = splits[0].fields[text_name]
label_fld = splits[0].fields[label_name]
if hasattr(label_fld, 'build_vocab'): label_fld.build_vocab(splits[0])
iters = torchtext.data.BucketIterator.splits(splits, batch_size=bs)
trn_iter,val_iter,test_iter = iters[0],iters[1],None
test_dl = None
if len(iters) == 3:
test_iter = iters[2]
test_dl = TextDataLoader(test_iter, text_name, label_name)
trn_dl = TextDataLoader(trn_iter, text_name, label_name)
val_dl = TextDataLoader(val_iter, text_name, label_name)
obj = cls.from_dls(path, trn_dl, val_dl, test_dl)
obj.bs = bs
obj.pad_idx = text_fld.vocab.stoi[text_fld.pad_token]
obj.nt = len(text_fld.vocab)
obj.c = (len(label_fld.vocab) if hasattr(label_fld, 'vocab')
else len(getattr(splits[0][0], label_name)))
return obj
def to_model(self, m, opt_fn):
model = TextModel(to_gpu(m))
return RNN_Learner(self, model, opt_fn=opt_fn)
def get_model(self, opt_fn, max_sl, bptt, emb_sz, n_hid, n_layers, dropout, **kwargs):
m = get_rnn_classifier(bptt, max_sl, self.c, self.nt,
layers=[emb_sz*3, self.c], drops=[dropout],
emb_sz=emb_sz, n_hid=n_hid, n_layers=n_layers, pad_token=self.pad_idx, **kwargs)
return self.to_model(m, opt_fn)
+204
View File
@@ -0,0 +1,204 @@
from .imports import *
from .torch_imports import *
from sklearn.metrics import confusion_matrix
def ceildiv(a, b):
return -(-a // b)
def plots(ims, figsize=(12,6), rows=1, interp=False, titles=None, maintitle=None):
if type(ims[0]) is np.ndarray:
ims = np.array(ims)
if (ims.shape[-1] != 3): ims = ims.transpose((0,2,3,1))
f = plt.figure(figsize=figsize)
if maintitle is not None:
plt.suptitle(maintitle, fontsize=16)
for i in range(len(ims)):
sp = f.add_subplot(rows, ceildiv(len(ims), rows), i+1)
sp.axis('Off')
if titles is not None: sp.set_title(titles[i], fontsize=16)
plt.imshow(ims[i], interpolation=None if interp else 'none')
def plots_from_files(imspaths, figsize=(10,5), rows=1, titles=None, maintitle=None):
"""Plots images given image files.
Arguments:
im_paths (list): list of paths
figsize (tuple): figure size
rows (int): number of rows
titles (list): list of titles
maintitle (string): main title
"""
f = plt.figure(figsize=figsize)
if maintitle is not None: plt.suptitle(maintitle, fontsize=16)
for i in range(len(imspaths)):
sp = f.add_subplot(rows, ceildiv(len(imspaths), rows), i+1)
sp.axis('Off')
if titles is not None: sp.set_title(titles[i], fontsize=16)
img = plt.imread(imspaths[i])
plt.imshow(img)
def plot_confusion_matrix(cm, classes, normalize=False, title='Confusion matrix', cmap=plt.cm.Blues, figsize=None):
"""
This function prints and plots the confusion matrix.
Normalization can be applied by setting `normalize=True`.
(This function is copied from the scikit docs.)
"""
plt.figure(figsize=figsize)
plt.imshow(cm, interpolation='nearest', cmap=cmap)
plt.title(title)
plt.colorbar()
tick_marks = np.arange(len(classes))
plt.xticks(tick_marks, classes, rotation=45)
plt.yticks(tick_marks, classes)
if normalize: cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
print(cm)
thresh = cm.max() / 2.
for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
plt.text(j, i, cm[i, j], horizontalalignment="center", color="white" if cm[i, j] > thresh else "black")
plt.tight_layout()
plt.ylabel('True label')
plt.xlabel('Predicted label')
def plots_raw(ims, figsize=(12,6), rows=1, titles=None):
f = plt.figure(figsize=figsize)
for i in range(len(ims)):
sp = f.add_subplot(rows, ceildiv(len(ims), rows), i+1)
sp.axis('Off')
if titles is not None: sp.set_title(titles[i], fontsize=16)
plt.imshow(ims[i])
def load_img_id(ds, idx, path): return np.array(PIL.Image.open(os.path.join(path, ds.fnames[idx])))
class ImageModelResults():
""" Visualize the results of an image model
Arguments:
ds (dataset): a dataset which contains the images
log_preds (numpy.ndarray): predictions for the dataset in log scale
Returns:
ImageModelResults
"""
def __init__(self, ds, log_preds):
"""Initialize an ImageModelResults class instance"""
self.ds = ds
# returns the indices of the maximum value of predictions along axis 1, representing the predicted class
# log_preds.shape = (number_of_samples, number_of_classes);
# preds.shape = (number_of_samples,)
self.preds = np.argmax(log_preds, axis=1)
# computes the probabilities
self.probs = np.exp(log_preds)
# extracts the number of classes
self.num_classes = log_preds.shape[1]
def plot_val_with_title(self, idxs, y):
""" Displays the images and their probabilities of belonging to a certain class
Arguments:
idxs (numpy.ndarray): indexes of the image samples from the dataset
y (int): the selected class
Returns:
Plots the images in n rows [rows = n]
"""
# if there are any samples to be displayed
if len(idxs) > 0:
imgs = np.stack([self.ds[x][0] for x in idxs])
title_probs = [self.probs[x,y] for x in idxs]
return plots(self.ds.denorm(imgs), rows=1, titles=title_probs)
# if idxs is empty return false
else:
return False;
def most_by_mask(self, mask, y, mult):
""" Extracts the first 4 most correct/incorrect indexes from the ordered list of probabilities
Arguments:
mask (numpy.ndarray): the mask of probabilities specific to the selected class; a boolean array with shape (num_of_samples,) which contains True where class==selected_class, and False everywhere else
y (int): the selected class
mult (int): sets the ordering; -1 descending, 1 ascending
Returns:
idxs (ndarray): An array of indexes of length 4
"""
idxs = np.where(mask)[0]
cnt = min(4, len(idxs))
return idxs[np.argsort(mult * self.probs[idxs,y])[:cnt]]
def most_uncertain_by_mask(self, mask, y):
""" Extracts the first 4 most uncertain indexes from the ordered list of probabilities
Arguments:
mask (numpy.ndarray): the mask of probabilities specific to the selected class; a boolean array with shape (num_of_samples,) which contains True where class==selected_class, and False everywhere else
y (int): the selected class
Returns:
idxs (ndarray): An array of indexes of length 4
"""
idxs = np.where(mask)[0]
# the most uncertain samples will have abs(probs-1/num_classes) close to 0;
return idxs[np.argsort(np.abs(self.probs[idxs,y]-(1/self.num_classes)))[:4]]
def most_by_correct(self, y, is_correct):
""" Extracts the predicted classes which correspond to the selected class (y) and to the specific case (prediction is correct - is_true=True, prediction is wrong - is_true=False)
Arguments:
y (int): the selected class
is_correct (boolean): a boolean flag (True, False) which specify the what to look for. Ex: True - most correct samples, False - most incorrect samples
Returns:
idxs (numpy.ndarray): An array of indexes (numpy.ndarray)
"""
# mult=-1 when the is_correct flag is true -> when we want to display the most correct classes we will make a descending sorting (argsort) because we want that the biggest probabilities to be displayed first.
# When is_correct is false, we want to display the most incorrect classes, so we want an ascending sorting since our interest is in the smallest probabilities.
mult = -1 if is_correct==True else 1
return self.most_by_mask(((self.preds == self.ds.y)==is_correct)
& (self.ds.y == y), y, mult)
def plot_by_correct(self, y, is_correct):
""" Plots the images which correspond to the selected class (y) and to the specific case (prediction is correct - is_true=True, prediction is wrong - is_true=False)
Arguments:
y (int): the selected class
is_correct (boolean): a boolean flag (True, False) which specify the what to look for. Ex: True - most correct samples, False - most incorrect samples
"""
return self.plot_val_with_title(self.most_by_correct(y, is_correct), y)
def most_by_uncertain(self, y):
""" Extracts the predicted classes which correspond to the selected class (y) and have probabilities nearest to 1/number_of_classes (eg. 0.5 for 2 classes, 0.33 for 3 classes) for the selected class.
Arguments:
y (int): the selected class
Returns:
idxs (numpy.ndarray): An array of indexes (numpy.ndarray)
"""
return self.most_uncertain_by_mask((self.ds.y == y), y)
def plot_most_correct(self, y):
""" Plots the images which correspond to the selected class (y) and are most correct.
Arguments:
y (int): the selected class
"""
return self.plot_by_correct(y, True)
def plot_most_incorrect(self, y):
""" Plots the images which correspond to the selected class (y) and are most incorrect.
Arguments:
y (int): the selected class
"""
return self.plot_by_correct(y, False)
def plot_most_uncertain(self, y):
""" Plots the images which correspond to the selected class (y) and are most uncertain i.e have probabilities nearest to 1/number_of_classes.
Arguments:
y (int): the selected class
"""
return self.plot_val_with_title(self.most_by_uncertain(y), y)
+190
View File
@@ -0,0 +1,190 @@
from .torch_imports import *
from .core import *
from functools import wraps
import torch.nn.functional as F
from torch.autograd import Variable
IS_TORCH_04 = LooseVersion(torch.__version__) >= LooseVersion('0.4')
def dropout_mask(x, sz, dropout):
""" Applies a dropout mask whose size is determined by passed argument 'sz'.
Args:
x (nn.Variable): A torch Variable object
sz (tuple(int, int, int)): The expected size of the new tensor
dropout (float): The dropout fraction to apply
This method uses the bernoulli distribution to decide which activations to keep.
Additionally, the sampled activations is rescaled is using the factor 1/(1 - dropout).
In the example given below, one can see that approximately .8 fraction of the
returned tensors are zero. Rescaling with the factor 1/(1 - 0.8) returns a tensor
with 5's in the unit places.
The official link to the pytorch bernoulli function is here:
http://pytorch.org/docs/master/torch.html#torch.bernoulli
Examples:
>>> a_Var = torch.autograd.Variable(torch.Tensor(2, 3, 4).uniform_(0, 1), requires_grad=False)
>>> a_Var
Variable containing:
(0 ,.,.) =
0.6890 0.5412 0.4303 0.8918
0.3871 0.7944 0.0791 0.5979
0.4575 0.7036 0.6186 0.7217
(1 ,.,.) =
0.8354 0.1690 0.1734 0.8099
0.6002 0.2602 0.7907 0.4446
0.5877 0.7464 0.4257 0.3386
[torch.FloatTensor of size 2x3x4]
>>> a_mask = dropout_mask(a_Var.data, (1,a_Var.size(1),a_Var.size(2)), dropout=0.8)
>>> a_mask
(0 ,.,.) =
0 5 0 0
0 0 0 5
5 0 5 0
[torch.FloatTensor of size 1x3x4]
"""
return x.new(*sz).bernoulli_(1-dropout)/(1-dropout)
class LockedDropout(nn.Module):
def __init__(self, p=0.5):
super().__init__()
self.p=p
def forward(self, x):
if not self.training or not self.p: return x
m = dropout_mask(x.data, (1, x.size(1), x.size(2)), self.p)
return Variable(m, requires_grad=False) * x
class WeightDrop(torch.nn.Module):
"""A custom torch layer that serves as a wrapper on another torch layer.
Primarily responsible for updating the weights in the wrapped module based
on a specified dropout.
"""
def __init__(self, module, dropout, weights=['weight_hh_l0']):
""" Default constructor for the WeightDrop module
Args:
module (torch.nn.Module): A pytorch layer being wrapped
dropout (float): a dropout value to apply
weights (list(str)): the parameters of the wrapped **module**
which should be fractionally dropped.
"""
super().__init__()
self.module,self.weights,self.dropout = module,weights,dropout
self._setup()
def _setup(self):
""" for each string defined in self.weights, the corresponding
attribute in the wrapped module is referenced, then deleted, and subsequently
registered as a new parameter with a slightly modified name.
Args:
None
Returns:
None
"""
if isinstance(self.module, torch.nn.RNNBase): self.module.flatten_parameters = noop
for name_w in self.weights:
w = getattr(self.module, name_w)
del self.module._parameters[name_w]
self.module.register_parameter(name_w + '_raw', nn.Parameter(w.data))
def _setweights(self):
""" Uses pytorch's built-in dropout function to apply dropout to the parameters of
the wrapped module.
Args:
None
Returns:
None
"""
for name_w in self.weights:
raw_w = getattr(self.module, name_w + '_raw')
w = torch.nn.functional.dropout(raw_w, p=self.dropout, training=self.training)
if hasattr(self.module, name_w):
delattr(self.module, name_w)
setattr(self.module, name_w, w)
def forward(self, *args):
""" updates weights and delegates the propagation of the tensor to the wrapped module's
forward method
Args:
*args: supplied arguments
Returns:
tensor obtained by running the forward method on the wrapped module.
"""
self._setweights()
return self.module.forward(*args)
class EmbeddingDropout(nn.Module):
""" Applies dropout in the embedding layer by zeroing out some elements of the embedding vector.
Uses the dropout_mask custom layer to achieve this.
Args:
embed (torch.nn.Embedding): An embedding torch layer
words (torch.nn.Variable): A torch variable
dropout (float): dropout fraction to apply to the embedding weights
scale (float): additional scaling to apply to the modified embedding weights
Returns:
tensor of size: (batch_size x seq_length x embedding_size)
Example:
>> embed = torch.nn.Embedding(10,3)
>> words = Variable(torch.LongTensor([[1,2,4,5] ,[4,3,2,9]]))
>> words.size()
(2,4)
>> embed_dropout_layer = EmbeddingDropout(embed)
>> dropout_out_ = embed_dropout_layer(embed, words, dropout=0.40)
>> dropout_out_
Variable containing:
(0 ,.,.) =
1.2549 1.8230 1.9367
0.0000 -0.0000 0.0000
2.2540 -0.1299 1.5448
0.0000 -0.0000 -0.0000
(1 ,.,.) =
2.2540 -0.1299 1.5448
-4.0457 2.4815 -0.2897
0.0000 -0.0000 0.0000
1.8796 -0.4022 3.8773
[torch.FloatTensor of size 2x4x3]
"""
def __init__(self, embed):
super().__init__()
self.embed = embed
def forward(self, words, dropout=0.1, scale=None):
if dropout:
size = (self.embed.weight.size(0),1)
mask = Variable(dropout_mask(self.embed.weight.data, size, dropout))
masked_embed_weight = mask * self.embed.weight
else: masked_embed_weight = self.embed.weight
if scale: masked_embed_weight = scale * masked_embed_weight
padding_idx = self.embed.padding_idx
if padding_idx is None: padding_idx = -1
if IS_TORCH_04:
X = F.embedding(words,
masked_embed_weight, padding_idx, self.embed.max_norm,
self.embed.norm_type, self.embed.scale_grad_by_freq, self.embed.sparse)
else:
X = self.embed._backend.Embedding.apply(words,
masked_embed_weight, padding_idx, self.embed.max_norm,
self.embed.norm_type, self.embed.scale_grad_by_freq, self.embed.sparse)
return X
+5
View File
@@ -0,0 +1,5 @@
import torch
import torch.nn as nn
from torch.autograd import Variable
from .core import *
+3
View File
@@ -0,0 +1,3 @@
from multiprocessing import set_start_method
set_start_method('spawn')
+609
View File
@@ -0,0 +1,609 @@
from .imports import *
from .layer_optimizer import *
from enum import IntEnum
from timeit import default_timer as timer
import copy
class Callback:
'''
An abstract class that all callback(e.g., LossRecorder) classes extends from.
Must be extended before usage.
'''
def on_train_begin(self): pass
def on_batch_begin(self): pass
def on_phase_begin(self): pass
def on_epoch_end(self, metrics): pass
def on_phase_end(self): pass
def on_batch_end(self, metrics): pass
def on_train_end(self): pass
# Useful for maintaining status of a long-running job.
#
# Usage:
# learn.fit(0.01, 1, callbacks = [LoggingCallback(save_path="/tmp/log")])
class LoggingCallback(Callback):
'''
A class useful for maintaining status of a long-running job.
e.g.: learn.fit(0.01, 1, callbacks = [LoggingCallback(save_path="/tmp/log")])
'''
def __init__(self, save_path):
super().__init__()
self.save_path=save_path
def on_train_begin(self):
self.batch = 0
self.epoch = 0
self.phase = 0
self.f = open(self.save_path, "a", 1)
self.log("\ton_train_begin")
def on_batch_begin(self):
self.log(str(self.batch)+"\ton_batch_begin")
def on_phase_begin(self):
self.log(str(self.phase)+"\ton_phase_begin")
def on_epoch_end(self, metrics):
self.log(str(self.epoch)+"\ton_epoch_end: "+str(metrics))
self.epoch += 1
def on_phase_end(self):
self.log(str(self.phase)+"\ton_phase_end")
self.phase+=1
def on_batch_end(self, metrics):
self.log(str(self.batch)+"\ton_batch_end: "+str(metrics))
self.batch += 1
def on_train_end(self):
self.log("\ton_train_end")
self.f.close()
def log(self, string):
self.f.write(time.strftime("%Y-%m-%dT%H:%M:%S")+"\t"+string+"\n")
class LossRecorder(Callback):
'''
Saves and displays loss functions and other metrics.
Default sched when none is specified in a learner.
'''
def __init__(self, layer_opt, save_path='', record_mom=False, metrics=[]):
super().__init__()
self.layer_opt=layer_opt
self.init_lrs=np.array(layer_opt.lrs)
self.save_path, self.record_mom, self.metrics = save_path, record_mom, metrics
def on_train_begin(self):
self.losses,self.lrs,self.iterations,self.epochs,self.times = [],[],[],[],[]
self.start_at = timer()
self.val_losses, self.rec_metrics = [], []
if self.record_mom:
self.momentums = []
self.iteration = 0
self.epoch = 0
def on_epoch_end(self, metrics):
self.epoch += 1
self.epochs.append(self.iteration)
self.times.append(timer() - self.start_at)
self.save_metrics(metrics)
def on_batch_end(self, loss):
self.iteration += 1
self.lrs.append(self.layer_opt.lr)
self.iterations.append(self.iteration)
if isinstance(loss, list):
self.losses.append(loss[0])
self.save_metrics(loss[1:])
else: self.losses.append(loss)
if self.record_mom: self.momentums.append(self.layer_opt.mom)
def save_metrics(self,vals):
self.val_losses.append(delistify(vals[0]))
if len(vals) > 2: self.rec_metrics.append(vals[1:])
elif len(vals) == 2: self.rec_metrics.append(vals[1])
def plot_loss(self, n_skip=10, n_skip_end=5):
'''
plots loss function as function of iterations.
When used in Jupyternotebook, plot will be displayed in notebook. Else, plot will be displayed in console and both plot and loss are saved in save_path.
'''
if not in_ipynb(): plt.switch_backend('agg')
plt.plot(self.iterations[n_skip:-n_skip_end], self.losses[n_skip:-n_skip_end])
if not in_ipynb():
plt.savefig(os.path.join(self.save_path, 'loss_plot.png'))
np.save(os.path.join(self.save_path, 'losses.npy'), self.losses[10:])
def plot_lr(self):
'''Plots learning rate in jupyter notebook or console, depending on the enviroment of the learner.'''
if not in_ipynb():
plt.switch_backend('agg')
if self.record_mom:
fig, axs = plt.subplots(1,2,figsize=(12,4))
for i in range(0,2): axs[i].set_xlabel('iterations')
axs[0].set_ylabel('learning rate')
axs[1].set_ylabel('momentum')
axs[0].plot(self.iterations,self.lrs)
axs[1].plot(self.iterations,self.momentums)
else:
plt.xlabel("iterations")
plt.ylabel("learning rate")
plt.plot(self.iterations, self.lrs)
if not in_ipynb():
plt.savefig(os.path.join(self.save_path, 'lr_plot.png'))
class LR_Updater(LossRecorder):
'''
Abstract class where all Learning Rate updaters inherit from. (e.g., CirularLR)
Calculates and updates new learning rate and momentum at the end of each batch.
Have to be extended.
'''
def on_train_begin(self):
super().on_train_begin()
self.update_lr()
if self.record_mom:
self.update_mom()
def on_batch_end(self, loss):
res = super().on_batch_end(loss)
self.update_lr()
if self.record_mom:
self.update_mom()
return res
def update_lr(self):
new_lrs = self.calc_lr(self.init_lrs)
self.layer_opt.set_lrs(new_lrs)
def update_mom(self):
new_mom = self.calc_mom()
self.layer_opt.set_mom(new_mom)
@abstractmethod
def calc_lr(self, init_lrs): raise NotImplementedError
@abstractmethod
def calc_mom(self): raise NotImplementedError
class LR_Finder(LR_Updater):
'''
Helps you find an optimal learning rate for a model, as per suggetion of 2015 CLR paper.
Learning rate is increased in linear or log scale, depending on user input, and the result of the loss funciton is retained and can be plotted later.
'''
def __init__(self, layer_opt, nb, end_lr=10, linear=False, metrics = []):
self.linear, self.stop_dv = linear, True
ratio = end_lr/layer_opt.lr
self.lr_mult = (ratio/nb) if linear else ratio**(1/nb)
super().__init__(layer_opt,metrics=metrics)
def on_train_begin(self):
super().on_train_begin()
self.best=1e9
def calc_lr(self, init_lrs):
mult = self.lr_mult*self.iteration if self.linear else self.lr_mult**self.iteration
return init_lrs * mult
def on_batch_end(self, metrics):
loss = metrics[0] if isinstance(metrics,list) else metrics
if self.stop_dv and (math.isnan(loss) or loss>self.best*4):
return True
if (loss<self.best and self.iteration>10): self.best=loss
return super().on_batch_end(metrics)
def plot(self, n_skip=10, n_skip_end=5):
'''
Plots the loss function with respect to learning rate, in log scale.
'''
plt.ylabel("validation loss")
plt.xlabel("learning rate (log scale)")
plt.plot(self.lrs[n_skip:-(n_skip_end+1)], self.losses[n_skip:-(n_skip_end+1)])
plt.xscale('log')
plt.savefig(os.path.join(self.save_path, 'lr_loss_plot.png'))
class LR_Finder2(LR_Finder):
"""
A variant of lr_find() that helps find the best learning rate. It doesn't do
an epoch but a fixed num of iterations (which may be more or less than an epoch
depending on your data).
"""
def __init__(self, layer_opt, nb, end_lr=10, linear=False, metrics=[], stop_dv=True):
self.nb, self.metrics = nb, metrics
super().__init__(layer_opt, nb, end_lr, linear, metrics)
self.stop_dv = stop_dv
def on_batch_end(self, loss):
if self.iteration == self.nb:
return True
return super().on_batch_end(loss)
def plot(self, n_skip=10, n_skip_end=5, smoothed=True):
if self.metrics is None: self.metrics = []
n_plots = len(self.metrics)+2
fig, axs = plt.subplots(n_plots,figsize=(6,4*n_plots))
for i in range(0,n_plots): axs[i].set_xlabel('learning rate')
axs[0].set_ylabel('training loss')
axs[1].set_ylabel('validation loss')
for i,m in enumerate(self.metrics):
axs[i+2].set_ylabel(m.__name__)
if len(self.metrics) == 1:
values = self.rec_metrics
else:
values = [rec[i] for rec in self.rec_metrics]
if smoothed: values = smooth_curve(values,0.98)
axs[i+2].plot(self.lrs[n_skip:-n_skip_end], values[n_skip:-n_skip_end])
plt_val_l = smooth_curve(self.val_losses, 0.98) if smoothed else self.val_losses
axs[0].plot(self.lrs[n_skip:-n_skip_end],self.losses[n_skip:-n_skip_end])
axs[1].plot(self.lrs[n_skip:-n_skip_end],plt_val_l[n_skip:-n_skip_end])
class CosAnneal(LR_Updater):
''' Learning rate scheduler that implements a cosine annealation schedule. '''
def __init__(self, layer_opt, nb, on_cycle_end=None, cycle_mult=1):
self.nb,self.on_cycle_end,self.cycle_mult = nb,on_cycle_end,cycle_mult
super().__init__(layer_opt)
def on_train_begin(self):
self.cycle_iter,self.cycle_count=0,0
super().on_train_begin()
def calc_lr(self, init_lrs):
if self.iteration<self.nb/20:
self.cycle_iter += 1
return init_lrs/100.
cos_out = np.cos(np.pi*(self.cycle_iter)/self.nb) + 1
self.cycle_iter += 1
if self.cycle_iter==self.nb:
self.cycle_iter = 0
self.nb *= self.cycle_mult
if self.on_cycle_end: self.on_cycle_end(self, self.cycle_count)
self.cycle_count += 1
return init_lrs / 2 * cos_out
class CircularLR(LR_Updater):
'''
A learning rate updater that implements the CircularLearningRate (CLR) scheme.
Learning rate is increased then decreased linearly.
'''
def __init__(self, layer_opt, nb, div=4, cut_div=8, on_cycle_end=None, momentums=None):
self.nb,self.div,self.cut_div,self.on_cycle_end = nb,div,cut_div,on_cycle_end
if momentums is not None:
self.moms = momentums
super().__init__(layer_opt, record_mom=(momentums is not None))
def on_train_begin(self):
self.cycle_iter,self.cycle_count=0,0
super().on_train_begin()
def calc_lr(self, init_lrs):
cut_pt = self.nb//self.cut_div
if self.cycle_iter>cut_pt:
pct = 1 - (self.cycle_iter - cut_pt)/(self.nb - cut_pt)
else: pct = self.cycle_iter/cut_pt
res = init_lrs * (1 + pct*(self.div-1)) / self.div
self.cycle_iter += 1
if self.cycle_iter==self.nb:
self.cycle_iter = 0
if self.on_cycle_end: self.on_cycle_end(self, self.cycle_count)
self.cycle_count += 1
return res
def calc_mom(self):
cut_pt = self.nb//self.cut_div
if self.cycle_iter>cut_pt:
pct = (self.cycle_iter - cut_pt)/(self.nb - cut_pt)
else: pct = 1 - self.cycle_iter/cut_pt
res = self.moms[1] + pct * (self.moms[0] - self.moms[1])
return res
class CircularLR_beta(LR_Updater):
def __init__(self, layer_opt, nb, div=10, pct=10, on_cycle_end=None, momentums=None):
self.nb,self.div,self.pct,self.on_cycle_end = nb,div,pct,on_cycle_end
self.cycle_nb = int(nb * (1-pct/100) / 2)
if momentums is not None:
self.moms = momentums
super().__init__(layer_opt, record_mom=(momentums is not None))
def on_train_begin(self):
self.cycle_iter,self.cycle_count=0,0
super().on_train_begin()
def calc_lr(self, init_lrs):
if self.cycle_iter>2 * self.cycle_nb:
pct = (self.cycle_iter - 2*self.cycle_nb)/(self.nb - 2*self.cycle_nb)
res = init_lrs * (1 + (pct * (1-100)/100)) / self.div
elif self.cycle_iter>self.cycle_nb:
pct = 1 - (self.cycle_iter - self.cycle_nb)/self.cycle_nb
res = init_lrs * (1 + pct*(self.div-1)) / self.div
else:
pct = self.cycle_iter/self.cycle_nb
res = init_lrs * (1 + pct*(self.div-1)) / self.div
self.cycle_iter += 1
if self.cycle_iter==self.nb:
self.cycle_iter = 0
if self.on_cycle_end: self.on_cycle_end(self, self.cycle_count)
self.cycle_count += 1
return res
def calc_mom(self):
if self.cycle_iter>2*self.cycle_nb:
res = self.moms[0]
elif self.cycle_iter>self.cycle_nb:
pct = 1 - (self.cycle_iter - self.cycle_nb)/self.cycle_nb
res = self.moms[0] + pct * (self.moms[1] - self.moms[0])
else:
pct = self.cycle_iter/self.cycle_nb
res = self.moms[0] + pct * (self.moms[1] - self.moms[0])
return res
class SaveBestModel(LossRecorder):
""" Save weights of the best model based during training.
If metrics are provided, the first metric in the list is used to
find the best model.
If no metrics are provided, the loss is used.
Args:
model: the fastai model
lr: indicate to use test images; otherwise use validation images
name: the name of filename of the weights without '.h5'
Usage:
Briefly, you have your model 'learn' variable and call fit.
>>> learn.fit(lr, 2, cycle_len=2, cycle_mult=1, best_save_name='mybestmodel')
....
>>> learn.load('mybestmodel')
For more details see http://forums.fast.ai/t/a-code-snippet-to-save-the-best-model-during-training/12066
"""
def __init__(self, model, layer_opt, metrics, name='best_model'):
super().__init__(layer_opt)
self.name = name
self.model = model
self.best_loss = None
self.best_acc = None
self.save_method = self.save_when_only_loss if metrics==None else self.save_when_acc
def save_when_only_loss(self, metrics):
loss = metrics[0]
if self.best_loss == None or loss < self.best_loss:
self.best_loss = loss
self.model.save(f'{self.name}')
def save_when_acc(self, metrics):
loss, acc = metrics[0], metrics[1]
if self.best_acc == None or acc > self.best_acc:
self.best_acc = acc
self.best_loss = loss
self.model.save(f'{self.name}')
elif acc == self.best_acc and loss < self.best_loss:
self.best_loss = loss
self.model.save(f'{self.name}')
def on_epoch_end(self, metrics):
super().on_epoch_end(metrics)
self.save_method(metrics)
class WeightDecaySchedule(Callback):
def __init__(self, layer_opt, batch_per_epoch, cycle_len, cycle_mult, n_cycles, norm_wds=False, wds_sched_mult=None):
"""
Implements the weight decay schedule as mentioned in https://arxiv.org/abs/1711.05101
:param layer_opt: The LayerOptimizer
:param batch_per_epoch: Num batches in 1 epoch
:param cycle_len: Num epochs in initial cycle. Subsequent cycle_len = previous cycle_len * cycle_mult
:param cycle_mult: Cycle multiplier
:param n_cycles: Number of cycles to be executed
"""
super().__init__()
self.layer_opt = layer_opt
self.batch_per_epoch = batch_per_epoch
self.init_wds = np.array(layer_opt.wds) # Weights as set by user
self.init_lrs = np.array(layer_opt.lrs) # Learning rates as set by user
self.new_wds = None # Holds the new weight decay factors, calculated in on_batch_begin()
self.iteration = 0
self.epoch = 0
self.wds_sched_mult = wds_sched_mult
self.norm_wds = norm_wds
self.wds_history = list()
# Pre calculating the number of epochs in the cycle of current running epoch
self.epoch_to_num_cycles, i = dict(), 0
for cycle in range(n_cycles):
for _ in range(cycle_len):
self.epoch_to_num_cycles[i] = cycle_len
i += 1
cycle_len *= cycle_mult
def on_train_begin(self):
self.iteration = 0
self.epoch = 0
def on_batch_begin(self):
# Prepare for decay of weights
# Default weight decay (as provided by user)
wdn = self.init_wds
# Weight decay multiplier (The 'eta' in the paper). Optional.
wdm = 1.0
if self.wds_sched_mult is not None:
wdm = self.wds_sched_mult(self)
# Weight decay normalized. Optional.
if self.norm_wds:
wdn = wdn / np.sqrt(self.batch_per_epoch * self.epoch_to_num_cycles[self.epoch])
# Final wds
self.new_wds = wdm * wdn
# Set weight_decay with zeros so that it is not applied in Adam, we will apply it outside in on_batch_end()
self.layer_opt.set_wds_out(self.new_wds)
# We have to save the existing weights before the optimizer changes the values
self.iteration += 1
def on_epoch_end(self, metrics):
self.epoch += 1
class DecayType(IntEnum):
''' Data class, each decay type is assigned a number. '''
NO = 1
LINEAR = 2
COSINE = 3
EXPONENTIAL = 4
POLYNOMIAL = 5
class DecayScheduler():
'''Given initial and endvalue, this class generates the next value depending on decay type and number of iterations. (by calling next_val().) '''
def __init__(self, dec_type, num_it, start_val, end_val=None, extra=None):
self.dec_type, self.nb, self.start_val, self.end_val, self.extra = dec_type, num_it, start_val, end_val, extra
self.it = 0
if self.end_val is None and not (self.dec_type in [1,4]): self.end_val = 0
def next_val(self):
self.it += 1
if self.dec_type == DecayType.NO:
return self.start_val
elif self.dec_type == DecayType.LINEAR:
pct = self.it/self.nb
return self.start_val + pct * (self.end_val-self.start_val)
elif self.dec_type == DecayType.COSINE:
cos_out = np.cos(np.pi*(self.it)/self.nb) + 1
return self.end_val + (self.start_val-self.end_val) / 2 * cos_out
elif self.dec_type == DecayType.EXPONENTIAL:
ratio = self.end_val / self.start_val
return self.start_val * (ratio ** (self.it/self.nb))
elif self.dec_type == DecayType.POLYNOMIAL:
return self.end_val + (self.start_val-self.end_val) * (1 - self.it/self.nb)**self.extra
class TrainingPhase():
'''
Object with training information for each phase, when multiple phases are involved during training.
Used in fit_opt_sched in learner.py
'''
def __init__(self, epochs=1, opt_fn=optim.SGD, lr=1e-2, lr_decay=DecayType.NO, momentum=0.9,
momentum_decay=DecayType.NO, beta=None, wds=None, wd_loss=True):
"""
Creates an object containing all the relevant informations for one part of a model training.
Args
epochs: number of epochs to train like this
opt_fn: an optimizer (example optim.Adam)
lr: one learning rate or a tuple of the form (start_lr,end_lr)
each of those can be a list/numpy array for differential learning rates
lr_decay: a DecayType object specifying how the learning rate should change
momentum: one momentum (or beta1 in case of Adam), or a tuple of the form (start_mom,end_mom)
momentum_decay: a DecayType object specifying how the momentum should change
beta: beta2 parameter of Adam or alpha parameter of RMSProp
wds: weight decay (can be an array for differential wds)
"""
self.epochs, self.opt_fn, self.lr, self.momentum, self.beta, self.wds = epochs, opt_fn, lr, momentum, beta, wds
if isinstance(lr_decay,tuple): self.lr_decay, self.extra_lr = lr_decay
else: self.lr_decay, self.extra_lr = lr_decay, None
if isinstance(momentum_decay,tuple): self.mom_decay, self.extra_mom = momentum_decay
else: self.mom_decay, self.extra_mom = momentum_decay, None
self.wd_loss = wd_loss
def phase_begin(self, layer_opt, nb_batches):
self.layer_opt = layer_opt
if isinstance(self.lr, tuple): start_lr,end_lr = self.lr
else: start_lr, end_lr = self.lr, None
self.lr_sched = DecayScheduler(self.lr_decay, nb_batches * self.epochs, start_lr, end_lr, extra=self.extra_lr)
if isinstance(self.momentum, tuple): start_mom,end_mom = self.momentum
else: start_mom, end_mom = self.momentum, None
self.mom_sched = DecayScheduler(self.mom_decay, nb_batches * self.epochs, start_mom, end_mom, extra=self.extra_mom)
self.layer_opt.set_opt_fn(self.opt_fn)
self.layer_opt.set_lrs(start_lr)
self.layer_opt.set_mom(start_mom)
if self.beta is not None: self.layer_opt.set_beta(self.beta)
if self.wds is not None:
if not isinstance(self.wds, Iterable): self.wds=[self.wds]
if len(self.wds)==1: self.wds=self.wds*len(self.layer_opt.layer_groups)
if self.wd_loss: self.layer_opt.set_wds(self.wds)
else: self.layer_opt.set_wds_out(self.wds)
def update(self):
new_lr, new_mom = self.lr_sched.next_val(), self.mom_sched.next_val()
self.layer_opt.set_lrs(new_lr)
self.layer_opt.set_mom(new_mom)
class OptimScheduler(LossRecorder):
'''Learning rate Scheduler for training involving multiple phases.'''
def __init__(self, layer_opt, phases, nb_batches, stop_div = False):
self.phases, self.nb_batches, self.stop_div = phases, nb_batches, stop_div
super().__init__(layer_opt, record_mom=True)
def on_train_begin(self):
super().on_train_begin()
self.phase,self.best=0,1e9
def on_batch_end(self, metrics):
loss = metrics[0] if isinstance(metrics,list) else metrics
if self.stop_div and (math.isnan(loss) or loss>self.best*4):
return True
if (loss<self.best and self.iteration>10): self.best=loss
super().on_batch_end(metrics)
self.phases[self.phase].update()
def on_phase_begin(self):
self.phases[self.phase].phase_begin(self.layer_opt, self.nb_batches[self.phase])
def on_phase_end(self):
self.phase += 1
def plot_lr(self, show_text=True, show_moms=True):
"""Plots the lr rate/momentum schedule"""
phase_limits = [0]
for nb_batch, phase in zip(self.nb_batches, self.phases):
phase_limits.append(phase_limits[-1] + nb_batch * phase.epochs)
if not in_ipynb():
plt.switch_backend('agg')
np_plts = 2 if show_moms else 1
fig, axs = plt.subplots(1,np_plts,figsize=(6*np_plts,4))
if not show_moms: axs = [axs]
for i in range(np_plts): axs[i].set_xlabel('iterations')
axs[0].set_ylabel('learning rate')
axs[0].plot(self.iterations,self.lrs)
if show_moms:
axs[1].set_ylabel('momentum')
axs[1].plot(self.iterations,self.momentums)
if show_text:
for i, phase in enumerate(self.phases):
text = phase.opt_fn.__name__
if phase.wds is not None: text+='\nwds='+str(phase.wds)
if phase.beta is not None: text+='\nbeta='+str(phase.beta)
for k in range(np_plts):
if i < len(self.phases)-1:
draw_line(axs[k], phase_limits[i+1])
draw_text(axs[k], (phase_limits[i]+phase_limits[i+1])/2, text)
if not in_ipynb():
plt.savefig(os.path.join(self.save_path, 'lr_plot.png'))
def plot(self, n_skip=10, n_skip_end=5, linear=None):
if linear is None: linear = self.phases[-1].lr_decay == DecayType.LINEAR
plt.ylabel("loss")
plt.plot(self.lrs[n_skip:-n_skip_end], self.losses[n_skip:-n_skip_end])
if linear: plt.xlabel("learning rate")
else:
plt.xlabel("learning rate (log scale)")
plt.xscale('log')
def draw_line(ax,x):
xmin, xmax, ymin, ymax = ax.axis()
ax.plot([x,x],[ymin,ymax], color='red', linestyle='dashed')
def draw_text(ax,x, text):
xmin, xmax, ymin, ymax = ax.axis()
ax.text(x,(ymin+ymax)/2,text, horizontalalignment='center', verticalalignment='center', fontsize=14, alpha=0.5)
def smooth_curve(vals, beta):
avg_val = 0
smoothed = []
for (i,v) in enumerate(vals):
avg_val = beta * avg_val + (1-beta) * v
smoothed.append(avg_val/(1-beta**(i+1)))
return smoothed
+475
View File
@@ -0,0 +1,475 @@
from .imports import *
from sklearn_pandas import DataFrameMapper
from sklearn.preprocessing import LabelEncoder, Imputer, StandardScaler
from pandas.api.types import is_string_dtype, is_numeric_dtype
from sklearn.ensemble import forest
from sklearn.tree import export_graphviz
def set_plot_sizes(sml, med, big):
plt.rc('font', size=sml) # controls default text sizes
plt.rc('axes', titlesize=sml) # fontsize of the axes title
plt.rc('axes', labelsize=med) # fontsize of the x and y labels
plt.rc('xtick', labelsize=sml) # fontsize of the tick labels
plt.rc('ytick', labelsize=sml) # fontsize of the tick labels
plt.rc('legend', fontsize=sml) # legend fontsize
plt.rc('figure', titlesize=big) # fontsize of the figure title
def parallel_trees(m, fn, n_jobs=8):
return list(ProcessPoolExecutor(n_jobs).map(fn, m.estimators_))
def draw_tree(t, df, size=10, ratio=0.6, precision=0):
""" Draws a representation of a random forest in IPython.
Parameters:
-----------
t: The tree you wish to draw
df: The data used to train the tree. This is used to get the names of the features.
"""
s=export_graphviz(t, out_file=None, feature_names=df.columns, filled=True,
special_characters=True, rotate=True, precision=precision)
IPython.display.display(graphviz.Source(re.sub('Tree {',
f'Tree {{ size={size}; ratio={ratio}', s)))
def combine_date(years, months=1, days=1, weeks=None, hours=None, minutes=None,
seconds=None, milliseconds=None, microseconds=None, nanoseconds=None):
years = np.asarray(years) - 1970
months = np.asarray(months) - 1
days = np.asarray(days) - 1
types = ('<M8[Y]', '<m8[M]', '<m8[D]', '<m8[W]', '<m8[h]',
'<m8[m]', '<m8[s]', '<m8[ms]', '<m8[us]', '<m8[ns]')
vals = (years, months, days, weeks, hours, minutes, seconds,
milliseconds, microseconds, nanoseconds)
return sum(np.asarray(v, dtype=t) for t, v in zip(types, vals)
if v is not None)
def get_sample(df,n):
""" Gets a random sample of n rows from df, without replacement.
Parameters:
-----------
df: A pandas data frame, that you wish to sample from.
n: The number of rows you wish to sample.
Returns:
--------
return value: A random sample of n rows of df.
Examples:
---------
>>> df = pd.DataFrame({'col1' : [1, 2, 3], 'col2' : ['a', 'b', 'a']})
>>> df
col1 col2
0 1 a
1 2 b
2 3 a
>>> get_sample(df, 2)
col1 col2
1 2 b
2 3 a
"""
idxs = sorted(np.random.permutation(len(df))[:n])
return df.iloc[idxs].copy()
def add_datepart(df, fldname, drop=True, time=False):
"""add_datepart converts a column of df from a datetime64 to many columns containing
the information from the date. This applies changes inplace.
Parameters:
-----------
df: A pandas data frame. df gain several new columns.
fldname: A string that is the name of the date column you wish to expand.
If it is not a datetime64 series, it will be converted to one with pd.to_datetime.
drop: If true then the original date column will be removed.
time: If true time features: Hour, Minute, Second will be added.
Examples:
---------
>>> df = pd.DataFrame({ 'A' : pd.to_datetime(['3/11/2000', '3/12/2000', '3/13/2000'], infer_datetime_format=False) })
>>> df
A
0 2000-03-11
1 2000-03-12
2 2000-03-13
>>> add_datepart(df, 'A')
>>> df
AYear AMonth AWeek ADay ADayofweek ADayofyear AIs_month_end AIs_month_start AIs_quarter_end AIs_quarter_start AIs_year_end AIs_year_start AElapsed
0 2000 3 10 11 5 71 False False False False False False 952732800
1 2000 3 10 12 6 72 False False False False False False 952819200
2 2000 3 11 13 0 73 False False False False False False 952905600
"""
fld = df[fldname]
fld_dtype = fld.dtype
if isinstance(fld_dtype, pd.core.dtypes.dtypes.DatetimeTZDtype):
fld_dtype = np.datetime64
if not np.issubdtype(fld_dtype, np.datetime64):
df[fldname] = fld = pd.to_datetime(fld, infer_datetime_format=True)
targ_pre = re.sub('[Dd]ate$', '', fldname)
attr = ['Year', 'Month', 'Week', 'Day', 'Dayofweek', 'Dayofyear',
'Is_month_end', 'Is_month_start', 'Is_quarter_end', 'Is_quarter_start', 'Is_year_end', 'Is_year_start']
if time: attr = attr + ['Hour', 'Minute', 'Second']
for n in attr: df[targ_pre + n] = getattr(fld.dt, n.lower())
df[targ_pre + 'Elapsed'] = fld.astype(np.int64) // 10 ** 9
if drop: df.drop(fldname, axis=1, inplace=True)
def is_date(x): return np.issubdtype(x.dtype, np.datetime64)
def train_cats(df):
"""Change any columns of strings in a panda's dataframe to a column of
catagorical values. This applies the changes inplace.
Parameters:
-----------
df: A pandas dataframe. Any columns of strings will be changed to
categorical values.
Examples:
---------
>>> df = pd.DataFrame({'col1' : [1, 2, 3], 'col2' : ['a', 'b', 'a']})
>>> df
col1 col2
0 1 a
1 2 b
2 3 a
note the type of col2 is string
>>> train_cats(df)
>>> df
col1 col2
0 1 a
1 2 b
2 3 a
now the type of col2 is category
"""
for n,c in df.items():
if is_string_dtype(c): df[n] = c.astype('category').cat.as_ordered()
def apply_cats(df, trn):
"""Changes any columns of strings in df into categorical variables using trn as
a template for the category codes.
Parameters:
-----------
df: A pandas dataframe. Any columns of strings will be changed to
categorical values. The category codes are determined by trn.
trn: A pandas dataframe. When creating a category for df, it looks up the
what the category's code were in trn and makes those the category codes
for df.
Examples:
---------
>>> df = pd.DataFrame({'col1' : [1, 2, 3], 'col2' : ['a', 'b', 'a']})
>>> df
col1 col2
0 1 a
1 2 b
2 3 a
note the type of col2 is string
>>> train_cats(df)
>>> df
col1 col2
0 1 a
1 2 b
2 3 a
now the type of col2 is category {a : 1, b : 2}
>>> df2 = pd.DataFrame({'col1' : [1, 2, 3], 'col2' : ['b', 'a', 'a']})
>>> apply_cats(df2, df)
col1 col2
0 1 b
1 2 a
2 3 a
now the type of col is category {a : 1, b : 2}
"""
for n,c in df.items():
if (n in trn.columns) and (trn[n].dtype.name=='category'):
df[n] = pd.Categorical(c, categories=trn[n].cat.categories, ordered=True)
def fix_missing(df, col, name, na_dict):
""" Fill missing data in a column of df with the median, and add a {name}_na column
which specifies if the data was missing.
Parameters:
-----------
df: The data frame that will be changed.
col: The column of data to fix by filling in missing data.
name: The name of the new filled column in df.
na_dict: A dictionary of values to create na's of and the value to insert. If
name is not a key of na_dict the median will fill any missing data. Also
if name is not a key of na_dict and there is no missing data in col, then
no {name}_na column is not created.
Examples:
---------
>>> df = pd.DataFrame({'col1' : [1, np.NaN, 3], 'col2' : [5, 2, 2]})
>>> df
col1 col2
0 1 5
1 nan 2
2 3 2
>>> fix_missing(df, df['col1'], 'col1', {})
>>> df
col1 col2 col1_na
0 1 5 False
1 2 2 True
2 3 2 False
>>> df = pd.DataFrame({'col1' : [1, np.NaN, 3], 'col2' : [5, 2, 2]})
>>> df
col1 col2
0 1 5
1 nan 2
2 3 2
>>> fix_missing(df, df['col2'], 'col2', {})
>>> df
col1 col2
0 1 5
1 nan 2
2 3 2
>>> df = pd.DataFrame({'col1' : [1, np.NaN, 3], 'col2' : [5, 2, 2]})
>>> df
col1 col2
0 1 5
1 nan 2
2 3 2
>>> fix_missing(df, df['col1'], 'col1', {'col1' : 500})
>>> df
col1 col2 col1_na
0 1 5 False
1 500 2 True
2 3 2 False
"""
if is_numeric_dtype(col):
if pd.isnull(col).sum() or (name in na_dict):
df[name+'_na'] = pd.isnull(col)
filler = na_dict[name] if name in na_dict else col.median()
df[name] = col.fillna(filler)
na_dict[name] = filler
return na_dict
def numericalize(df, col, name, max_n_cat):
""" Changes the column col from a categorical type to it's integer codes.
Parameters:
-----------
df: A pandas dataframe. df[name] will be filled with the integer codes from
col.
col: The column you wish to change into the categories.
name: The column name you wish to insert into df. This column will hold the
integer codes.
max_n_cat: If col has more categories than max_n_cat it will not change the
it to its integer codes. If max_n_cat is None, then col will always be
converted.
Examples:
---------
>>> df = pd.DataFrame({'col1' : [1, 2, 3], 'col2' : ['a', 'b', 'a']})
>>> df
col1 col2
0 1 a
1 2 b
2 3 a
note the type of col2 is string
>>> train_cats(df)
>>> df
col1 col2
0 1 a
1 2 b
2 3 a
now the type of col2 is category { a : 1, b : 2}
>>> numericalize(df, df['col2'], 'col3', None)
col1 col2 col3
0 1 a 1
1 2 b 2
2 3 a 1
"""
if not is_numeric_dtype(col) and ( max_n_cat is None or col.nunique()>max_n_cat):
df[name] = col.cat.codes+1
def scale_vars(df, mapper):
warnings.filterwarnings('ignore', category=sklearn.exceptions.DataConversionWarning)
if mapper is None:
map_f = [([n],StandardScaler()) for n in df.columns if is_numeric_dtype(df[n])]
mapper = DataFrameMapper(map_f).fit(df)
df[mapper.transformed_names_] = mapper.transform(df)
return mapper
def proc_df(df, y_fld=None, skip_flds=None, ignore_flds=None, do_scale=False, na_dict=None,
preproc_fn=None, max_n_cat=None, subset=None, mapper=None):
""" proc_df takes a data frame df and splits off the response variable, and
changes the df into an entirely numeric dataframe.
Parameters:
-----------
df: The data frame you wish to process.
y_fld: The name of the response variable
skip_flds: A list of fields that dropped from df.
ignore_flds: A list of fields that are ignored during processing.
do_scale: Standardizes each column in df. Takes Boolean Values(True,False)
na_dict: a dictionary of na columns to add. Na columns are also added if there
are any missing values.
preproc_fn: A function that gets applied to df.
max_n_cat: The maximum number of categories to break into dummy values, instead
of integer codes.
subset: Takes a random subset of size subset from df.
mapper: If do_scale is set as True, the mapper variable
calculates the values used for scaling of variables during training time (mean and standard deviation).
Returns:
--------
[x, y, nas, mapper(optional)]:
x: x is the transformed version of df. x will not have the response variable
and is entirely numeric.
y: y is the response variable
nas: returns a dictionary of which nas it created, and the associated median.
mapper: A DataFrameMapper which stores the mean and standard deviation of the corresponding continuous
variables which is then used for scaling of during test-time.
Examples:
---------
>>> df = pd.DataFrame({'col1' : [1, 2, 3], 'col2' : ['a', 'b', 'a']})
>>> df
col1 col2
0 1 a
1 2 b
2 3 a
note the type of col2 is string
>>> train_cats(df)
>>> df
col1 col2
0 1 a
1 2 b
2 3 a
now the type of col2 is category { a : 1, b : 2}
>>> x, y, nas = proc_df(df, 'col1')
>>> x
col2
0 1
1 2
2 1
>>> data = DataFrame(pet=["cat", "dog", "dog", "fish", "cat", "dog", "cat", "fish"],
children=[4., 6, 3, 3, 2, 3, 5, 4],
salary=[90, 24, 44, 27, 32, 59, 36, 27])
>>> mapper = DataFrameMapper([(:pet, LabelBinarizer()),
([:children], StandardScaler())])
>>>round(fit_transform!(mapper, copy(data)), 2)
8x4 Array{Float64,2}:
1.0 0.0 0.0 0.21
0.0 1.0 0.0 1.88
0.0 1.0 0.0 -0.63
0.0 0.0 1.0 -0.63
1.0 0.0 0.0 -1.46
0.0 1.0 0.0 -0.63
1.0 0.0 0.0 1.04
0.0 0.0 1.0 0.21
"""
if not ignore_flds: ignore_flds=[]
if not skip_flds: skip_flds=[]
if subset: df = get_sample(df,subset)
else: df = df.copy()
ignored_flds = df.loc[:, ignore_flds]
df.drop(ignore_flds, axis=1, inplace=True)
if preproc_fn: preproc_fn(df)
if y_fld is None: y = None
else:
if not is_numeric_dtype(df[y_fld]): df[y_fld] = df[y_fld].cat.codes
y = df[y_fld].values
skip_flds += [y_fld]
df.drop(skip_flds, axis=1, inplace=True)
if na_dict is None: na_dict = {}
for n,c in df.items(): na_dict = fix_missing(df, c, n, na_dict)
if do_scale: mapper = scale_vars(df, mapper)
for n,c in df.items(): numericalize(df, c, n, max_n_cat)
df = pd.get_dummies(df, dummy_na=True)
df = pd.concat([ignored_flds, df], axis=1)
res = [df, y, na_dict]
if do_scale: res = res + [mapper]
return res
def rf_feat_importance(m, df):
return pd.DataFrame({'cols':df.columns, 'imp':m.feature_importances_}
).sort_values('imp', ascending=False)
def set_rf_samples(n):
""" Changes Scikit learn's random forests to give each tree a random sample of
n random rows.
"""
forest._generate_sample_indices = (lambda rs, n_samples:
forest.check_random_state(rs).randint(0, n_samples, n))
def reset_rf_samples():
""" Undoes the changes produced by set_rf_samples.
"""
forest._generate_sample_indices = (lambda rs, n_samples:
forest.check_random_state(rs).randint(0, n_samples, n_samples))
def get_nn_mappers(df, cat_vars, contin_vars):
# Replace nulls with 0 for continuous, "" for categorical.
for v in contin_vars: df[v] = df[v].fillna(df[v].max()+100,)
for v in cat_vars: df[v].fillna('#NA#', inplace=True)
# list of tuples, containing variable and instance of a transformer for that variable
# for categoricals, use LabelEncoder to map to integers. For continuous, standardize
cat_maps = [(o, LabelEncoder()) for o in cat_vars]
contin_maps = [([o], StandardScaler()) for o in contin_vars]
return DataFrameMapper(cat_maps).fit(df), DataFrameMapper(contin_maps).fit(df)
+83
View File
@@ -0,0 +1,83 @@
"""
From the paper:
Averaging Weights Leads to Wider Optima and Better Generalization
Pavel Izmailov, Dmitrii Podoprikhin, Timur Garipov, Dmitry Vetrov, Andrew Gordon Wilson
https://arxiv.org/abs/1803.05407
2018
Author's implementation: https://github.com/timgaripov/swa
"""
import torch
from .sgdr import *
from .core import *
class SWA(Callback):
def __init__(self, model, swa_model, swa_start):
super().__init__()
self.model,self.swa_model,self.swa_start=model,swa_model,swa_start
def on_train_begin(self):
self.epoch = 0
self.swa_n = 0
def on_epoch_end(self, metrics):
if (self.epoch + 1) >= self.swa_start:
self.update_average_model()
self.swa_n += 1
self.epoch += 1
def update_average_model(self):
# update running average of parameters
model_params = self.model.parameters()
swa_params = self.swa_model.parameters()
for model_param, swa_param in zip(model_params, swa_params):
swa_param.data *= self.swa_n
swa_param.data += model_param.data
swa_param.data /= (self.swa_n + 1)
def collect_bn_modules(module, bn_modules):
if isinstance(module, torch.nn.modules.batchnorm._BatchNorm):
bn_modules.append(module)
def fix_batchnorm(swa_model, train_dl):
"""
During training, batch norm layers keep track of a running mean and
variance of the previous layer's activations. Because the parameters
of the SWA model are computed as the average of other models' parameters,
the SWA model never sees the training data itself, and therefore has no
opportunity to compute the correct batch norm statistics. Before performing
inference with the SWA model, we perform a single pass over the training data
to calculate an accurate running mean and variance for each batch norm layer.
"""
bn_modules = []
swa_model.apply(lambda module: collect_bn_modules(module, bn_modules))
if not bn_modules: return
swa_model.train()
for module in bn_modules:
module.running_mean = torch.zeros_like(module.running_mean)
module.running_var = torch.ones_like(module.running_var)
momenta = [m.momentum for m in bn_modules]
inputs_seen = 0
for (*x,y) in iter(train_dl):
xs = V(x)
batch_size = xs[0].size(0)
momentum = batch_size / (inputs_seen + batch_size)
for module in bn_modules:
module.momentum = momentum
res = swa_model(*xs)
inputs_seen += batch_size
for module, momentum in zip(bn_modules, momenta):
module.momentum = momentum
+221
View File
@@ -0,0 +1,221 @@
from .core import *
from .learner import *
from .lm_rnn import *
from torch.utils.data.sampler import Sampler
import spacy
from spacy.symbols import ORTH
re_tok = re.compile(f'([{string.punctuation}“”¨«»®´·º½¾¿¡§£₤‘’])')
def tokenize(s): return re_tok.sub(r' \1 ', s).split()
def texts_labels_from_folders(path, folders):
texts,labels = [],[]
for idx,label in enumerate(folders):
for fname in glob(os.path.join(path, label, '*.*')):
texts.append(open(fname, 'r').read())
labels.append(idx)
return texts, np.array(labels).astype(np.int64)
def numericalize_tok(tokens, max_vocab=50000, min_freq=0, unk_tok="_unk_", pad_tok="_pad_", bos_tok="_bos_", eos_tok="_eos_"):
"""Takes in text tokens and returns int2tok and tok2int converters
Arguments:
tokens(list): List of tokens. Can be a list of strings, or a list of lists of strings.
max_vocab(int): Number of tokens to return in the vocab (sorted by frequency)
min_freq(int): Minimum number of instances a token must be present in order to be preserved.
unk_tok(str): Token to use when unknown tokens are encountered in the source text.
pad_tok(str): Token to use when padding sequences.
"""
if isinstance(tokens, str):
raise ValueError("Expected to receive a list of tokens. Received a string instead")
if isinstance(tokens[0], list):
tokens = [p for o in tokens for p in o]
freq = Counter(tokens)
int2tok = [o for o,c in freq.most_common(max_vocab) if c>min_freq]
unk_id = 3
int2tok.insert(0, bos_tok)
int2tok.insert(1, pad_tok)
int2tok.insert(2, eos_tok)
int2tok.insert(unk_id, unk_tok)
tok2int = collections.defaultdict(lambda:unk_id, {v:k for k,v in enumerate(int2tok)})
return int2tok, tok2int
class Tokenizer():
def __init__(self, lang='en'):
self.re_br = re.compile(r'<\s*br\s*/?>', re.IGNORECASE)
self.tok = spacy.load(lang)
for w in ('<eos>','<bos>','<unk>'):
self.tok.tokenizer.add_special_case(w, [{ORTH: w}])
def sub_br(self,x): return self.re_br.sub("\n", x)
def spacy_tok(self,x):
return [t.text for t in self.tok.tokenizer(self.sub_br(x))]
re_rep = re.compile(r'(\S)(\1{3,})')
re_word_rep = re.compile(r'(\b\w+\W+)(\1{3,})')
@staticmethod
def replace_rep(m):
TK_REP = 'tk_rep'
c,cc = m.groups()
return f' {TK_REP} {len(cc)+1} {c} '
@staticmethod
def replace_wrep(m):
TK_WREP = 'tk_wrep'
c,cc = m.groups()
return f' {TK_WREP} {len(cc.split())+1} {c} '
@staticmethod
def do_caps(ss):
TOK_UP,TOK_SENT,TOK_MIX = ' t_up ',' t_st ',' t_mx '
res = []
prev='.'
re_word = re.compile('\w')
re_nonsp = re.compile('\S')
for s in re.findall(r'\w+|\W+', ss):
res += ([TOK_UP,s.lower()] if (s.isupper() and (len(s)>2))
# else [TOK_SENT,s.lower()] if (s.istitle() and re_word.search(prev))
else [s.lower()])
# if re_nonsp.search(s): prev = s
return ''.join(res)
def proc_text(self, s):
s = self.re_rep.sub(Tokenizer.replace_rep, s)
s = self.re_word_rep.sub(Tokenizer.replace_wrep, s)
s = Tokenizer.do_caps(s)
s = re.sub(r'([/#])', r' \1 ', s)
s = re.sub(' {2,}', ' ', s)
return self.spacy_tok(s)
@staticmethod
def proc_all(ss, lang):
tok = Tokenizer(lang)
return [tok.proc_text(s) for s in ss]
@staticmethod
def proc_all_mp(ss, lang='en'):
ncpus = num_cpus()//2
with ProcessPoolExecutor(ncpus) as e:
return sum(e.map(Tokenizer.proc_all, ss, [lang]*len(ss)), [])
class TextDataset(Dataset):
def __init__(self, x, y, backwards=False, sos=None, eos=None):
self.x,self.y,self.backwards,self.sos,self.eos = x,y,backwards,sos,eos
def __getitem__(self, idx):
x = self.x[idx]
if self.backwards: x = list(reversed(x))
if self.eos is not None: x = x + [self.eos]
if self.sos is not None: x = [self.sos]+x
return np.array(x),self.y[idx]
def __len__(self): return len(self.x)
class SortSampler(Sampler):
def __init__(self, data_source, key): self.data_source,self.key = data_source,key
def __len__(self): return len(self.data_source)
def __iter__(self):
return iter(sorted(range(len(self.data_source)), key=self.key, reverse=True))
class SortishSampler(Sampler):
"""Returns an iterator that traverses the the data in randomly ordered batches that are approximately the same size.
The max key size batch is always returned in the first call because of pytorch cuda memory allocation sequencing.
Without that max key returned first multiple buffers may be allocated when the first created isn't large enough
to hold the next in the sequence.
"""
def __init__(self, data_source, key, bs):
self.data_source,self.key,self.bs = data_source,key,bs
def __len__(self): return len(self.data_source)
def __iter__(self):
idxs = np.random.permutation(len(self.data_source))
sz = self.bs*50
ck_idx = [idxs[i:i+sz] for i in range(0, len(idxs), sz)]
sort_idx = np.concatenate([sorted(s, key=self.key, reverse=True) for s in ck_idx])
sz = self.bs
ck_idx = [sort_idx[i:i+sz] for i in range(0, len(sort_idx), sz)]
max_ck = np.argmax([self.key(ck[0]) for ck in ck_idx]) # find the chunk with the largest key,
ck_idx[0],ck_idx[max_ck] = ck_idx[max_ck],ck_idx[0] # then make sure it goes first.
sort_idx = np.concatenate(np.random.permutation(ck_idx[1:]))
sort_idx = np.concatenate((ck_idx[0], sort_idx))
return iter(sort_idx)
class LanguageModelLoader():
""" Returns a language model iterator that iterates through batches that are of length N(bptt,5)
The first batch returned is always bptt+25; the max possible width. This is done because of they way that pytorch
allocates cuda memory in order to prevent multiple buffers from being created as the batch width grows.
"""
def __init__(self, nums, bs, bptt, backwards=False):
self.bs,self.bptt,self.backwards = bs,bptt,backwards
self.data = self.batchify(nums)
self.i,self.iter = 0,0
self.n = len(self.data)
def __iter__(self):
self.i,self.iter = 0,0
while self.i < self.n-1 and self.iter<len(self):
if self.i == 0:
seq_len = self.bptt + 5 * 5
else:
bptt = self.bptt if np.random.random() < 0.95 else self.bptt / 2.
seq_len = max(5, int(np.random.normal(bptt, 5)))
res = self.get_batch(self.i, seq_len)
self.i += seq_len
self.iter += 1
yield res
def __len__(self): return self.n // self.bptt - 1
def batchify(self, data):
nb = data.shape[0] // self.bs
data = np.array(data[:nb*self.bs])
data = data.reshape(self.bs, -1).T
if self.backwards: data=data[::-1]
return T(data)
def get_batch(self, i, seq_len):
source = self.data
seq_len = min(seq_len, len(source) - 1 - i)
return source[i:i+seq_len], source[i+1:i+1+seq_len].view(-1)
class LanguageModel(BasicModel):
def get_layer_groups(self):
m = self.model[0]
return [*zip(m.rnns, m.dropouths), (self.model[1], m.dropouti)]
class LanguageModelData():
def __init__(self, path, pad_idx, n_tok, trn_dl, val_dl, test_dl=None, **kwargs):
self.path,self.pad_idx,self.n_tok = path,pad_idx,n_tok
self.trn_dl,self.val_dl,self.test_dl = trn_dl,val_dl,test_dl
def get_model(self, opt_fn, emb_sz, n_hid, n_layers, **kwargs):
m = get_language_model(self.n_tok, emb_sz, n_hid, n_layers, self.pad_idx, **kwargs)
model = LanguageModel(to_gpu(m))
return RNN_Learner(self, model, opt_fn=opt_fn)
class RNN_Learner(Learner):
def __init__(self, data, models, **kwargs):
super().__init__(data, models, **kwargs)
def _get_crit(self, data): return F.cross_entropy
def fit(self, *args, **kwargs): return super().fit(*args, **kwargs, seq_first=True)
def save_encoder(self, name): save_model(self.model[0], self.get_model_path(name))
def load_encoder(self, name): load_model(self.model[0], self.get_model_path(name))
class TextModel(BasicModel):
def get_layer_groups(self):
m = self.model[0]
return [(m.encoder, m.dropouti), *zip(m.rnns, m.dropouths), (self.model[1])]
+101
View File
@@ -0,0 +1,101 @@
import os
from distutils.version import LooseVersion
import torch, torchvision, torchtext
from torch import nn, cuda, backends, FloatTensor, LongTensor, optim
import torch.nn.functional as F
from torch.autograd import Variable
from torch.utils.data import Dataset, TensorDataset
from torch.nn.init import kaiming_uniform, kaiming_normal
from torchvision.transforms import Compose
from torchvision.models import resnet18, resnet34, resnet50, resnet101, resnet152
from torchvision.models import vgg16_bn, vgg19_bn
from torchvision.models import densenet121, densenet161, densenet169, densenet201
from .models.resnext_50_32x4d import resnext_50_32x4d
from .models.resnext_101_32x4d import resnext_101_32x4d
from .models.resnext_101_64x4d import resnext_101_64x4d
from .models.wrn_50_2f import wrn_50_2f
from .models.inceptionresnetv2 import InceptionResnetV2
from .models.inceptionv4 import inceptionv4
from .models.nasnet import nasnetalarge
from .models.fa_resnet import *
import warnings
warnings.filterwarnings('ignore', message='Implicit dimension choice', category=UserWarning)
def children(m): return m if isinstance(m, (list, tuple)) else list(m.children())
def save_model(m, p): torch.save(m.state_dict(), p)
def load_model(m, p):
sd = torch.load(p, map_location=lambda storage, loc: storage)
names = set(m.state_dict().keys())
for n in list(sd.keys()): # list "detatches" the iterator
if n not in names and n+'_raw' in names:
if n+'_raw' not in sd: sd[n+'_raw'] = sd[n]
del sd[n]
m.load_state_dict(sd)
def load_pre(pre, f, fn):
m = f()
path = os.path.dirname(__file__)
if pre: load_model(m, f'{path}/weights/{fn}.pth')
return m
def _fastai_model(name, paper_title, paper_href):
def add_docs_wrapper(f):
f.__doc__ = f"""{name} model from
`"{paper_title}" <{paper_href}>`_
Args:
pre (bool): If True, returns a model pre-trained on ImageNet
"""
return f
return add_docs_wrapper
@_fastai_model('Inception 4', 'Inception-v4, Inception-ResNet and the Impact of Residual Connections on Learning',
'https://arxiv.org/pdf/1602.07261.pdf')
def inception_4(pre): return children(inceptionv4(pretrained=pre))[0]
@_fastai_model('Inception 4', 'Inception-v4, Inception-ResNet and the Impact of Residual Connections on Learning',
'https://arxiv.org/pdf/1602.07261.pdf')
def inceptionresnet_2(pre): return load_pre(pre, InceptionResnetV2, 'inceptionresnetv2-d579a627')
@_fastai_model('ResNeXt 50', 'Aggregated Residual Transformations for Deep Neural Networks',
'https://arxiv.org/abs/1611.05431')
def resnext50(pre): return load_pre(pre, resnext_50_32x4d, 'resnext_50_32x4d')
@_fastai_model('ResNeXt 101_32', 'Aggregated Residual Transformations for Deep Neural Networks',
'https://arxiv.org/abs/1611.05431')
def resnext101(pre): return load_pre(pre, resnext_101_32x4d, 'resnext_101_32x4d')
@_fastai_model('ResNeXt 101_64', 'Aggregated Residual Transformations for Deep Neural Networks',
'https://arxiv.org/abs/1611.05431')
def resnext101_64(pre): return load_pre(pre, resnext_101_64x4d, 'resnext_101_64x4d')
@_fastai_model('Wide Residual Networks', 'Wide Residual Networks',
'https://arxiv.org/pdf/1605.07146.pdf')
def wrn(pre): return load_pre(pre, wrn_50_2f, 'wrn_50_2f')
@_fastai_model('Densenet-121', 'Densely Connected Convolutional Networks',
'https://arxiv.org/pdf/1608.06993.pdf')
def dn121(pre): return children(densenet121(pre))[0]
@_fastai_model('Densenet-169', 'Densely Connected Convolutional Networks',
'https://arxiv.org/pdf/1608.06993.pdf')
def dn161(pre): return children(densenet161(pre))[0]
@_fastai_model('Densenet-161', 'Densely Connected Convolutional Networks',
'https://arxiv.org/pdf/1608.06993.pdf')
def dn169(pre): return children(densenet169(pre))[0]
@_fastai_model('Densenet-201', 'Densely Connected Convolutional Networks',
'https://arxiv.org/pdf/1608.06993.pdf')
def dn201(pre): return children(densenet201(pre))[0]
@_fastai_model('Vgg-16 with batch norm added', 'Very Deep Convolutional Networks for Large-Scale Image Recognition',
'https://arxiv.org/pdf/1409.1556.pdf')
def vgg16(pre): return children(vgg16_bn(pre))[0]
@_fastai_model('Vgg-19 with batch norm added', 'Very Deep Convolutional Networks for Large-Scale Image Recognition',
'https://arxiv.org/pdf/1409.1556.pdf')
def vgg19(pre): return children(vgg19_bn(pre))[0]
+182
View File
@@ -0,0 +1,182 @@
import math
import torch
from torch.autograd import Variable
from cupy.cuda import function
from cupy.cuda.compiler import _NVRTCProgram
from collections import namedtuple
##Adapted from the code here https://github.com/salesforce/pytorch-qrnn, implementation of the QRNN by
##Bradbury, James and Merity, Stephen and Xiong, Caiming and Socher, Richard
##https://arxiv.org/abs/1611.01576
kernel = '''
extern "C"
__global__ void recurrent_forget_mult(float *dst, const float *f, const float *x, int SEQ, int BATCH, int HIDDEN)
{
/*
Note: destination is assumed to be one timestep longer than f or x where dst[0] = h_{-1}
This means dst array has a separate index than that of f or x
*/
int hid = blockIdx.x * blockDim.x + threadIdx.x;
int bid = blockIdx.y * blockDim.y + threadIdx.y;
if(hid >= HIDDEN || bid >= BATCH)
return;
//
for (int ts = 0 + 1; ts < SEQ + 1; ts++) {
// Good sanity check for debugging - only perform additions to a zeroed chunk of memory
// Addition seems atomic or near atomic - you should get incorrect answers if doubling up via threads
// Note: the index i needs to be offset by one as f[0] (f_t) is used for dst[1] (h_t) etc
// To move timesteps, we step HIDDEN * BATCH
// To move batches, we move HIDDEN
// To move neurons, we move +- 1
// Note: dst[dst_i] = ts * 100 + bid * 10 + hid; is useful for debugging
int i = (ts - 1) * HIDDEN * BATCH + bid * HIDDEN + hid;
int dst_i = (ts - 0) * HIDDEN * BATCH + bid * HIDDEN + hid;
int dst_iminus1 = (ts - 1) * HIDDEN * BATCH + bid * HIDDEN + hid;
dst[dst_i] = f[i] * x[i];
dst[dst_i] += (1 - f[i]) * dst[dst_iminus1];
}
}
extern "C"
__global__ void bwd_recurrent_forget_mult(const float *h, const float *f, const float *x, const float *gh, float *gf, float *gx, float *ghinit, int SEQ, int BATCH, int HIDDEN)
{
/*
Note: h is assumed to be one timestep longer than f, x, gf, gx, or gh where dst[0] = h_{-1}
This means dst array has a separate index than that of f or x
*/
int hid = blockIdx.x * blockDim.x + threadIdx.x;
int bid = blockIdx.y * blockDim.y + threadIdx.y;
if(hid >= HIDDEN || bid >= BATCH)
return;
//
double running_f = 0;
for (int ts = SEQ - 1 + 1; ts >= 0 + 1; ts--) {
int i = (ts - 1) * HIDDEN * BATCH + bid * HIDDEN + hid;
int dst_i = (ts - 0) * HIDDEN * BATCH + bid * HIDDEN + hid;
int dst_iminus1 = (ts - 1) * HIDDEN * BATCH + bid * HIDDEN + hid;
//
running_f += gh[dst_iminus1];
// Gradient of X
gx[i] = f[i] * running_f;
// Gradient of F
gf[i] = (x[i] - h[dst_iminus1]) * running_f;
//
// The line below is likely more numerically stable than (1 - f[i]) * running_f;
running_f = running_f - f[i] * running_f;
}
ghinit[bid * HIDDEN + hid] = running_f;
}
'''
###
class CPUForgetMult(torch.nn.Module):
def __init__(self):
super(CPUForgetMult, self).__init__()
def forward(self, f, x, hidden_init=None):
result = []
###
forgets = f.split(1, dim=0)
prev_h = hidden_init
for i, h in enumerate((f * x).split(1, dim=0)):
if prev_h is not None: h = h + (1 - forgets[i]) * prev_h
# h is (1, batch, hidden) when it needs to be (batch_hidden)
# Calling squeeze will result in badness if batch size is 1
h = h.view(h.size()[1:])
result.append(h)
prev_h = h
###
return torch.stack(result)
class GPUForgetMult(torch.autograd.Function):
configured_gpus = {}
ptx = None
def __init__(self):
super(GPUForgetMult, self).__init__()
def compile(self):
if self.ptx is None:
program = _NVRTCProgram(kernel.encode(), 'recurrent_forget_mult.cu'.encode())
GPUForgetMult.ptx = program.compile()
if torch.cuda.current_device() not in GPUForgetMult.configured_gpus:
m = function.Module()
m.load(bytes(self.ptx.encode()))
self.forget_mult = m.get_function('recurrent_forget_mult')
self.bwd_forget_mult = m.get_function('bwd_recurrent_forget_mult')
Stream = namedtuple('Stream', ['ptr'])
self.stream = Stream(ptr=torch.cuda.current_stream().cuda_stream)
GPUForgetMult.configured_gpus[torch.cuda.current_device()] = (self.forget_mult, self.bwd_forget_mult, self.stream)
self.forget_mult, self.bwd_forget_mult, self.stream = GPUForgetMult.configured_gpus[torch.cuda.current_device()]
def forward(self, f, x, hidden_init=None):
self.compile()
seq_size, batch_size, hidden_size = f.size()
result = f.new(seq_size + 1, batch_size, hidden_size)
# We only zero the result array (result[0]) if we don't set a hidden initial state
# All other values (result[1:]) are overwritten by default
if hidden_init is not None: result[0, :, :] = hidden_init
else: result = result.zero_()
###
grid_hidden_size = min(hidden_size, 512)
grid = (math.ceil(hidden_size / grid_hidden_size), batch_size)
self.forget_mult(grid=grid, block=(grid_hidden_size, 1), args=[result.data_ptr(), f.data_ptr(), x.data_ptr(), seq_size, batch_size, hidden_size], stream=self.stream)
self.save_for_backward(f, x, hidden_init)
self.result = result
return result[1:, :, :]
def backward(self, grad_h):
self.compile()
f, x, hidden_init = self.saved_tensors
h = self.result
###
seq_size, batch_size, hidden_size = f.size()
# Zeroing is not necessary as these will be overwritten
grad_f = f.new(*f.size())
grad_x = f.new(*f.size())
grad_h_init = f.new(batch_size, hidden_size)
###
grid_hidden_size = min(hidden_size, 512)
grid = (math.ceil(hidden_size / grid_hidden_size), batch_size)
self.bwd_forget_mult(grid=grid, block=(grid_hidden_size, 1), args=[h.data_ptr(), f.data_ptr(), x.data_ptr(), grad_h.data_ptr(), grad_f.data_ptr(), grad_x.data_ptr(), grad_h_init.data_ptr(), seq_size, batch_size, hidden_size], stream=self.stream)
###
if hidden_init is not None:
return grad_f, grad_x, grad_h_init
return grad_f, grad_x
class ForgetMult(torch.nn.Module):
r"""ForgetMult computes a simple recurrent equation:
h_t = f_t * x_t + (1 - f_t) * h_{t-1}
This equation is equivalent to dynamic weighted averaging.
Inputs: X, hidden
- X (seq_len, batch, input_size): tensor containing the features of the input sequence.
- F (seq_len, batch, input_size): tensor containing the forget gate values, assumed in range [0, 1].
- hidden_init (batch, input_size): tensor containing the initial hidden state for the recurrence (h_{t-1}).
- use_cuda: If True, use the fast element-wise CUDA kernel for recurrence. If False, uses naive for loop. Default: True.
"""
def __init__(self):
super(ForgetMult, self).__init__()
def forward(self, f, x, hidden_init=None, use_cuda=True):
# Use CUDA by default unless it's available
use_cuda = use_cuda and torch.cuda.is_available()
# Ensure the user is aware when ForgetMult is not GPU version as it's far faster
if use_cuda: assert f.is_cuda and x.is_cuda, 'GPU ForgetMult with fast element-wise CUDA kernel requested but tensors not on GPU'
###
# Avoiding 'RuntimeError: expected a Variable argument, but got NoneType' when hidden_init is None
if hidden_init is None: return GPUForgetMult()(f, x) if use_cuda else CPUForgetMult()(f, x)
return GPUForgetMult()(f, x, hidden_init) if use_cuda else CPUForgetMult()(f, x, hidden_init)
+172
View File
@@ -0,0 +1,172 @@
import torch
from torch import nn
from torch.autograd import Variable
from .forget_mult import ForgetMult
##Adapted from the code here https://github.com/salesforce/pytorch-qrnn, implementation of the QRNN by
##Bradbury, James and Merity, Stephen and Xiong, Caiming and Socher, Richard
##https://arxiv.org/abs/1611.01576
class QRNNLayer(nn.Module):
r"""Applies a single layer Quasi-Recurrent Neural Network (QRNN) to an input sequence.
Args:
input_size: The number of expected features in the input x.
hidden_size: The number of features in the hidden state h. If not specified, the input size is used.
save_prev_x: Whether to store previous inputs for use in future convolutional windows (i.e. for a continuing sequence such as in language modeling). If true, you must call reset to remove cached previous values of x. Default: False.
window: Defines the size of the convolutional window (how many previous tokens to look when computing the QRNN values). Supports 1 and 2. Default: 1.
zoneout: Whether to apply zoneout (i.e. failing to update elements in the hidden state) to the hidden state updates. Default: 0.
output_gate: If True, performs QRNN-fo (applying an output gate to the output). If False, performs QRNN-f. Default: True.
use_cuda: If True, uses fast custom CUDA kernel. If False, uses naive for loop. Default: True.
Inputs: X, hidden
- X (seq_len, batch, input_size): tensor containing the features of the input sequence.
- hidden (batch, hidden_size): tensor containing the initial hidden state for the QRNN.
Outputs: output, h_n
- output (seq_len, batch, hidden_size): tensor containing the output of the QRNN for each timestep.
- h_n (batch, hidden_size): tensor containing the hidden state for t=seq_len
"""
def __init__(self, input_size, hidden_size=None, save_prev_x=False, zoneout=0, window=1, output_gate=True, use_cuda=True):
super(QRNNLayer, self).__init__()
assert window in [1, 2], "This QRNN implementation currently only handles convolutional window of size 1 or size 2"
self.window = window
self.input_size = input_size
self.hidden_size = hidden_size if hidden_size else input_size
self.zoneout = zoneout
self.save_prev_x = save_prev_x
self.prevX = None
self.output_gate = output_gate
self.use_cuda = use_cuda
# One large matmul with concat is faster than N small matmuls and no concat
self.linear = nn.Linear(self.window * self.input_size, 3 * self.hidden_size if self.output_gate else 2 * self.hidden_size)
def reset(self):
# If you are saving the previous value of x, you should call this when starting with a new state
self.prevX = None
def forward(self, X, hidden=None):
seq_len, batch_size, _ = X.size()
source = None
if self.window == 1:
source = X
elif self.window == 2:
# Construct the x_{t-1} tensor with optional x_{-1}, otherwise a zeroed out value for x_{-1}
Xm1 = []
Xm1.append(self.prevX if self.prevX is not None else X[:1, :, :] * 0)
# Note: in case of len(X) == 1, X[:-1, :, :] results in slicing of empty tensor == bad
if len(X) > 1:
Xm1.append(X[:-1, :, :])
Xm1 = torch.cat(Xm1, 0)
# Convert two (seq_len, batch_size, hidden) tensors to (seq_len, batch_size, 2 * hidden)
source = torch.cat([X, Xm1], 2)
# Matrix multiplication for the three outputs: Z, F, O
Y = self.linear(source)
# Convert the tensor back to (batch, seq_len, len([Z, F, O]) * hidden_size)
if self.output_gate:
Y = Y.view(seq_len, batch_size, 3 * self.hidden_size)
Z, F, O = Y.chunk(3, dim=2)
else:
Y = Y.view(seq_len, batch_size, 2 * self.hidden_size)
Z, F = Y.chunk(2, dim=2)
###
Z = torch.nn.functional.tanh(Z)
F = torch.nn.functional.sigmoid(F)
# If zoneout is specified, we perform dropout on the forget gates in F
# If an element of F is zero, that means the corresponding neuron keeps the old value
if self.zoneout:
if self.training:
mask = Variable(F.data.new(*F.size()).bernoulli_(1 - self.zoneout), requires_grad=False)
F = F * mask
else:
F *= 1 - self.zoneout
# Ensure the memory is laid out as expected for the CUDA kernel
# This is a null op if the tensor is already contiguous
Z = Z.contiguous()
F = F.contiguous()
# The O gate doesn't need to be contiguous as it isn't used in the CUDA kernel
# Forget Mult
# For testing QRNN without ForgetMult CUDA kernel, C = Z * F may be useful
C = ForgetMult()(F, Z, hidden, use_cuda=self.use_cuda)
# Apply (potentially optional) output gate
if self.output_gate:
H = torch.nn.functional.sigmoid(O) * C
else:
H = C
# In an optimal world we may want to backprop to x_{t-1} but ...
if self.window > 1 and self.save_prev_x:
self.prevX = Variable(X[-1:, :, :].data, requires_grad=False)
return H, C[-1:, :, :]
class QRNN(torch.nn.Module):
r"""Applies a multiple layer Quasi-Recurrent Neural Network (QRNN) to an input sequence.
Args:
input_size: The number of expected features in the input x.
hidden_size: The number of features in the hidden state h. If not specified, the input size is used.
num_layers: The number of QRNN layers to produce.
layers: List of preconstructed QRNN layers to use for the QRNN module (optional).
save_prev_x: Whether to store previous inputs for use in future convolutional windows (i.e. for a continuing sequence such as in language modeling). If true, you must call reset to remove cached previous values of x. Default: False.
window: Defines the size of the convolutional window (how many previous tokens to look when computing the QRNN values). Supports 1 and 2. Default: 1.
zoneout: Whether to apply zoneout (i.e. failing to update elements in the hidden state) to the hidden state updates. Default: 0.
output_gate: If True, performs QRNN-fo (applying an output gate to the output). If False, performs QRNN-f. Default: True.
use_cuda: If True, uses fast custom CUDA kernel. If False, uses naive for loop. Default: True.
Inputs: X, hidden
- X (seq_len, batch, input_size): tensor containing the features of the input sequence.
- hidden (layers, batch, hidden_size): tensor containing the initial hidden state for the QRNN.
Outputs: output, h_n
- output (seq_len, batch, hidden_size): tensor containing the output of the QRNN for each timestep.
- h_n (layers, batch, hidden_size): tensor containing the hidden state for t=seq_len
"""
def __init__(self, input_size, hidden_size,
num_layers=1, bias=True, batch_first=False,
dropout=0, bidirectional=False, layers=None, **kwargs):
assert bidirectional == False, 'Bidirectional QRNN is not yet supported'
assert batch_first == False, 'Batch first mode is not yet supported'
assert bias == True, 'Removing underlying bias is not yet supported'
super(QRNN, self).__init__()
self.layers = torch.nn.ModuleList(layers if layers else [QRNNLayer(input_size if l == 0 else hidden_size, hidden_size, **kwargs) for l in range(num_layers)])
self.input_size = input_size
self.hidden_size = hidden_size
self.num_layers = len(layers) if layers else num_layers
self.bias = bias
self.batch_first = batch_first
self.dropout = dropout
self.bidirectional = bidirectional
def reset(self):
r'''If your convolutional window is greater than 1, you must reset at the beginning of each new sequence'''
[layer.reset() for layer in self.layers]
def forward(self, input, hidden=None):
next_hidden = []
for i, layer in enumerate(self.layers):
input, hn = layer(input, None if hidden is None else hidden[i])
next_hidden.append(hn)
if self.dropout != 0 and i < len(self.layers) - 1:
input = torch.nn.functional.dropout(input, p=self.dropout, training=self.training, inplace=False)
next_hidden = torch.cat(next_hidden, 0).view(self.num_layers, *next_hidden[0].size()[-2:])
return input, next_hidden
+735
View File
@@ -0,0 +1,735 @@
from .imports import *
from .layer_optimizer import *
from enum import IntEnum
def scale_min(im, targ, interpolation=cv2.INTER_AREA):
""" Scale the image so that the smallest axis is of size targ.
Arguments:
im (array): image
targ (int): target size
"""
r,c,*_ = im.shape
ratio = targ/min(r,c)
sz = (scale_to(c, ratio, targ), scale_to(r, ratio, targ))
return cv2.resize(im, sz, interpolation=interpolation)
def zoom_cv(x,z):
""" Zoom the center of image x by a factor of z+1 while retaining the original image size and proportion. """
if z==0: return x
r,c,*_ = x.shape
M = cv2.getRotationMatrix2D((c/2,r/2),0,z+1.)
return cv2.warpAffine(x,M,(c,r))
def stretch_cv(x,sr,sc,interpolation=cv2.INTER_AREA):
""" Stretches image x horizontally by sr+1, and vertically by sc+1 while retaining the original image size and proportion. """
if sr==0 and sc==0: return x
r,c,*_ = x.shape
x = cv2.resize(x, None, fx=sr+1, fy=sc+1, interpolation=interpolation)
nr,nc,*_ = x.shape
cr = (nr-r)//2; cc = (nc-c)//2
return x[cr:r+cr, cc:c+cc]
def dihedral(x, dih):
""" Perform any of 8 permutations of 90-degrees rotations or flips for image x. """
x = np.rot90(x, dih%4)
return x if dih<4 else np.fliplr(x)
def lighting(im, b, c):
""" Adjust image balance and contrast """
if b==0 and c==1: return im
mu = np.average(im)
return np.clip((im-mu)*c+mu+b,0.,1.).astype(np.float32)
def rotate_cv(im, deg, mode=cv2.BORDER_CONSTANT, interpolation=cv2.INTER_AREA):
""" Rotate an image by deg degrees
Arguments:
deg (float): degree to rotate.
"""
r,c,*_ = im.shape
M = cv2.getRotationMatrix2D((c//2,r//2),deg,1)
return cv2.warpAffine(im,M,(c,r), borderMode=mode, flags=cv2.WARP_FILL_OUTLIERS+interpolation)
def no_crop(im, min_sz=None, interpolation=cv2.INTER_AREA):
""" Return a squared resized image """
r,c,*_ = im.shape
if min_sz is None: min_sz = min(r,c)
return cv2.resize(im, (min_sz, min_sz), interpolation=interpolation)
def center_crop(im, min_sz=None):
""" Return a center crop of an image """
r,c,*_ = im.shape
if min_sz is None: min_sz = min(r,c)
start_r = math.ceil((r-min_sz)/2)
start_c = math.ceil((c-min_sz)/2)
return crop(im, start_r, start_c, min_sz)
def googlenet_resize(im, targ, min_area_frac, min_aspect_ratio, max_aspect_ratio, flip_hw_p, interpolation=cv2.INTER_AREA):
""" Randomly crop an image with an aspect ratio and returns a squared resized image of size targ
References:
1. https://arxiv.org/pdf/1409.4842.pdf
2. https://arxiv.org/pdf/1802.07888.pdf
"""
h,w,*_ = im.shape
area = h*w
for _ in range(10):
targetArea = random.uniform(min_area_frac, 1.0) * area
aspectR = random.uniform(min_aspect_ratio, max_aspect_ratio)
ww = int(np.sqrt(targetArea * aspectR) + 0.5)
hh = int(np.sqrt(targetArea / aspectR) + 0.5)
if flip_hw_p:
ww, hh = hh, ww
if hh <= h and ww <= w:
x1 = 0 if w == ww else random.randint(0, w - ww)
y1 = 0 if h == hh else random.randint(0, h - hh)
out = im[y1:y1 + hh, x1:x1 + ww]
out = cv2.resize(out, (targ, targ), interpolation=interpolation)
return out
out = scale_min(im, targ, interpolation=interpolation)
out = center_crop(out)
return out
def cutout(im, n_holes, length):
""" Cut out n_holes number of square holes of size length in image at random locations. Holes may overlap. """
r,c,*_ = im.shape
mask = np.ones((r, c), np.int32)
for n in range(n_holes):
y = np.random.randint(length / 2, r - length / 2)
x = np.random.randint(length / 2, c - length / 2)
y1 = int(np.clip(y - length / 2, 0, r))
y2 = int(np.clip(y + length / 2, 0, r))
x1 = int(np.clip(x - length / 2, 0, c))
x2 = int(np.clip(x + length / 2, 0, c))
mask[y1: y2, x1: x2] = 0.
mask = mask[:,:,None]
im = im * mask
return im
def scale_to(x, ratio, targ):
'''Calculate dimension of an image during scaling with aspect ratio'''
return max(math.floor(x*ratio), targ)
def crop(im, r, c, sz):
'''
crop image into a square of size sz,
'''
return im[r:r+sz, c:c+sz]
def det_dihedral(dih): return lambda x: dihedral(x, dih)
def det_stretch(sr, sc): return lambda x: stretch_cv(x, sr, sc)
def det_lighting(b, c): return lambda x: lighting(x, b, c)
def det_rotate(deg): return lambda x: rotate_cv(x, deg)
def det_zoom(zoom): return lambda x: zoom_cv(x, zoom)
def rand0(s): return random.random()*(s*2)-s
class TfmType(IntEnum):
""" Type of transformation.
Parameters
IntEnum: predefined types of transformations
NO: the default, y does not get transformed when x is transformed.
PIXEL: x and y are images and should be transformed in the same way.
Example: image segmentation.
COORD: y are coordinates (i.e bounding boxes)
CLASS: y are class labels (same behaviour as PIXEL, except no normalization)
"""
NO = 1
PIXEL = 2
COORD = 3
CLASS = 4
class Denormalize():
""" De-normalizes an image, returning it to original format.
"""
def __init__(self, m, s):
self.m=np.array(m, dtype=np.float32)
self.s=np.array(s, dtype=np.float32)
def __call__(self, x): return x*self.s+self.m
class Normalize():
""" Normalizes an image to zero mean and unit standard deviation, given the mean m and std s of the original image """
def __init__(self, m, s, tfm_y=TfmType.NO):
self.m=np.array(m, dtype=np.float32)
self.s=np.array(s, dtype=np.float32)
self.tfm_y=tfm_y
def __call__(self, x, y=None):
x = (x-self.m)/self.s
if self.tfm_y==TfmType.PIXEL and y is not None: y = (y-self.m)/self.s
return x,y
class ChannelOrder():
'''
changes image array shape from (h, w, 3) to (3, h, w).
tfm_y decides the transformation done to the y element.
'''
def __init__(self, tfm_y=TfmType.NO): self.tfm_y=tfm_y
def __call__(self, x, y):
x = np.rollaxis(x, 2)
#if isinstance(y,np.ndarray) and (len(y.shape)==3):
if self.tfm_y==TfmType.PIXEL: y = np.rollaxis(y, 2)
elif self.tfm_y==TfmType.CLASS: y = y[...,0]
return x,y
def to_bb(YY, y="deprecated"):
"""Convert mask YY to a bounding box, assumes 0 as background nonzero object"""
cols,rows = np.nonzero(YY)
if len(cols)==0: return np.zeros(4, dtype=np.float32)
top_row = np.min(rows)
left_col = np.min(cols)
bottom_row = np.max(rows)
right_col = np.max(cols)
return np.array([left_col, top_row, right_col, bottom_row], dtype=np.float32)
def coords2px(y, x):
""" Transforming coordinates to pixels.
Arguments:
y : np array
vector in which (y[0], y[1]) and (y[2], y[3]) are the
the corners of a bounding box.
x : image
an image
Returns:
Y : image
of shape x.shape
"""
rows = np.rint([y[0], y[0], y[2], y[2]]).astype(int)
cols = np.rint([y[1], y[3], y[1], y[3]]).astype(int)
r,c,*_ = x.shape
Y = np.zeros((r, c))
Y[rows, cols] = 1
return Y
class Transform():
""" A class that represents a transform.
All other transforms should subclass it. All subclasses should override
do_transform.
Arguments
---------
tfm_y : TfmType
type of transform
"""
def __init__(self, tfm_y=TfmType.NO):
self.tfm_y=tfm_y
self.store = threading.local()
def set_state(self): pass
def __call__(self, x, y):
self.set_state()
x,y = ((self.transform(x),y) if self.tfm_y==TfmType.NO
else self.transform(x,y) if self.tfm_y in (TfmType.PIXEL, TfmType.CLASS)
else self.transform_coord(x,y))
return x, y
def transform_coord(self, x, y): return self.transform(x),y
def transform(self, x, y=None):
x = self.do_transform(x,False)
return (x, self.do_transform(y,True)) if y is not None else x
@abstractmethod
def do_transform(self, x, is_y): raise NotImplementedError
class CoordTransform(Transform):
""" A coordinate transform. """
@staticmethod
def make_square(y, x):
r,c,*_ = x.shape
y1 = np.zeros((r, c))
y = y.astype(np.int)
y1[y[0]:y[2], y[1]:y[3]] = 1.
return y1
def map_y(self, y0, x):
y = CoordTransform.make_square(y0, x)
y_tr = self.do_transform(y, True)
return to_bb(y_tr)
def transform_coord(self, x, ys):
yp = partition(ys, 4)
y2 = [self.map_y(y,x) for y in yp]
x = self.do_transform(x, False)
return x, np.concatenate(y2)
class AddPadding(CoordTransform):
""" A class that represents adding paddings to an image.
The default padding is border_reflect
Arguments
---------
pad : int
size of padding on top, bottom, left and right
mode:
type of cv2 padding modes. (e.g., constant, reflect, wrap, replicate. etc. )
"""
def __init__(self, pad, mode=cv2.BORDER_REFLECT, tfm_y=TfmType.NO):
super().__init__(tfm_y)
self.pad,self.mode = pad,mode
def do_transform(self, im, is_y):
return cv2.copyMakeBorder(im, self.pad, self.pad, self.pad, self.pad, self.mode)
class CenterCrop(CoordTransform):
""" A class that represents a Center Crop.
This transforms (optionally) transforms x,y at with the same parameters.
Arguments
---------
sz: int
size of the crop.
tfm_y : TfmType
type of y transformation.
"""
def __init__(self, sz, tfm_y=TfmType.NO, sz_y=None):
super().__init__(tfm_y)
self.min_sz,self.sz_y = sz,sz_y
def do_transform(self, x, is_y):
return center_crop(x, self.sz_y if is_y else self.min_sz)
class RandomCrop(CoordTransform):
""" A class that represents a Random Crop transformation.
This transforms (optionally) transforms x,y at with the same parameters.
Arguments
---------
targ: int
target size of the crop.
tfm_y: TfmType
type of y transformation.
"""
def __init__(self, targ_sz, tfm_y=TfmType.NO, sz_y=None):
super().__init__(tfm_y)
self.targ_sz,self.sz_y = targ_sz,sz_y
def set_state(self):
self.store.rand_r = random.uniform(0, 1)
self.store.rand_c = random.uniform(0, 1)
def do_transform(self, x, is_y):
r,c,*_ = x.shape
sz = self.sz_y if is_y else self.targ_sz
start_r = np.floor(self.store.rand_r*(r-sz)).astype(int)
start_c = np.floor(self.store.rand_c*(c-sz)).astype(int)
return crop(x, start_r, start_c, sz)
class NoCrop(CoordTransform):
""" A transformation that resize to a square image without cropping.
This transforms (optionally) resizes x,y at with the same parameters.
Arguments:
targ: int
target size of the crop.
tfm_y (TfmType): type of y transformation.
"""
def __init__(self, sz, tfm_y=TfmType.NO, sz_y=None):
super().__init__(tfm_y)
self.sz,self.sz_y = sz,sz_y
def do_transform(self, x, is_y):
if is_y: return no_crop(x, self.sz_y, cv2.INTER_AREA if self.tfm_y == TfmType.PIXEL else cv2.INTER_NEAREST)
else : return no_crop(x, self.sz, cv2.INTER_AREA )
class Scale(CoordTransform):
""" A transformation that scales the min size to sz.
Arguments:
sz: int
target size to scale minimum size.
tfm_y: TfmType
type of y transformation.
"""
def __init__(self, sz, tfm_y=TfmType.NO, sz_y=None):
super().__init__(tfm_y)
self.sz,self.sz_y = sz,sz_y
def do_transform(self, x, is_y):
if is_y: return scale_min(x, self.sz_y, cv2.INTER_AREA if self.tfm_y == TfmType.PIXEL else cv2.INTER_NEAREST)
else : return scale_min(x, self.sz, cv2.INTER_AREA )
class RandomScale(CoordTransform):
""" Scales an image so that the min size is a random number between [sz, sz*max_zoom]
This transforms (optionally) scales x,y at with the same parameters.
Arguments:
sz: int
target size
max_zoom: float
float >= 1.0
p : float
a probability for doing the random sizing
tfm_y: TfmType
type of y transform
"""
def __init__(self, sz, max_zoom, p=0.75, tfm_y=TfmType.NO, sz_y=None):
super().__init__(tfm_y)
self.sz,self.max_zoom,self.p,self.sz_y = sz,max_zoom,p,sz_y
def set_state(self):
min_z = 1.
max_z = self.max_zoom
if isinstance(self.max_zoom, collections.Iterable):
min_z, max_z = self.max_zoom
self.store.mult = random.uniform(min_z, max_z) if random.random()<self.p else 1
self.store.new_sz = int(self.store.mult*self.sz)
if self.sz_y is not None: self.store.new_sz_y = int(self.store.mult*self.sz_y)
def do_transform(self, x, is_y):
if is_y: return scale_min(x, self.store.new_sz_y, cv2.INTER_AREA if self.tfm_y == TfmType.PIXEL else cv2.INTER_NEAREST)
else : return scale_min(x, self.store.new_sz, cv2.INTER_AREA )
class RandomRotate(CoordTransform):
""" Rotates images and (optionally) target y.
Rotating coordinates is treated differently for x and y on this
transform.
Arguments:
deg (float): degree to rotate.
p (float): probability of rotation
mode: type of border
tfm_y (TfmType): type of y transform
"""
def __init__(self, deg, p=0.75, mode=cv2.BORDER_REFLECT, tfm_y=TfmType.NO):
super().__init__(tfm_y)
self.deg,self.p = deg,p
if tfm_y == TfmType.COORD or tfm_y == TfmType.CLASS:
self.modes = (mode,cv2.BORDER_CONSTANT)
else:
self.modes = (mode,mode)
def set_state(self):
self.store.rdeg = rand0(self.deg)
self.store.rp = random.random()<self.p
def do_transform(self, x, is_y):
if self.store.rp: x = rotate_cv(x, self.store.rdeg,
mode= self.modes[1] if is_y else self.modes[0],
interpolation=cv2.INTER_NEAREST if is_y else cv2.INTER_AREA)
return x
class RandomDihedral(CoordTransform):
"""
Rotates images by random multiples of 90 degrees and/or reflection.
Please reference D8(dihedral group of order eight), the group of all symmetries of the square.
"""
def set_state(self):
self.store.rot_times = random.randint(0,3)
self.store.do_flip = random.random()<0.5
def do_transform(self, x, is_y):
x = np.rot90(x, self.store.rot_times)
return np.fliplr(x).copy() if self.store.do_flip else x
class RandomFlip(CoordTransform):
def __init__(self, tfm_y=TfmType.NO, p=0.5):
super().__init__(tfm_y=tfm_y)
self.p=p
def set_state(self): self.store.do_flip = random.random()<self.p
def do_transform(self, x, is_y): return np.fliplr(x).copy() if self.store.do_flip else x
class RandomLighting(Transform):
def __init__(self, b, c, tfm_y=TfmType.NO):
super().__init__(tfm_y)
self.b,self.c = b,c
def set_state(self):
self.store.b_rand = rand0(self.b)
self.store.c_rand = rand0(self.c)
def do_transform(self, x, is_y):
if is_y and self.tfm_y != TfmType.PIXEL: return x
b = self.store.b_rand
c = self.store.c_rand
c = -1/(c-1) if c<0 else c+1
x = lighting(x, b, c)
return x
class RandomRotateZoom(CoordTransform):
"""
Selects between a rotate, zoom, stretch, or no transform.
Arguments:
deg - maximum degrees of rotation.
zoom - maximum fraction of zoom.
stretch - maximum fraction of stretch.
ps - probabilities for each transform. List of length 4. The order for these probabilities is as listed respectively (4th probability is 'no transform'.
"""
def __init__(self, deg, zoom, stretch, ps=None, mode=cv2.BORDER_REFLECT, tfm_y=TfmType.NO):
super().__init__(tfm_y)
if ps is None: ps = [0.25,0.25,0.25,0.25]
assert len(ps) == 4, 'does not have 4 probabilities for p, it has %d' % len(ps)
self.transforms = RandomRotate(deg, p=1, mode=mode, tfm_y=tfm_y), RandomZoom(zoom, tfm_y=tfm_y), RandomStretch(stretch,tfm_y=tfm_y)
self.pass_t = PassThru()
self.cum_ps = np.cumsum(ps)
assert self.cum_ps[3]==1, 'probabilites do not sum to 1; they sum to %d' % self.cum_ps[3]
def set_state(self):
self.store.trans = self.pass_t
self.store.choice = self.cum_ps[3]*random.random()
for i in range(len(self.transforms)):
if self.store.choice < self.cum_ps[i]:
self.store.trans = self.transforms[i]
break
self.store.trans.set_state()
def do_transform(self, x, is_y): return self.store.trans.do_transform(x, is_y)
class RandomZoom(CoordTransform):
def __init__(self, zoom_max, zoom_min=0, mode=cv2.BORDER_REFLECT, tfm_y=TfmType.NO):
super().__init__(tfm_y)
self.zoom_max, self.zoom_min = zoom_max, zoom_min
def set_state(self):
self.store.zoom = self.zoom_min+(self.zoom_max-self.zoom_min)*random.random()
def do_transform(self, x, is_y):
return zoom_cv(x, self.store.zoom)
class RandomStretch(CoordTransform):
def __init__(self, max_stretch, tfm_y=TfmType.NO):
super().__init__(tfm_y)
self.max_stretch = max_stretch
def set_state(self):
self.store.stretch = self.max_stretch*random.random()
self.store.stretch_dir = random.randint(0,1)
def do_transform(self, x, is_y):
if self.store.stretch_dir==0: x = stretch_cv(x, self.store.stretch, 0)
else: x = stretch_cv(x, 0, self.store.stretch)
return x
class PassThru(CoordTransform):
def do_transform(self, x, is_y):
return x
class RandomBlur(Transform):
"""
Adds a gaussian blur to the image at chance.
Multiple blur strengths can be configured, one of them is used by random chance.
"""
def __init__(self, blur_strengths=5, probability=0.5, tfm_y=TfmType.NO):
# Blur strength must be an odd number, because it is used as a kernel size.
super().__init__(tfm_y)
self.blur_strengths = (np.array(blur_strengths, ndmin=1) * 2) - 1
if np.any(self.blur_strengths < 0):
raise ValueError("all blur_strengths must be > 0")
self.probability = probability
self.apply_transform = False
def set_state(self):
self.store.apply_transform = random.random() < self.probability
kernel_size = np.random.choice(self.blur_strengths)
self.store.kernel = (kernel_size, kernel_size)
def do_transform(self, x, is_y):
return cv2.GaussianBlur(src=x, ksize=self.store.kernel, sigmaX=0) if self.apply_transform else x
class Cutout(Transform):
""" Randomly masks squares of size length on the image.
https://arxiv.org/pdf/1708.04552.pdf
Arguments:
n_holes: number of squares
length: size of the square
p: probability to apply cutout
tfm_y: type of y transform
"""
def __init__(self, n_holes, length, p=0.5, tfm_y=TfmType.NO):
super().__init__(tfm_y)
self.n_holes, self.length, self.p = n_holes, length, p
def set_state(self):
self.apply_transform = random.random() < self.p
def do_transform(self, img, is_y):
return cutout(img, self.n_holes, self.length) if self.apply_transform else img
class GoogleNetResize(CoordTransform):
""" Randomly crops an image with an aspect ratio and returns a squared resized image of size targ
Arguments:
targ_sz: int
target size
min_area_frac: float < 1.0
minimum area of the original image for cropping
min_aspect_ratio : float
minimum aspect ratio
max_aspect_ratio : float
maximum aspect ratio
flip_hw_p : float
probability for flipping magnitudes of height and width
tfm_y: TfmType
type of y transform
"""
def __init__(self, targ_sz,
min_area_frac=0.08, min_aspect_ratio=0.75, max_aspect_ratio=1.333, flip_hw_p=0.5,
tfm_y=TfmType.NO, sz_y=None):
super().__init__(tfm_y)
self.targ_sz, self.tfm_y, self.sz_y = targ_sz, tfm_y, sz_y
self.min_area_frac, self.min_aspect_ratio, self.max_aspect_ratio, self.flip_hw_p = min_area_frac, min_aspect_ratio, max_aspect_ratio, flip_hw_p
def set_state(self):
# if self.random_state: random.seed(self.random_state)
self.store.fp = random.random()<self.flip_hw_p
def do_transform(self, x, is_y):
sz = self.sz_y if is_y else self.targ_sz
if is_y:
interpolation = cv2.INTER_NEAREST if self.tfm_y in (TfmType.COORD, TfmType.CLASS) else cv2.INTER_AREA
else:
interpolation = cv2.INTER_AREA
return googlenet_resize(x, sz, self.min_area_frac, self.min_aspect_ratio, self.max_aspect_ratio, self.store.fp, interpolation=interpolation)
def compose(im, y, fns):
""" Apply a collection of transformation functions :fns: to images """
for fn in fns:
#pdb.set_trace()
im, y =fn(im, y)
return im if y is None else (im, y)
class CropType(IntEnum):
""" Type of image cropping. """
RANDOM = 1
CENTER = 2
NO = 3
GOOGLENET = 4
crop_fn_lu = {CropType.RANDOM: RandomCrop, CropType.CENTER: CenterCrop, CropType.NO: NoCrop, CropType.GOOGLENET: GoogleNetResize}
class Transforms():
def __init__(self, sz, tfms, normalizer, denorm, crop_type=CropType.CENTER,
tfm_y=TfmType.NO, sz_y=None):
if sz_y is None: sz_y = sz
self.sz,self.denorm,self.norm,self.sz_y = sz,denorm,normalizer,sz_y
crop_tfm = crop_fn_lu[crop_type](sz, tfm_y, sz_y)
self.tfms = tfms
self.tfms.append(crop_tfm)
if normalizer is not None: self.tfms.append(normalizer)
self.tfms.append(ChannelOrder(tfm_y))
def __call__(self, im, y=None): return compose(im, y, self.tfms)
def __repr__(self): return str(self.tfms)
def image_gen(normalizer, denorm, sz, tfms=None, max_zoom=None, pad=0, crop_type=None,
tfm_y=None, sz_y=None, pad_mode=cv2.BORDER_REFLECT, scale=None):
"""
Generate a standard set of transformations
Arguments
---------
normalizer :
image normalizing function
denorm :
image denormalizing function
sz :
size, sz_y = sz if not specified.
tfms :
iterable collection of transformation functions
max_zoom : float,
maximum zoom
pad : int,
padding on top, left, right and bottom
crop_type :
crop type
tfm_y :
y axis specific transformations
sz_y :
y size, height
pad_mode :
cv2 padding style: repeat, reflect, etc.
Returns
-------
type : ``Transforms``
transformer for specified image operations.
See Also
--------
Transforms: the transformer object returned by this function
"""
if tfm_y is None: tfm_y=TfmType.NO
if tfms is None: tfms=[]
elif not isinstance(tfms, collections.Iterable): tfms=[tfms]
if sz_y is None: sz_y = sz
if scale is None:
scale = [RandomScale(sz, max_zoom, tfm_y=tfm_y, sz_y=sz_y) if max_zoom is not None
else Scale(sz, tfm_y, sz_y=sz_y)]
elif not is_listy(scale): scale = [scale]
if pad: scale.append(AddPadding(pad, mode=pad_mode))
if crop_type!=CropType.GOOGLENET: tfms=scale+tfms
return Transforms(sz, tfms, normalizer, denorm, crop_type,
tfm_y=tfm_y, sz_y=sz_y)
def noop(x):
"""dummy function for do-nothing.
equivalent to: lambda x: x"""
return x
transforms_basic = [RandomRotate(10), RandomLighting(0.05, 0.05)]
transforms_side_on = transforms_basic + [RandomFlip()]
transforms_top_down = transforms_basic + [RandomDihedral()]
imagenet_stats = A([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
"""Statistics pertaining to image data from image net. mean and std of the images of each color channel"""
inception_stats = A([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
inception_models = (inception_4, inceptionresnet_2)
def tfms_from_stats(stats, sz, aug_tfms=None, max_zoom=None, pad=0, crop_type=CropType.RANDOM,
tfm_y=None, sz_y=None, pad_mode=cv2.BORDER_REFLECT, norm_y=True, scale=None):
""" Given the statistics of the training image sets, returns separate training and validation transform functions
"""
if aug_tfms is None: aug_tfms=[]
tfm_norm = Normalize(*stats, tfm_y=tfm_y if norm_y else TfmType.NO) if stats is not None else None
tfm_denorm = Denormalize(*stats) if stats is not None else None
val_crop = CropType.CENTER if crop_type in (CropType.RANDOM,CropType.GOOGLENET) else crop_type
val_tfm = image_gen(tfm_norm, tfm_denorm, sz, pad=pad, crop_type=val_crop,
tfm_y=tfm_y, sz_y=sz_y, scale=scale)
trn_tfm = image_gen(tfm_norm, tfm_denorm, sz, pad=pad, crop_type=crop_type,
tfm_y=tfm_y, sz_y=sz_y, tfms=aug_tfms, max_zoom=max_zoom, pad_mode=pad_mode, scale=scale)
return trn_tfm, val_tfm
def tfms_from_model(f_model, sz, aug_tfms=None, max_zoom=None, pad=0, crop_type=CropType.RANDOM,
tfm_y=None, sz_y=None, pad_mode=cv2.BORDER_REFLECT, norm_y=True, scale=None):
""" Returns separate transformers of images for training and validation.
Transformers are constructed according to the image statistics given b y the model. (See tfms_from_stats)
Arguments:
f_model: model, pretrained or not pretrained
"""
stats = inception_stats if f_model in inception_models else imagenet_stats
return tfms_from_stats(stats, sz, aug_tfms, max_zoom=max_zoom, pad=pad, crop_type=crop_type,
tfm_y=tfm_y, sz_y=sz_y, pad_mode=pad_mode, norm_y=norm_y, scale=scale)
+43
View File
@@ -0,0 +1,43 @@
import torch
import numpy as np
class Cutout(object):
"""Randomly mask out one or more patches from an image.
Args:
n_holes (int): Number of patches to cut out of each image.
length (int): The length (in pixels) of each square patch.
"""
def __init__(self, n_holes, length):
self.n_holes = n_holes
self.length = length
def __call__(self, img):
"""
Args:
img (Tensor): Tensor image of size (C, H, W).
Returns:
Tensor: Image with n_holes of dimension length x length cut out of it.
"""
h = img.size(1)
w = img.size(2)
mask = np.ones((h, w), np.float32)
for n in range(self.n_holes):
y = np.random.randint(h)
x = np.random.randint(w)
y1 = np.clip(y - self.length / 2, 0, h)
y2 = np.clip(y + self.length / 2, 0, h)
x1 = np.clip(x - self.length / 2, 0, w)
x2 = np.clip(x + self.length / 2, 0, w)
mask[y1: y2, x1: x2] = 0.
mask = torch.from_numpy(mask)
mask = mask.expand_as(img)
img = img * mask
return img
+126
View File
@@ -0,0 +1,126 @@
import math, os, json, sys, re, numpy as np, pickle, PIL, scipy
from PIL import Image
from glob import glob
from matplotlib import pyplot as plt
from operator import itemgetter, attrgetter, methodcaller
from collections import OrderedDict
import itertools
from itertools import chain
import pandas as pd
from numpy.random import random, permutation, randn, normal, uniform, choice
from numpy import newaxis
from scipy import misc, ndimage
from scipy.ndimage.interpolation import zoom
from scipy.ndimage import imread
from sklearn.metrics import confusion_matrix
from sklearn.preprocessing import OneHotEncoder
from sklearn.manifold import TSNE
import bcolz
from IPython.lib.display import FileLink
import keras
from keras import backend as K
from keras.utils.data_utils import get_file
from keras.utils import np_utils
from keras.utils.np_utils import to_categorical
from keras.models import Sequential, Model
from keras.layers import Input, Embedding, Reshape, merge, LSTM, Bidirectional
from keras.layers import TimeDistributed, Activation, SimpleRNN, GRU
from keras.layers import Flatten, Dense, Dropout, Lambda
from keras.regularizers import l2, l1
from keras.layers.normalization import BatchNormalization
from keras.optimizers import SGD, RMSprop, Adam
from keras.layers import deserialize as layer_from_config
from keras.metrics import categorical_crossentropy, categorical_accuracy
from keras.layers.convolutional import *
from keras.preprocessing import image, sequence
from keras.preprocessing.text import Tokenizer
from vgg16 import Vgg16
np.set_printoptions(precision=4, linewidth=100)
to_bw = np.array([0.299, 0.587, 0.114])
def gray(img): return np.rollaxis(img, 0, 1).dot(to_bw)
def to_plot(img): return np.rollaxis(img, 0, 1).astype(np.uint8)
def plot(img): plt.imshow(to_plot(img))
def floor(x): return int(math.floor(x))
def ceil(x): return int(math.ceil(x))
def plots(ims, figsize=(12,6), rows=1, interp=False, titles=None):
if type(ims[0]) is np.ndarray:
ims = np.array(ims).astype(np.uint8)
if (ims.shape[-1] != 3): ims = ims.transpose((0,2,3,1))
f = plt.figure(figsize=figsize)
for i in range(len(ims)):
sp = f.add_subplot(rows, len(ims)//rows, i+1)
sp.axis('Off')
if titles is not None: sp.set_title(titles[i], fontsize=16)
plt.imshow(ims[i], interpolation=None if interp else 'none')
def do_clip(arr, mx):
clipped = np.clip(arr, (1-mx)/1, mx)
return clipped/clipped.sum(axis=1)[:, np.newaxis]
def wrap_config(layer):
return {'class_name': layer.__class__.__name__, 'config': layer.get_config()}
def copy_layer(layer): return layer_from_config(wrap_config(layer))
def copy_layers(layers): return [copy_layer(layer) for layer in layers]
def copy_weights(from_layers, to_layers):
for from_layer,to_layer in zip(from_layers, to_layers):
to_layer.set_weights(from_layer.get_weights())
def save_array(fname, arr):
c=bcolz.carray(arr, rootdir=fname, mode='w')
c.flush()
def load_array(fname): return bcolz.open(fname)[:]
def get_classes(path):
batches = get_batches(path+'train', shuffle=False, batch_size=1)
val_batches = get_batches(path+'valid', shuffle=False, batch_size=1)
test_batches = get_batches(path+'test', shuffle=False, batch_size=1)
return (val_batches.classes, batches.classes, onehot(val_batches.classes), onehot(batches.classes),
val_batches.filenames, batches.filenames, test_batches.filenames)
def limit_mem():
K.get_session().close()
cfg = K.tf.ConfigProto()
cfg.gpu_options.allow_growth = True
K.set_session(K.tf.Session(config=cfg))
class MixIterator(object):
def __init__(self, iters):
self.iters = iters
self.multi = type(iters) is list
if self.multi:
self.N = sum([it[0].N for it in self.iters])
else:
self.N = sum([it.N for it in self.iters])
def reset(self):
for it in self.iters: it.reset()
def __iter__(self):
return self
def next(self, *args, **kwargs):
if self.multi:
nexts = [[next(it) for it in o] for o in self.iters]
n0 = np.concatenate([n[0] for n in nexts])
n1 = np.concatenate([n[1] for n in nexts])
return (n0, n1)
else:
nexts = [next(it) for it in self.iters]
n0 = np.concatenate([n[0] for n in nexts])
n1 = np.concatenate([n[1] for n in nexts])
return (n0, n1)