class_docstr: FDSA is similar with the GRU4RecF implemented in RecBole, which uses two different Transformer encoders to
  encode items and features respectively and concatenates the two subparts' outputs as the final output.
class_docstr_tok: ["FDSA","is","similar","with","the","GRU4RecF","implemented","in","RecBole",",","which","uses","two","different","Transformer","encoders","to","encode","items","and","features","respectively","and","concatenates","the","two","subparts","'","outputs","as","the","final","output","."]
code: def forward(self, item_seq, item_seq_len):
      item_emb = self.item_embedding(item_seq)
  
      position_ids = torch.arange(
          item_seq.size(1), dtype=torch.long, device=item_seq.device
      )
      position_ids = position_ids.unsqueeze(0).expand_as(item_seq)
      position_embedding = self.position_embedding(position_ids)
  
      # get item_trm_input
      # item position add position embedding
      item_emb = item_emb + position_embedding
      item_emb = self.LayerNorm(item_emb)
      item_trm_input = self.dropout(item_emb)
  
      sparse_embedding, dense_embedding = self.feature_embed_layer(
          None, item_seq
      )
      sparse_embedding = sparse_embedding["item"]
      dense_embedding = dense_embedding["item"]
  
      # concat the sparse embedding and float embedding
      feature_table = []
      if sparse_embedding is not None:
          feature_table.append(sparse_embedding)
      if dense_embedding is not None:
          feature_table.append(dense_embedding)
  
      # [batch len num_features hidden_size]
      feature_table = torch.cat(feature_table, dim=-2)
  
      # feature_emb [batch len hidden]
      # weight [batch len num_features]
      # if only one feature, the weight would be 1.0
      feature_emb, attn_weight = self.feature_att_layer(feature_table)
      # feature position add position embedding
      feature_emb = feature_emb + position_embedding
      feature_emb = self.LayerNorm(feature_emb)
      feature_trm_input = self.dropout(feature_emb)
  
      extended_attention_mask = self.get_attention_mask(item_seq)
  
      item_trm_output = self.item_trm_encoder(
          item_trm_input,
          extended_attention_mask,
          output_all_encoded_layers=True,
      )
      item_output = item_trm_output[-1]
  
      feature_trm_output = self.feature_trm_encoder(
          feature_trm_input,
          extended_attention_mask,
          output_all_encoded_layers=True,
      )  # [B Len H]
      feature_output = feature_trm_output[-1]
  
      item_output = self.gather_indexes(
          item_output, item_seq_len - 1
      )  # [B H]
      feature_output = self.gather_indexes(
          feature_output, item_seq_len - 1
      )  # [B H]
  
      output_concat = torch.cat(
          (item_output, feature_output), -1
      )  # [B 2*H]
      output = self.concat_layer(output_concat)
      output = self.LayerNorm(output)
      seq_output = self.dropout(output)
      return seq_output
  
file_path: RecBole/recbole/model/sequential_recommender/fdsa.py
imports: import torch
  from torch import nn
  from recbole.model.abstract_recommender import SequentialRecommender
  from recbole.model.layers import TransformerEncoder, FeatureSeqEmbLayer, VanillaAttention
  from recbole.model.loss import BPRLoss
label_desc: Use image node_id 3 for calling the FDSA obj's underlying member method code with example usage: obj.forward(item_seq, item_seq_len) and returns: seq_output