ChatGPT解决这个技术问题 Extra ChatGPT

如何使用带有灰度图像的预训练神经网络?

我有一个包含灰度图像的数据集,我想在它们上训练一个最先进的 CNN。我非常想微调一个预训练模型(例如 here)。

问题是我能找到权重的几乎所有模型都在包含 RGB 图像的 ImageNet 数据集上进行了训练。

我不能使用其中一个模型,因为在我的情况下,它们的输入层需要一批形状 (batch_size, height, width, 3)(64, 224, 224, 3),但我的图像批次是 (64, 224, 224)

有什么方法可以使用其中一种模型吗?在加载权重并添加自己的权重后,我曾考虑删除输入层(就像我们对顶层所做的那样)。这种方法正确吗?

您可以尝试删除输入层并添加自己的。然后您可以尝试仅训练该层。如果你没有看到所有其他层被锁定时损失减少,那么这种方式对你来说是行不通的。
不要问我们这种做法是否正确:问问电脑!试试看!另一种方法是将输入向量增加三倍:将灰度值提供给所有三个颜色层。
我个人的感觉是,这对你来说是行不通的。这些分类网络肯定是使用颜色之间的相互关系来对物体进行分类,而这些信息在中间层的权重中根深蒂固
@Prune 训练这些模型可能需要几天时间,如果有人以前遇到过这个问题,我会很感激一些见解......
正如其他人所提到的,堆叠 3 个相同的灰度数组作为输入是可行的——但我会将此作为实现更多数据增强的机会——将图像过滤器应用于原始灰度图像并将它们随机分配给 3 个通道。

D
Djib2011

模型的架构无法更改,因为权重已针对特定输入配置进行了训练。用你自己的替换第一层几乎会使其余的权重无用。

-- 编辑:由 Prune 建议的详细说明 -- CNN 的构建是为了随着它们的深入,它们可以从前一层提取的低级特征中提取高级特征。通过删除 CNN 的初始层,您正在破坏该特征层次结构,因为后续层将不会接收它们应该作为输入的特征。在您的情况下,第二层已被训练以期望第一层的特征。通过用随机权重替换你的第一层,你基本上放弃了在后续层上完成的任何训练,因为它们需要重新训练。我怀疑他们能否保留在初始培训中学到的任何知识。 --- 结束编辑 ---

不过,有一种简单的方法可以让您的模型处理灰度图像。您只需要使图像看起来是 RGB。最简单的方法是在新维度上重复图像数组 3 次。因为您将在所有 3 个通道上拥有相同的图像,所以模型的性能应该与它在 RGB 图像上的性能相同。

在 numpy 中,这可以像这样轻松完成:

print(grayscale_batch.shape)  # (64, 224, 224)
rgb_batch = np.repeat(grayscale_batch[..., np.newaxis], 3, -1)
print(rgb_batch.shape)  # (64, 224, 224, 3)

它的工作方式是它首先创建一个新维度(放置通道),然后在这个新维度上重复现有数组 3 次。

我也很确定 keras 的 ImageDataGenerator 可以将灰度图像加载为 RGB。


堆叠1通道图像很容易,但问题不是如何制作3通道图像,而是当他的原始图像为1通道时,他是否可以使用预训练模型进行分类,我认为答案可能是否定的
这几乎是处理灰度图像时的默认方法。我已经做了几次,它工作正常,它甚至是 keras 的 ImageDataGenerator 中的默认设置,用于加载重复 3 次的灰度图像。将其视为反向 RGB -> 灰度变换(其中灰度 =(R+B+G)/3)。
这显示了如何进行我建议的第二次尝试;它没有回答原始问题。这会导致对灰度输入的有效微调吗?
您回答的第一段是直接部分:您能否详细说明以说服 OP?
“用你自己的替换第一层几乎会使其余的权重无用。” - 您确定吗?检查这一点的实验是在 ImageNet 上训练一个神经网络,看看它“通常”需要多长时间才能达到一定的准确度。然后重新初始化输入层,看看需要多长时间才能再次达到该精度。我相信初始化网络会花费更少的时间。
r
rwightman

根据当前接受的答案将灰度图像转换为 RGB 是解决此问题的一种方法,但不是最有效的方法。您当然可以修改模型的第一个卷积层的权重并实现既定目标。修改后的模型既可以开箱即用(精度降低),也可以微调。修改第一层的权重不会像其他人建议的那样使其余的权重无用。

为此,您必须在加载预训练权重的位置添加一些代码。在您选择的框架中,您需要弄清楚如何获取网络中第一个卷积层的权重并在分配给您的单通道模型之前对其进行修改。所需的修改是在输入通道的维度上对权重张量求和。权重张量的组织方式因框架而异。 PyTorch 默认为 [out_channels, in_channels, kernel_height, kernel_width]。在 Tensorflow 中,我相信它是 [kernel_height, kernel_width, in_channels, out_channels]。

以 PyTorch 为例,在 Torchvision (https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py) 的 ResNet50 模型中,conv1 的权重形状为 [64, 3, 7, 7]。对维度 1 求和会产生一个形状为 [64, 1, 7, 7] 的张量。在底部,我包含了一段代码,它可以与 Torchvision 中的 ResNet 模型一起使用,假设添加了一个参数 (inchans) 来为模型指定不同数量的输入通道。

为了证明这项工作,我在 ResNet50 上使用预训练的权重进行了三轮 ImageNet 验证。运行 2 和 3 的数字略有不同,但它是最小的,一旦微调应该是无关紧要的。

带 RGB 图像的未修改 ResNet50:Prec @1:75.6,Prec @5:92.8 带 3 通道灰度的未修改 ResNet50 图像:Prec @1:64.6,Prec @5:86.4 带 1 通道灰度的修改 1 通道 ResNet50图片:Prec @1:63.8,Prec @5:86.1

def _load_pretrained(model, url, inchans=3):
    state_dict = model_zoo.load_url(url)
    if inchans == 1:
        conv1_weight = state_dict['conv1.weight']
        state_dict['conv1.weight'] = conv1_weight.sum(dim=1, keepdim=True)
    elif inchans != 3:
        assert False, "Invalid number of inchans for pretrained weights"
    model.load_state_dict(state_dict)

def resnet50(pretrained=False, inchans=3):
    """Constructs a ResNet-50 model.
    Args:
        pretrained (bool): If True, returns a model pre-trained on ImageNet
    """
    model = ResNet(Bottleneck, [3, 4, 6, 3], inchans=inchans)
    if pretrained:
        _load_pretrained(model, model_urls['resnet50'], inchans=inchans)
    return model

听起来超级酷!所以这是有效的,因为当 r = g = b(灰度)时 r*w0+g*w1+b*w2 等效于 r*(w0+w1+w2)
所以,这看起来正是我感兴趣的,但是,尚不清楚我们是否会获得 3 倍的处理速度?我有一个 RGB 图像,其中绿色 chan 比其他图像好得多,并且希望只使用绿色会快得多。您能否澄清一下这是什么时候,以及这种方法的目的是否确实是为了加快训练和预测?非常感谢!
@Cat 您可能会观察到小地图加速,但远不及 3 倍,因为这只是一层,网络的其余部分保持不变。
这个答案与接受的答案相矛盾。如果这个答案是正确的(我认为是正确的),它应该被标记为接受的答案。否则,人们在阅读接受的答案后会被误导。
m
mmrbulbul

一个简单的方法是在基础模型之前添加一个卷积层,然后将输出提供给基础模型。像这样:

from keras.models import Model
from keras.layers import Input 

resnet = Resnet50(weights='imagenet',include_top= 'TRUE') 

input_tensor = Input(shape=(IMG_SIZE,IMG_SIZE,1) )
x = Conv2D(3,(3,3),padding='same')(input_tensor)    # x has a dimension of (IMG_SIZE,IMG_SIZE,3)
out = resnet (x) 

model = Model(inputs=input_tensor,outputs=out)



ValueError: You are trying to load a weight file containing 13 layers into a model with 14 layers. 知道如何避免这种情况吗?
@马达拉你能分享更多细节吗?另外请检查您是否包括最后一层。
# input_shape = (64,64,1) input_tensor = Input(shape=image_shape) model_input = Conv2D(filters = 3, kernel_size=3, padding='same', name="input_conv")(input_tensor) 现在我导入 VGG16 模型 vgg16 = VGG16(include_top=False, weights='imagenet',input_tensor=model_input) 然后我尝试添加一个 Conv 层 X = Conv2D(channels, kernel_size=3, padding='same')(vgg16.output) X = Activation('tanh')(X) 最后的模型:model = Model(inputs = input_tensor, outputs = X)
只需将行 vgg16 = VGG16(include_top=False, weights='imagenet',input_tensor=model_input) 分成 vgg16 = VGG16(include_top=False, weights='imagenet') vgg16 = vgg16(model_input) 应该可以工作。
H
Hu Xixi

为什么不尝试将灰度图像转换为 RGB 图像?

tf.image.grayscale_to_rgb(
    images,
    name=None
)

N
NielsSchneider

删除输入层是行不通的。这将导致所有后续层都会受到影响。

您可以做的是将 3 个黑白图像连接在一起以扩展您的颜色维度。

img_input = tf.keras.layers.Input(shape=(img_size_target, img_size_target,1))
img_conc = tf.keras.layers.Concatenate()([img_input, img_input, img_input])    

model = ResNet50(include_top=True, weights='imagenet', input_tensor=img_conc)

虽然此代码可能会解决问题,但包括解释如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提出问题的人。请编辑您的答案以添加解释并说明适用的限制和假设。在此链接中查看有关如何回答的更多详细信息:stackoverflow.com/help/how-to-answer
h
hafiz031

我在使用 VGG16 和灰度图像时遇到了同样的问题。我解决了这个问题,如下所示:

假设我们的训练图像在 train_gray_images 中,每一行包含展开的灰度图像强度。因此,如果我们直接将其传递给 fit 函数,则会产生错误,因为 fit 函数需要 3 通道 (RGB) 图像数据集而不是灰度数据集。因此,在传递给 fit 函数之前,请执行以下操作:

创建一个虚拟 RGB 图像数据集,就像具有相同形状的灰度数据集(此处为 dummy_RGB_image)。唯一的区别是这里我们使用的通道数是 3。

dummy_RGB_images = np.ndarray(shape=(train_gray_images.shape[0], train_gray_images.shape[1], train_gray_images.shape[2], 3), dtype= np.uint8) 

因此只需将整个数据集复制 3 次到“dummy_RGB_images”的每个通道。 (这里的尺寸是 [no_of_examples, height, width, channel])

dummy_RGB_images[:, :, :, 0] = train_gray_images[:, :, :, 0]
dummy_RGB_images[:, :, :, 1] = train_gray_images[:, :, :, 0]
dummy_RGB_images[:, :, :, 2] = train_gray_images[:, :, :, 0]

最后传递 dummy_RGB_images 而不是灰度数据集,例如:

model.fit(dummy_RGB_images,...)

A
Ahmed Baruwa

numpy 的深度堆栈函数 np.dstack((img, img, img)) 是一种自然的方式。


他们基本上产生相同的结果, np.dstack 似乎更简单一点
p
pepe

如果您已经在使用 scikit-image,则可以使用 gray2RGB 获得所需的结果。

from skimage.color import gray2rgb
rgb_img = gray2rgb(gray_img)

J
Julian

我相信您可以使用带有 1 通道灰度图像的预训练 resnet,而无需重复 3 次图像。

我所做的是替换第一层(这是pythorch而不是keras,但想法可能相似):

(conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)

使用以下图层:

(conv1): Conv2d(1, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)

然后将权重的总和(在通道轴上)复制到新层,例如,原始权重的形状为:

torch.Size([64, 3, 7, 7])

所以我做了:

resnet18.conv1.weight.data = resnet18.conv1.weight.data.sum(axis=1).reshape(64, 1, 7, 7)

然后检查新模型的输出是否与灰度图像的输出相同:

y_1 = model_resnet_1(input_image_1)
y_3 = model_resnet_3(input_image_3)
print(torch.abs(y_1).sum(), torch.abs(y_3).sum())
(tensor(710.8860, grad_fn=<SumBackward0>),
 tensor(710.8861, grad_fn=<SumBackward0>))

input_image_1:一个通道图像

input_image_3:3通道图像(灰度-所有通道相等)

model_resnet_1:修改后的模型

model_resnet_3:原始 resnet 模型


A
Ali Amini Bagh

这真的很容易! 'resnet50' 的例子:在做之前你应该有:

resnet_50= torchvision.models.resnet50()     
print(resnet_50.conv1)

Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)

就这样做吧!

resnet_50.conv1 = nn.Conv2d(1, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)

最后一步是更新 state_dict。

resnet_50.state_dict()['conv1.weight'] = resnet_50.state_dict()['conv1.weight'].sum(dim=1, keepdim=True)

所以如果运行如下:

print(resnet_50.conv1)

结果将是:

Conv2d(1, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)

如您所见,输入通道用于灰度图像。


S
Sian Cao

我所做的只是通过使用以下变换阶段将灰度扩展为 RGB 图像:

import torchvision as tv
tv.transforms.Compose([
    tv.transforms.ToTensor(),
    tv.transforms.Lambda(lambda x: x.broadcast_to(3, x.shape[1], x.shape[2])),
])

T
Tufail Waris

您可以使用 OpenCV 将灰度转换为 RGB。

cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)

COLOR_GRAY2BGR色彩模式基本上是把所有的B、G、R通道都换成灰度值Y,所以B=Y,G=Y,R=Y。
j
jae heo

将 Resnet 添加到模型时,应在 Resnet 定义中输入 input_shape,例如

模型 = ResNet50(include_top=True,input_shape=(256,256,1))

.


这不运行:ValueError: The input must have 3 channels

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅