日本熟妇hd丰满老熟妇,中文字幕一区二区三区在线不卡 ,亚洲成片在线观看,免费女同在线一区二区

基于組件化EasyRec框架快速搭建深度推薦算法模型

有層出不窮的算法idea想要快速驗(yàn)證?核心算法模塊如何快速?gòu)?fù)用到不同場(chǎng)景的不同模型中?如何通過(guò)排列組合構(gòu)建出新的模型?組件化EasyRec框架可以幫助你以“搭積木”的方式快速構(gòu)建想要的模型結(jié)構(gòu),快來(lái)試一試吧!

限制說(shuō)明

僅支持0.8.0或以上版本的組件化的EasyRec框架。

為何需要組件化

1. 靈活搭建模型,所思即所得

依靠動(dòng)態(tài)可插拔的公共組件,以“搭積木”的方式快速構(gòu)建想要的模型結(jié)構(gòu)??蚣芴峁┝?膠水"語(yǔ)法,實(shí)現(xiàn)組件間的無(wú)縫銜接。

2. 實(shí)現(xiàn)組件復(fù)用,一次開(kāi)發(fā)到處可用

很多模型之所以被稱(chēng)之為一個(gè)新的模型,是因?yàn)橐肓艘粋€(gè)或多個(gè)特殊的子模塊(組件),然而這些子模塊并不僅只能用在該模型中,通過(guò)組合各個(gè)不同的子模塊可以輕易組裝一個(gè)新的模型。

過(guò)去一個(gè)新開(kāi)發(fā)的公共可選模塊,比如Dense Feature Embedding LayerSENet添加到現(xiàn)有模型中,需要修改所有模型的代碼才能用上新的特性,過(guò)程繁瑣易出錯(cuò)。隨著模型數(shù)量和公共模塊數(shù)量的增加,為所有模型集成所有公共可選模塊將產(chǎn)生組合爆炸的不可控局面。

組件化實(shí)現(xiàn)了底層公共模塊與上層模型的解耦。

3. 提高實(shí)驗(yàn)迭代效率,好的想法值得快速驗(yàn)證

為已有模型添加新特性將變得十分方便。開(kāi)發(fā)一個(gè)新的模型,只需要實(shí)現(xiàn)特殊的新模塊,其余部分可以通過(guò)組件庫(kù)中的已有組件拼裝。

現(xiàn)在我們只需要為新的特征開(kāi)發(fā)一個(gè)Keras Layer類(lèi),并在指定package中添加import語(yǔ)句,框架就能自動(dòng)識(shí)別并添加到組件庫(kù)中,不需要額外操作。新人不再需要熟悉EasyRec的方方面面就可以為框架添加功能,開(kāi)發(fā)效率大大提高。

組件化的目標(biāo)

不再需要實(shí)現(xiàn)新的模型,只需要實(shí)現(xiàn)新的組件! 模型通過(guò)組裝組件完成。

各個(gè)組件專(zhuān)注自身功能的實(shí)現(xiàn),模塊中代碼高度聚合,只負(fù)責(zé)一項(xiàng)任務(wù),也就是常說(shuō)的單一職責(zé)原則。

主干網(wǎng)絡(luò)

組件化EasyRec模型使用一個(gè)可配置的主干網(wǎng)絡(luò)作為核心部件。主干網(wǎng)絡(luò)是由多個(gè)組件塊組成的一個(gè)有向無(wú)環(huán)圖(DAG),框架負(fù)責(zé)按照DAG的拓?fù)渑判驁?zhí)行個(gè)組件塊關(guān)聯(lián)的代碼邏輯,構(gòu)建TF Graph的一個(gè)子圖。DAG的輸出節(jié)點(diǎn)由concat_blocks配置項(xiàng)定義,各輸出組件塊的輸出tensor拼接之后輸入給一個(gè)可選的頂部MLP層,或者直接鏈接到最終的預(yù)測(cè)層。

image.png

案例1. Wide&Deep 模型

配置文件:wide_and_deep_backbone_on_movielens.config

model_config: {
 model_name: "WideAndDeep"
 model_class: "RankModel"
 feature_groups: {
   group_name: 'wide'
   feature_names: 'user_id'
   feature_names: 'movie_id'
   feature_names: 'job_id'
   feature_names: 'age'
   feature_names: 'gender'
   feature_names: 'year'
   feature_names: 'genres'
   wide_deep: WIDE
 }
 feature_groups: {
   group_name: 'deep'
   feature_names: 'user_id'
   feature_names: 'movie_id'
   feature_names: 'job_id'
   feature_names: 'age'
   feature_names: 'gender'
   feature_names: 'year'
   feature_names: 'genres'
   wide_deep: DEEP
 }
 backbone {
   blocks {
     name: 'wide'
     inputs {
     	feature_group_name: 'wide'
     }
     input_layer {
     	only_output_feature_list: true
     	wide_output_dim: 1
     }
   }
   blocks {
     name: 'deep_logit'
     inputs {
     	feature_group_name: 'deep'
     }
     keras_layer {
     	 class_name: 'MLP'
       mlp {
         hidden_units: [256, 256, 256, 1]
         use_final_bn: false
         final_activation: 'linear'
       }
     }
   }
   blocks {
     name: 'final_logit'
     inputs {
       block_name: 'wide'
       input_fn: 'lambda x: tf.add_n(x)'
     }
     inputs {
       block_name: 'deep_logit'
     }
     merge_inputs_into_list: true
     keras_layer {
       class_name: 'Add'
     }
   }
   concat_blocks: 'final_logit'
 }
 model_params {
 	l2_regularization: 1e-4
 }
 embedding_regularization: 1e-4
}

MovieLens-1M數(shù)據(jù)集效果對(duì)比:

Model

Epoch

AUC

Wide&Deep

1

0.8558

Wide&Deep(Backbone)

1

0.8854

備注:通過(guò)組件化的方式搭建的模型效果比內(nèi)置的模型效果更好是因?yàn)?code data-tag="code" code-type="xCode" class="code">MLP組件有更好的初始化方法。

通過(guò)protobuf messagebackbone來(lái)定義主干網(wǎng)絡(luò),主干網(wǎng)絡(luò)有多個(gè)積木塊(block)組成,每個(gè)block代表一個(gè)可復(fù)用的組件。

  • 每個(gè)block有一個(gè)唯一的名字(name),并且有一個(gè)或多個(gè)輸入和輸出。

  • 每個(gè)輸入只能是某個(gè)feature group的name,或者另一個(gè)block的name,或者是一個(gè)block package的名字。當(dāng)一個(gè)block有多個(gè)輸入時(shí),會(huì)自動(dòng)執(zhí)行merge操作(輸入為list時(shí)自動(dòng)合并,輸入為tensor時(shí)自動(dòng)concat)。

  • 所有block根據(jù)輸入與輸出的關(guān)系組成一個(gè)有向無(wú)環(huán)圖(DAG),框架自動(dòng)解析出DAG的拓?fù)潢P(guān)系,按照拓?fù)渑判驁?zhí)行塊所關(guān)聯(lián)的模塊。

  • 當(dāng)block有多個(gè)輸出時(shí),返回一個(gè)python元組(tuple),下游block可以配置input_slice通過(guò)python切片語(yǔ)法獲取到輸入元組的某個(gè)元素作為輸入,或者通過(guò)自定義的input_fn配置一個(gè)lambda表達(dá)式函數(shù)獲取元組的某個(gè)值。

  • 每個(gè)block關(guān)聯(lián)的模塊通常是一個(gè)keras layer對(duì)象,實(shí)現(xiàn)了一個(gè)可復(fù)用的子網(wǎng)絡(luò)模塊??蚣苤С旨虞d自定義的keras layer,以及所有系統(tǒng)內(nèi)置的keras layer。

  • 可以為block關(guān)聯(lián)一個(gè)input_layer對(duì)輸入的feature group配置的特征做一些額外的加工,比如執(zhí)行batch normalization、layer normalizationfeature dropout等操作,并且可以指定輸出的tensor的格式(2d、3d、list等)。注意:當(dāng)block關(guān)聯(lián)的模塊是input_layer時(shí),必須設(shè)定feature_group_name為某個(gè)feature group的名字,當(dāng)block關(guān)聯(lián)的模塊不是input_layer時(shí),block的name不可與某個(gè)feature group重名。

  • 還有一些特殊的block關(guān)聯(lián)了一個(gè)特殊的模塊,包括lambda layer、sequential layers、repeated layerrecurrent layer。這些特殊layer分別實(shí)現(xiàn)了自定義表達(dá)式、順序執(zhí)行多個(gè)layer、重復(fù)執(zhí)行某個(gè)layer、循環(huán)執(zhí)行某個(gè)layer的功能。

  • DAG的輸出節(jié)點(diǎn)名由concat_blocks配置項(xiàng)指定,配置了多個(gè)輸出節(jié)點(diǎn)時(shí)自動(dòng)執(zhí)行tensor的concat操作。

  • 如果不配置concat_blocks,框架會(huì)自動(dòng)拼接DAG的所有葉子節(jié)點(diǎn)并輸出。

  • 可以為主干網(wǎng)絡(luò)配置一個(gè)可選的MLP模塊。

image.png

案例2:DeepFM 模型

配置文件:deepfm_backbone_on_movielens.config

這個(gè)Case重點(diǎn)關(guān)注下兩個(gè)特殊的block,一個(gè)使用了lambda表達(dá)式配置了一個(gè)自定義函數(shù);另一個(gè)的加載了一個(gè)內(nèi)置的keras layertf.keras.layers.Add

model_config: {
  model_name: 'DeepFM'
  model_class: 'RankModel'
  feature_groups: {
    group_name: 'wide'
    feature_names: 'user_id'
    feature_names: 'movie_id'
    feature_names: 'job_id'
    feature_names: 'age'
    feature_names: 'gender'
    feature_names: 'year'
    feature_names: 'genres'
    wide_deep: WIDE
  }
  feature_groups: {
    group_name: 'features'
    feature_names: 'user_id'
    feature_names: 'movie_id'
    feature_names: 'job_id'
    feature_names: 'age'
    feature_names: 'gender'
    feature_names: 'year'
    feature_names: 'genres'
    feature_names: 'title'
    wide_deep: DEEP
  }
  backbone {
    blocks {
      name: 'wide_logit'
      inputs {
        feature_group_name: 'wide'
      }
      input_layer {
        wide_output_dim: 1
      }
    }
    blocks {
      name: 'features'
      inputs {
        feature_group_name: 'features'
      }
      input_layer {
        output_2d_tensor_and_feature_list: true
      }
    }
    blocks {
      name: 'fm'
      inputs {
        block_name: 'features'
        input_slice: '[1]'
      }
      keras_layer {
        class_name: 'FM'
      }
    }
    blocks {
      name: 'deep'
      inputs {
        block_name: 'features'
        input_slice: '[0]'
      }
      keras_layer {
        class_name: 'MLP'
        mlp {
          hidden_units: [256, 128, 64, 1]
          use_final_bn: false
          final_activation: 'linear'
        }
      }
    }
    blocks {
      name: 'add'
      inputs {
        block_name: 'wide_logit'
        input_fn: 'lambda x: tf.reduce_sum(x, axis=1, keepdims=True)'
      }
      inputs {
        block_name: 'fm'
      }
      inputs {
        block_name: 'deep'
      }
      merge_inputs_into_list: true
      keras_layer {
        class_name: 'Add'
      }
    }
    concat_blocks: 'add'
  }
  model_params {
    l2_regularization: 1e-4
  }
  embedding_regularization: 1e-4
}

MovieLens-1M數(shù)據(jù)集效果對(duì)比:

Model

Epoch

AUC

DeepFM

1

0.8867

DeepFM(Backbone)

1

0.8872

案例3:DCN 模型

配置文件:dcn_backbone_on_movielens.config

這個(gè)Case重點(diǎn)關(guān)注一個(gè)特殊的 DCNblock,用了recurrent layer實(shí)現(xiàn)了循環(huán)調(diào)用某個(gè)模塊多次的效果。通過(guò)該Case還是在DAG之上添加了MLP模塊。

model_config: {
  model_name: 'DCN V2'
  model_class: 'RankModel'
  feature_groups: {
    group_name: 'all'
    feature_names: 'user_id'
    feature_names: 'movie_id'
    feature_names: 'job_id'
    feature_names: 'age'
    feature_names: 'gender'
    feature_names: 'year'
    feature_names: 'genres'
    wide_deep: DEEP
  }
  backbone {
    blocks {
      name: "deep"
      inputs {
        feature_group_name: 'all'
      }
      keras_layer {
        class_name: 'MLP'
        mlp {
          hidden_units: [256, 128, 64]
        }
      }
    }
    blocks {
      name: "dcn"
      inputs {
        feature_group_name: 'all'
        input_fn: 'lambda x: [x, x]'
      }
      recurrent {
        num_steps: 3
        fixed_input_index: 0
        keras_layer {
          class_name: 'Cross'
        }
      }
    }
    concat_blocks: ['deep', 'dcn']
    top_mlp {
      hidden_units: [64, 32, 16]
    }
  }
  model_params {
    l2_regularization: 1e-4
  }
  embedding_regularization: 1e-4
}

上述配置對(duì)CrossLayer循環(huán)調(diào)用了3次,邏輯上等價(jià)于執(zhí)行如下語(yǔ)句:

x1 = Cross()(x0, x0)
x2 = Cross()(x0, x1)
x3 = Cross()(x0, x2)

MovieLens-1M數(shù)據(jù)集效果對(duì)比:

Model

Epoch

AUC

DCN (內(nèi)置)

1

0.8576

DCN_v2 (backbone)

1

0.8770

備注:新實(shí)現(xiàn)的Cross組件對(duì)應(yīng)了參數(shù)量更多的v2版本的DCN,而內(nèi)置的DCN模型對(duì)應(yīng)了v1版本的DCN。

image.png

案例4:DLRM 模型

配置文件:dlrm_backbone_on_criteo.config

model_config: {
  model_name: 'DLRM'
  model_class: 'RankModel'
  feature_groups: {
    group_name: "dense"
    feature_names: "F1"
    feature_names: "F2"
    ...
    wide_deep:DEEP
  }
  feature_groups: {
    group_name: "sparse"
    feature_names: "C1"
    feature_names: "C2"
    feature_names: "C3"
    ...
    wide_deep:DEEP
  }
  backbone {
    blocks {
      name: 'bottom_mlp'
      inputs {
        feature_group_name: 'dense'
      }
      keras_layer {
        class_name: 'MLP'
        mlp {
          hidden_units: [64, 32, 16]
        }
      }
    }
    blocks {
      name: 'sparse'
      inputs {
        feature_group_name: 'sparse'
      }
      input_layer {
        output_2d_tensor_and_feature_list: true
      }
    }
    blocks {
      name: 'dot'
      inputs {
        block_name: 'bottom_mlp'
      }
      inputs {
        block_name: 'sparse'
        input_slice: '[1]'
      }
      keras_layer {
        class_name: 'DotInteraction'
      }
    }
    blocks {
      name: 'sparse_2d'
      inputs {
        block_name: 'sparse'
        input_slice: '[0]'
      }
    }
    concat_blocks: ['sparse_2d', 'dot']
    top_mlp {
      hidden_units: [256, 128, 64]
    }
  }
  model_params {
    l2_regularization: 1e-5
  }
  embedding_regularization: 1e-5
}

Criteo數(shù)據(jù)集效果對(duì)比:

Model

Epoch

AUC

DLRM

1

0.79785

DLRM (backbone)

1

0.7993

備注:DotInteraction是新開(kāi)發(fā)的特征兩兩交叉做內(nèi)積運(yùn)算的模塊。

這個(gè)案例中'dot' block的第一個(gè)輸入是一個(gè)tensor,第二個(gè)輸入是一個(gè)list,這種情況下第一個(gè)輸入會(huì)插入到list中,合并成一個(gè)更大的list,作為block的輸入。

案例5:為 DLRM 模型添加一個(gè)新的數(shù)值特征Embedding組件

配置文件:dlrm_on_criteo_with_periodic.config

與上一個(gè)案例相比,多了一個(gè)PeriodicEmbeddingLayer,組件化編程的靈活性與可擴(kuò)展性由此可見(jiàn)一斑。

重點(diǎn)關(guān)注一下PeriodicEmbeddingLayer的參數(shù)配置方式,這里并沒(méi)有使用自定義protobuf message的傳參方式,而是采用了內(nèi)置的google.protobuf.Struct對(duì)象作為自定義Layer的參數(shù)。實(shí)際上,該自定義Layer也支持通過(guò)自定義message傳參??蚣芴峁┝艘粋€(gè)通用的ParameterAPI 用通用的方式處理兩種傳參方式。

model_config: {
  model_class: 'RankModel'
  feature_groups: {
    group_name: "dense"
    feature_names: "F1"
    feature_names: "F2"
    ...
    wide_deep:DEEP
  }
  feature_groups: {
    group_name: "sparse"
    feature_names: "C1"
    feature_names: "C2"
    ...
    wide_deep:DEEP
  }
  backbone {
    blocks {
      name: 'num_emb'
      inputs {
        feature_group_name: 'dense'
      }
      keras_layer {
        class_name: 'PeriodicEmbedding'
        st_params {
          fields {
            key: "output_tensor_list"
            value { bool_value: true }
          }
          fields {
            key: "embedding_dim"
            value { number_value: 16 }
          }
          fields {
            key: "sigma"
            value { number_value: 0.005 }
          }
        }
      }
    }
    blocks {
      name: 'sparse'
      inputs {
        feature_group_name: 'sparse'
      }
      input_layer {
        output_2d_tensor_and_feature_list: true
      }
    }
    blocks {
      name: 'dot'
      inputs {
        block_name: 'num_emb'
        input_slice: '[1]'
      }
      inputs {
        block_name: 'sparse'
        input_slice: '[1]'
      }
      keras_layer {
        class_name: 'DotInteraction'
      }
    }
    blocks {
      name: 'sparse_2d'
      inputs {
        block_name: 'sparse'
        input_slice: '[0]'
      }
    }
    blocks {
      name: 'num_emb_2d'
      inputs {
        block_name: 'num_emb'
        input_slice: '[0]'
      }
    }
    concat_blocks: ['num_emb_2d', 'dot', 'sparse_2d']
    top_mlp {
      hidden_units: [256, 128, 64]
    }
  }
  model_params {
    l2_regularization: 1e-5
  }
  embedding_regularization: 1e-5
}

image.png

Criteo數(shù)據(jù)集效果對(duì)比:

Model

Epoch

AUC

DLRM

1

0.79785

DLRM (backbone)

1

0.7993

DLRM (periodic)

1

0.7998

案例6:使用內(nèi)置的keras layer搭建DNN模型

配置文件:mlp_on_movielens.config

該案例只為了演示可以組件化EasyRec可以使用TF內(nèi)置的原子粒度keras layer作為通用組件,實(shí)際上我們已經(jīng)有了一個(gè)自定義的MLP組件,使用會(huì)更加方便。

該案例重點(diǎn)關(guān)注一個(gè)特殊的sequential block,這個(gè)組件塊內(nèi)可以定義多個(gè)串聯(lián)在一起的layers,前一個(gè)layer的輸出作為后一個(gè)layer的輸入。相比定義多個(gè)普通block的方式,sequential block會(huì)更加方便。

備注:調(diào)用系統(tǒng)內(nèi)置的keras layer,只能通過(guò)google.proto.Struct的格式傳參。

model_config: {
  model_class: "RankModel"
  feature_groups: {
    group_name: 'features'
    feature_names: 'user_id'
    feature_names: 'movie_id'
    feature_names: 'job_id'
    feature_names: 'age'
    feature_names: 'gender'
    feature_names: 'year'
    feature_names: 'genres'
    wide_deep: DEEP
  }
  backbone {
    blocks {
      name: 'mlp'
      inputs {
        feature_group_name: 'features'
      }
      layers {
        keras_layer {
          class_name: 'Dense'
          st_params {
            fields {
              key: 'units'
              value: { number_value: 256 }
            }
            fields {
              key: 'activation'
              value: { string_value: 'relu' }
            }
          }
        }
      }
      layers {
        keras_layer {
          class_name: 'Dropout'
          st_params {
            fields {
              key: 'rate'
              value: { number_value: 0.5 }
            }
          }
        }
      }
      layers {
        keras_layer {
          class_name: 'Dense'
          st_params {
            fields {
              key: 'units'
              value: { number_value: 256 }
            }
            fields {
              key: 'activation'
              value: { string_value: 'relu' }
            }
          }
        }
      }
      layers {
        keras_layer {
          class_name: 'Dropout'
          st_params {
            fields {
              key: 'rate'
              value: { number_value: 0.5 }
            }
          }
        }
      }
      layers {
        keras_layer {
          class_name: 'Dense'
          st_params {
            fields {
              key: 'units'
              value: { number_value: 1 }
            }
          }
        }
      }
    }
    concat_blocks: 'mlp'
  }
  model_params {
    l2_regularization: 1e-4
  }
  embedding_regularization: 1e-4
}

MovieLens-1M數(shù)據(jù)集效果:

Model

Epoch

AUC

MLP

1

0.8616

案例7:對(duì)比學(xué)習(xí)(使用組件包)

配置文件:contrastive_learning_on_movielens.config

該案例為了演示block package的使用,block package可以打包一組block,構(gòu)成一個(gè)可被復(fù)用的子網(wǎng)絡(luò),即被打包的子網(wǎng)絡(luò)以共享參數(shù)的方式在同一個(gè)模型中調(diào)用多次。與之相反,沒(méi)有打包的block是不能被多次調(diào)用的(但是可以多次復(fù)用結(jié)果)。

block package主要為自監(jiān)督學(xué)習(xí)、對(duì)比學(xué)習(xí)等場(chǎng)景設(shè)計(jì)。

model_config: {
  model_name: "ContrastiveLearning"
  model_class: "RankModel"
  feature_groups: {
    group_name: 'user'
    feature_names: 'user_id'
    feature_names: 'job_id'
    feature_names: 'age'
    feature_names: 'gender'
    wide_deep: DEEP
  }
  feature_groups: {
    group_name: 'item'
    feature_names: 'movie_id'
    feature_names: 'year'
    feature_names: 'genres'
    wide_deep: DEEP
  }
  backbone {
    blocks {
      name: 'user_tower'
      inputs {
        feature_group_name: 'user'
      }
      keras_layer {
        class_name: 'MLP'
        mlp {
          hidden_units: [256, 128]
        }
      }
    }
    packages {
      name: 'item_tower'
      blocks {
        name: 'item'
        inputs {
          feature_group_name: 'item'
        }
        input_layer {
          dropout_rate: 0.2
        }
      }
      blocks {
        name: 'item_encoder'
        inputs {
          block_name: 'item'
        }
        keras_layer {
          class_name: 'MLP'
          mlp {
            hidden_units: [256, 128]
          }
        }
      }
    }
    blocks {
      name: 'contrastive_learning'
      inputs {
        package_name: 'item_tower'
      }
      inputs {
        package_name: 'item_tower'
      }
      merge_inputs_into_list: true
      keras_layer {
        class_name: 'AuxiliaryLoss'
        st_params {
          fields {
            key: 'loss_type'
            value: { string_value: 'info_nce' }
          }
          fields {
            key: 'loss_weight'
            value: { number_value: 0.1 }
          }
          fields {
            key: 'temperature'
            value: { number_value: 0.2 }
          }
        }
      }
    }
    blocks {
      name: 'top_mlp'
      inputs {
        block_name: 'contrastive_learning'
        ignore_input: true
      }
      inputs {
        block_name: 'user_tower'
      }
      inputs {
        package_name: 'item_tower'
        reset_input {}
      }
      keras_layer {
        class_name: 'MLP'
        mlp {
          hidden_units: [128, 64]
        }
      }
    }
    concat_blocks: 'top_mlp'
  }
  model_params {
    l2_regularization: 1e-4
  }
  embedding_regularization: 1e-4
}

AuxiliaryLoss是用來(lái)計(jì)算對(duì)比學(xué)習(xí)損失的layer,詳見(jiàn)'組件詳細(xì)參數(shù)'。

額外的input配置:

  • ignore_input: true 表示忽略當(dāng)前這路的輸入;添加該路輸入只是為了控制拓?fù)浣Y(jié)構(gòu)的執(zhí)行順序

  • reset_input: 重置本次package調(diào)用時(shí)input_layer的配置項(xiàng);可以配置與package定義時(shí)不同的參數(shù)

注意這個(gè)案例沒(méi)有為名為item_tower的package配置concat_blocks,框架會(huì)自動(dòng)設(shè)置為DAG的葉子節(jié)點(diǎn)。

在當(dāng)前案例中,item_tower被調(diào)用了3次,前2次調(diào)用時(shí)輸入層dropout配置生效,用于計(jì)算對(duì)比學(xué)習(xí)損失函數(shù);最后1次調(diào)用時(shí)重置了輸入層配置,不執(zhí)行dropout。 主模型的item_tower與對(duì)比學(xué)習(xí)輔助任務(wù)中的item_tower共享參數(shù);輔助任務(wù)中的item_tower通過(guò)對(duì)輸入特征embedding做dropout來(lái)生成augmented sample;主模型的item_tower不執(zhí)行數(shù)據(jù)增強(qiáng)操作。

MovieLens-1M數(shù)據(jù)集效果:

Model

Epoch

AUC

MultiTower

1

0.8814

ContrastiveLearning

1

0.8728

一個(gè)更復(fù)雜一點(diǎn)的對(duì)比學(xué)習(xí)模型案例:CL4SRec

案例8:多目標(biāo)模型 MMoE

多目標(biāo)模型的model_class一般配置為"MultiTaskModel",并且需要在model_params里配置多個(gè)目標(biāo)對(duì)應(yīng)的Tower。model_name為任意自定義字符串,僅有注釋作用。

model_config {
  model_name: "MMoE"
  model_class: "MultiTaskModel"
  feature_groups {
    group_name: "all"
    feature_names: "user_id"
    feature_names: "cms_segid"
    ...
    feature_names: "tag_brand_list"
    wide_deep: DEEP
  }
  backbone {
    blocks {
      name: 'all'
      inputs {
        feature_group_name: 'all'
      }
      input_layer {
        only_output_feature_list: true
      }
    }
    blocks {
      name: "senet"
      inputs {
        block_name: "all"
      }
      keras_layer {
        class_name: 'SENet'
        senet {
          reduction_ratio: 4
        }
      }
    }
    blocks {
      name: "mmoe"
      inputs {
        block_name: "senet"
      }
      keras_layer {
        class_name: 'MMoE'
        mmoe {
          num_task: 2
          num_expert: 3
          expert_mlp {
            hidden_units: [256, 128]
          }
        }
      }
    }
  }
  model_params {
    task_towers {
      tower_name: "ctr"
      label_name: "clk"
      dnn {
        hidden_units: [128, 64]
      }
      num_class: 1
      weight: 1.0
      loss_type: CLASSIFICATION
      metrics_set: {
       auc {}
      }
    }
    task_towers {
      tower_name: "cvr"
      label_name: "buy"
      dnn {
        hidden_units: [128, 64]
      }
      num_class: 1
      weight: 1.0
      loss_type: CLASSIFICATION
      metrics_set: {
       auc {}
      }
    }
    l2_regularization: 1e-06
  }
  embedding_regularization: 5e-05
}

注意這個(gè)案例沒(méi)有為backbone配置concat_blocks,框架會(huì)自動(dòng)設(shè)置為DAG的葉子節(jié)點(diǎn)。

案例9:多目標(biāo)模型 DBMTL

多目標(biāo)模型的model_class一般配置為"MultiTaskModel",并且需要在model_params里配置多個(gè)目標(biāo)對(duì)應(yīng)的Tower。model_name為任意自定義字符串,僅有注釋作用。

model_config {
 model_name: "DBMTL"
 model_class: "MultiTaskModel"
 feature_groups {
 group_name: "all"
 feature_names: "user_id"
 feature_names: "cms_segid"
 ...
 feature_names: "tag_brand_list"
 wide_deep: DEEP
 }
 backbone {
 blocks {
 name: "mask_net"
 inputs {
 feature_group_name: "all"
 }
 keras_layer {
 class_name: 'MaskNet'
 masknet {
 mask_blocks {
 aggregation_size: 512
 output_size: 256
 }
 mask_blocks {
 aggregation_size: 512
 output_size: 256
 }
 mask_blocks {
 aggregation_size: 512
 output_size: 256
 }
 mlp {
 hidden_units: [512, 256]
 }
 }
 }
 }
 }
 model_params {
 task_towers {
 tower_name: "ctr"
 label_name: "clk"
 loss_type: CLASSIFICATION
 metrics_set: {
 auc {}
 }
 dnn {
 hidden_units: [256, 128, 64]
 }
 relation_dnn {
 hidden_units: [32]
 }
 weight: 1.0
 }
 task_towers {
 tower_name: "cvr"
 label_name: "buy"
 loss_type: CLASSIFICATION
 metrics_set: {
 auc {}
 }
 dnn {
 hidden_units: [256, 128, 64]
 }
 relation_tower_names: ["ctr"]
 relation_dnn {
 hidden_units: [32]
 }
 weight: 1.0
 }
 l2_regularization: 1e-6
 }
 embedding_regularization: 5e-6
}

DBMTL模型需要在model_params里為每個(gè)子任務(wù)的Tower配置relation_dnn,同時(shí)還需要通relation_tower_names配置任務(wù)間的依賴(lài)關(guān)系。

這個(gè)案例同樣沒(méi)有為backbone配置concat_blocks,框架會(huì)自動(dòng)設(shè)置為DAG的葉子節(jié)點(diǎn)。

案例10:MaskNet + PPNet + MMoE

model_config: {
  model_name: 'MaskNet + PPNet + MMoE'
  model_class: 'RankModel'
  feature_groups: {
    group_name: 'memorize'
    feature_names: 'user_id'
    feature_names: 'adgroup_id'
    feature_names: 'pid'
    wide_deep: DEEP
  }
  feature_groups: {
    group_name: 'general'
    feature_names: 'age_level'
    feature_names: 'shopping_level'
    ...
    wide_deep: DEEP
  }
  backbone {
    blocks {
      name: "mask_net"
      inputs {
        feature_group_name: "general"
      }
      repeat {
        num_repeat: 3
        keras_layer {
          class_name: "MaskBlock"
          mask_block {
            output_size: 512
            aggregation_size: 1024
          }
        }
      }
    }
    blocks {
      name: "ppnet"
      inputs {
        block_name: "mask_net"
      }
      inputs {
        feature_group_name: "memorize"
      }
      merge_inputs_into_list: true
      repeat {
        num_repeat: 3
        input_fn: "lambda x, i: [x[0][i], x[1]]"
        keras_layer {
          class_name: "PPNet"
          ppnet {
            mlp {
              hidden_units: [256, 128, 64]
            }
            gate_params {
              output_dim: 512
            }
            mode: "eager"
            full_gate_input: false
          }
        }
      }
    }
    blocks {
      name: "mmoe"
      inputs {
        block_name: "ppnet"
      }
      inputs {
        feature_group_name: "general"
      }
      keras_layer {
        class_name: "MMoE"
        mmoe {
          num_task: 2
          num_expert: 3
        }
      }
    }
  }
  model_params {
    l2_regularization: 0.0
    task_towers {
      tower_name: "ctr"
      label_name: "is_click"
      metrics_set {
        auc {
          num_thresholds: 20000
        }
      }
      loss_type: CLASSIFICATION
      num_class: 1
      dnn {
        hidden_units: 64
        hidden_units: 32
      }
      weight: 1.0
    }
    task_towers {
      tower_name: "cvr"
      label_name: "is_train"
      metrics_set {
        auc {
          num_thresholds: 20000
        }
      }
      loss_type: CLASSIFICATION
      num_class: 1
      dnn {
        hidden_units: 64
        hidden_units: 32
      }
      weight: 1.0
    }
  }
}

該案例體現(xiàn)了如何應(yīng)用重復(fù)組件塊。

更多案例

兩個(gè)新的模型:

MovieLens-1M數(shù)據(jù)集效果:

Model

Epoch

AUC

MaskNet

1

0.8872

FibiNet

1

0.8893

序列模型:

其他模型:

組件庫(kù)介紹

1.基礎(chǔ)組件

類(lèi)名

功能

說(shuō)明

示例

MLP

多層感知機(jī)

可定制激活函數(shù)、initializer、Dropout、BN等

案例1

Highway

類(lèi)似殘差鏈接

可用來(lái)對(duì)預(yù)訓(xùn)練embedding做增量微調(diào)

Highway Network

Gate

門(mén)控

多個(gè)輸入的加權(quán)求和

CDN

PeriodicEmbedding

周期激活函數(shù)

數(shù)值特征Embedding

案例5

AutoDisEmbedding

自動(dòng)離散化

數(shù)值特征Embedding

dlrm_on_criteo_with_autodis.config

備注:Gate組件的第一個(gè)輸入是權(quán)重向量,后面的輸入拼湊成一個(gè)列表,權(quán)重向量的長(zhǎng)度應(yīng)等于列表的長(zhǎng)度

2.特征交叉組件

類(lèi)名

功能

說(shuō)明

示例

FM

二階交叉

DeepFM模型的組件

案例2

DotInteraction

二階內(nèi)積交叉

DLRM模型的組件

案例4

Cross

bit-wise交叉

DCN v2模型的組件

案例3

BiLinear

雙線(xiàn)性

FiBiNet模型的組件

fibinet_on_movielens.config

FiBiNet

SENet & BiLinear

FiBiNet模型

fibinet_on_movielens.config

3.特征重要度學(xué)習(xí)組件

類(lèi)名

功能

說(shuō)明

示例

SENet

建模特征重要度

FiBiNet模型的組件

MMoE

MaskBlock

建模特征重要度

MaskNet模型的組件

CDN

MaskNet

多個(gè)串行或并行的MaskBlock

MaskNet模型

DBMTL

PPNet

參數(shù)個(gè)性化網(wǎng)絡(luò)

PPNet模型

PPNet

4. 序列特征編碼組件

類(lèi)名

功能

說(shuō)明

示例

DIN

target attention

DIN模型的組件

DIN_backbone.config

BST

transformer

BST模型的組件

BST_backbone.config

SeqAugment

序列數(shù)據(jù)增強(qiáng)

crop, mask, reorder

CL4SRec

5. 多目標(biāo)學(xué)習(xí)組件

類(lèi)名

功能

說(shuō)明

示例

MMoE

Multiple Mixture of Experts

MMoE模型的組件

案例8

6. 輔助損失函數(shù)組件

類(lèi)名

功能

說(shuō)明

示例

AuxiliaryLoss

用來(lái)計(jì)算輔助損失函數(shù)

常用在自監(jiān)督學(xué)習(xí)中

案例7

各組件的詳細(xì)參數(shù)請(qǐng)查看“組件詳細(xì)參數(shù)”。

如何自定義組件

easy_rec/python/layers/keras目錄下新建一個(gè)py文件,也可直接添加到一個(gè)已有的文件中。我們建議目標(biāo)類(lèi)似的組件定義在同一個(gè)文件中,減少文件數(shù)量;比如特征交叉的組件都放在interaction.py里。

定義一個(gè)繼承tf.keras.layers.Layer的組件類(lèi),至少實(shí)現(xiàn)兩個(gè)方法:__init__、call

def __init__(self, params, name='xxx', reuse=None, **kwargs):
 pass
def call(self, inputs, training=None, **kwargs):
 pass

__init__方法的第一個(gè)參數(shù)params接收框架傳遞給當(dāng)前組件的參數(shù)。支持兩種參數(shù)配置的方式:google.protobuf.Struct、自定義的protobuf message對(duì)象。params對(duì)象封裝了對(duì)這兩種格式的參數(shù)的統(tǒng)一讀取接口,如下:

  • 檢查必傳參數(shù),缺失時(shí)報(bào)錯(cuò)退出:params.check_required(['embedding_dim', 'sigma'])

  • 用點(diǎn)操作符讀取參數(shù):sigma = params.sigma;支持連續(xù)點(diǎn)操作符,如params.a.b

  • 注意數(shù)值型參數(shù)的類(lèi)型,Struct只支持float類(lèi)型,整型需要強(qiáng)制轉(zhuǎn)換:embedding_dim = int(params.embedding_dim)

  • 數(shù)組類(lèi)型也需要強(qiáng)制類(lèi)型轉(zhuǎn)換:units = list(params.hidden_units)

  • 指定默認(rèn)值讀取,返回值會(huì)被強(qiáng)制轉(zhuǎn)換為與默認(rèn)值同類(lèi)型:activation = params.get_or_default('activation', 'relu')

  • 支持嵌套子結(jié)構(gòu)的默認(rèn)值讀?。?code data-tag="code" code-type="xCode" class="code">params.field.get_or_default('key', def_val)

  • 判斷某個(gè)參數(shù)是否存在:params.has_field(key)

  • 【不建議,會(huì)限定傳參方式】獲取自定義的proto對(duì)象:params.get_pb_config()

  • 讀寫(xiě)l2_regularizer屬性:params.l2_regularizer,傳給Dense層或dense函數(shù)。

【可選】如需要自定義protobuf message參數(shù),先在easy_rec/python/protos/layer.proto添加參數(shù)message的定義, 再把參數(shù)注冊(cè)到定義在easy_rec/python/protos/keras_layer.protoKerasLayer.params消息體中。

__init__方法的reuse參數(shù)表示該Layer對(duì)象的權(quán)重參數(shù)是否需要被復(fù)用。開(kāi)發(fā)時(shí)需要按照可復(fù)用的邏輯來(lái)實(shí)現(xiàn)Layer對(duì)象,推薦嚴(yán)格按照keras layer的規(guī)范來(lái)實(shí)現(xiàn)。 盡量在__init__方法中聲明需要依賴(lài)的keras layer對(duì)象;僅在必要時(shí)才使用tf.layers.*函數(shù),且需要傳遞reuse參數(shù)。

提示:實(shí)現(xiàn)Layer對(duì)象時(shí)盡量使用原生的 tf.keras.layers.* 對(duì)象,且全部在 __init__ 方法中預(yù)先聲明好。

call方法用來(lái)實(shí)現(xiàn)主要的模塊邏輯,其inputs參數(shù)可以是一個(gè)tenor,或者是一個(gè)tensor列表??蛇x的training參數(shù)用來(lái)標(biāo)識(shí)當(dāng)前是否是訓(xùn)練模型。

最后也是最重要的一點(diǎn),新開(kāi)發(fā)的Layer需要在easy_rec.python.layers.keras.__init__.py文件中導(dǎo)出才能被框架識(shí)別為組件庫(kù)中的一員。例如要導(dǎo)出blocks.py文件中的MLP類(lèi),則需要添加:from .blocks import MLP

FM layer的代碼示例:

class FM(tf.keras.layers.Layer):
 """Factorization Machine models pairwise (order-2) feature interactions without linear term and bias.

 References
 - [Factorization Machines](https://www.csie.ntu.edu.tw/~b97053/paper/Rendle2010FM.pdf)
 Input shape.
 - List of 2D tensor with shape: ``(batch_size,embedding_size)``.
 - Or a 3D tensor with shape: ``(batch_size,field_size,embedding_size)``
 Output shape
 - 2D tensor with shape: ``(batch_size, 1)``.
 """

 def __init__(self, params, name='fm', reuse=None, **kwargs):
 super(FM, self).__init__(name, **kwargs)
 self.reuse = reuse
 self.use_variant = params.get_or_default('use_variant', False)

 def call(self, inputs, **kwargs):
 if type(inputs) == list:
 emb_dims = set(map(lambda x: int(x.shape[-1]), inputs))
 if len(emb_dims) != 1:
 dims = ','.join([str(d) for d in emb_dims])
 raise ValueError('all embedding dim must be equal in FM layer:' + dims)
 with tf.name_scope(self.name):
 fea = tf.stack(inputs, axis=1)
 else:
 assert inputs.shape.ndims == 3, 'input of FM layer must be a 3D tensor or a list of 2D tensors'
 fea = inputs

 with tf.name_scope(self.name):
 square_of_sum = tf.square(tf.reduce_sum(fea, axis=1))
 sum_of_square = tf.reduce_sum(tf.square(fea), axis=1)
 cross_term = tf.subtract(square_of_sum, sum_of_square)
 if self.use_variant:
 cross_term = 0.5 * cross_term
 else:
 cross_term = 0.5 * tf.reduce_sum(cross_term, axis=-1, keepdims=True)
 return cross_term

如何搭建模型

組件塊組件包是搭建主干網(wǎng)絡(luò)的核心部件,本小節(jié)將會(huì)介紹組件塊的類(lèi)型、功能和配置參數(shù);同時(shí)還會(huì)介紹專(zhuān)門(mén)為參數(shù)共享子網(wǎng)絡(luò)設(shè)計(jì)的組件包。

通過(guò)組件塊組件包搭建模型的配置方法請(qǐng)參考上文描述的各個(gè)案例。

組件塊的protobuf定義如下:

message Block {
 required string name = 1;
 // the input names of feature groups or other blocks
 repeated Input inputs = 2;
 optional int32 input_concat_axis = 3 [default = -1];
 optional bool merge_inputs_into_list = 4;
 optional string extra_input_fn = 5;

 // sequential layers
 repeated Layer layers = 6;
 // only take effect when there are no layers
 oneof layer {
 InputLayer input_layer = 101;
 Lambda lambda = 102;
 KerasLayer keras_layer = 103;
 RecurrentLayer recurrent = 104;
 RepeatLayer repeat = 105;
 }
}

組件塊會(huì)自動(dòng)合并多個(gè)輸入:

  1. 若多路輸入中某一路的輸入類(lèi)型是list,則最終結(jié)果被Merge成一個(gè)大的list,保持順序不變;

  2. 若多路輸入中的每一路輸入都是tensor,默認(rèn)是執(zhí)行輸入tensors按照最后一個(gè)維度做拼接(concat),以下配置項(xiàng)可以改變默認(rèn)行為:

  • input_concat_axis用來(lái)指定輸入tensors拼接的維度

  • merge_inputs_into_list設(shè)為true,則把輸入合并到一個(gè)列表里,不做concat操作

message Input {
 oneof name {
 string feature_group_name = 1;
 string block_name = 2;
 string package_name = 3;
 }
 optional string input_fn = 11;
 optional string input_slice = 12;
}
  • 每一路輸入可以配置一個(gè)可選的input_fn,指定一個(gè)lambda函數(shù)對(duì)輸入做一些簡(jiǎn)單的變換。比如配置input_fn: 'lambda x: [x]'可以把輸入變成列表格式。

  • input_slice可以用來(lái)獲取輸入元組/列表的某個(gè)切片。比如,當(dāng)某路輸入是一個(gè)列表對(duì)象是,可以用input_slice: '[1]'配置項(xiàng)獲取列表的第二個(gè)元素值作為這一路的輸入。

  • extra_input_fn是一個(gè)可選的配置項(xiàng),用來(lái)對(duì)合并后的多路輸入結(jié)果做一些額外的變換,需要配置成lambda函數(shù)的格式。

目前總共有7種類(lèi)型的組件塊,分別是空組件塊、輸入組件塊Lambda組件塊、KerasLayer組件塊、循環(huán)組件塊重復(fù)組件塊、序列組件塊

1. 空組件塊

當(dāng)一個(gè)block不配置任何layer時(shí)就稱(chēng)之為空組件塊,空組件塊只執(zhí)行多路輸入的Merge操作。

2. 輸入組件塊

輸入組件塊關(guān)聯(lián)一個(gè)input_layer,獲取、加工并返回原始的特征輸入。

輸入組件塊比較特殊,它只能有且只有一路輸入,并且只能用feature_group_name項(xiàng)配置輸入為一個(gè)feature_groupname。

輸入組件塊有一個(gè)特權(quán):它的名字可以與其輸入的feature_group同名。其他組件塊則無(wú)此殊榮。

配置示例:

blocks {
 name: 'all'
 inputs {
 feature_group_name: 'all'
 }
 input_layer {
 only_output_feature_list: true
 }
}

InputLayer可以通過(guò)配置獲取不同格式的輸入,并且可以執(zhí)行一些如dropout之類(lèi)的額外操作,其參數(shù)定義的protobuf如下:

message InputLayer {
 optional bool do_batch_norm = 1;
 optional bool do_layer_norm = 2;
 optional float dropout_rate = 3;
 optional float feature_dropout_rate = 4;
 optional bool only_output_feature_list = 5;
 optional bool only_output_3d_tensor = 6;
 optional bool output_2d_tensor_and_feature_list = 7;
 optional bool output_seq_and_normal_feature = 8;
}

輸入層的定義如上,配置下說(shuō)明如下:

  • do_batch_norm是否對(duì)輸入特征做batch normalization

  • do_layer_norm是否對(duì)輸入特征做layer normalization

  • dropout_rate輸入層執(zhí)行dropout的概率,默認(rèn)不執(zhí)行dropout

  • feature_dropout_rate對(duì)特征整體執(zhí)行dropout的概率,默認(rèn)不執(zhí)行

  • only_output_feature_list輸出list格式的各個(gè)特征

  • only_output_3d_tensor輸出feature group對(duì)應(yīng)的一個(gè)3d tensor,在embedding_dim相同時(shí)可配置該項(xiàng)

  • output_2d_tensor_and_feature_list是否同時(shí)輸出2d tensor與特征list

  • output_seq_and_normal_feature是否輸出(sequence特征, 常規(guī)特征)元組

3. Lambda組件塊

Lambda組件塊可以配置一個(gè)lambda函數(shù),執(zhí)行一些較簡(jiǎn)單的操作。示例如下:

blocks {
 name: 'wide_logit'
 inputs {
 feature_group_name: 'wide'
 }
 lambda {
 expression: 'lambda x: tf.reduce_sum(x, axis=1, keepdims=True)'
 }
}

4. KerasLayer組件塊

KerasLayer組件塊是最核心的組件塊,負(fù)責(zé)加載、執(zhí)行組件代碼邏輯。

  • class_name是要加載的Keras Layer的類(lèi)名,支持加載自定義的類(lèi)和系統(tǒng)內(nèi)置的Layer類(lèi)。

  • st_params是以google.protobuf.Struct對(duì)象格式配置的參數(shù);

  • 還可以用自定義的protobuf message的格式傳遞參數(shù)給加載的Layer對(duì)象。

配置示例:

keras_layer {
 class_name: 'MLP'
 mlp {
 hidden_units: [64, 32, 16]
 }
}

keras_layer {
 class_name: 'Dropout'
 st_params {
 fields {
 key: 'rate'
 value: { number_value: 0.5 }
 }
 }
}

5. 循環(huán)組件塊

循環(huán)組件塊可以實(shí)現(xiàn)類(lèi)似RNN的循環(huán)調(diào)用結(jié)構(gòu),可以執(zhí)行某個(gè)Layer多次,每次執(zhí)行的輸入包含了上一次執(zhí)行的輸出。在DCN網(wǎng)絡(luò)中有循環(huán)組件塊的示例,如下:

recurrent {
 num_steps: 3
 fixed_input_index: 0
 keras_layer {
 class_name: 'Cross'
 }
}

上述配置對(duì)CrossLayer循環(huán)調(diào)用了3次,邏輯上等價(jià)于執(zhí)行如下語(yǔ)句:

x1 = Cross()(x0, x0)
x2 = Cross()(x0, x1)
x3 = Cross()(x0, x2)
  • num_steps配置循環(huán)執(zhí)行的次數(shù)

  • fixed_input_index配置每次執(zhí)行的多路輸入組成的列表中固定不變的元素;比如上述示例中的x0

  • keras_layer配置需要執(zhí)行的組件

6. 重復(fù)組件塊

重復(fù)組件塊可以使用相同的輸入重復(fù)執(zhí)行某個(gè)組件多次,實(shí)現(xiàn)multi-head的邏輯。示例如下:

repeat {
 num_repeat: 2
 keras_layer {
 class_name: "MaskBlock"
 mask_block {
 output_size: 512
 aggregation_size: 2048
 input_layer_norm: false
 }
 }
}
  • num_repeat配置重復(fù)執(zhí)行的次數(shù)

  • output_concat_axis配置多次執(zhí)行結(jié)果tensors的拼接維度,若不配置則輸出多次執(zhí)行結(jié)果的列表

  • keras_layer配置需要執(zhí)行的組件

  • input_slice配置每個(gè)執(zhí)行組件的輸入切片,例如[i]獲取輸入列表的第 i 個(gè)元素作為第 i 次重復(fù)執(zhí)行時(shí)的輸入;不配置時(shí)獲取所有輸入

  • input_fn配置每個(gè)執(zhí)行組件的輸入函數(shù),例如input_fn: "lambda x, i: [x[0][i], x[1]]"

重復(fù)組件塊的使用案例MaskNet+PPNet+MMoE

7. 序列組件塊

序列組件塊可以依次執(zhí)行配置的多個(gè)Layer,前一個(gè)Layer的輸出是后一個(gè)Layer的輸入。序列組件塊相對(duì)于配置多個(gè)首尾相連的普通組件塊要更加簡(jiǎn)單。示例如下:

blocks {
 name: 'mlp'
 inputs {
 feature_group_name: 'features'
 }
 layers {
 keras_layer {
 class_name: 'Dense'
 st_params {
 fields {
 key: 'units'
 value: { number_value: 256 }
 }

 fields {
 key: 'activation'
 value: { string_value: 'relu' }
 }
 }
 }
 }
 layers {
 keras_layer {
 class_name: 'Dropout'
 st_params {
 fields {
 key: 'rate'
 value: { number_value: 0.5 }
 }
 }
 }
 }
 layers {
 keras_layer {
 class_name: 'Dense'
 st_params {
 fields {
 key: 'units'
 value: { number_value: 1 }
 }
 }
 }
 }
}

通過(guò)組件包實(shí)現(xiàn)參數(shù)共享的子網(wǎng)絡(luò)

組件包封裝了由多個(gè)組件塊搭建的一個(gè)子網(wǎng)絡(luò)DAG,作為整體可以被以參數(shù)共享的方式多次調(diào)用,通常用在自監(jiān)督學(xué)習(xí)模型中。

組件包的protobuf消息定義如下:

message BlockPackage {
 // package name
 required string name = 1;
 // a few blocks generating a DAG
 repeated Block blocks = 2;
 // the names of output blocks
 repeated string concat_blocks = 3;
}

組件塊通過(guò)package_name參數(shù)配置一路輸入來(lái)調(diào)用組件包。

一個(gè)使用組件包來(lái)實(shí)現(xiàn)對(duì)比學(xué)習(xí)的案例如下:

model_config {
 model_class: "RankModel"
 feature_groups {
 group_name: "all"
 feature_names: "adgroup_id"
 feature_names: "user"
 ...
 feature_names: "pid"
 wide_deep: DEEP
 }

 backbone {
 packages {
 name: 'feature_encoder'
 blocks {
 name: "fea_dropout"
 inputs {
 feature_group_name: "all"
 }
 input_layer {
 dropout_rate: 0.5
 only_output_3d_tensor: true
 }
 }
 blocks {
 name: "encode"
 inputs {
 block_name: "fea_dropout"
 }
 layers {
 keras_layer {
 class_name: 'BSTCTR'
 bst {
 hidden_size: 128
 num_attention_heads: 4
 num_hidden_layers: 3
 intermediate_size: 128
 hidden_act: 'gelu'
 max_position_embeddings: 50
 hidden_dropout_prob: 0.1
 attention_probs_dropout_prob: 0
 }
 }
 }
 layers {
 keras_layer {
 class_name: 'Dense'
 st_params {
 fields {
 key: 'units'
 value: { number_value: 128 }
 }
 fields {
 key: 'kernel_initializer'
 value: { string_value: 'zeros' }
 }
 }
 }
 }
 }
 }
 blocks {
 name: "all"
 inputs {
 name: "all"
 }
 input_layer {
 only_output_3d_tensor: true
 }
 }
 blocks {
 name: "loss_ctr"
 merge_inputs_into_list: true
 inputs {
 package_name: 'feature_encoder'
 }
 inputs {
 package_name: 'feature_encoder'
 }
 inputs {
 package_name: 'all'
 }
 keras_layer {
 class_name: 'LOSSCTR'
 st_params{
 fields {
 key: 'cl_weight'
 value: { number_value: 1 }
 }
 fields {
 key: 'au_weight'
 value: { number_value: 0.01 }
 }
 }
 }
 }
 }
 model_params {
 l2_regularization: 1e-5
 }
 embedding_regularization: 1e-5
}

真實(shí)案例

在工業(yè)級(jí)推薦系統(tǒng)中,物品獲得的用戶(hù)反饋行為通常遵循長(zhǎng)尾分布,少量頭部物品獲得了絕大部分的用戶(hù)行為(點(diǎn)擊、收藏、轉(zhuǎn)化等),剩余的大量中長(zhǎng)尾物品獲得的反饋數(shù)據(jù)卻很少。基于長(zhǎng)尾分布的用戶(hù)行為日志訓(xùn)練的推薦模型會(huì)越來(lái)越偏好頭部物品,在導(dǎo)致“富者越富”的同時(shí)傷害中長(zhǎng)尾物品的曝光機(jī)會(huì)和用戶(hù)滿(mǎn)意度。

我們?cè)跇I(yè)務(wù)效果優(yōu)化的過(guò)程中,觀(guān)察到如下現(xiàn)象:

  1. 召回?cái)U(kuò)量(增加召回?cái)?shù)量或新的召回類(lèi)型)很多時(shí)候不能帶來(lái)總體大盤(pán)指標(biāo)的提升;

  2. 召回結(jié)果過(guò)濾掉“精品池”之外的物品通常能夠帶來(lái)效果指標(biāo)的提升;

  3. 添加粗排模型,粗排覆蓋率指標(biāo)提升,但不一定能帶來(lái)總體業(yè)務(wù)指標(biāo)的提升;

本質(zhì)上,越能夠保持“獨(dú)立同分布”假設(shè)的優(yōu)化越能夠帶來(lái)大盤(pán)指標(biāo)的提升,而越偏離“獨(dú)立同分布”假設(shè)的優(yōu)化通常都不能帶來(lái)理想的效果。這里的“獨(dú)立同分布”假設(shè)是指精排模型的訓(xùn)練集數(shù)據(jù)和測(cè)試集數(shù)據(jù)(通常是生產(chǎn)環(huán)境獲得的截?cái)嗪蟮恼倩鼗虼峙沤Y(jié)果)應(yīng)遵循同一數(shù)據(jù)分布。精排模型是在高度傾斜的長(zhǎng)尾行為數(shù)據(jù)上訓(xùn)練出來(lái)的,因而會(huì)在頭部物品上產(chǎn)生“過(guò)擬合”現(xiàn)象,而在中長(zhǎng)尾物品上產(chǎn)生“欠擬合”現(xiàn)象。

  • “精品池”過(guò)濾進(jìn)一步強(qiáng)化了召回的物品滿(mǎn)足行為的長(zhǎng)尾分布,匹配精排模型的“獨(dú)立同分布”要求,最終也帶來(lái)了業(yè)務(wù)指標(biāo)的提升;

  • 召回?cái)U(kuò)量、添加粗排模型是在讓長(zhǎng)尾分布變得平滑,試圖增加中長(zhǎng)尾物品的數(shù)量,偏離了精排模型的“獨(dú)立同分布”要求,最終往往無(wú)法達(dá)成期望的效果提升。

通過(guò)分析精排模型的特征重要度,我們發(fā)現(xiàn)重要度較高的特征主要集中在少量的“記憶性”特征上,而大量的中長(zhǎng)尾特征的重要度都很低?!坝洃浶浴碧卣髦傅氖菦](méi)有泛化能力的特征,如物品ID、用戶(hù)對(duì)物品ID在過(guò)去一段時(shí)間上的行為統(tǒng)計(jì),在這些特征上無(wú)法學(xué)到能夠遷移到其他物品的知識(shí)。常規(guī)的模型結(jié)構(gòu)會(huì)產(chǎn)生特征重要度的長(zhǎng)尾分布,最終帶來(lái)了模型偏好物品的長(zhǎng)尾分布。

基于以上分析,亟需設(shè)計(jì)一種更加合理的模型結(jié)構(gòu),讓模型能夠在“記憶”能力之外學(xué)習(xí)到更多“泛化”能力。Cross Decoupling Network (CDN) 為上述問(wèn)題提出了一個(gè)可行的解決方案,它引入一個(gè)基于物品分布的門(mén)控機(jī)制,讓頭部的物品主要擬合“記憶特征”,中長(zhǎng)尾物品主要擬合“泛化特征”。通過(guò)加權(quán)求和的方式在各個(gè)特征上學(xué)習(xí)到的表征特征,再去擬合最終的業(yè)務(wù)目標(biāo)。

我們?cè)谝粋€(gè)真實(shí)的業(yè)務(wù)場(chǎng)景設(shè)計(jì)了如下圖的模型結(jié)構(gòu),并基于組件化EasyRec輕松搭建了模型。

image.png

該案例的配置請(qǐng)查看文檔:基于組件化EasyRec搭建深度推薦算法模型

組件化EasyRec詳細(xì)使用文檔:https://easyrec.readthedocs.io/en/latest/component/backbone.html