Comprensión de recordatorio multidireccional

La estrategia de "recuperación de múltiples rutas" se refiere al uso de diferentes estrategias, funciones o modelos simples para recuperar por separado una parte del conjunto de candidatos y luego mezclar los conjuntos de candidatos para su uso posterior en el modelo de clasificación. Se puede ver claramente que la "estrategia de recuperación de rutas múltiples" es el resultado de una compensación entre "velocidad de cálculo" y "tasa de recuperación".
Se utilizan una variedad de estrategias diferentes para obtener el conjunto de productos candidatos clasificados por los usuarios, y las estrategias de recuperación específicas que se utilizan están en realidad muy relacionadas con el negocio. Para las diferentes tareas, habrá reglas de recuperación que deben tenerse en cuenta en el negocio real. guión. Por ejemplo, para las recomendaciones de noticias, las reglas de recuperación pueden ser "video caliente", "recuperación del director", "recuperación del actor", "lanzado recientemente", "tendencia popular", "recuperación de género", etc.
importar pandas como pd
importar numpy como np
de tqdm importar tqdm
de colecciones importar defaultdict
importar sistema operativo, matemáticas, advertencias, matemáticas, pickle
de tqdm importar tqdm
importar faiss
importar colecciones
importar aleatoriamente
desde sklearn.preprocesar importar MinMaxScaler
desde sklearn.preprocesar importar LabelEncoder
desde fecha y hora importar fecha y hora
desde deepctr.feature_column importar SparseFeat, VarLenSparseFeat
desde sklearn.preprocessing importar LabelEncoder
desde tensorflow.python.keras importar backend como K
desde tensorflow.python.keras.models importar modelo
desde tensorflow.python.keras.preprocessing.sequence importar pad_sequences

from deepmatch.models import *
from deepmatch.utils import sampledsoftmaxloss
warnings.filterwarnings ('ignore')
data_path = './data_raw/'
save_path = './temp_results/'

Una señal de evaluación de la recuperación, si no la evalúa, utilizará directamente la cantidad total de datos para la recuperación.

metric_recall = False para
leer datos

  1. Modo de depuración: el propósito de esto es ayudarnos a construir una línea de base simple basada en los datos y ejecutarla, para asegurar que no haya problemas con el código de línea de base escrito. Dado que los datos para las competiciones recomendadas a menudo son muy grandes, si utiliza directamente todos los datos para el análisis y crea un marco de referencia, a menudo traerá pérdida de tiempo y equipo, por lo que en este momento a menudo necesitamos extraer al azar una parte del entrenamiento. conjunto de datos masivos Muestra para depurar (train_click_log_sample), primero ejecute una línea de base.
  2. Modo de verificación fuera de línea: el propósito de esto es ayudarnos a elegir un modelo adecuado y algunos hiperparámetros basados ​​en los datos del conjunto de entrenamiento existente fuera de línea. Por lo tanto, nuestra pieza solo necesita cargar el conjunto de entrenamiento completo (train_click_log) y luego dividir el conjunto de entrenamiento completo en conjunto de entrenamiento y conjunto de validación. El conjunto de entrenamiento son los datos de entrenamiento del modelo, y la parte del conjunto de validación nos ayuda a ajustar los parámetros del modelo y algunos otros hiperparámetros.
  3. Modo en línea: usamos el modo de depuración para construir una línea de base para la competencia del sistema de recomendación, y usamos el modo de verificación fuera de línea para seleccionar el modelo y algunos hiperparámetros. Esta parte es la predicción real para el conjunto de prueba dado y la enviamos a la línea. Entonces, el conjunto de datos de entrenamiento utilizado en esta pieza es el conjunto de datos completo (train_click_log + test_click_log)

Modo de depuración: parte de los datos se extraen del conjunto de entrenamiento para depurar el código.

def get_all_click_sample (data_path, sample_nums = 10000):
"" "
Muestra una parte de los datos en el conjunto de entrenamiento. Debug
data_path: la ruta de almacenamiento de los datos originales
sample_nums: el número de muestras (debido a la limitación de memoria de la máquina, el usuario puede realizar el muestreo aquí)
"" "
all_click = pd. read_csv (data_path + 'train_click_log.csv')
all_user_ids = all_click.user_id.unique ()

sample_user_ids = np.random.choice(all_user_ids, size=sample_nums, replace=False) 
all_click = all_click[all_click['user_id'].isin(sample_user_ids)]

all_click = all_click.drop_duplicates((['user_id', 'click_article_id', 'click_timestamp']))
return all_click

Lea los datos de clic, aquí se divide en en línea y fuera de línea, si se trata de obtener los resultados de envío en línea, los datos de clic en el conjunto de prueba deben fusionarse con los datos totales

Si es para verificar la efectividad del modelo o la efectividad de la función fuera de línea, solo puede usar el conjunto de capacitación

def get_all_click_df (data_path = '. / data_raw /', offline = True):
si está desconectado:
all_click = pd.read_csv (data_path + 'train_click_log.csv')
else:
trn_click = pd.read_csv (data_path + 'train_click_log.csv')
tst_click = pd.read_csv (ruta_datos + 'testA_click_log.csv')

    all_click = trn_click.append(tst_click)

all_click = all_click.drop_duplicates((['user_id', 'click_article_id', 'click_timestamp']))
return all_click

Lea los atributos básicos del artículo.

def get_item_info_df (ruta_datos):
item_info_df = pd.read_csv (ruta_datos + 'articulos.csv')

# 为了方便与训练集中的click_article_id拼接,需要把article_id修改成click_article_id
item_info_df = item_info_df.rename(columns={'article_id': 'click_article_id'})

return item_info_df

Lea los datos de incrustación del artículo

def get_item_emb_dict (ruta_datos):
item_emb_df = pd.read_csv (ruta_datos + 'articulos_emb.csv')

item_emb_cols = [x for x in item_emb_df.columns if 'emb' in x]
item_emb_np = np.ascontiguousarray(item_emb_df[item_emb_cols])
# 进行归一化
item_emb_np = item_emb_np / np.linalg.norm(item_emb_np, axis=1, keepdims=True)

item_emb_dict = dict(zip(item_emb_df['article_id'], item_emb_np))
pickle.dump(item_emb_dict, open(save_path + 'item_content_emb.pkl', 'wb'))

return item_emb_dict

escala_mín_máx = lambda x: (x-np.min (x)) / (np.max (x) -np.min (x))

Datos muestreados

all_click_df = get_all_click_sample (ruta_datos)

Conjunto de entrenamiento completo

all_click_df = get_all_click_df (offline = False)

Normalizar la marca de tiempo para calcular el peso en las reglas de asociación

all_click_df ['click_timestamp'] = all_click_df [['click_timestamp']]. apply (max_min_scaler)
item_info_df = get_item_info_df (data_path)
item_emb_dict = get_item_emb_dict (data_path)

Obtenga la secuencia de artículos en los que hizo clic el usuario según la hora del clic {usuario1: {artículo1: hora1, artículo2: hora2…}…}

def get_user_item_time (click_df):

click_df = click_df.sort_values('click_timestamp')

def make_item_time_pair(df):
    return list(zip(df['click_article_id'], df['click_timestamp']))

user_item_time_df = click_df.groupby('user_id')['click_article_id', 'click_timestamp'].apply(lambda x: make_item_time_pair(x))\
                                                        .reset_index().rename(columns={0: 'item_time_list'})
user_item_time_dict = dict(zip(user_item_time_df['user_id'], user_item_time_df['item_time_list']))

return user_item_time_dict

Obtenga el clic histórico y el último clic de los datos actuales

def get_hist_and_last_click (all_click):

all_click = all_click.sort_values(by=['user_id', 'click_timestamp'])
click_last_df = all_click.groupby('user_id').tail(1)

# 如果用户只有一个点击,hist为空了,会导致训练的时候这个用户不可见,此时默认泄露一下
def hist_func(user_df):
    if len(user_df) == 1:
        return user_df
    else:
        return user_df[:-1]

click_hist_df = all_click.groupby('user_id').apply(hist_func).reset_index(drop=True)

return click_hist_df, click_last_df

Obtenga los atributos básicos correspondientes a la identificación del artículo y guárdelo en forma de diccionario, que es conveniente para la fase de recuperación posterior y la fase de inicio en frío para uso directo

def get_item_info_dict (item_info_df):
max_min_scaler = lambda x: (x-np.min (x)) / (np.max (x) -np.min (x))
item_info_df ['created_at_ts'] = item_info_df [['created_at_ts' ]]. aplicar (max_min_scaler)

item_type_dict = dict(zip(item_info_df['click_article_id'], item_info_df['category_id']))
item_words_dict = dict(zip(item_info_df['click_article_id'], item_info_df['words_count']))
item_created_time_dict = dict(zip(item_info_df['click_article_id'], item_info_df['created_at_ts']))

return item_type_dict, item_words_dict, item_created_time_dict

Obtenga la información del artículo del clic histórico del usuario
def get_user_hist_item_info_dict (all_click):

# 获取user_id对应的用户历史点击文章类型的集合字典
user_hist_item_typs = all_click.groupby('user_id')['category_id'].agg(set).reset_index()
user_hist_item_typs_dict = dict(zip(user_hist_item_typs['user_id'], user_hist_item_typs['category_id']))

# 获取user_id对应的用户点击文章的集合
user_hist_item_ids_dict = all_click.groupby('user_id')['click_article_id'].agg(set).reset_index()
user_hist_item_ids_dict = dict(zip(user_hist_item_ids_dict['user_id'], user_hist_item_ids_dict['click_article_id']))

# 获取user_id对应的用户历史点击的文章的平均字数字典
user_hist_item_words = all_click.groupby('user_id')['words_count'].agg('mean').reset_index()
user_hist_item_words_dict = dict(zip(user_hist_item_words['user_id'], user_hist_item_words['words_count']))

# 获取user_id对应的用户最后一次点击的文章的创建时间
all_click_ = all_click.sort_values('click_timestamp')
user_last_item_created_time = all_click_.groupby('user_id')['created_at_ts'].apply(lambda x: x.iloc[-1]).reset_index()

max_min_scaler = lambda x : (x-np.min(x))/(np.max(x)-np.min(x))
user_last_item_created_time['created_at_ts'] = user_last_item_created_time[['created_at_ts']].apply(max_min_scaler)

user_last_item_created_time_dict = dict(zip(user_last_item_created_time['user_id'], \
                                            user_last_item_created_time['created_at_ts']))

return user_hist_item_typs_dict, user_hist_item_ids_dict, user_hist_item_words_dict, user_last_item_created_time_dict

Obtenga los artículos de Top-k con la mayor cantidad de clics

Obtenga los artículos con más clics recientemente

def get_item_topk_click (click_df, k):
topk_click = click_df ['click_article_id']. value_counts (). index [: k]
return topk_click
define el diccionario de recuperación multicanal

Obtenga la información de atributos del artículo y guárdelo en forma de diccionario para facilitar la consulta

item_type_dict, item_words_dict, item_created_time_dict = get_item_info_dict (item_info_df)

Defina un diccionario de recuperaciones múltiples y guarde los resultados de cada recuperación en este diccionario.

user_multi_recall_dict = {'itemcf_sim_itemcf_recall': {},
'embedding_sim_item_recall': {},
'youtubednn_recall': {},
'youtubednn_usercf_recall': {},
'cold_start_recall': {}}

Extraiga el último clic como una evaluación de recuperación. Si no se requiere una evaluación de recuperación, use el conjunto de entrenamiento completo para recuperación (modelo de verificación fuera de línea)

Si no es una evaluación de recuperación, use la cantidad total de datos directamente para la recuperación sin extraer la última vez

trn_hist_click_df, trn_last_click_df = get_hist_and_last_click (all_click_df) La
evaluación del efecto de recuperación se
completa. A veces, el método o los parámetros de recuperación actuales deben ajustarse para lograr un mejor efecto de recuperación, porque el resultado de la recuperación determina el límite superior de la clasificación final, que también se proporcionará a continuación Un método de evaluación del retiro

Evaluar la tasa de aciertos de los 10, 20, 30, 40 y 50 principales artículos retirados sucesivamente.

def metrics_recall (user_recall_items_dict, trn_last_click_df, topk = 5):
last_click_item_dict = dict (zip (trn_last_click_df ['user_id'], trn_last_click_df ['click_article_id']))
user_num = len_call_item

for k in range(10, topk+1, 10):
    hit_num = 0
    for user, item_list in user_recall_items_dict.items():
        # 获取前k个召回的结果
        tmp_recall_items = [x[0] for x in user_recall_items_dict[user][:k]]
        if last_click_item_dict[user] in set(tmp_recall_items):
            hit_num += 1
    
    hit_rate = round(hit_num * 1.0 / user_num, 5)
    print(' topk: ', k, ' : ', 'hit_num: ', hit_num, 'hit_rate: ', hit_rate, 'user_num : ', user_num)

def itemcf_sim (df, item_created_time_dict): cálculo de la matriz de similitud entre artículos
"" "
y artículos
: param df: tabla de datos
: item_created_time_dict: diccionario del tiempo de creación del artículo
retorno: matriz de similitud de artículo y artículo

    思路: 基于物品的协同过滤(详细请参考上一期推荐系统基础的组队学习) + 关联规则
"""

user_item_time_dict = get_user_item_time(df)

# 计算物品相似度
i2i_sim = {}
item_cnt = defaultdict(int)
for user, item_time_list in tqdm(user_item_time_dict.items()):
    # 在基于商品的协同过滤优化的时候可以考虑时间因素
    for loc1, (i, i_click_time) in enumerate(item_time_list):
        item_cnt[i] += 1
        i2i_sim.setdefault(i, {})
        for loc2, (j, j_click_time) in enumerate(item_time_list):
            if(i == j):
                continue
                
            # 考虑文章的正向顺序点击和反向顺序点击    
            loc_alpha = 1.0 if loc2 > loc1 else 0.7
            # 位置信息权重,其中的参数可以调节
            loc_weight = loc_alpha * (0.9 ** (np.abs(loc2 - loc1) - 1))
            # 点击时间权重,其中的参数可以调节
            click_time_weight = np.exp(0.7 ** np.abs(i_click_time - j_click_time))
            # 两篇文章创建时间的权重,其中的参数可以调节
            created_time_weight = np.exp(0.8 ** np.abs(item_created_time_dict[i] - item_created_time_dict[j]))
            i2i_sim[i].setdefault(j, 0)
            # 考虑多种因素的权重计算最终的文章之间的相似度
            i2i_sim[i][j] += loc_weight * click_time_weight * created_time_weight / math.log(len(item_time_list) + 1)
            
i2i_sim_ = i2i_sim.copy()
for i, related_items in i2i_sim.items():
    for j, wij in related_items.items():
        i2i_sim_[i][j] = wij / math.sqrt(item_cnt[i] * item_cnt[j])

# 将得到的相似性矩阵保存到本地
pickle.dump(i2i_sim_, open(save_path + 'itemcf_i2i_sim.pkl', 'wb'))

return i2i_sim_

i2i_sim = itemcf_sim (all_click_df, item_created_time_dict)
100% | ██████████ | 250000/250000 [14:20 <00:00, 290.38it / s]
userCF u2u_sim
def get_user_activate_degree_dict (all_click_df):
all_click_df_ = all_click_df.groupby ('user_id') ['click_article_id']. Count (). Reset_index ()

# 用户活跃度归一化
mm = MinMaxScaler()
all_click_df_['click_article_id'] = mm.fit_transform(all_click_df_[['click_article_id']])
user_activate_degree_dict = dict(zip(all_click_df_['user_id'], all_click_df_['click_article_id']))

return user_activate_degree_dict

def usercf_sim (all_click_df, user_activate_degree_dict):
"" "
cálculo de la matriz de similitud del usuario
: param all_click_df: tabla de datos
: param user_activate_degree_dict: diccionario de grado de actividad del
usuario retorno matriz de similitud del usuario

    思路: 基于用户的协同过滤(详细请参考上一期推荐系统基础的组队学习) + 关联规则
"""
item_user_time_dict = get_item_user_time_dict(all_click_df)

u2u_sim = {}
user_cnt = defaultdict(int)
for item, user_time_list in tqdm(item_user_time_dict.items()):
    for u, click_time in user_time_list:
        user_cnt[u] += 1
        u2u_sim.setdefault(u, {})
        for v, click_time in user_time_list:
            u2u_sim[u].setdefault(v, 0)
            if u == v:
                continue
            # 用户平均活跃度作为活跃度的权重,这里的式子也可以改善
            activate_weight = 100 * 0.5 * (user_activate_degree_dict[u] + user_activate_degree_dict[v])   
            u2u_sim[u][v] += activate_weight / math.log(len(user_time_list) + 1)

u2u_sim_ = u2u_sim.copy()
for u, related_users in u2u_sim.items():
    for v, wij in related_users.items():
        u2u_sim_[u][v] = wij / math.sqrt(user_cnt[u] * user_cnt[v])

# 将得到的相似性矩阵保存到本地
pickle.dump(u2u_sim_, open(save_path + 'usercf_u2u_sim.pkl', 'wb'))

return u2u_sim_

Debido a que usercf consume demasiada memoria al calcular, no se ejecutará directamente aquí

Si se trata de un muestreo, se puede ejecutar

user_activate_degree_dict = get_user_activate_degree_dict (all_click_df)
u2u_sim = usercf_sim (all_click_df, user_activate_degree_dict)
sim de incrustación de elementos

Cálculo de similitud de recuperación de vectores

Topk se refiere a cada artículo, faiss devuelve los artículos topk más similares después de la búsqueda

def embdding_sim (click_df, item_emb_df, save_path, topk):
"" "
Cálculo de matriz de similitud de incrustación de artículo basado en el contenido
: param click_df: tabla de datos
: param item_emb_df: incrustación de artículo
: param save_path: guardar ruta
: patam topk: encontrar el más similar El topk artículo
retorno matriz de similitud del artículo
...
Hay mucho contenido aquí

Supongo que te gusta

Origin blog.csdn.net/m0_49978528/article/details/110405603
Recomendado
Clasificación