Framework
Pytorch vs Tensorflow
The image range is different for each framework. In PyTorch, the image range is 0-1 while TensorFlow uses a range from 0 to 255. To use TensorFlow, we have to adapt the image range.
To TF
def dataset_to_tf(
dataset,
cols_to_retain,
collate_fn,
collate_fn_args,
columns_to_np_types,
output_signature,
shuffle,
batch_size,
drop_remainder,
):
"""Create a tf.data.Dataset from the underlying Dataset. This is a single-process method - the multiprocess
equivalent is multiprocess_dataset_to_tf.
Args:
dataset (`Dataset`): Dataset to wrap with tf.data.Dataset.
cols_to_retain (`List[str]`): Dataset column(s) to load in the
tf.data.Dataset. It is acceptable to include column names that are created by the `collate_fn` and
that do not exist in the original dataset.
collate_fn(`Callable`): A function or callable object (such as a `DataCollator`) that will collate
lists of samples into a batch.
collate_fn_args (`Dict`): A `dict` of keyword arguments to be passed to the
`collate_fn`. Can be empty.
columns_to_np_types (`Dict[str, np.dtype]`): A `dict` mapping column names to numpy dtypes.
output_signature (`Dict[str, tf.TensorSpec]`): A `dict` mapping column names to
`tf.TensorSpec` objects.
shuffle(`bool`): Shuffle the dataset order when loading. Recommended True for training, False for
validation/evaluation.
batch_size (`int`): Size of batches to load from the dataset.
drop_remainder(`bool`, default `None`): Drop the last incomplete batch when loading. If not provided,
defaults to the same setting as shuffle.
Returns:
`tf.data.Dataset`
"""
if config.TF_AVAILABLE:
import tensorflow as tf
else:
raise ImportError("Called a Tensorflow-specific function but Tensorflow is not installed.")
getter_fn = partial(
np_get_batch,
dataset=dataset,
cols_to_retain=cols_to_retain,
collate_fn=collate_fn,
collate_fn_args=collate_fn_args,
columns_to_np_types=columns_to_np_types,
return_dict=False, # TF expects numpy_function to return a list and will not accept a dict
)
@tf.function(input_signature=[tf.TensorSpec(None, tf.int64)])
def fetch_function(indices):
output = tf.numpy_function(
getter_fn,
inp=[indices],
# This works because dictionaries always output in the same order
Tout=[tf.dtypes.as_dtype(dtype) for dtype in columns_to_np_types.values()],
)
return {key: output[i] for i, key in enumerate(columns_to_np_types.keys())}
tf_dataset = tf.data.Dataset.from_tensor_slices(np.arange(len(dataset), dtype=np.int64))
if shuffle:
tf_dataset = tf_dataset.shuffle(len(dataset))
tf_dataset = tf_dataset.batch(batch_size, drop_remainder=drop_remainder).map(fetch_function)
def ensure_shapes(input_dict):
return {key: tf.ensure_shape(val, output_signature[key].shape) for key, val in input_dict.items()}
return tf_dataset.map(ensure_shapes)
pytorch dataset to tf dataset
import tensorflow as tf
import torch
# Assume that we have a PyTorch Dataset object called 'dataset'
def pytorch_dataset_to_tensorflow_dataset(dataset):
def generator():
for data in dataset:
# Convert data from PyTorch tensors to TensorFlow tensors
data = [tf.convert_to_tensor(x) for x in data]
yield data
# Create a TensorFlow Dataset from the generator
dataset = tf.data.Dataset.from_generator(generator, output_types=data[0].dtype, output_shapes=data[0].shape)
return dataset
# Create a TensorFlow Dataset from the PyTorch Dataset
dataset = pytorch_dataset_to_tensorflow_dataset(dataset)
# Create a TensorFlow DataLoader from the TensorFlow Dataset
dataloader = tf.data.DataLoader(dataset, batch_size=32, num_parallel_calls=tf.data.AUTOTUNE)
image = tf.io.read_file(filename=filepath)
image = tf.image.decode_jpeg(image, channels=3) #or decode_png
The opposite of unsqueeze
and squeeze
is expand_dims
:
img = tf.expand_dims(img,axis=0)
yield the desired/necessary transformations.
As for the photos, I am quite sure that you missed a /255.0 in case of PyTorch or added a 255.0 division in case of TensorFlow.
In fact, when digging deep into the Keras backend, you can see that when you call your preprocessing function, it will call this function here:
def _preprocess_numpy_input(x, data_format, mode):
"""Preprocesses a Numpy array encoding a batch of images.
Arguments:
x: Input array, 3D or 4D.
data_format: Data format of the image array.
mode: One of "caffe", "tf" or "torch".
- caffe: will convert the images from RGB to BGR,
then will zero-center each color channel with
respect to the ImageNet dataset,
without scaling.
- tf: will scale pixels between -1 and 1,
sample-wise.
- torch: will scale pixels between 0 and 1 and then
will normalize each channel with respect to the
ImageNet dataset.
Returns:
Preprocessed Numpy array.
"""
if not issubclass(x.dtype.type, np.floating):
x = x.astype(backend.floatx(), copy=False)
if mode == 'tf':
x /= 127.5
x -= 1.
return x
elif mode == 'torch':
x /= 255.
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
else:
if data_format == 'channels_first':
# 'RGB'->'BGR'
if x.ndim == 3:
x = x[::-1, ...]
else:
x = x[:, ::-1, ...]
else:
# 'RGB'->'BGR'
x = x[..., ::-1]
mean = [103.939, 116.779, 123.68]
std = None
# Zero-center by mean pixel
if data_format == 'channels_first':
if x.ndim == 3:
x[0, :, :] -= mean[0]
x[1, :, :] -= mean[1]
x[2, :, :] -= mean[2]
if std is not None:
x[0, :, :] /= std[0]
x[1, :, :] /= std[1]
x[2, :, :] /= std[2]
else:
x[:, 0, :, :] -= mean[0]
x[:, 1, :, :] -= mean[1]
x[:, 2, :, :] -= mean[2]
if std is not None:
x[:, 0, :, :] /= std[0]
x[:, 1, :, :] /= std[1]
x[:, 2, :, :] /= std[2]
else:
x[..., 0] -= mean[0]
x[..., 1] -= mean[1]
x[..., 2] -= mean[2]
if std is not None:
x[..., 0] /= std[0]
x[..., 1] /= std[1]
x[..., 2] /= std[2]
return x
mean and std
mean = 0.0
std = 0.0
for images, _ in dl:
batch_samples = images.size(0) # batch size (the last batch can have smaller size!)
images = images.view(batch_samples, images.size(1), -1)
mean += images.mean(2).sum(0)
std += images.std(2).sum(0)
mean /= len(dl.dataset)
std /= len(dl.dataset)
DataGenerator(keras.utils.Sequence):
import numpy as np
import keras
class DataGenerator(keras.utils.Sequence):
'Generates data for Keras'
def __init__(self, list_IDs, labels, batch_size=32, dim=(32,32,32), n_channels=1,
n_classes=10, shuffle=True):
'Initialization'
self.dim = dim
self.batch_size = batch_size
self.labels = labels
self.list_IDs = list_IDs
self.n_channels = n_channels
self.n_classes = n_classes
self.shuffle = shuffle
self.on_epoch_end()
def __len__(self):
'Denotes the number of batches per epoch'
return int(np.floor(len(self.list_IDs) / self.batch_size))
def __getitem__(self, index):
'Generate one batch of data'
# Generate indexes of the batch
indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]
# Find list of IDs
list_IDs_temp = [self.list_IDs[k] for k in indexes]
# Generate data
X, y = self.__data_generation(list_IDs_temp)
return X, y
def on_epoch_end(self):
'Updates indexes after each epoch'
self.indexes = np.arange(len(self.list_IDs))
if self.shuffle == True:
np.random.shuffle(self.indexes)
def __data_generation(self, list_IDs_temp):
'Generates data containing batch_size samples' # X : (n_samples, *dim, n_channels)
# Initialization
X = np.empty((self.batch_size, *self.dim, self.n_channels))
y = np.empty((self.batch_size), dtype=int)
# Generate data
for i, ID in enumerate(list_IDs_temp):
# Store sample
X[i,] = np.load('data/' + ID + '.npy')
# Store class
y[i] = self.labels[ID]
return X, keras.utils.to_categorical(y, num_classes=self.n_classes)
class ImageDataGenerator(tf.keras.preprocessing.image.ImageDataGenerator):
def __init__(self):
super().__init__(
rescale=1.0 / 255.0,
rotation_range=10,
width_shift_range=0.2,
height_shift_range=0.2,
zoom_range=[0.95, 1.05],
shear_range=0.1,
fill_mode="wrap",
horizontal_flip=True,
vertical_flip=True,
)
class Generator(object):
def __init__(self, batch_size, name_x, name_y):
data_f = None # h5py.File(open_directory, "r")
self.x = data_f[name_x]
self.y = data_f[name_y]
if len(self.x.shape) == 4:
self.shape_x = (None, self.x.shape[1], self.x.shape[2], self.x.shape[3])
if len(self.x.shape) == 3:
self.shape_x = (None, self.x.shape[1], self.x.shape[2])
if len(self.y.shape) == 4:
self.shape_y = (None, self.y.shape[1], self.y.shape[2], self.y.shape[3])
if len(self.y.shape) == 3:
self.shape_y = (None, self.y.shape[1], self.y.shape[2])
self.num_samples = self.x.shape[0]
self.batch_size = batch_size
self.epoch_size = self.num_samples // self.batch_size + 1 * (
self.num_samples % self.batch_size != 0
)
self.pointer = 0
self.sample_nums = np.arange(0, self.num_samples)
np.random.shuffle(self.sample_nums)
def data_generator(self):
for batch_num in range(self.epoch_size):
x = []
y = []
for elem_num in range(self.batch_size):
sample_num = self.sample_nums[self.pointer]
x += [self.x[sample_num]]
y += [self.y[sample_num]]
self.pointer += 1
if self.pointer == self.num_samples:
self.pointer = 0
np.random.shuffle(self.sample_nums)
break
x = np.array(x, dtype=np.float32)
y = np.array(y, dtype=np.float32)
yield x, y
def get_dataset(self):
dataset = tf.data.Dataset.from_generator(
self.data_generator,
output_signature=(
tf.TensorSpec(shape=(), dtype=tf.int32),
tf.RaggedTensorSpec(shape=(2, None), dtype=tf.int32),
),
)
dataset = dataset.prefetch(1)
return dataset
def _load_image(self, image_path):
image = cv2.imread(image_path) # BGR
# image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# image = tf.io.read_file(image_path)
# image = tf.io.decode_image(
# image,
# channels=self.num_channels,
# dtype=tf.dtypes.uint8,
# expand_animations=False,
# )
# image = tf.image.resize(
# image,
# self.dim,
# method=tf.image.ResizeMethod.BILINEAR,
# preserve_aspect_ratio=True,
# antialias=False,
# name=None,
# )
# if not issubclass(image.dtype.type, np.floating):
image = image.astype(np.float32)
# image = image.astype(tf.keras.backend.floatx(), copy=False)
image = self.apply_image_transforms(image)
# 'RGB'->'BGR'
# image = image[..., ::-1]
# image = tf.image.convert_image_dtype(image, dtype=tf.uint8, saturate=False)
# image = tf.cast(image, tf.float32) # / 127.5
# image -= 1.0
mean = [103.939, 116.779, 123.68]
# mean_tensor = tf.keras.backend.constant(-np.array(mean))
# if tf.keras.backend.dtype(image) != tf.keras.backend.dtype(mean_tensor):
# image = tf.keras.backend.bias_add(
# image,
# tf.keras.backend.cast(mean_tensor, tf.keras.backend.dtype(image)),
# data_format="channels_last",
# )
# else:
# image = tf.keras.backend.bias_add(image, mean_tensor, "channels_last")
# image[0, :, :] -= mean[0]
# image[1, :, :] -= mean[1]
# image[2, :, :] -= mean[2]
image[..., 0] -= mean[0]
image[..., 1] -= mean[1]
image[..., 2] -= mean[2]
# image = tf.keras.applications.vgg16.preprocess_input(image)
""" Preprocessed numpy.array or a tf.Tensor with type float32. The images are converted from RGB to BGR,
then each color channel is zero-centered with respect to the ImageNet dataset, without scaling. """
return image
Unfreeze specific layers
Here is one way to unfreeze specific layers. We pick the same model and some layers (e.g. block14_sepconv2
). The purpose is to unfreeze these layers and make the rest of the layers freeze.
from tensorflow import keras
base_model = keras.applications.Xception(
weights='imagenet',
input_shape=(150,150,3),
include_top=False
)
# free all layer except the desired layers
# which is in [ ... ]
for layer in base_model.layers:
if layer.name not in ['block14_sepconv2', 'block13_sepconv1']:
layer.trainable = False
if layer.trainable:
print(layer.name)
block14_sepconv2
block13_sepconv1
Compute the trainable and non-trainable variables.
import tensorflow.keras.backend as K
import numpy as np
trainable_count = np.sum([K.count_params(w) \
for w in base_model.trainable_weights])
non_trainable_count = np.sum([K.count_params(w) \
for w in base_model.non_trainable_weights])
print('Total params: {:,}'.format(trainable_count + non_trainable_count))
print('Trainable params: {:,}'.format(trainable_count))
print('Non-trainable params: {:,}'.format(non_trainable_count))
Total params: 20,861,480
Trainable params: 3,696,088
Non-trainable params: 17,165,392
tensorflow-macos Releases
tensorflow-macos | tensorflow-metal | macOS version | Features |
---|---|---|---|
v2.5 | 0.1.2 | 12.0+ | Pluggable device |
v2.6 | 0.2.0 | 12.0+ | Variable sequences for RNN layers |
v2.7 | 0.3.0 | 12.0+ | Custom op support |
v2.8 | 0.4.0 | 12.0+ | RNN performance improvements |
v2.9 | 0.5.0 | 12.1+ | Distributed training |