文章推薦系統(tǒng) | 十二、基于 FTRL 優(yōu)化的在線排序

推薦閱讀:
文章推薦系統(tǒng) | 一、推薦流程設(shè)計(jì)
文章推薦系統(tǒng) | 二、同步業(yè)務(wù)數(shù)據(jù)
文章推薦系統(tǒng) | 三、收集用戶行為數(shù)據(jù)
文章推薦系統(tǒng) | 四、構(gòu)建離線文章畫(huà)像
文章推薦系統(tǒng) | 五、計(jì)算文章相似度
文章推薦系統(tǒng) | 六、構(gòu)建離線用戶畫(huà)像
文章推薦系統(tǒng) | 七、構(gòu)建離線文章特征和用戶特征
文章推薦系統(tǒng) | 八、基于模型的離線召回
文章推薦系統(tǒng) | 九、基于內(nèi)容的離線及在線召回
文章推薦系統(tǒng) | 十、基于熱門(mén)文章和新文章的在線召回
文章推薦系統(tǒng) | 十一、基于 LR 模型的離線排序

構(gòu)造 TFRecord 訓(xùn)練集

和前面的 LR 離線模型一樣,這里也是使用 LR 模型,只是選擇 FTRL 優(yōu)化方法,首先也是要完成訓(xùn)練集的構(gòu)建。在上篇文章中,我們已經(jīng)知道,可以通過(guò)讀取用戶歷史行為數(shù)據(jù),及文章特征和用戶特征,構(gòu)建出訓(xùn)練集 train,其中包括 features 和 label 兩列數(shù)據(jù),features 是文章特征和用戶特征的組合。在 TensorFlow 通常使用 TFRecord 文件進(jìn)行數(shù)據(jù)的存取。接下來(lái),我們就要將 train 保存到 TFRecord 文件中。首先開(kāi)啟會(huì)話,將 train 中的特征和標(biāo)簽分別傳入 write_to_tfrecords() 方法,并利用多線程執(zhí)行

import tensorflow as tf

with tf.Session() as sess:
    # 創(chuàng)建線程協(xié)調(diào)器
    coord = tf.train.Coordinator()
    # 開(kāi)啟子線程去讀取數(shù)據(jù)
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    # 存入數(shù)據(jù)
    write_to_tfrecords(train.iloc[:, 0], train.iloc[:, 1])
    # 關(guān)閉子線程,回收
    coord.request_stop()
    coord.join(threads)

接著,在 write_to_tfrecords() 方法中,遍歷訓(xùn)練集數(shù)據(jù),將每個(gè)樣本構(gòu)造為 tf.train.Example,其中 feature 為 BytesList 類(lèi)型,label 為 Int64List 類(lèi)型,并保存到 TFRecords 文件中

def write_to_tfrecords(feature_batch, click_batch):
    """將用戶與文章的點(diǎn)擊日志構(gòu)造的樣本寫(xiě)入TFRecords文件
    """
    # 1、構(gòu)造tfrecords的存儲(chǔ)實(shí)例
    writer = tf.python_io.TFRecordWriter("./train_ctr_20190605.tfrecords")

    # 2、循環(huán)將所有樣本一個(gè)個(gè)封裝成example,寫(xiě)入文件
    for i in range(len(click_batch)):
        # 取出第i個(gè)樣本的特征值和目標(biāo)值,格式轉(zhuǎn)換
        click = click_batch[i]
        feature = feature_batch[i].tostring()
        # 構(gòu)造example
        example = tf.train.Example(features=tf.train.Features(feature={
            "feature": tf.train.Feature(bytes_list=tf.train.BytesList(value=[feature])),
            "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[click]))
        }))
        # 序列化example,寫(xiě)入文件
        writer.write(example.SerializeToString())

    writer.close()

離線訓(xùn)練

FTRL(Follow The Regularized Leader)是一種獲得稀疏模型的優(yōu)化方法,我們利用構(gòu)建好的 TFRecord 樣本數(shù)據(jù)對(duì) LR 模型進(jìn)行離線訓(xùn)練。首先,定義 read_ctr_records() 方法來(lái)讀取 TFRecord 文件,并通過(guò)調(diào)用 parse_tfrecords() 方法遍歷解析每個(gè)樣本,并設(shè)置了批大小和迭代次數(shù)

def read_ctr_records():
    train = tf.data.TFRecordDataset(["./train_ctr_20190605.tfrecords"])
    train = train.map(parse_tfrecords)
    train = train.batch(64)
    train = train.repeat(10000)

解析每個(gè)樣本,將 TFRecord 中序列化的 feature 列,解析成 channel_id (1), article_vector (100), user_weights (10), article_weights (10)

FEATURE_COLUMNS = ['channel_id', 'article_vector', 'user_weigths', 'article_weights']

def parse_tfrecords(example):
    features = {
        "feature": tf.FixedLenFeature([], tf.string),
        "label": tf.FixedLenFeature([], tf.int64)
    }
    parsed_features = tf.parse_single_example(example, features)
    feature = tf.decode_raw(parsed_features['feature'], tf.float64)
    feature = tf.reshape(tf.cast(feature, tf.float32), [1, 121])
    
    channel_id = tf.cast(tf.slice(feature, [0, 0], [1, 1]), tf.int32)
    article_vector = tf.reduce_sum(tf.slice(feature, [0, 1], [1, 100]), axis=1)
    user_weights = tf.reduce_sum(tf.slice(feature, [0, 101], [1, 10]), axis=1)
    article_weights = tf.reduce_sum(tf.slice(feature, [0, 111], [1, 10]), axis=1)

    label = tf.cast(parsed_features['label'], tf.float32)

    # 構(gòu)造字典 名稱(chēng)-tensor
    tensor_list = [channel_id, article_vector, user_weights, article_weights]
    feature_dict = dict(zip(FEATURE_COLUMNS, tensor_list))

    return feature_dict, label

指定輸入特征的數(shù)據(jù)類(lèi)型,并定義 LR 模型 model 及 FTRL 優(yōu)化方法

# 定義離散類(lèi)型特征
article_id = tf.feature_column.categorical_column_with_identity('channel_id', num_buckets=25)
# 定義連續(xù)類(lèi)型特征
article_vector = tf.feature_column.numeric_column('article_vector')
user_weigths = tf.feature_column.numeric_column('user_weigths')
article_weights = tf.feature_column.numeric_column('article_weights')

feature_columns = [article_id, article_vector, user_weigths, article_weights]

model = tf.estimator.LinearClassifier(feature_columns=feature_columns,
                                           optimizer=tf.train.FtrlOptimizer(learning_rate=0.1,
                                                                            l1_regularization_strength=10,
                                                                            l2_regularization_strength=10))

通過(guò)調(diào)用 read_ctr_records() 方法,來(lái)讀取 TFRecod 文件中的訓(xùn)練數(shù)據(jù),并設(shè)置訓(xùn)練步長(zhǎng),對(duì)定義好的 LR 模型進(jìn)行訓(xùn)練及預(yù)估

model.train(read_ctr_records, steps=1000)
result = model.evaluate(read_ctr_records)

通常需要編寫(xiě)離線任務(wù),定時(shí)讀取用戶行為數(shù)據(jù)作為訓(xùn)練集和驗(yàn)證集,對(duì)訓(xùn)練集及驗(yàn)證集進(jìn)行 CTR 預(yù)估,并根據(jù)離線指標(biāo)對(duì)結(jié)果進(jìn)行分析,決定是否更新模型。

在線排序

通常在線排序是根據(jù)用戶實(shí)時(shí)的推薦請(qǐng)求,對(duì)召回結(jié)果進(jìn)行 CTR 預(yù)估,進(jìn)而計(jì)算出排序結(jié)果并返回。我們需要根據(jù)召回結(jié)果構(gòu)造測(cè)試集,其中每個(gè)測(cè)試樣本包括用戶特征和文章特征。首先,根據(jù)用戶 ID 和頻道 ID 讀取用戶特征(用戶在每個(gè)頻道的特征不同,所以是分頻道存儲(chǔ)的)

try:
    user_feature = eval(hbu.get_table_row('ctr_feature_user',
                              '{}'.format(temp.user_id).encode(),
                              'channel:{}'.format(temp.channel_id).encode()))
except Exception as e:
    user_feature = []

再根據(jù)用戶 ID 讀取召回結(jié)果

recall_set = read_hbase_recall('cb_recall', 
                'recall:user:{}'.format(temp.user_id).encode(), 
                'als:{}'.format(temp.channel_id).encode())

接著,遍歷召回結(jié)果,獲取文章特征,并將用戶特征合并,作為測(cè)試樣本

test = []
for article_id in recall_set:
    try:
        article_feature = eval(hbu.get_table_row('ctr_feature_article',
                                  '{}'.format(article_id).encode(),
                                  'article:{}'.format(article_id).encode()))
    except Exception as e:
        article_feature = []

    if not article_feature:
        article_feature = [0.0] * 111
    feature = []
    feature.extend(user_feature)
    feature.extend(article_feature)

    test.append(f)

加載本地 LR 模型并對(duì)測(cè)試樣本進(jìn)行 CTR 預(yù)估

test_array = np.array(test)
model.load_weights('/root/toutiao_project/reco_sys/offline/models/ckpt/ctr_lr_ftrl.h5')
init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    predictions = self.model.predict(sess.run(tf.constant(test_array)))

對(duì)結(jié)果進(jìn)行排序并提取 CTR 最高的前 K 個(gè)文章,這樣就得到了 FTRL 優(yōu)化的在線排序的結(jié)果。

res = pd.DataFrame(np.concatenate((np.array(recall_set).reshape(len(recall_set), 1), predictions),
                                 axis=1), columns=['article_id', 'prob'])

res_sort = res.sort_values(by=['prob'], ascending=True)

# 排序后,只將排名在前100個(gè)文章ID作為推薦結(jié)果返回給用戶
if len(res_sort) > 100:
    recall_set = list(res_sort.iloc[:100, 0])
recall_set = list(res_sort.iloc[:, 0])

參考

https://www.bilibili.com/video/av68356229
https://pan.baidu.com/s/1-uvGJ-mEskjhtaial0Xmgw(學(xué)習(xí)資源已保存至網(wǎng)盤(pán), 提取碼:eakp)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 推薦閱讀:文章推薦系統(tǒng) | 一、推薦流程設(shè)計(jì)文章推薦系統(tǒng) | 二、同步業(yè)務(wù)數(shù)據(jù)文章推薦系統(tǒng) | 三、收集用戶行為數(shù)...
    小王子特洛伊閱讀 2,786評(píng)論 1 5
  • 推薦閱讀:文章推薦系統(tǒng) | 一、推薦流程設(shè)計(jì)文章推薦系統(tǒng) | 二、同步業(yè)務(wù)數(shù)據(jù)文章推薦系統(tǒng) | 三、收集用戶行為數(shù)...
    小王子特洛伊閱讀 4,119評(píng)論 1 8
  • 基于離線訓(xùn)練的推薦系統(tǒng)架構(gòu) 離線訓(xùn)練指使用歷史一段時(shí)間(一周或幾周)的數(shù)據(jù)進(jìn)行訓(xùn)練,模型迭代的周期較長(zhǎng)(一般以小時(shí)...
    歐文坐公交閱讀 1,673評(píng)論 0 1
  • 記得有一次在煙臺(tái)上課的時(shí)候,一個(gè)學(xué)員這樣問(wèn)老師說(shuō):“老師,我現(xiàn)在有一個(gè)問(wèn)題,是婆媳問(wèn)題,二胎政策開(kāi)放以后,我跟老公...
    云隨心動(dòng)閱讀 260評(píng)論 1 5
  • 最近跟負(fù)責(zé)賽事的老師交談,一位很讓人頭疼的家長(zhǎng)糾纏不休,問(wèn)及孩子的情緒怎么樣,她倆異口同聲地說(shuō),這孩子太厲害了!當(dāng)...
    二雙閱讀 164評(píng)論 0 0