这是一个 FabricNet 示例实现(官方代码)。正如研究论文中所述,输入图像尺寸为 120x120x3。我尝试了下面的代码,但它抛出错误“tuple”对象没有属性“rank”。这是输入形状的打印结果:第0层输入形状:(None, 8, 8, 728)。它显然是一个元组,但模型需要一个张量。同一问题的答案之一提到使用 tf.constant(res) 将其转换为 Numpy 数组。但它随后抛出错误;
A KerasTensor is symbolic; it's a placeholder for a shape or dtype. It doesn't have any actual numerical value. You cannot convert it to a NumPy array.
import cv2
import numpy as np
from sklearn.model_selection import train_test_split
import tensorflow as tf
from tensorflow.python.keras.layers import *
import matplotlib.pyplot as plt
def load_data_from_txt(txt_file, image_size=(120, 120)):
image_paths = []
labels = []
with open(txt_file, 'r') as f:
for line in f:
path, label = line.strip().split(', ')
image_paths.append(path)
labels.append(int(label))
images = []
for path in image_paths:
try:
img = cv2.imread(path)
if img is None:
print(f"Warning: Unable to read image at {path}")
continue
img = cv2.resize(img, image_size) # Resize to desired size
images.append(img)
except Exception as e:
print(f"Error processing {path}: {e}")
continue
if len(images) == 0:
raise ValueError("No images were loaded. Check the file paths.")
images = np.array(images, dtype=np.float32) / 255.0 # Normalize images
labels = np.array(labels[:len(images)]) # Ensure labels match the number of loaded images
return images, labels
# Load data
x_data, y_data = load_data_from_txt('FISR.txt')
print(f"x_data shape: {x_data.shape}")
# Split data into training and testing sets
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.25, stratify=y_data)
# One-hot encode labels
num_classes = 7
y_train = tf.one_hot(np.squeeze(y_train), depth=7).numpy()
y_test = tf.one_hot(np.squeeze(y_test), depth=7).numpy()
def parser(tok, id, cls):
'''
SepConv: S,f,k,s
MaxPool: P,s
ReLU : R
Dropout: D
'''
print(f"Parsing token: {tok}")
if tok[0] == 'S':
filters, ks, stride = list(map(int, tok[1:].split(',')))
print(f"Creating SeparableConv2D with filters={filters}, kernel_size={ks}, strides={stride}")
return SeparableConv2D(filters=filters, kernel_size=ks, strides=stride, padding='same', name=f'sepconv2d_d{id}_c{cls}')
elif tok[0] == 'P':
print(f"Creating MaxPool2D with pool_size={int(tok[1:])}")
return MaxPool2D(pool_size=int(tok[1:]), name=f'MaxPool2D_d{id}_c{cls}')
elif tok[0] == 'R':
print("Creating ReLU")
return ReLU(name=f'ReLU_d{id}_c{cls}')
elif tok[0] == 'D':
print("Creating Dropout with rate=0.1")
return Dropout(0.1, name=f'dropout_d{id}_c{cls}')
elif tok[0] == 'N':
print("Creating BatchNormalization")
return BatchNormalization()
else:
print('Invalid')
# How to define ensemble structure:
# S: SeparableConv2D, follows by filter, kernel, and stride
# example: S64,3,2
# P: MaxPool, followed by pool size
# example: P2
# R: ReLU activation
# D: Dropout, defaults to 0.1 drop
#
# A full example: 'S8,3,2_R_D_S8,3,2_R_D'
# The network is as follows:
# SeparableConv2D(filters=8, kernel_size=3, strides=2)
# ReLU()
# Dropout(0.1)
# SeparableConv2D(filters=8, kernel_size=3, strides=2)
# ReLU()
# Dropout(0.1)
ensemble_structure = ['S64,3,2_R_D_S64,3,2_R_D',
'S32,3,2_R_D_S64,3,2_R_D',
'S16,3,2_R_D_S32,3,2_R_D',
'S8,3,2_R_D_S8,3,2_R_D',
'S4,3,2_S16,3,2']
def Xception_block(input_shape, classes,
activation='sigmoid',
flows=7,
ensemble='S4,3,2_S16,3,2',
loss='binary_crossentropy',
learning_rate=0.001,
weights='imagenet'):
'''
input_shape : input shape of image
classes : number of classes
breakindex : number of xception block-flows to be used
ensemble : the default ensemble model to be followed for each class
'''
assert flows > 0 and flows < 10, "The number of flows should be between [1-9]"
assert loss in ['binary_crossentropy', 'categorical_crossentropy'], "loss can be either 'binary_crossentropy' or 'categorical_crossentropy'"
tf.keras.backend.clear_session()
breakindex = [36, 46, 56, 66, 76, 86, 96, 106, 116]
input_img = tf.keras.Input(input_shape, name='input')
base_model = tf.keras.applications.Xception(input_tensor=input_img,
include_top=False,
weights=weights)
totLayers = len(base_model.layers)
# for i in range(totLayers):
# base_model.layers[i+1]._name += '_head'
# Last index of the head CNN
comb_out = base_model.layers[breakindex[flows-1]].output
#base_model.layers[breakindex[flows-1]]._name += '_lastcom'
seps = []
if ensemble != '':
esets = ensemble.split('_')
print('esets:', esets)
else:
esets = None
for seg in range(classes):
if esets != None:
res = comb_out
past = None
for i, tok in enumerate(esets):
print(f"Layer {i} type: {tok[0]}")
print(f"Layer {i} input shape: {res.shape}")
conv_layer = parser(tok, i, seg)
res = conv_layer(res)
print(f"Layer {i} output shape: {res.shape}")
if 'S' in tok and past != None and res.shape[-1] == past.shape[-1]:
res = Add()([res, past])
print('Residual added')
if 'S' in tok:
past = res
else:
res = SeparableConv2D(filters=16, kernel_size=3,
strides=2, name=f'sepconv2d_d1_c{seg}')(comb_out)
res = ReLU(name=f'maxpool2d_d1_c{seg}')(res)
res = SeparableConv2D(filters=8, kernel_size=3, strides=2, name=f'sepconv2d_d2_c{seg}')(res)
res = ReLU(name=f'maxpool2d_d2_c{seg}')(res)
res = Flatten(name='flatten_'+str(seg))(res)
res = Dense(1)(res)
seps.append(res)
out = Concatenate()(seps)
out = Activation('sigmoid' if loss=='binary_crossentropy' else 'softmax')(out)
model = tf.keras.Model(input_img, out)
print('Params', model.count_params())
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate),
loss=loss,
metrics=[tf.keras.metrics.CategoricalAccuracy(),
tf.keras.metrics.BinaryAccuracy(),
tf.keras.metrics.Precision(),
tf.keras.metrics.Recall(),
tf.keras.metrics.TopKCategoricalAccuracy(),
tf.keras.metrics.AUC(num_thresholds=200,
multi_label=True),
tf.keras.metrics.TruePositives(),
tf.keras.metrics.FalsePositives(),
],
)
return model
model = Xception_block(x_train.shape[1:], 7,
flows=1, ensemble=ensemble_structure[-1],
loss='categorical_crossentropy')
model.summary()
#tf.keras.utils.plot_model(model, to_file="model.png",show_shapes=True,
# show_layer_names=True, dpi=96)
history = model.fit(x_train, y_train, validation_data=(x_test, y_test),
epochs=10,
callbacks=[tf.keras.callbacks.EarlyStopping(monitor='val_loss',
patience=3,
restore_best_weights=True)])
epochs = np.arange(1, len(history.history['loss'])+1)
plt.plot(epochs, history.history['loss'], label='loss')
plt.plot(epochs, history.history['auc'], label='auc')
plt.plot(epochs, history.history['precision'], label='precision')
plt.plot(epochs, history.history['recall'], label='recall')
plt.xlabel('Epochs')
plt.ylabel('Metric')
plt.grid()
plt.legend()
plt.show()
plt.plot(epochs, history.history['val_loss'], label='val_loss')
plt.plot(epochs, history.history['val_auc'], label='val_auc')
plt.plot(epochs, history.history['val_precision'], label='val_precision')
plt.plot(epochs, history.history['val_recall'], label='val_recall')
plt.xlabel('Epochs')
plt.ylabel('Metric')
plt.grid()
plt.legend()
plt.show()
我尝试使用“tf.constant(res)”将第 0 层输入形状转换为 numpy 数组,但它不起作用。所以我删除了那部分。当我运行代码时,它抛出了他的错误;
libpng error: IDAT: CRC error
Warning: Unable to read image at Fabrics/Cotton/1221/im_4.png
x_data shape: (2212, 120, 120, 3)
esets: ['S4,3,2', 'S16,3,2']
Layer 0 type: S
Layer 0 input shape: (None, 8, 8, 728)
Parsing token: S4,3,2
Creating SeparableConv2D with filters=4, kernel_size=3, strides=2
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
File /Users/wadugekaveeshakavindifernando/Documents/fyp/FabricNet/fabricnet.py:209
193 model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate),
194 loss=loss,
195 metrics=[tf.keras.metrics.CategoricalAccuracy(),
(...)
204 ],
205 )
207 return model
--> 209 model = Xception_block(x_train.shape[1:], 7,
210 flows=1, ensemble=ensemble_structure[-1],
211 loss='categorical_crossentropy')
213 model.summary()
215 #tf.keras.utils.plot_model(model, to_file="model.png",show_shapes=True,
216 # show_layer_names=True, dpi=96)
File /Users/wadugekaveeshakavindifernando/Documents/fyp/FabricNet/fabricnet.py:169
166 print(f"Layer {i} input shape: {res.shape}")
167 conv_layer = parser(tok, i, seg)
--> 169 res = conv_layer(res)
170 print(f"Layer {i} output shape: {res.shape}")
171 if 'S' in tok and past != None and res.shape[-1] == past.shape[-1]:
File /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/tensorflow/python/keras/engine/base_layer.py:1026, in Layer.__call__(self, *args, **kwargs)
...
231 ', found ndim=' + str(ndim) +
232 '. Full shape received: ' +
233 str(tuple(shape)))
AttributeError: 'tuple' object has no attribute 'rank'
给出错误的代码行是“from tensorflow.python.keras.layers import *”。删除 python 并仅使用“from tensorflow.keras.layers import *”对我有用。出现此问题是因为 Xception 模型层输出的数据类型与集成模型所需的参数数据类型不匹配。