在 TensorFlow 中从 network.foward() 收集特征

Collecting features from network.foward() in TensorFlow

所以基本上我想实现与此代码相同的目标,但在 TensorFlow 中

def get_function(network, loader):
    ''' Collect function (features) from the self.network.module.forward_features() routine '''
    features = []
    for batch_idx, (inputs, targets) in enumerate(loader):
        inputs, targets = inputs.to('cpu'), targets.to('cpu')

        features.append([f.cpu().data.numpy().astype(np.float16) for f in network.forward_features(inputs)])

    return [np.concatenate(list(zip(*features))[i]) for i in range(len(features[0]))]

是否有任何简洁的方法可以使用 TensorFlow 迭代器执行此操作?这是我想在 TensorFlow 中复制的火炬代码。 https://pastecode.io/s/b03cpoyv

为了回答您的问题,我只需要确保您正确理解您的原文 code。那么,这是您的工作流程

class LeNet(nn.Module):
     def forward:
         # few bunch of layers 
         return output

     def forward_features:
         # same as forward function 
         return [each layer output] 

现在,接下来,您将使用 torch_get_function 方法并检索模型中定义的 forward_features 函数输出的所有图层。 torch_get_function 给出了总共 4 个输出列表,您只选择第一个特征并在最后连接批次。

def torch_get_function(network, loader):
    features = []
    for batch_idx, (inputs, targets) in enumerate(loader):

        print('0', network.forward_features(inputs)[0].shape)
        print('1', network.forward_features(inputs)[1].shape)
        print('2', network.forward_features(inputs)[2].shape)
        print('3', network.forward_features(inputs)[3].shape)
        print()

        features.append([f... for f in network.forward_features(inputs)])
    return [np.concatenate(list(zip(*features))[i]) for i in range(len(features[0]))]

for epoch in epochs:
    dataset = torchvision.datasets.MNIST...
    dataset = torch.utils.data.Subset(dataset, list(range(0, 1000)))
    functloader = torch.utils.data.DataLoader(...)

    # for x , y in functloader:
    #     print('a ', x.shape, y.shape) 
    # a  torch.Size([100, 1, 28, 28]) torch.Size([100])
        
    activs = torch_get_function(net, functloader)
    print(activs[0].shape)
    break

这就是为什么当我 运行 你的代码时,我得到

# These are the 4 output that returned by forward_features(inputs)
0 torch.Size([100, 10, 12, 12])
1 torch.Size([100, 320])
2 torch.Size([100, 50])
3 torch.Size([100, 10])

...

# In the return statement of forward_features -
# You take only the first index feature and concate across batches.
(1000, 10, 12, 12)

因此,模型的输入大小为 (batch_size, 1, 28, 28),最终输出为 (1000, 10, 12, 12)


让我们在 中做同样的事情,一步一步。

import numpy as np 
from tqdm import tqdm 

import tensorflow as tf 
from tensorflow import keras 
from tensorflow.keras.layers import (Conv2D, Dropout, MaxPooling2D, 
                                     Dense, Flatten)

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_test = x_test.astype("float32") / 255.0
x_test = np.reshape(x_test, (-1, 28, 28, 1))
dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test))
dataset = dataset.shuffle(buffer_size=1024).batch(100)

# it's like torch.utils.data.Subset
dataset = dataset.take(1000)
dataset
<TakeDataset shapes: ((None, 28, 28, 1), (None,)), types: (tf.float32, tf.uint8)>

现在让我们构建模型。为了让你熟悉,我写在 sub-class API.

class LeNet(keras.Model):
    def __init__(self, num_classes, input_size=28):
        super(LeNet, self).__init__()
        self.conv1 = Conv2D(10, (5, 5))
        self.conv2 = Conv2D(20, (5, 5))
        self.conv2_drop = Dropout(rate=0.5)
        self.fc1 = Dense(50)
        self.fc2 = Dense(num_classes)

    def call(self, inputs, training=None):
        x1 = tf.nn.relu(MaxPooling2D(2)(self.conv1(inputs)))
        x2 = tf.nn.relu(MaxPooling2D(2)(self.conv2_drop(self.conv2(x1))))
        x2 = Flatten()(x2)
        x3 = tf.nn.relu(self.fc1(x2))
        x4 = tf.nn.softmax(self.fc2(x3), axis=1)

        # in tf/keras, when we will call model.fit / model.evaluate 
        # to train the model only x4 will return 
        if training:
            x4
        else: # but when model(input)/model.predict(), we can return many :)
            return [x1, x2, x3, x4]

lenet = LeNet(10)
lenet.build(input_shape=(None, 28, 28, 1))

获取所需的功能

features = []

for input, target in tqdm(dataset):
    # lenet(...) will give 4 output as we model
    # but as we're interested on the first index feature... 
    features.append(lenet(input, training=False)[0])

print(len(features))
features = np.concatenate(features, axis=0)
features.shape
(10000, 12, 12, 10)

在tensorflow中,通道轴默认设置为last,而不是torch。在 torch 中,你收到 (1000, 10, 12, 12) 而在 tensorflow 中,它给你 (10000, 12, 12, 10) 但你当然可以更改它,(). Here is the working colab.