DNN은 모든 변수의 관계(feature interaction)를 자동으로 학습한다. 이 과정은 모델 내부적으로, implicit하게 이루어지기 때문에 종종 의미없는 interaction을 학습하여 성능이 하락하기도 한다. DCN
은 implicit한 모델을 explicit하게 표현하여, 제한된 차원의 변수 관계(bounded-degree feature interaction)를 학습한다.
모델의 인풋(input) 는 카테고리인 필드(field)를 임베딩(embedding) 한 벡터 와 수치형 변수(numerical feature) 벡터인 로 이루어져 있다.
Deep&Cross Network
는 Cross network
와 Deep network
두 파트로 이루어져 있다. 둘은 같은 인풋을 공유하며 마지막에 두 아웃풋(output)을 결합(stack)하여 예측 확률을 반환한다. 모델의 구조는 아래 그림과 같으며 우측의 Deep network는 기본적인 DNN으로 다루지 않는다.
Cross network
Cross network 과정을 나타내는 수식은 다음과 같다.
residual connection
)※ CTR 시리즈의 모든 코드는
FuxiCTR
을 참고했으며 함수 구조와 이름 등은 개인적으로 수정하여 사용하였다.
feature_dict
은 각 변수의 설명이 들어있는 dict()이다. (ex, Dict[str, Dict[str, int]]) EmbeddingDict()
은 각 필드의 임베딩 벡터(embedding vector)를 반환하는 ModuleDict()
으로 아웃풋은 [배치(batch) 크기, 필드 수, 임베딩 크기] 형태이다.
일단 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
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
CrossNet
을 DNN
과 결합하여 최종 모델을 구성한다.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