KTV歌曲推荐-因子分解机和DeepFM

FM因子分解机

在FM出现以前大多使用SVM来做CTR预估,当然还有其他的比如SVD++,PITF,FPMC等,但是这些模型对稀疏矩阵显得捉襟见肘,而且参数规模很大。 那FM解决了什么问题:

更适合做稀疏矩阵的参数计算

减少了需要训练的参数规模,而且特征和参数数量是线性关系

FM可以使用任何真实数据进行计算

其实FM出现主要解决了特征之间的交叉特征关系,此处省略了稀疏矩阵导致的w参数失效的模型直接说最终模型:

这里通过一个向量v的交叉来解决了稀疏矩阵导致的导致参数失效的问题。 那他参数的规模为什么小呢,接下来就是推导后面二次项部分:

从这里可以看出参数的复杂度是线性的O(kn)。

Keras对FM建模

这里是单纯的FM模型代码,这代码是借鉴别人的,我发现有一个问题就是,他最后repeat了二次项,这块我不是太明白,贴出来大家有兴趣可以一起讨论。

importosos.environ["CUDA_VISIBLE_DEVICES"]="-1"importkeras.backendasKfromkerasimportactivationsfromkeras.engine.topologyimportLayer, InputSpecclassFMLayer(Layer):def__init__(self, output_dim,

                factor_order,

                activation=None,

                **kwargs):if'input_shape'notinkwargsand'input_dim'inkwargs:            kwargs['input_shape'] = (kwargs.pop('input_dim'),)        super(FMLayer, self).__init__(**kwargs)        self.output_dim = output_dim        self.factor_order = factor_order        self.activation = activations.get(activation)        self.input_spec = InputSpec(ndim=2)defbuild(self, input_shape):assertlen(input_shape) ==2input_dim = input_shape[1]        self.input_spec = InputSpec(dtype=K.floatx(), shape=(None, input_dim))        self.w = self.add_weight(name='one',                                  shape=(input_dim, self.output_dim),                                initializer='glorot_uniform',                                trainable=True)        self.v = self.add_weight(name='two',                                  shape=(input_dim, self.factor_order),                                initializer='glorot_uniform',                                trainable=True)        self.b = self.add_weight(name='bias',                                  shape=(self.output_dim,),                                initializer='zeros',                                trainable=True)        super(FMLayer, self).build(input_shape)defcall(self, inputs, **kwargs):X_square = K.square(inputs)        xv = K.square(K.dot(inputs, self.v))        xw = K.dot(inputs, self.w)        p =0.5* K.sum(xv - K.dot(X_square, K.square(self.v)),1)                rp = K.repeat_elements(K.expand_dims(p,1), self.output_dim, axis=1)        f = xw + rp + self.b        output = K.reshape(f, (-1, self.output_dim))ifself.activationisnotNone:            output = self.activation(output)returnoutputdefcompute_output_shape(self, input_shape):assertinput_shapeandlen(input_shape) ==2returninput_shape[0], self.output_dim    inp = Input(shape=(np.shape(x_train)[1],))x = FMLayer(200,100)(inp)x = Dense(2, activation='sigmoid')(x)model.compile(loss='binary_crossentropy',                      optimizer='adam',                      metrics=['accuracy'])model.fit(x_train, y_train,          batch_size=32,          epochs=10,          validation_data=(x_test, y_test))

运行结果

Trainon2998samples,validateon750samplesEpoch1/102998/2998[==============================]-2s 791us/step - loss: 0.0580 - accuracy: 0.9872 - val_loss: 0.7466 - val_accuracy:0.8127Epoch2/102998/2998[==============================]-2s 683us/step - loss: 0.0650 - accuracy: 0.9893 - val_loss: 0.7845 - val_accuracy:0.8067Epoch3/102998/2998[==============================]-2s 674us/step - loss: 0.0803 - accuracy: 0.9915 - val_loss: 0.8730 - val_accuracy:0.7960Epoch4/102998/2998[==============================]-2s 681us/step - loss: 0.0362 - accuracy: 0.9943 - val_loss: 0.8771 - val_accuracy:0.8013Epoch5/102998/2998[==============================]-2s 683us/step - loss: 0.0212 - accuracy: 0.9953 - val_loss: 0.9035 - val_accuracy:0.8007Epoch6/102998/2998[==============================]-2s 721us/step - loss: 0.0188 - accuracy: 0.9965 - val_loss: 0.9295 - val_accuracy:0.7993Epoch7/102998/2998[==============================]-2s 719us/step - loss: 0.0168 - accuracy: 0.9972 - val_loss: 0.9597 - val_accuracy:0.8007Epoch8/102998/2998[==============================]-2s 693us/step - loss: 0.0150 - accuracy: 0.9973 - val_loss: 0.9851 - val_accuracy:0.7993Epoch9/102998/2998[==============================]-2s 677us/step - loss: 0.0137 - accuracy: 0.9972 - val_loss: 1.0114 - val_accuracy:0.7987Epoch10/102998/2998[==============================]-2s 684us/step - loss: 0.0126 - accuracy: 0.9977 - val_loss: 1.0361 - val_accuracy:0.8000

FFM算法

上面FM算法也可以看出来只是有一组特征,但是现实生活中可能会有多组特征,例如论文中举例:

此处包含了用户,电影,用户打分的其他电影,时间信息等,所以光是一组特征的交叉还不够,可能涉及到不同组特征的交叉。所以FFM应运而生。此处不详细介绍,直接说deepFM。

DeepFM算法

DeepFM一样我就不详细介绍了,不明白的自己看上面论文,我直说重点。

DeepFM的优点

结合了FM和DNN,结合了高阶特征建模DNN和低阶特征建模FM

DeepFM低阶部分和高阶部分共享了相同的特征,让计算更有效率

DeepFM在CTR预测中效果最好

DeepFM网络结构

FM部分网络结构

FM部分就是把一次项和二次项结合到一起就好

# 一次项fm_w_1 = Activation('linear')(K.dot(inputs, self.fm_w))# 二次项dot_latent = {}dot_cat = []foriinrange(1, self.num_fields):    print(self.num_fields)    dot_latent[i] = {}forfinself.field_combinations[i]:        print(len(self.field_combinations))        dot_latent[i][f] = Dot(axes=-1, normalize=False)([latent[i], latent[f]])        dot_cat.append(dot_latent[i][f])print(dot_cat)fm_w_2 = Concatenate()(dot_cat)# Merge 一次和二次项得到FMfm_w = Concatenate()([fm_w_1, fm_w_2])

DNN部分网络结构

 DNN部分比较简单

# 加俩隐藏层deep1 = Activation('relu')(K.bias_add(K.dot(ConcatLatent,self.h1_w),self.h1_b))deep2 = Activation('relu')(K.bias_add(K.dot(deep1, self.h2_w), self.h2_b))

整体网络结构

 这里先做Embeding,然后给DNN和FM提供数据,代码如下:

# 不同fields组合foriinrange(1, self.num_fields +1):    sparse[i] = Lambda(lambdax: x[:, self.id_start[i]:self.id_stop[i]],                              output_shape=((self.len_field[i],)))(inputTensor)    latent[i] = Activation('linear')(K.bias_add(K.dot(sparse[i],self.embed_w[i]),self.embed_b[i]))# merge 不同 fieldConcatLatent = Concatenate()(list(latent.values()))

DeepFM代码

整体代码如下:

fromkeras.layersimportDense, Concatenate, Lambda, Add, Dot, Activationfromkeras.engine.topologyimportLayerfromkerasimportbackendasKclassDeepFMLayer(Layer):def__init__(self, embeddin_size, field_len_group=None, **kwargs):self.output_dim =1self.embedding_size =10self.input_spec = InputSpec(ndim=2)                self.field_count = len(field_len_group)        self.num_fields = len(field_len_group)        self.field_lengths = field_len_group        self.embed_w = {}        self.embed_b = {}        self.h1 =10self.h2 =10defstart_stop_indices(field_lengths, num_fields):len_field = {}            id_start = {}            id_stop = {}            len_input =0foriinrange(1, num_fields +1):                len_field[i] = field_lengths[i -1]                id_start[i] = len_input                len_input += len_field[i]                id_stop[i] = len_inputreturnlen_field, len_input, id_start, id_stop        self.len_field, self.len_input, self.id_start, self.id_stop = \            start_stop_indices(self.field_lengths,self.num_fields)defField_Combos(num_fields):field_list = list(range(1, num_fields))            combo = {}            combo_count =0foridx, fieldinenumerate(field_list):                sub_list = list(range(field +1, num_fields +1))                combo_count += len(sub_list)                combo[field] = sub_listreturncombo, combo_count        self.field_combinations, self.combo_count = Field_Combos(self.num_fields)        print(field_len_group)        print(self.field_combinations)        print(self.num_fields)        super(DeepFMLayer, self).__init__(**kwargs)defbuild(self, input_shape):assertlen(input_shape) ==2total_embed_size =0self.input_spec = InputSpec(dtype=K.floatx(), shape=(None, input_shape[1]))foriinrange(1, self.num_fields +1):            input_dim = self.len_field[i]            _name ="embed_W"+ str(i)            self.embed_w[i] = self.add_weight(shape=(input_dim, self.embedding_size), initializer='glorot_uniform', name=_name)            _name ="embed_b"+ str(i)            self.embed_b[i] = self.add_weight(shape=(self.embedding_size,), initializer='zeros', name=_name)            total_embed_size += self.embedding_size                self.fm_w = self.add_weight(name='fm_w', shape=(input_shape[1],1), initializer='glorot_uniform', trainable=True)                        self.h1_w = self.add_weight(shape=(total_embed_size, self.h1), initializer='glorot_uniform', name='h1_w')        self.h1_b = self.add_weight(shape=(self.h1,), initializer='zeros', name='h1_b')        self.h2_w = self.add_weight(shape=(self.h1, self.h2), initializer='glorot_uniform', name='h2_W')        self.h2_b = self.add_weight(shape=(self.h2,), initializer='zeros', name='h2_b')        self.w = self.add_weight(name='w', shape=(self.h2,1), initializer='glorot_uniform', trainable=True)        self.b = self.add_weight(name='b', shape=(1,), initializer='zeros', trainable=True)        super(DeepFMLayer, self).build(input_shape)defcall(self, inputs):latent = {}        sparse = {}# 不同fields组合foriinrange(1, self.num_fields +1):            sparse[i] = Lambda(lambdax: x[:, self.id_start[i]:self.id_stop[i]],                                      output_shape=((self.len_field[i],)))(inputTensor)            latent[i] = Activation('linear')(K.bias_add(K.dot(sparse[i],self.embed_w[i]),self.embed_b[i]))# merge 不同 fieldConcatLatent = Concatenate()(list(latent.values()))# 加俩隐藏层deep1 = Activation('relu')(K.bias_add(K.dot(ConcatLatent,self.h1_w),self.h1_b))        deep2 = Activation('relu')(K.bias_add(K.dot(deep1, self.h2_w), self.h2_b))# 一次项fm_w_1 = Activation('linear')(K.dot(inputs, self.fm_w))# 二次项dot_latent = {}        dot_cat = []foriinrange(1, self.num_fields):            print(self.num_fields)            dot_latent[i] = {}forfinself.field_combinations[i]:                print(len(self.field_combinations))                dot_latent[i][f] = Dot(axes=-1, normalize=False)([latent[i], latent[f]])                dot_cat.append(dot_latent[i][f])        print(dot_cat)        fm_w_2 = Concatenate()(dot_cat)# Merge 一次和二次项得到FMfm_w = Concatenate()([fm_w_1, fm_w_2])                fm = Lambda(lambdax: K.sum(x, axis=1, keepdims=True))(fm_w)                deep_wx = Activation('linear')(K.bias_add(K.dot(deep2, self.w),self.b))        print('build finish')returnAdd()([fm, deep_wx])defcompute_output_shape(self, input_shape):return(input_shape[0], self.output_dim)inputTensor = Input(shape=(np.shape(x_train)[1],))deepFM_out = DeepFMLayer(10, {0:10,1:10,2:np.shape(x_train)[1]-20})(inputTensor)out = Dense(2, activation="sigmoid", trainable=True)(deepFM_out)model = Model(inputTensor, out)model.compile(loss='binary_crossentropy',                      optimizer='adam',                      metrics=['accuracy'])model.fit(x_train, y_train,                  batch_size=32,                  epochs=10,                  validation_data=(x_test, y_test))

运行结果:

Trainon2998samples,validateon750samplesEpoch1/102998/2998[==============================]-1s 425us/step - loss: 0.6403 - accuracy: 0.6344 - val_loss: 0.5578 - val_accuracy:0.7533Epoch2/102998/2998[==============================]-1s 226us/step - loss: 0.4451 - accuracy: 0.8721 - val_loss: 0.4727 - val_accuracy:0.8240Epoch3/102998/2998[==============================]-1s 215us/step - loss: 0.2469 - accuracy: 0.9445 - val_loss: 0.5188 - val_accuracy:0.8360Epoch4/102998/2998[==============================]-1s 200us/step - loss: 0.1319 - accuracy: 0.9678 - val_loss: 0.6488 - val_accuracy:0.8233Epoch5/102998/2998[==============================]-1s 211us/step - loss: 0.0693 - accuracy: 0.9843 - val_loss: 0.7755 - val_accuracy:0.8247Epoch6/102998/2998[==============================]-1s 225us/step - loss: 0.0392 - accuracy: 0.9932 - val_loss: 0.9234 - val_accuracy:0.8187Epoch7/102998/2998[==============================]-1s 204us/step - loss: 0.0224 - accuracy: 0.9967 - val_loss: 1.0437 - val_accuracy:0.8200Epoch8/102998/2998[==============================]-1s 190us/step - loss: 0.0163 - accuracy: 0.9972 - val_loss: 1.1618 - val_accuracy:0.8173Epoch9/102998/2998[==============================]-1s 190us/step - loss: 0.0106 - accuracy: 0.9980 - val_loss: 1.2746 - val_accuracy:0.8147Epoch10/102998/2998[==============================]-1s 213us/step - loss: 0.0083 - accuracy: 0.9987 - val_loss: 1.3395 - val_accuracy:0.8167Model:"model_19"

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容