CNN人脸分类训练报错:数组转标量及序列赋值异常排查
问题
我有总计1500张人脸数据集,每位人员对应500张人脸图像,计划用CNN做人脸预测,已经通过文件名(如mike.1.jpg)提取人员姓名。训练过程中出现only size-1 arrays can be converted to Python scalars和setting an array element with a sequence错误,不确定是数组格式问题还是CNN参数/网络层导致的,相关代码及报错信息如下:
数据集生成代码
# Generate dataset def create_dataset(): face_classifier = cv2.CascadeClassifier("/Users/germplus/PycharmProjects" "/MAIDS-Thesis-Project/haarcascade_frontalface_default.xml") def image_cropped(image): # convert image from RGB to gray scale to reduce complexity gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Scale images with scaling factor eg: 1.3 and minimum neighbour eg:) face = face_classifier.detectMultiScale(gray, 1.3, 5) if face is (): return None # crop the faces for (x, y, w, h) in face: cropped_face = image[y:y + h, x:x + w] return cropped_face # connect to web or external camera cam = cv2.VideoCapture(0) # Participant id participant_image_name = participant # image id image_id = 0 while True: ret, frame = cam.read() if image_cropped(frame) is not None: image_id += 1 # resize face face = cv2.resize(image_cropped(frame), (200, 200)) face = cv2.cvtColor(face, cv2.COLOR_BGR2GRAY) # save images to file file_path = "/Users/germplus/PycharmProjects/" \ "MAIDS-Thesis-Project/images/" \ + str(participant_image_name) + '.' + str(image_id) + '.jpg' cv2.imwrite(file_path, face) # font scale = 1 # thickness = 2 cv2.putText(face, str(image_id), (50, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2) cv2.imshow('Cropped face', face) # stop taking samples if you press enter or if image samples are up to 1000 if cv2.waitKey(1) == 13 or int(image_id) == 500: break cam.release() cv2.destroyAllWindows() print("Sample images collection is completed.........")
标签与数据加载代码
def my_label(image_name): name = image_name.split('.')[-3] # names of participants in the research if name == 'Xavi': return np.array([1, 0, 0]) # return np.array([1, 0, 0, 0, 0, 0, 0]) elif name == 'mama_africa': return np.array([0, 1, 0]) # return np.array([0, 1, 0, 0, 0, 0, 0]) elif name == 'Isaac': return np.array([0, 0, 1]) # return np.array([0, 0, 1, 0, 0, 0, 0]) # elif name == Data_collection.participant: # return np.array([0, 0, 0, 1, 0, 0, 0]) # elif name == Data_collection.participant: # return np.array([0, 0, 0, 0, 1, 0, 0]) # elif name == Data_collection.participant: # return np.array([0, 0, 0, 0, 0, 1, 0]) # elif name == Data_collection.participant: # return np.array([0, 0, 0, 0, 0, 0, 1]) def my_data(): images = [] for img in tqdm(os.listdir("/Users/germplus/PycharmProjects/MAIDS-Thesis-Project/images")): path = os.path.join("/Users/germplus/PycharmProjects/MAIDS-Thesis-Project/images", img) img_data = cv2.imread(path, cv2.IMREAD_GRAYSCALE) img_data = cv2.resize(img_data, (50, 50)) images.append([np.array(img_data), my_label(img)]) shuffle(images) return images data = my_data()
模型训练代码
# split data into train and testing train = create_label.data[:1200] test = create_label.data[1200:] # x train in 0 index. -1 calculates the x-train number of train 50, 50 is the image shape. # 1 is grayscale image X_train = np.array([i[0] for i in train]).reshape(-1, 50, 50, 1) print(f'X_train shape is {X_train.shape}') # y train in 1 index -1 calculates the y-train number of train 50, 50 is the image shape y_train = [i[1] for i in train] X_test = np.array([i[0] for i in test]).reshape(-1, 50, 50, 1) print(f'X_test shape is {X_test.shape}') y_test = [i[1] for i in test] print(X_train.ndim) # DNN # input_shape = input_data(shape=[50,50,1]) # model = tflearn.DNN convnet = input_data(shape=[50,50,1]) convnet = conv_2d(convnet, 32, 5, activation='relu') # 32 filters and stride=5 so that the filter will move 5 pixel or unit at a time convnet = max_pool_2d(convnet, 5) convnet = conv_2d(convnet, 64, 5, activation='relu') convnet = max_pool_2d(convnet, 5) convnet = conv_2d(convnet, 128, 5, activation='relu') convnet = max_pool_2d(convnet, 5) convnet = conv_2d(convnet, 64, 5, activation='relu') convnet = max_pool_2d(convnet, 5) convnet = conv_2d(convnet, 32, 5, activation='relu') convnet = max_pool_2d(convnet, 5) convnet = fully_connected(convnet, 1024, activation='relu') convnet = dropout(convnet, 0.8) convnet = fully_connected(convnet, 3, activation='softmax') convnet = regression(convnet, optimizer='adam', learning_rate = 0.001, loss='categorical_crossentropy') model = tflearn.DNN(convnet, tensorboard_verbose=1) model.fit(X_train, y_train, n_epoch=12, validation_set=(X_test, y_test))
报错信息
Training samples: 1200 Validation samples: 300 -- 2022-08-15 10:15:12.869457: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:112] Plugin optimizer for device_type GPU is enabled. TypeError: only size-1 arrays can be converted to Python scalars The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/Users/germplus/PycharmProjects/MAIDS-Thesis-Project/model_fit.py", line 86, in <module> model.fit(X_train, y_train, n_epoch=12, validation_set=(X_test, y_test) ) File "/Users/germplus/miniforge3/envs/mlp/lib/python3.8/site-packages/tflearn/models/dnn.py", line 196, in fit self.trainer.fit(feed_dicts, val_feed_dicts=val_feed_dicts, File "/Users/germplus/miniforge3/envs/mlp/lib/python3.8/site-packages/tflearn/helpers/trainer.py", line 341, in fit snapshot = train_op._train(self.training_state.step, File "/Users/germplus/miniforge3/envs/mlp/lib/python3.8/site-packages/tflearn/helpers/trainer.py", line 827, in _train _, train_summ_str = self.session.run([self.train, self.summ_op], File "/Users/germplus/miniforge3/envs/mlp/lib/python3.8/site-packages/tensorflow/python/client/session.py", line 970, in run result = self._run(None, fetches, feed_dict, options_ptr, File "/Users/germplus/miniforge3/envs/mlp/lib/python3.8/site-packages/tensorflow/python/client/session.py", line 1163, in _run np_val = np.asarray(subfeed_val, dtype=subfeed_dtype) ValueError: setting an array element with a sequence. Process finished with exit code 1
解决方案
这两个错误的核心原因是标签数据格式不匹配,TFlearn的model.fit()要求标签是二维numpy数组,而你当前的y_train和y_test是numpy数组的列表,不是统一的二维数组。另外还有一处数据引用错误,具体修改步骤如下:
1. 修正数据引用错误
训练代码里的train = create_label.data[:1200]是错误的,你之前加载的数据存在data变量里,应该改成:
train = data[:1200] test = data[1200:]
2. 转换标签为二维numpy数组
当前y_train和y_test是列表,每个元素是一个长度为3的numpy数组,需要把它们转换成形状为(样本数, 类别数)的二维numpy数组:
y_train = np.array([i[1] for i in train]) y_test = np.array([i[1] for i in test])
3. 可选:图像数据归一化
CNN训练时,将图像像素值归一化到0-1范围能提升训练稳定性,建议对X_train和X_test做归一化:
X_train = X_train / 255.0 X_test = X_test / 255.0
4. 修正后的完整训练代码片段
# split data into train and testing train = data[:1200] test = data[1200:] # x train in 0 index. -1 calculates the x-train number of train 50, 50 is the image shape. # 1 is grayscale image X_train = np.array([i[0] for i in train]).reshape(-1, 50, 50, 1) X_train = X_train / 255.0 # 归一化 print(f'X_train shape is {X_train.shape}') # y train转换为二维numpy数组 y_train = np.array([i[1] for i in train]) X_test = np.array([i[0] for i in test]).reshape(-1, 50, 50, 1) X_test = X_test / 255.0 # 归一化 print(f'X_test shape is {X_test.shape}') y_test = np.array([i[1] for i in test]) print(X_train.ndim) # 后续模型定义和训练代码不变 convnet = input_data(shape=[50,50,1]) convnet = conv_2d(convnet, 32, 5, activation='relu') convnet = max_pool_2d(convnet, 5) convnet = conv_2d(convnet, 64, 5, activation='relu') convnet = max_pool_2d(convnet, 5) convnet = conv_2d(convnet, 128, 5, activation='relu') convnet = max_pool_2d(convnet, 5) convnet = conv_2d(convnet, 64, 5, activation='relu') convnet = max_pool_2d(convnet, 5) convnet = conv_2d(convnet, 32, 5, activation='relu') convnet = max_pool_2d(convnet, 5) convnet = fully_connected(convnet, 1024, activation='relu') convnet = dropout(convnet, 0.8) convnet = fully_connected(convnet, 3, activation='softmax') convnet = regression(convnet, optimizer='adam', learning_rate = 0.001, loss='categorical_crossentropy') model = tflearn.DNN(convnet, tensorboard_verbose=1) model.fit(X_train, y_train, n_epoch=12, validation_set=(X_test, y_test))
错误原因解释
setting an array element with a sequence:因为y_train是列表,每个元素是数组,TensorFlow无法将这种结构转换成统一的张量格式,必须转换成二维numpy数组。only size-1 arrays can be converted to Python scalars:是列表转数组时的衍生错误,本质还是因为输入的数据结构不符合要求。
另外,检查my_label函数是否处理了所有可能的文件名,避免出现未匹配的情况导致返回None,如果有未匹配的样本,会导致标签数据出现异常,建议在函数末尾添加默认返回或者过滤掉这些异常样本。
内容的提问来源于stack exchange,提问作者Rambo
相关产品推荐
相关产品推荐

