I can create a FasterRCNN object using
model = fasterrcnn_resnet50_fpn(...)
which I want to inherit from, as
class MyDetector(FasterRCNN):
...
but overwrite the superclass instance from the fasterrcnn_resnet50_fpn() factory. I have tried using __new__, as:
class MyDetector(FasterRCNN):
def __new__(cls):
return fasterrcnn_resnet50_fpn(weights=FasterRCNN_ResNet50_FPN_Weights.DEFAULT)
def __init__(self):
num_features_in = self.roi_heads.box_predictor.cls_score.in_features
self.roi_heads.box_predictor = FastRCNNPredictor(num_features_in, num_classes=2)
def some_func(self):
pass
so that I can add custom methods to the child class and so forth. What is the correct way of doing this?
I guess you'd be better to make your own factory function.
import libraries
from typing import Optional, Any
import torch
from torch import nn
import torchvision
from torchvision.models.resnet import resnet50, ResNet50_Weights
from torchvision.models.detection import FasterRCNN_ResNet50_FPN_Weights, FasterRCNN
from torchvision.models._utils import _ovewrite_value_param
from torchvision.models.detection.backbone_utils import (
_validate_trainable_layers,
_resnet_fpn_extractor,
)
from torchvision.models.detection._utils import overwrite_eps
from torchvision.ops import misc as misc_nn_ops
class MyDetector
class MyDetector(FasterRCNN):
def __init__(self, backbone, num_classes=None, **kwarg):
super().__init__(backbone=backbone, num_classes=num_classes, **kwarg)
def some_func(self):
pass
MyDetector factory function
# https://github.com/pytorch/vision/blob/main/torchvision/models/detection/faster_rcnn.py#L459
def mydetector_resnet50_fpn(
*,
weights: Optional[FasterRCNN_ResNet50_FPN_Weights] = None,
progress: bool = True,
num_classes: Optional[int] = None,
weights_backbone: Optional[ResNet50_Weights] = ResNet50_Weights.IMAGENET1K_V1,
trainable_backbone_layers: Optional[int] = None,
**kwargs: Any,
) -> MyDetector:
weights = FasterRCNN_ResNet50_FPN_Weights.verify(weights)
weights_backbone = ResNet50_Weights.verify(weights_backbone)
if weights is not None:
weights_backbone = None
num_classes = _ovewrite_value_param(
"num_classes", num_classes, len(weights.meta["categories"])
)
elif num_classes is None:
num_classes = 91
is_trained = weights is not None or weights_backbone is not None
trainable_backbone_layers = _validate_trainable_layers(
is_trained, trainable_backbone_layers, 5, 3
)
norm_layer = misc_nn_ops.FrozenBatchNorm2d if is_trained else nn.BatchNorm2d
backbone = resnet50(
weights=weights_backbone, progress=progress, norm_layer=norm_layer
)
backbone = _resnet_fpn_extractor(backbone, trainable_backbone_layers)
model = MyDetector(backbone, num_classes=num_classes, **kwargs)
if weights is not None:
model.load_state_dict(weights.get_state_dict(progress=progress))
if weights == FasterRCNN_ResNet50_FPN_Weights.COCO_V1:
overwrite_eps(model, 0.0)
return model
utility for checking
# https://discuss.pytorch.org/t/check-if-models-have-same-weights/4351/6
def compare_models(model_1, model_2):
models_differ = 0
for key_item_1, key_item_2 in zip(
model_1.state_dict().items(), model_2.state_dict().items()
):
if torch.equal(key_item_1[1], key_item_2[1]):
pass
else:
models_differ += 1
if key_item_1[0] == key_item_2[0]:
print("Mismtach found at", key_item_1[0])
else:
raise Exception
if models_differ == 0:
print("Models match perfectly! :)")
test
model = torchvision.models.detection.fasterrcnn_resnet50_fpn(
weights=FasterRCNN_ResNet50_FPN_Weights.DEFAULT
)
my_model = mydetector_resnet50_fpn(weights=FasterRCNN_ResNet50_FPN_Weights.DEFAULT)
compare_models(model, my_model)
output
Models match perfectly! :)
And I also tried to make hard coded version. But as you know, customizing settings of FPN is somewhat complicated.
from torchvision.models.resnet import resnet50, ResNet50_Weights
from torchvision.models.detection import FasterRCNN_ResNet50_FPN_Weights, FasterRCNN
from torchvision.models.detection.backbone_utils import _resnet_fpn_extractor
from torchvision.ops import misc as misc_nn_ops
class MyDetector(FasterRCNN):
def __init__(self, **kwarg):
weights = FasterRCNN_ResNet50_FPN_Weights.DEFAULT
backbone = resnet50(
weights=ResNet50_Weights.IMAGENET1K_V1,
norm_layer=misc_nn_ops.FrozenBatchNorm2d,
)
backbone = _resnet_fpn_extractor(backbone, trainable_layers=3)
# default of num_classes is 91
# this num_classes is used for setting FastRCNNPreditcor
# https://github.com/pytorch/vision/blob/main/torchvision/models/detection/faster_rcnn.py#L257
num_classes = len(weights.meta["categories"])
super().__init__(backbone=backbone, num_classes=num_classes, **kwarg)
self.load_state_dict(weights.get_state_dict(progress=True))
def some_func(self):
pass
m = MyDetector()
Related
Here is the error about deepcopy, how should I do it.
error:
target_encoder = copy.deepcopy(self.online_encoder)
RuntimeError: Only Tensors created explicitly by the user (graph leaves) support the deepcopy protocol at the moment
class Model(nn.Module):
def __init__(
self,
model, # byol
projection_size=256,
pred_size = 256,
projection_hidden_size=4096,
moving_average_decay=0.99,
use_momentum=True,
):
super(SSL, self).__init__()
self.online_encoder = Pre_model(model) # 256
self.use_momentum = use_momentum
self.target_encoder = None
self.target_ema_updater = EMA(moving_average_decay)
def _get_target_encoder(self):
target_encoder = copy.deepcopy(self.online_encoder)
set_requires_grad(target_encoder, False)
return target_encoder
def forward(self, x):
anchors = x['anchor'].cuda(non_blocking = True)
neighbors = x['neighbor'].cuda(non_blocking = True)
online_anchor_proj = self.online_encoder(anchors)
online_neighbor_proj = self.online_encoder(neighbors)
with torch.no_grad():
target_online = self._get_target_encoder() if self.use_momentum else self.online_encoder
target_anchor_proj= target_online(anchors)
target_neighbor_proj = target_online(neighbors)
I am using the following script predictor.py in order to get predictions from a Keras model hosted in GCP AI Platform.
import os
import pickle
import tensorflow as tf
import numpy as np
import logging
class MyPredictor(object):
def __init__(self, model, bow_model):
self._model = model
self._bow_model = bow_model
def predict(self, instances, **kwargs):
vectors = self.embedding([instances])
vectors = vectors.tolist()
output = self._model.predict(vectors)
return output
def embedding(self, statement):
vector = self._bow_model.transform(statement).toarray()
#vector = vector.to_list()
return vector
#classmethod
def from_path(cls, model_dir):
model_path = os.path.join(model_dir, 'model.h5')
model = tf.keras.models.load_model(model_path, compile = False)
preprocessor_path = os.path.join(model_dir, 'bow.pkl')
with open(preprocessor_path, 'rb') as f:
bow_model = pickle.load(f)
return cls(model, bow_model)
However i get
Prediction failed: Error when checking input: expected dense_input to have shape (2898,) but got array with shape (1,)
The problem seems to be due to the dimensions of my input data when trying to do the actual predictions, in line output = self._model.predict([vectors]). The model is expecting a vector of shape (2898, )
I am finding this quite odd... since when I print the shape and dimensions of the vector I get the following
This is the shape
(1, 2898)
This is the dim number
2
This is the vector
[[0 0 0 ... 0 0 0]]
So the dimensions and the shape is fine and it should really be working....
Furthermore, I did a test to get the predictions of the model stored locally and it works fine. This is the test file:
import os
import pickle
import tensorflow as tf
import numpy as np
class MyPredictor(object):
def __init__(self, model, bow_model):
self._model = model
self._bow_model = bow_model
def predict(self, instances, **kwargs):
print("These are the instances ", instances)
vector = self.embedding([instances])
output = self._model.predict(vector)
return output
def embedding(self, statement):
vector = self._bow_model.transform(statement).toarray()
#vector = vector.to_list()
return vector
model_path = 'model.h5'
model = tf.keras.models.load_model(model_path, compile = False)
preprocessor_path = 'bow.pkl'
with open(preprocessor_path, 'rb') as f:
bow_model = pickle.load(f)
instances = 'test'
predictor = MyPredictor(model, bow_model)
outputs = predictor.predict(instances)
print(outputs)
Solved it!
It was as silly as adding a set of parenthesis to this line output = self._model.predict([vectors])
After that I got another error regarding the output of the prediction not being json serializable. This I solved simply by adding .tolist() to the return return output.to_list()
import os
import pickle
import tensorflow as tf
import numpy as np
import logging
class MyPredictor(object):
def __init__(self, model, bow_model):
self._model = model
self._bow_model = bow_model
def predict(self, instances, **kwargs):
vectors = self.embedding([instances])
vectors = vectors.tolist()
output = self._model.predict([vectors])
return output.to_list()
def embedding(self, statement):
vector = self._bow_model.transform(statement).toarray()
#vector = vector.to_list()
return vector
#classmethod
def from_path(cls, model_dir):
model_path = os.path.join(model_dir, 'model.h5')
model = tf.keras.models.load_model(model_path, compile = False)
preprocessor_path = os.path.join(model_dir, 'bow.pkl')
with open(preprocessor_path, 'rb') as f:
bow_model = pickle.load(f)
return cls(model, bow_model)
I have an old trained tf1.x model (let it be Model1), constructed with Placeholders, tf.contrib and so on. I can use this model by restoring graph from .ckpt checkpoint in tf.Session (in tf1.x).
I resolved the easiest way to use the Model1 is to export it:
# tf1.x code
tf.saved_model.simple_save(sess, saved_Model1_path,
inputs={'input':'Placeholder:0'}, outputs={'output':'.../Sigmoid:0'})
I can use the obtained saved_model.pb even in tf2.0:
# tf2.0 code
Model1 = tf.saved_model.load(saved_Model1_path)
out = Model1.signatures['serving_default'](tf.convert_to_tensor(data))['output'].numpy()
out = Model1.signatures['serving_default'].prune('Placeholder:0', '.../Sigmoid:0')(data)
out = Model1.prune('Placeholder:0', '.../Sigmoid:0')(data)
Now imagine, I have a pre/post processing written with tf2.0 tf.function.
I wish the construction of preprocessing -> Model1-> postprocessing to be exported in a single saved_model.pb in tf2.0.
And here come the problems due to saved_model.pb of Model1 utilizes tf.Placeholders (smth like this, I'm not an expert here).
At the same time, I can easily build saved_model.pb from other tf2.0 exported model:
import os
import tensorflow as tf
assert tf.__version__[0] == '2'
class M1(tf.Module):
def __init__(self):
super(M1, self).__init__()
self.v = tf.Variable(2.)
#tf.function(input_signature=[tf.TensorSpec([], tf.float32)])
def M1_func(self, x):
return x * self.v
# build some saved_model.pb
m1 = M1()
path_1 = './save1'
path_to_save = os.path.realpath(path_1)
tf.saved_model.save(m1, path_to_save)
# load built saved_model.pb and check it works
m1 = tf.saved_model.load(path_1)
assert 6 == m1.M1_func(3.).numpy()
# build other saved_model.pb using first saved_model.pb as a part of computing graph
class M2(tf.Module):
def __init__(self):
super(M2, self).__init__()
self.run = m1
self.v = tf.Variable(3.)
#tf.function(input_signature=[tf.TensorSpec([], tf.float32)])
def M2_func(self, x):
return self.run.M1_func(x) * self.v
m2 = M2()
path_2 = './save2'
path_to_save = os.path.realpath(path_2)
tf.saved_model.save(m2, path_to_save)
m2 = tf.saved_model.load(path_2)
assert 18 == m2.M2_func(3.).numpy()
But when I'm trying to do the same, except replacing first saved_model.pb from tf2.0 saving on tf1.x saving, It doesn't work:
# save first saved_model.pb with tf1.x
import tensorflow as tf
assert tf.__version__[0] == '1'
inp = tf.placeholder(shape=[],dtype=tf.float32)
a = tf.Variable(1.5)
out = a*inp
sess = tf.Session()
sess.run(tf.global_variables_initializer())
assert 7.5 == out.eval({inp:5.}, sess)
path_3 = './save3'
path_to_save = os.path.realpath(path_3)
tf.saved_model.simple_save(sess, path_to_save, inputs={'input': inp}, outputs={'output': out})
Now switch to tf2.0 and try to build new saved_model.pb with first one as a part of a computing graph:
import os
import tensorflow as tf
assert tf.__version__[0] == '2'
path_3 = './save3'
path_to_save = os.path.realpath(path_3)
m1 = tf.saved_model.load(path_to_save)
class M2(tf.Module):
def __init__(self):
super(M2, self).__init__()
self.run = m1.signatures['serving_default'].prune('Placeholder:0', 'mul:0')
self.v = tf.Variable(3.)
#tf.function(input_signature=[tf.TensorSpec([], tf.float32)])
def M2_func(self, x):
return self.run(x) * self.v
m2 = M2()
assert 22.5 == m2.M2_func(5.) # ofc eager execution works
# now save M2 to saved_model.pb and check it works (it does not)
path_4 = './save4'
path_to_save = os.path.realpath(path_4)
tf.saved_model.save(m2, path_to_save)
m2 = tf.saved_model.load(path_4)
m2.M2_func(5.) # error:
tensorflow.python.framework.errors_impl.FailedPreconditionError: Attempting to use uninitialized value StatefulPartitionedCall/StatefulPartitionedCall/Variable
[[{{node StatefulPartitionedCall/StatefulPartitionedCall/Variable/read}}]] [Op:__inference_restored_function_body_207]
Function call stack:
restored_function_body
So the question is: how to save this architecture in a single saved_model.pb in tf2.0
preprocessing (tf2.0 #tf.function) -> Model1 (saved_model.pb created in tf1.x) -> postprocessing (tf2.0 #tf.function)
The problem was solved. Look at this exporting function and how to use it. This function implementation accepts a single input tensor name and a list of out tensor names.
import tensorflow as tf
def export_tf1(session, in_tnsr_fullname, out_tnsrS_fullname, export_dir='./export'):
assert isinstance(in_tnsr_fullname, str)
assert all([isinstance(out_tnsr_fullname, str) for out_tnsr_fullname in out_tnsrS_fullname])
in_tnsr_name = in_tnsr_fullname.split(':')[0]
out_tnsrS_name = [out_tnsr_fullname.split(':')[0] for out_tnsr_fullname in out_tnsrS_fullname]
graph_def = tf.graph_util.convert_variables_to_constants(session, session.graph.as_graph_def(), out_tnsrS_name)
tf.reset_default_graph()
outs = tf.import_graph_def(graph_def, name="", return_elements=out_tnsrS_fullname)
g = outs[0].graph
builder = tf.saved_model.builder.SavedModelBuilder(export_dir)
with tf.Session(graph=g) as sess:
input_signatures = {in_tnsr_name: g.get_tensor_by_name(in_tnsr_fullname)}
output_signatures = {}
for out_tnsr_name, out_tnsr_fullname in zip(out_tnsrS_name, out_tnsrS_fullname):
output_signatures[out_tnsr_name] = g.get_tensor_by_name(out_tnsr_fullname)
signature = tf.saved_model.signature_def_utils.predict_signature_def(input_signatures, output_signatures)
builder.add_meta_graph_and_variables(
sess,
[tf.saved_model.tag_constants.SERVING],
{tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: signature},
clear_devices=True
)
builder.save()
How to use the exporting function to receive .pb from tf1_ckpt checkpoint:
import tensorflow as tf
assert tf.__version__[0] == '1'
g = tf.get_default_graph()
sess = tf.Session(graph=g)
ckpt_tf1_path = 'some_directory/name.ckpt' # just an example
tf.train.Saver().restore(sess, ckpt_tf1_path)
input_tensor_name = 'x_tnsr:0' # just an example
out_tensor_name = 'y_tnsr:0' # just an example
export_tf1(sess, input_tensor_name, [out_tensor_name], export_dir)
How to reuse .pb from tf1_ckpt in .pb with tf2.0:
import tensorflow as tf
assert tf.__version__[0] == '2'
class Export(tf.Module):
def __init__(self):
super(Export, self).__init__()
tf1_saved_model_directory = 'directory/saved_model' # just an example
self.tf1_model = tf.saved_model.load(tf1_saved_model_directory)
input_tensor_name = 'x_tnsr:0' # just an example
out_tensor_name = 'y_tnsr:0' # just an example
self.tf1_model = self.tf1_model.prune(input_tensor_name, out_tensor_name)
#tf.function
def __call__(self, x):
out = self.tf1_model(x)
return out
export_dir = './saved_model'
tf.saved_model.save(Export(), export_dir)
Here, I have LSTM Autoencoder written in Keras. I want to convert the code to Chainer.
import numpy as np
from keras.layers import Input, GRU
from keras.models import Model
input_feat = Input(shape=(30, 2000))
l = GRU( 100, return_sequences=True, activation="tanh", recurrent_activation="hard_sigmoid")(input_feat)
l = GRU(2000, return_sequences=True, activation="tanh", recurrent_activation="hard_sigmoid")(l)
model = Model(input_feat, l)
model.compile(optimizer="RMSprop", loss="mean_squared_error")
feat = np.load("feat.npy")
model.fit(feat, feat[:, ::-1, :], epochs=200, batch_size=250)
feat is numpy whose dimension is (269, 30, 2000). I could run above code and the result was reasonable. I had written below Chainer code.
import numpy as np
from chainer import Chain, Variable, optimizers
import chainer.functions as F
import chainer.links as L
class GRUAutoEncoder(Chain):
def __init__(self):
super().__init__()
with self.init_scope():
self.encode = L.GRU(2000, 100)
self.decode = L.GRU(100, 2000)
def __call__(self, h, mode):
if mode == "encode":
h = F.tanh(self.encode(h))
return h
if mode == "decode":
h = F.tanh(self.decode(h))
return h
def reset(self):
self.encode.reset_state()
self.decode.reset_state()
def main():
feat = np.load("feat.npy") #(269, 30, 2000)
gru_autoencoder = GRUAutoEncoder()
optimizer = optimizers.RMSprop(lr=0.01).setup(gru_autoencoder)
N = len(feat)
batch_size = 250
for epoch in range(200):
index = np.random.randint(0, N-batch_size+1)
input_splices = feat[index:index+batch_size] #(250, 30, 2000)
#Encoding
input_vector = np.zeros((30, batch_size, 2000), dtype="float32")
h = []
for i in range(frame_rate):
input_vector[i] = input_splices[:, i, :] #(250, 1, 2000)
tmp = Variable(input_vector[i])
h.append(gru_autoencoder(tmp, "encode")) #(250, 100)
#Decoding
output_vector = []
for i in range(frame_rate):
tmp = h[i]
output_vector.append(gru_autoencoder(tmp, "decode"))
x = input_vector[0]
t = output_vector[0]
for i in range(len(output_vector)):
x = F.concat((x,input_vector[i]), axis=1)
t = F.concat((t,output_vector[i]), axis=1)
loss = F.mean_squared_error(x, t)
gru_autoencoder.cleargrads()
loss.backward()
optimizer.update()
gru_autoencoder.reset()
if __name__ == "__main__":
main()
But the result of above code was not reasonable. I think the Chainer code has something wrong but I cannot find where it is.
In Keras code,
model.fit(feat, feat[:, ::-1, :])
So, I tried to reverse output_vector in Chainer code,
output_vector.reverse()
but the result was not still reasonable.
.. note: This answer is a translation of [Japanese SO].(https://ja.stackoverflow.com/questions/52162/keras%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92chainer%E3%81%AB%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88%E3%81%9F%E3%81%84lstm-autoencoder%E3%81%AE%E5%AE%9F%E8%A3%85/52213#52213)
You should avoid using L.GRU and should use L.NStepGRU, because for L.GRU you have to write "recurrence-aware" code. In other words, you have to apply L.GRU multiple times to one timeseries, therefore "batch" must be treated with great care. L.NStepGRU (with n_layers=1) wraps the batch-processing, so it would be user-friendly.
An instance of L.StepGRU takes two input arguments: one is initial state, and the other is a list of timeserieses, which composes a batch. Conventionally, the initial state is None.
Therefore, the whole answer for your question is as follows.
### dataset.py
from chainer.dataset import DatasetMixin
import numpy as np
class MyDataset(DatasetMixin):
N_SAMPLES = 269
N_TIMESERIES = 30
N_DIMS = 2000
def __init__(self):
super().__init__()
self.data = np.random.randn(self.N_SAMPLES, self.N_TIMESERIES, self.N_DIMS) \
.astype(np.float32)
def __len__(self):
return self.N_SAMPLES
def get_example(self, i):
return self.data[i, :, :]
### model.py
import chainer
from chainer import links as L
from chainer import functions as F
from chainer.link import Chain
class MyModel(Chain):
N_IN_CHANNEL = 2000
N_HIDDEN_CHANNEL = 100
N_OUT_CHANNEL = 2000
def __init__(self):
super().__init__()
self.encoder = L.NStepGRU(n_layers=1, in_size=self.N_IN_CHANNEL, out_size=self.N_HIDDEN_CHANNEL, dropout=0)
self.decoder = L.NStepGRU(n_layers=1, in_size=self.N_HIDDEN_CHANNEL, out_size=self.N_OUT_CHANNEL, dropout=0)
def to_gpu(self, device=None):
self.encoder.to_gpu(device)
self.decoder.to_gpu(device)
def to_cpu(self):
self.encoder.to_cpu()
self.decoder.to_cpu()
#staticmethod
def flip_list(source_list):
return [F.flip(source, axis=1) for source in source_list]
def __call__(self, source_list):
"""
.. note:
This implementation makes use of "auto-encoding"
by avoiding redundant copy in GPU device.
In the typical implementation, this function should receive
both of ``source_list`` and ``target_list``.
"""
target_list = self.flip_list(source_list)
_, h_list = self.encoder(hx=None, xs=source_list)
_, predicted_list = self.decoder(hx=None, xs=h_list)
diff_list = [F.mean_squared_error(target, predicted).reshape((1,)) for target, predicted in zip(target_list, predicted_list)]
loss = F.sum(F.concat(diff_list, axis=0))
chainer.report({'loss': loss}, self)
return loss
### converter.py (referring examples/seq2seq/seq2seq.py)
from chainer.dataset import to_device
def convert(batch, device):
"""
.. note:
batch must be list(batch_size) of array
"""
if device is None:
return batch
else:
return [to_device(device, x) for x in batch]
### train.py
from chainer.iterators import SerialIterator
from chainer.optimizers import RMSprop
from chainer.training.updaters import StandardUpdater
from chainer.training.trainer import Trainer
dataset = MyDataset()
BATCH_SIZE = 32
iterator = SerialIterator(dataset, BATCH_SIZE)
model = MyModel()
optimizer = RMSprop()
optimizer.setup(model)
updater = StandardUpdater(iterator, optimizer, convert, device=0)
trainer = Trainer(updater, (100, 'iteration'))
from chainer.training.extensions import snapshot_object
trainer.extend(snapshot_object(model, "model_iter_{.updater.iteration}"), trigger=(10, 'iteration'))
from chainer.training.extensions import LogReport, PrintReport, ProgressBar
trainer.extend(LogReport(['epoch', 'iteration', 'main/loss'], (1, 'iteration')))
trainer.extend(PrintReport(['epoch', 'iteration', 'main/loss']), trigger=(1, 'iteration'))
trainer.extend(ProgressBar(update_interval=1))
trainer.run()
In the function read_train_sets() an empty class is created called DataSets. It has no methods or variables. An object called data_sets is then created.
My question is, is data_sets.train an object of the class DataSet().
Or are you creating a method called train() and setting it equal to an object of the DataSet() class.
Note that there are two classes called DataSet and DataSets in the code.
import cv2
import os
import glob
from sklearn.utils import shuffle
import numpy as np
def load_train(train_path, image_size, classes):
images = []
labels = []
img_names = []
cls = []
print('Going to read training images')
for fields in classes:
index = classes.index(fields)
print('Now going to read {} files (Index: {})'.format(fields, index))
path = os.path.join(train_path, fields, '*g')
files = glob.glob(path)
for fl in files:
image = cv2.imread(fl)
image = cv2.resize(image, (image_size, image_size),0,0, cv2.INTER_LINEAR)
image = image.astype(np.float32)
image = np.multiply(image, 1.0 / 255.0)
images.append(image)
label = np.zeros(len(classes))
label[index] = 1.0
labels.append(label)
flbase = os.path.basename(fl)
img_names.append(flbase)
cls.append(fields)
images = np.array(images)
labels = np.array(labels)
img_names = np.array(img_names)
cls = np.array(cls)
return images, labels, img_names, cls
class DataSet(object):
def __init__(self, images, labels, img_names, cls):
self._num_examples = images.shape[0]
self._images = images
self._labels = labels
self._img_names = img_names
self._cls = cls
self._epochs_done = 0
self._index_in_epoch = 0
#property
def images(self):
return self._images
#property
def labels(self):
return self._labels
#property
def img_names(self):
return self._img_names
#property
def cls(self):
return self._cls
#property
def num_examples(self):
return self._num_examples
#property
def epochs_done(self):
return self._epochs_done
def next_batch(self, batch_size):
"""Return the next `batch_size` examples from this data set."""
start = self._index_in_epoch
self._index_in_epoch += batch_size
if self._index_in_epoch > self._num_examples:
# After each epoch we update this
self._epochs_done += 1
start = 0
self._index_in_epoch = batch_size
assert batch_size <= self._num_examples
end = self._index_in_epoch
return self._images[start:end], self._labels[start:end], self._img_names[start:end], self._cls[start:end]
def read_train_sets(train_path, image_size, classes, validation_size):
class DataSets(object):
pass
data_sets = DataSets()
images, labels, img_names, cls = load_train(train_path, image_size, classes)
images, labels, img_names, cls = shuffle(images, labels, img_names, cls)
if isinstance(validation_size, float):
validation_size = int(validation_size * images.shape[0])
validation_images = images[:validation_size]
validation_labels = labels[:validation_size]
validation_img_names = img_names[:validation_size]
validation_cls = cls[:validation_size]
train_images = images[validation_size:]
train_labels = labels[validation_size:]
train_img_names = img_names[validation_size:]
train_cls = cls[validation_size:]
data_sets.train = DataSet(train_images, train_labels, train_img_names, train_cls)
data_sets.valid = DataSet(validation_images, validation_labels, validation_img_names, validation_cls)
return data_sets
You can dynamically assign attributes to your objects in Python. Try inserting hasattr(data_sets, 'train') which asks if data_sets has attribute train after you assign it and see what you get. Also you can call type(data_sets.train) and convince yourself that it is indeed of type DataSet.
data_sets.train = DataSet(train_images, train_labels, train_img_names, train_cls)
This is quite clear since we are assigning a Class object to the data_sets.train
With respect to data_sets object, train and validate will be 2 attributes to it. Hope this helps.