[DCN] Deep&Cross Network

KIDA·2023년 2월 2일
0

CTR

목록 보기
6/8

Intro

DNN은 모든 변수의 관계(feature interaction)를 자동으로 학습한다. 이 과정은 모델 내부적으로, implicit하게 이루어지기 때문에 종종 의미없는 interaction을 학습하여 성능이 하락하기도 한다. DCN은 implicit한 모델을 explicit하게 표현하여, 제한된 차원의 변수 관계(bounded-degree feature interaction)를 학습한다.

Data

모델의 인풋(input) x0\mathbf{x_0}는 카테고리인 필드(field)를 임베딩(embedding) 한 벡터 xembed,iT\mathbf{x}_{embed, i}^{T}와 수치형 변수(numerical feature) 벡터인 xdenseT\mathbf{x}_{dense}^{T}로 이루어져 있다.

x0=[xembed,1T,...,xembed,kT,xdenseT],\mathbf{x_0} = [\mathbf{x}_{embed, 1}^{T}, ..., \mathbf{x}_{embed, k}^{T}, \mathbf{x}_{dense}^{T}],

Model

Deep&Cross NetworkCross networkDeep network 두 파트로 이루어져 있다. 둘은 같은 인풋을 공유하며 마지막에 두 아웃풋(output)을 결합(stack)하여 예측 확률을 반환한다. 모델의 구조는 아래 그림과 같으며 우측의 Deep network는 기본적인 DNN으로 다루지 않는다.

Cross network
Cross network 과정을 나타내는 수식은 다음과 같다.

  1. x0xlT\mathbf{x}_0\mathbf{x}_l^T를 통해 임베딩의 각 요소별 관계(interaction element)를 계산한다. 이 과정은 (임베딩 크기 X 임베딩 크기) 행렬을 반환하며, 여기서 차원이 단계적으로 상승하기 때문에 bounded-degree feature interaction를 갖게된다.
  2. wl\mathbf{w_l}로 interaction element x0xlT\mathbf{x}_0\mathbf{x}_l^T에 가중치(weight)를 주고 bias bl\mathbf{b_l}을 더해준다,
  3. xl\mathbf{x_l}를 더한다. (residual connection)

Note

  1. 수치형 변수는 log 변환을 하고, 카테고리 변수는 임베딩 크기를 6(cardinality)1/46*(cardinality)^{1/4}로 사용하였다. (여기서 cardinalitycardinality는 각 field 별 feature의 수를 의미한다.)
  2. Deep 파트와 Cross 파트에서 인풋값을 공유하기 때문에 모델을 효율적으로 학습할 수 있으며 feature interaction에 대해 더 일반화된(generalized) 성능을 보이고 noise에도 덜 민감하게(robust) 된다.

Code

CTR 시리즈의 모든 코드는 FuxiCTR을 참고했으며 함수 구조와 이름 등은 개인적으로 수정하여 사용하였다.

  1. feature_dict은 각 변수의 설명이 들어있는 dict()이다. (ex, Dict[str, Dict[str, int]]) EmbeddingDict()은 각 필드의 임베딩 벡터(embedding vector)를 반환하는 ModuleDict()으로 아웃풋은 [배치(batch) 크기, 필드 수, 임베딩 크기] 형태이다.

  2. 일단 CrossNetLayer 클래스로 1-layer cross network를 정의한다. 아래 코드는 Cross network의 수식을 의미한다. (residual connection 제외)

class CrossNetLayer(nn.Module):
    def __init__(self, input_dim):
        super(CrossNetLayer, self).__init__()
        self.weight = nn.Linear(input_dim, 1, bias=False)
        self.bias = nn.Parameter(torch.zeros(input_dim))
    
    def forward(self, X_0, X_i):
        return self.weight(X_i) * X_0 + self.bias
  1. n개의 CrossNetLayer를 쌓아 CrossNet을 정의한다. 여기서 residual connection을 포함한다.
class CrossNet(nn.Module):
    def __init__(self, num_layers, input_dim):
        super(CrossNet, self).__init__()
        self.num_layers = num_layers
        self.crossnet = nn.ModuleList(
            CrossNetLayer(input_dim) for _ in range(self.num_layers))
        
    def forward(self, X_0):
        X_i = X_0
        for i in range(self.num_layers):
            X_i = X_i + self.crossnet[i](X_0, X_i)
        return X_i
  1. CrossNetDNN과 결합하여 최종 모델을 구성한다.
class DCN(BaseModel):
    def __init__(self, feature_dict, num_layers, hidden_dim_list, embed_dim=CFG.embed_dim):
        super(DCN, self).__init__()
        self._input_dim = len(feature_dict) * embed_dim
        self.embedding = EmbeddingDict(feature_dict=feature_dict)
        self.crossnet = CrossNet(num_layers=num_layers, input_dim=self._input_dim)
        self.dnn = DNNLayer(input_dim=self._input_dim, hidden_dim_list=hidden_dim_list)
        self.linear = nn.Linear(self._input_dim + 1, 1)  # dnn output size = 1
		
        # training method
        # self.compile(CFG.optimizer, CFG.loss, CFG.learning_rate)
        # self.init_params()
        # self.model_to_device()

    def forward(self, inputs):
        X, y = self.inputs_to_device(inputs)
        X_emb = self.embedding(X).flatten(start_dim=1)
        crossnet_out = self.crossnet(X_emb)
        dnn_out = self.dnn(X_emb)
        concat_out = torch.cat([crossnet_out, dnn_out], dim=1)
        y_pred = self.linear(concat_out) 
        return {'y_true': y, 'y_pred': y_pred}

Deep & Cross Network for Ad Click Predictions
FuxiCTR Github

profile
까먹지 않기 위한 노트 (ว˙∇˙)ง

0개의 댓글