Создавайте и развертывайте модели Wide и Deep с помощью TensorFlow Serving.

Google искусственный интеллект TensorFlow Docker
Создавайте и развертывайте модели Wide и Deep с помощью TensorFlow Serving.

Модель Wide & Deep — это модель, упомянутая в статье Google за 2016 год. В статье Google объединил модель LR с глубокой нейронной сетью в качестве рекомендации для Google Play и добился определенных результатов. После этой статьи Youtube, Meituan и другие компании также предприняли соответствующие попытки и опубликовали свои работы (ссылки по теме см. в нижней части этой статьи).

Официальная модель Wide & Deep (называемая моделью WD)руководствоВсе они представляют собой разработку функций с использованием функций, которые поставляются с TensorFlow (сокращенно TF), и модель также инкапсулирована, но иногда наша разработка функций также использует sklearn, numpy, pandas для выполнения, когда мы хотим быстро проверить WD. лучше, чем старая модель не очень удобна, поэтому в этой статье показано, как использовать TF для построения четкой структуры и более индивидуальной настройки модель ВД. После обучения модели WD нам также необходимо быстро увидеть эффект предсказания модели, поэтому в этой статье мы используем Docker для быстрого развертывания модели TensorFlow, которую можно обслуживать, то есть для предоставления сервисного API.

Таким образом, содержание этой статьи следующее:

  • Используйте TF для построения сетевой структуры WD
  • Используйте Docker для быстрого развертывания моделей

Соответствующий кодовый адрес:GitHub.com/Эдвард Ху А/А…
Добро пожаловатьstar

Структура модели WD, реализованной в данной статье, показана на следующем рисунке:

本文构建的网络
Сеть, построенная в этой статье

Нетрудно заметить, что модель Wide на самом деле является моделью LR, а часть модели Deep справа представляет собой нейронную сеть с тремя скрытыми слоями, количество нейронов в этих трех скрытых слоях равно 256-12-64. , и, наконец, Wide. Результаты модели и модели Deep добавляются, а результаты прогнозирования выводятся после прохождения через функцию активации ReLu. Хорошо, давайте посмотрим на код части модели Wide.

def wide_model(input_data):
    """
    一层的神经网络,相当于是 LR
    :param input_data: 
    :return: 
    """
    input_len = int(input_data.shape[1])
    with tf.name_scope("wide"):
        # 修正的方式初始化权重,输出层结点只有一个
        weights = tf.Variable(tf.truncated_normal([input_len, 1],
                                                  stddev=1.0 / math.sqrt(float(input_len))
                                                  ), name="weights"
                              )
        output = tf.matmul(input_data, weights)
        # 沿着行这个纬度来求和
        output = tf.reduce_sum(output, 1, name="reduce_sum")
        # 输出每个样本经过计算的值
        output = tf.reshape(output, [-1, 1])
    return output

Давайте посмотрим на код модели Deep.

def deep_model(input_data, hidden1_units, hidden2_units, hidden3_units):
    """
    三层的神经网络
    :param input_data: 2-D tensor
    :param hidden1_units: int
    :param hidden2_units: int
    :param hidden3_units: int
    :return: 
    """
    # 得到每个样本的维度
    input_len = int(input_data.shape[1])
    with tf.name_scope("hidden1"):
        # 修正的方式初始化权重
        weights = tf.Variable(tf.truncated_normal([input_len, hidden1_units],
                                                  stddev=1.0 / math.sqrt(float(input_len))
                                                  ), name="weights1"
                              )
        biases = tf.Variable(tf.zeros([hidden1_units]), name='biases1')
        hidden1 = tf.nn.relu(tf.matmul(input_data, weights)) + biases

    ···
    # 另外两层隐藏层代码相似,所以这里省略掉,具体的代码请看 Github 仓库
    ··· 

    with tf.name_scope("output"):
        # 修正的方式初始化权重
        weights = tf.Variable(tf.truncated_normal([hidden3_units, 1],
                                                  stddev=1.0 / math.sqrt(float(input_len))
                                                  ), name="weights4"
                              )
        biases = tf.Variable(tf.zeros([1]), name='biases4')
        output = tf.nn.relu(tf.matmul(hidden3, weights) + biases)

    return tf.nn.relu(output)

Хотя код модели Deep имеет некоторую избыточность, нам удобно модифицировать и корректировать структуру сети.

Наконец, после добавления результатов модели Wide и модели Deep прогнозируемые результаты выводятся через функцию активации ReLu.

def build_wdl(deep_input, wide_input, y):
    """
    得到模型和损失函数
    :param deep_input: 
    :param wide_input: 
    :param y: 
    :return: 
    """
    central_bias = tf.Variable([np.random.randn()], name="central_bias")
    dmodel = deep_model(deep_input, 256, 128, 64)
    wmodel = wide_model(wide_input)

    # 使用 LR 将两个模型组合在一起
    dmodel_weight = tf.Variable(tf.truncated_normal([1, 1]), name="dmodel_weight")
    wmodel_weight = tf.Variable(tf.truncated_normal([1, 1]), name="wmodel_weight")

    network = tf.add(
        tf.matmul(dmodel, dmodel_weight),
        tf.matmul(wmodel, wmodel_weight)
    )

    prediction = tf.nn.sigmoid(tf.add(network, central_bias), name="prediction")

    loss = tf.reduce_mean(
        tf.nn.sigmoid_cross_entropy_with_logits(labels=y, logits=prediction)
    )
    train_step = tf.train.AdamOptimizer(0.001).minimize(loss)
    return train_step, loss, prediction

После построения структуры мы можем сгенерировать некоторые случайные данные для тестирования модели Wide & Deep, здесь мы случайным образом генерируем 1000 выборок, каждая размером 10, в качестве обучающих выборок, для простоты не создаются проверочные выборки. Обучение повторяется только один раз, то есть проходится только одна обучающая выборка. Значение метки каждой выборки здесь равно 0 или 1, поэтому целевой функцией является перекрестная энтропия. Код выглядит следующим образом:

def build_and_saved_wdl():
    """
    训练并保存模型
    :return: 
    """
    # 训练数据
    x_deep_data = np.random.rand(10000)
    x_deep_data = x_deep_data.reshape(-1, 10)

    x_wide_data = np.random.rand(10000)
    x_wide_data = x_wide_data.reshape(-1, 10)

    x_deep = tf.placeholder(tf.float32, [None, 10])
    x_wide = tf.placeholder(tf.float32, [None, 10])
    y = tf.placeholder(tf.float32, [None, 1])

    y_data = np.array(
        [random.randint(0, 1) for i in range(1000)]
    )
    y_data = y_data.reshape(-1, 1)

    # 为了简单起见,这里没有验证集,也就没有验证集的 loss
    train_step, loss, prediction = build_wdl(x_deep, x_wide, y)
    sess = tf.Session()
    sess.run(tf.global_variables_initializer())
    sess.run(train_step, feed_dict={x_deep: x_deep_data, x_wide: x_wide_data, y: y_data})

После завершения обучения вам также необходимо сохранить модель.Если вы хотите использовать ее в TensorFlow Serving, вам нужно использоватьSavedModelBuilderДля сохранения модели код выглядит следующим образом:

def build_and_saved_wdl():
   ···
   # 将训练好的模型保存在当前的文件夹下
   builder = tf.saved_model.builder.SavedModelBuilder(join("./model_name", MODEL_VERSION))
   inputs = {
       "x_wide": tf.saved_model.utils.build_tensor_info(x_wide),
       "x_deep": tf.saved_model.utils.build_tensor_info(x_deep)
   }
   output = {"output": tf.saved_model.utils.build_tensor_info(prediction)}
   prediction_signature = tf.saved_model.signature_def_utils.build_signature_def(
       inputs=inputs,
       outputs=output,
       method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME
   )

   builder.add_meta_graph_and_variables(
       sess,
       [tf.saved_model.tag_constants.SERVING],
       {tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: prediction_signature}
   )
   builder.save()

Здесь следует отметить, что MODEL_VERSION должен быть числом (представляющим версию модели).По умолчанию TF Serving загрузит только модель с наибольшим числом.Например, после того, как мы выполним код здесь, каталог, в котором находится имя_модели модель сохраняется следующим образом:

λ root [tf_demo/servering/model_name] → tree
.
└── 1
    ├── saved_model.pb
    └── variables
        ├── variables.data-00000-of-00001
        └── variables.index

2 directories, 3 files 

После сохранения модели здесь мы используем контейнер для развертывания модели.Конечно, вы также можете самостоятельно настроить соответствующую среду на машине.Используемый нами образ предоставлен Bitnami (Нажмите здесь, чтобы узнать адрес Dockerhub.), когда вам нужно развернуть модель, вам нужно только сопоставить путь, где находится модель, с путем в контейнере/bitnami/model-dataпо пути, то есть введите следующую команду

λ edvard [tf_demo/servering/model_name] → docker run -it -p 5000:9000 --volume /root/tf_demo/servering/model_name:/bitnami/model-data bitnami/tensorflow-serving
 
Welcome to the Bitnami tensorflow-serving container
 
...
 
2017-11-01 03:43:55.983106: I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded servable version {name: inception version: 1}
2017-11-01 03:43:55.986130: I tensorflow_serving/model_servers/main.cc:288] Running ModelServer at 0.0.0.0:9000 ...

Здесь вам могут понадобиться некоторые знания, связанные с Docker.Я предоставил очень хорошую вводную книгу Gitbook в справочном материале.Если вам интересно, вы можете взглянуть.

Мы сопоставили службу в контейнере с портом 5000 хоста, а затем давайте протестируем интерфейс API. код показывает, как показано ниже:

def test_servable_api():
    """
    测试 API
    :return: 
    """
    # 随机产生 10 条测试数据
    x_deep_data = np.random.rand(100).reshape(-1, 10)

    x_wide_data = np.random.rand(100).reshape(-1, 10)

    channel = implementations.insecure_channel('127.0.0.1', int(5000))
    stub = prediction_service_pb2.beta_create_PredictionService_stub(channel)

    # 发送请求
    request = predict_pb2.PredictRequest()
    request.model_spec.name = 'inception'
    request.model_spec.signature_name = tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY
    request.inputs[INPUT_WIDE_KEY].CopyFrom(
        tf.contrib.util.make_tensor_proto(x_wide_data, shape=[10, WIDE_DIM], dtype=tf.float32))
    request.inputs[INPUT_DEEP_KEY].CopyFrom(
        tf.contrib.util.make_tensor_proto(x_deep_data, shape=[10, DEEP_DIM], dtype=tf.float32))
    # 10 秒超时
    res = stub.Predict(request, 10.0)

    pprint(res.outputs[OUTPUT_KEY])

Структура выходных результатов прогнозирования выглядит следующим образом:

dtype: DT_FLOAT
tensor_shape {
  dim {
    size: 10
  }
  dim {
    size: 1
  }
}
float_val: 0.355874538422
float_val: 0.3225004673
float_val: 0.32104665041
float_val: 0.233089879155
float_val: 0.376621931791
float_val: 0.144557282329
float_val: 0.34686845541
float_val: 0.304817527533
float_val: 0.367866277695
float_val: 0.393035560846

использованная литература

Google Широкая и глубокая бумага
Глубокие рекомендательные документы Youtube
Технические статьи о глубинной сортировке Meituan Dianping
Docker от входа к практике (Gitbook)