"""
    1- 编码器 由 6层编码器层 组成
    2- 每个 编码器层由如下的结构组成：
        2.1- 第一个子层：多头自注意力子层
            多头自注意力 + 层归一化 + 残差连接
        2.2- 第二个子层：前馈网络子层
            前馈网络 + 层归一化 + 残差连接
"""

from demo02_component import *

# 编码器层
class EncoderLayer(nn.Module):
    def __init__(self,d_model,multi_head_self_attn,feed_forward,dropout_p=0.1):
        """
        定义编码器层的结构
        :param d_model: 词向量维度
        :param multi_head_self_attn: 多头自注意力类的实例对象
        :param feed_forward: 前馈网络类的实例对象
        :param dropout_p:
        """
        # 1- 初始化父类
        super().__init__()

        # 2- 设置属性值
        self.d_model = d_model
        self.dropout_p = dropout_p
        self.multi_head_self_attn = multi_head_self_attn
        self.feed_forward = feed_forward

        # 3- 创建子层
        # 3.1- 第一个子层：多头自注意力子层
        self.multi_layer = SubLayerConnection(self.d_model,self.dropout_p)

        # 3.2- 第二个子层：前馈网络子层
        self.feed_forward_layer = SubLayerConnection(self.d_model,self.dropout_p)

    def forward(self,data):
        # 1- 数据经过第一个层的处理：多头自注意力子层
        multi_output = self.multi_layer(data, lambda x: self.multi_head_self_attn(query=x, key=x, value=x, mask=None))
        # 2- 数据经过第二个层的处理：前馈网络子层
        encoder_output = self.feed_forward_layer(multi_output, lambda x: self.feed_forward(x))

        return encoder_output

# 编码器
class Encoder(nn.Module):
    def __init__(self, encoder_layer:EncoderLayer, N):
        """
        定义编码器结构。也就是将编码器层重复N层
        :param encoder_layer: 编码器层
        :param N:
        """
        # 1- 初始化父类
        super().__init__()

        # 2- 将编码器层创建N对象
        self.encoder_layer_list = clones(encoder_layer,N)

        # 3- 层归一化
        """
            这东西不是必须的，只是为了缓解经过N层处理后的数据，让数据变得更加平稳。这一层不是必须的
        """
        self.layer_norm = LayerNorm(encoder_layer.d_model)

    def forward(self,data):

        """
            注意：for循环中两个data需要与传递给forward的参数名称完全一致，否则6层网络处理的数据就会断开
        """
        for encoder_layer in self.encoder_layer_list:
            data = encoder_layer(data)

        return self.layer_norm(data)

# 测试编码器
# 测试最终的编码器
def use_encoder():
    # 1- 得到词向量数据：词向量 + 位置编码
    position_data = use_positional_encoding()

    d_model = 512
    dropout = 0.1

    # 2- 创建编码器的实例对象
    # 2.1- 多头自注意力子层的实例对象
    multi_head = MultiHeadAttention(d_model=d_model,head=8,dropout_p=dropout)
    # 2.2- 前馈网络子层的实例对象
    ff = FeedForward(d_model=d_model,output_dim=1024,dropout_p=dropout)
    # 2.3- 编码器层的实例对象
    encoder_layder = EncoderLayer(d_model=d_model,multi_head_self_attn=multi_head,feed_forward=ff,dropout_p=dropout)
    # 2.4- 编码器层 堆叠N层得到编码器
    encoder = Encoder(encoder_layder,6)

    # 3- 数据进行前向传播
    result = encoder(data=position_data)

    print(f"编码器最终的输出结果是：{result.shape}")
    return result

if __name__ == '__main__':
    use_encoder()



