TensorFlow人脸识别API请求耗时递增问题求助
解决TensorFlow人脸识别API部署后预测耗时递增的问题
看起来你遇到的问题核心是TensorFlow队列运行器的线程残留和图/会话状态累积,导致每次API请求时,sess.run([images, labels])会处理之前请求遗留的队列任务,进而耗时越来越长。下面我会拆解问题原因,并给出具体的修复方案:
问题根源分析
你的代码使用了旧版TensorFlow的队列运行器(tf.train.start_queue_runners)来加载数据,而每次API请求调用train函数时:
- 没有创建全新的计算图,而是复用了默认图,导致图中累积了大量重复的队列操作;
- 队列线程没有被彻底清理,残留的线程会一直等待数据,后续请求的
sess.run需要协调所有累积的线程,自然耗时递增; - 当
num_epochs=1时,队列第一次耗尽后会抛出OutOfRangeError,但队列的状态没有被重置,下次请求时线程仍会尝试从这个已耗尽的队列读取数据,陷入无效等待。
具体修复方案
方案1:每次请求创建全新的图和会话
确保每个API请求都使用独立的TensorFlow图和会话,避免状态累积:
def train(): # 为每个请求创建全新的计算图,隔离状态 with tf.Graph().as_default(): with tf.Session(config=tf.ConfigProto(log_device_placement=False)) as sess: test_set = _get_test_data(input_directory) images, labels = _load_images_and_labels(test_set, image_size=160, batch_size=batch_size, num_threads=4, num_epochs=1) _load_model(model_filepath=model_path) # 初始化变量 init_op = tf.group(tf.global_variables_initializer(), tf.local_variables_initializer()) sess.run(init_op) # 获取模型张量 images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0") embedding_layer = tf.get_default_graph().get_tensor_by_name("embeddings:0") phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0") coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(sess=sess, coord=coord) try: emb_array, label_array = _create_embeddings(embedding_layer, images, labels, images_placeholder, phase_train_placeholder, sess) classifier_filename = classifier_output_path class_name, prob = _evaluate_classifier(emb_array, label_array, classifier_filename) finally: # 强制停止所有队列线程 coord.request_stop() coord.join(threads, timeout=5) # 确保所有线程都被清理 for thread in threads: if thread.is_alive(): thread.join(timeout=1)
方案2:改用tf.data API替代队列运行器(推荐)
旧版队列运行器的线程管理非常繁琐,tf.data.Dataset是TensorFlow官方推荐的数据加载方式,更易维护且不会出现线程残留问题:
首先修改_load_images_and_labels函数:
def _load_images_and_labels(test_set, image_size=160, batch_size=32): # 定义图片解析函数 def parse_image(path, label): img = tf.io.read_file(path) img = tf.image.decode_jpeg(img, channels=3) img = tf.image.resize(img, [image_size, image_size]) # 加入你的预处理逻辑(比如归一化) img = (img - 127.5) / 128.0 return img, label # 从测试集中提取路径和标签 paths = [item[0] for item in test_set] labels = [item[1] for item in test_set] # 构建tf.data数据集 dataset = tf.data.Dataset.from_tensor_slices((paths, labels)) dataset = dataset.map(parse_image, num_parallel_calls=tf.data.AUTOTUNE) dataset = dataset.batch(batch_size) dataset = dataset.repeat(1) # 对应num_epochs=1 # 创建迭代器 iterator = dataset.make_one_shot_iterator() images, labels = iterator.get_next() return images, labels
然后调整_create_embeddings的异常处理逻辑,让它更清晰:
def _create_embeddings(embedding_layer, images, labels, images_placeholder, phase_train_placeholder, sess): emb_array = None label_array = None i = 0 while True: try: batch_images, batch_labels = sess.run([images, labels]) print('Processing iteration {} batch of size: {}'.format(i, len(batch_labels))) # 计算embedding emb = sess.run(embedding_layer, feed_dict={ images_placeholder: batch_images, phase_train_placeholder: False }) # 拼接结果 emb_array = np.concatenate([emb_array, emb]) if emb_array is not None else emb label_array = np.concatenate([label_array, batch_labels]) if label_array is not None else batch_labels i += 1 except tf.errors.OutOfRangeError: print("Dataset exhausted, stopping embedding creation") break return emb_array, label_array
额外注意事项
- 不要在API服务中复用TensorFlow会话或图,每个请求都应该独立初始化,避免状态污染;
- 如果你的模型是预训练好的,可以考虑将模型的加载逻辑放到请求之外(比如服务启动时只加载一次),但数据加载部分必须每个请求独立处理;
- 部署API时,建议使用TensorFlow Serving或者FastAPI+Uvicorn这类高性能框架,配合上述修改,就能彻底解决耗时递增的问题。
内容的提问来源于stack exchange,提问作者Rakshita Jain
相关产品推荐
相关产品推荐

