当前位置:首页>学习笔记>transformer学习笔记(零代码实现)

transformer学习笔记(零代码实现)

  • 2026-02-01 06:20:09
transformer学习笔记(零代码实现)

最近一段时间上下班路上除了背单词,就是看视频听各位博主讲解 transformer 的原理及奇妙之处

起因是大模型越用越上瘾,越用越有用,越有用就越好奇,这个东西究竟是怎么回事,为啥它知道那么多【知识】这些【知识】又是怎么存储下来的,看了一段时间后虽然对 Q、K、V 有了概念,对 MultiHeadAttention 有了了解,但总感觉差点意思,所以准备实际操作一下

早实际操作的时候,不太想像之前学习那些课程一样,从原始的手撕环境、硬磕代码开始,所以选择了 Trae 这个 IDE,尝试一下【是不是大模型在让我更好的理解大模型】上具备优秀的潜质,短短半个小时(从周六的11点到11点30)就搞定了代码,训练了一个小时就能运行了......虽然因为各种精简(比如只读取1000行比如减少嵌入维度比如减少编码层数等等)词生词预测上差异较大

但是这只是开始,从能预测出第一个字,就可以开始连环追问大法了,对着代码对着参数对着实时修改带来的模型结果,连环提问

这种方式学习速度可太快太刺激了,看起来真的可以从高中生选取人才,只要算力足够,毕竟已经快到【有问题就有答案】的时代了,VLM我也得摸一下,感觉更神奇更有趣

我用过的几个问题如下

1、给每一行代码写一段注释,要写的简洁明了,写完后你自己检查一下,不明朗的地方自己直接改,不要等我问你2、为什么transformer出来这么久了,最近三年才开始大放异彩,你有什么理由么,真的是这样么,你的理由是错的吧,正确的概率有多少3、它从训练素材里学到的知识,是存储在哪个矩阵里的,Q\K\V这三个矩阵,怎么通俗理解,有什么好的比喻么4、为什么最初你给我的不是BPE分词,而是最普通的方式,这两种方式差异是什么,优劣势是什么,真的是这样么,你听谁说的,检查一下,我觉得你说的不对5、嵌入维度是越高越好么,不对吧,这个参数跟样本量之间的关系是什么, 为啥要用128,而不是1280,通义、deepseek、豆包用的也是这一个数值么6、看到BPE结果了,为啥才这么少,你给我讲讲全球大模型BPE的词汇量都是多少,真的是这样么,你帮我调整一下参数,再验证训练结果,测试一下从10005000结果是什么样子,我觉得你说的不对,你在忽悠我7、位置编码为啥要用正弦和余弦,好像还有很多位置编码方式,给我介绍一下,同时说明不同的优劣势,咱们一个个试一下8、用更直白的方式,给我说一下这个网络结构,跟以前网络结构的差异是什么,大模型怎么做翻译的,咱们验证一个翻译模型吧, 你来写我来问,写不出来不能停止
准备步骤 /
下载并安装登录Traehttps://www.trae.cn/
下载训练素材:https://www.zxbooks.cc/book/2YryoYhttps://www.isyd.net/books/1468/下载后,将文件直接放置在新建的transformer目录下;

Prompt 如下

你需要训练一个Transformer模型,使用提供的训练素材(斗破苍穹.txt,凡人修仙传.txt),训练目标是验证集loss ≤ 0.1;如果训练loss > 0.1,就继续训练;如果loss下降速率到达平台区但依旧没有达成目标,你需要调整参数继续训练直到达到目标;在训练过程中,你需要实时显示训练进度和loss,并通过这个输出来判断编码是否结束,而不是通过CPU/GPU状态来判断训练是否进行,并且将用到的代码放到一个文件里,不要分成多个文件,同时记得保留或者及时更新最优的模型下来;分词的方式使用BPE(byte pair embedding), 分词耗时也比较长,记得实时显示进度,并通过进度判断状态,而不是通过CPUGPU状态之类的,代码统一放到同一个文件里,不要分成好几份,过程中遇到的所有问题包括软件安装等你都自己尝试解决,我这是台式机,有两张1080ti 所以你安装的工具要是GPU版本,来加速训练,明白了的话,就开始执行吧
这时候就能看到 Trae 开始尝试理解任务,并开始编码实现,编码过程都比较顺利,只是在它自己运行训练脚本的时候能明显看到它没有时间概念,会很急躁,epoch 要耗费将近 3 个小时,预期执行 50 个 epoch,Trae 会在三分钟左右的时候开始【慌张】
但其实就是单纯因为训练慢,而且Trae过于心急,就好像一个身高两米一的小朋友去种花一样,总是每隔两三天就拔起来看看长根了没有......这时候有必要在他代码写好后接管训练,手工执行
训练时间太长了,我看到loss下降到0.16时候停止了,依旧是让Trae 写一段测试代码,

Prompt 如下

创建了一个交互式文本生成验证脚本,该脚本包含完整的Transformer模型架构(包括位置编码、多头注意力机制、前馈神经网络等),最终通过测试脚本验证提示词"萧炎使用了"的生成功能,预期脚本能够成功加载训练好的模型并生成文本,如果没有成功你就持续优化脚本,直到成功,之后运行一下给我验证
测试一下:
附录代码 /
训练代码段:100%由trae实现
# -*- coding: utf-8 -*-"""完整训练脚本 - 包含BPE分词器、Transformer模型和训练循环"""import torch  # 导入PyTorch深度学习框架import torch.nn as nn  # 导入PyTorch神经网络模块import torch.optim as optim  # 导入PyTorch优化器模块from torch.utils.data import Dataset, DataLoader  # 导入数据集和数据加载器import os  # 导入操作系统接口模块import time  # 导入时间模块import math  # 导入数学函数模块from collections import Counter  # 导入计数器工具import pickle  # 导入序列化模块import json  # 导入JSON处理模块from tqdm import tqdm  # 导入进度条工具class BPETokenizer:    """BPE分词器"""    def __init__(self, vocab_size=5000):  # 初始化分词器,设置词表大小        self.vocab_size = vocab_size  # 保存词表大小        self.vocab = {}  # 创建词汇表字典(token到ID)        self.inv_vocab = {}  # 创建反向词汇表字典(ID到token)        self.merges = []  # 创建合并规则列表        self.special_tokens = ['<PAD>''<SOS>''<EOS>''<UNK>']  # 定义特殊标记        for i, token in enumerate(self.special_tokens):  # 遍历特殊标记            self.vocab[token] = i  # 将特殊标记添加到词汇表            self.inv_vocab[i] = token  # 将ID映射到特殊标记    def get_vocab_size(self):  # 获取词表大小的方法        return len(self.vocab)  # 返回词汇表长度    def train(self, texts, verbose=True):  # 训练BPE分词器的方法        if verbose:  # 如果需要显示详细信息            print("开始训练BPE分词器...")  # 打印训练开始信息        word_freqs = Counter()  # 创建词频计数器        for text in tqdm(texts, desc="统计字符频率", disable=not verbose):  # 遍历所有文本            words = text.split()  # 分割文本为单词            for word in words:  # 遍历每个单词                word_freqs[word + '</w>'] += 1  # 统计单词频率        vocab = set()  # 创建词汇集合        for word in word_freqs:  # 遍历所有单词            for char in word:  # 遍历单词中的字符                vocab.add(char)  # 将字符添加到词汇集合        for token in self.special_tokens:  # 遍历特殊标记            vocab.add(token)  # 将特殊标记添加到词汇集合        for i, token in enumerate(self.special_tokens):  # 遍历特殊标记            self.vocab[token] = i  # 将特殊标记映射到ID            self.inv_vocab[i] = token  # 将ID映射到特殊标记        idx = len(self.special_tokens)  # 设置起始索引        for char in sorted(vocab):  # 遍历排序后的字符            if char not in self.special_tokens:  # 如果不是特殊标记                self.vocab[char] = idx  # 将字符映射到ID                self.inv_vocab[idx] = char  # 将ID映射到字符                idx += 1  # 索引递增        num_merges = self.vocab_size - len(self.vocab)  # 计算需要合并的次数        if verbose:  # 如果需要显示详细信息            print(f"需要合并 {num_merges} 次")  # 打印合并次数        for i in range(num_merges):  # 执行合并循环            pairs = Counter()  # 创建符号对计数器            for word, freq in word_freqs.items():  # 遍历词频字典                symbols = word.split()  # 将单词分割为符号                for j in range(len(symbols) - 1):  # 遍历相邻符号对                    pairs[(symbols[j], symbols[j+1])] += freq  # 统计符号对频率            if not pairs:  # 如果没有符号对                break  # 退出循环            best_pair = max(pairs, key=pairs.get)  # 找到最高频的符号对            new_symbol = best_pair[0] + best_pair[1]  # 合并符号对            self.merges.append(best_pair)  # 保存合并规则            if new_symbol not in self.vocab:  # 如果新符号不在词汇表中                self.vocab[new_symbol] = idx  # 添加新符号到词汇表                self.inv_vocab[idx] = new_symbol  # 添加反向映射                idx += 1  # 索引递增            new_word_freqs = {}  # 创建新词频字典            for word, freq in word_freqs.items():  # 遍历旧词频字典                new_word = word.replace(' '.join(best_pair), new_symbol)  # 应用合并规则                new_word_freqs[new_word] = freq  # 保存新词频            word_freqs = new_word_freqs  # 更新词频字典            if verbose and (i + 1) % 500 == 0:  # 每500次合并打印一次                print(f"合并 {i+1}/{num_merges}{best_pair} -> {new_symbol}")  # 打印合并信息        if verbose:  # 如果需要显示详细信息            print(f"BPE分词器训练完成,词表大小: {len(self.vocab)}")  # 打印完成信息    def encode(self, text):  # 编码文本的方法        words = text.split()  # 分割文本为单词        encoded = []  # 创建编码结果列表        for word in words:  # 遍历每个单词            word += '</w>'  # 添加词尾标记            symbols = list(word)  # 将单词转换为符号列表            while len(symbols) > 1:  # 当符号数量大于1时                merged = False  # 设置合并标志                for pair in self.merges:  # 遍历合并规则                    for i in range(len(symbols) - 1):  # 遍历符号位置                        if symbols[i] == pair[0and symbols[i+1] == pair[1]:  # 如果匹配符号对                            symbols[i:i+2] = [pair[0] + pair[1]]  # 合并符号对                            merged = True  # 设置合并标志                            break  # 退出内层循环                    if merged:  # 如果已合并                        break  # 退出外层循环                if not merged:  # 如果没有合并                    break  # 退出循环            for symbol in symbols:  # 遍历符号                if symbol in self.vocab:  # 如果符号在词汇表中                    encoded.append(self.vocab[symbol])  # 添加ID到编码结果                else:  # 如果符号不在词汇表中                    encoded.append(self.vocab['<UNK>'])  # 添加未知标记ID        return encoded  # 返回编码结果    def decode(self, ids):  # 解码ID序列的方法        text = []  # 创建解码结果列表        for idx in ids:  # 遍历ID序列            if idx in self.inv_vocab:  # 如果ID在反向词汇表中                token = self.inv_vocab[idx]  # 获取对应的token                if token not in self.special_tokens:  # 如果不是特殊标记                    text.append(token.replace('</w>'' '))  # 添加解码文本        return ''.join(text)  # 返回解码结果    def save(self, filepath):  # 保存分词器的方法        with open(filepath, 'wb'as f:  # 以二进制写模式打开文件            pickle.dump({  # 序列化分词器数据                'vocab'self.vocab,  # 保存词汇表                'inv_vocab'self.inv_vocab,  # 保存反向词汇表                'merges'self.merges,  # 保存合并规则                'vocab_size'self.vocab_size  # 保存词表大小            }, f)  # 写入文件    def load(self, filepath):  # 加载分词器的方法        with open(filepath, 'rb'as f:  # 以二进制读模式打开文件            data = pickle.load(f)  # 反序列化分词器数据            self.vocab = data['vocab']  # 加载词汇表            self.inv_vocab = data['inv_vocab']  # 加载反向词汇表            self.merges = data['merges']  # 加载合并规则            self.vocab_size = data['vocab_size']  # 加载词表大小        return self  # 返回分词器实例class MultiHeadAttention(nn.Module):  # 多头注意力机制类    """多头注意力机制"""    def __init__(self, d_model, num_heads):  # 初始化方法        super().__init__()  # 调用父类初始化        self.d_model = d_model  # 保存模型维度        self.num_heads = num_heads  # 保存注意力头数        self.d_k = d_model // num_heads  # 计算每个头的维度        self.W_q = nn.Linear(d_model, d_model)  # 创建查询权重矩阵        self.W_k = nn.Linear(d_model, d_model)  # 创建键权重矩阵        self.W_v = nn.Linear(d_model, d_model)  # 创建值权重矩阵        self.W_o = nn.Linear(d_model, d_model)  # 创建输出权重矩阵    def forward(self, query, key, value, mask=None):  # 前向传播方法        batch_size = query.size(0)  # 获取批次大小        Q = self.W_q(query)  # 计算查询向量        K = self.W_k(key)  # 计算键向量        V = self.W_v(value)  # 计算值向量        Q = Q.view(batch_size, -1self.num_heads, self.d_k).transpose(12)  # 重塑并转置查询向量        K = K.view(batch_size, -1self.num_heads, self.d_k).transpose(12)  # 重塑并转置键向量        V = V.view(batch_size, -1self.num_heads, self.d_k).transpose(12)  # 重塑并转置值向量        scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.d_k ** 0.5)  # 计算注意力分数        if mask is not None:  # 如果有掩码            scores = scores.masked_fill(mask == 0, -1e9)  # 应用掩码        attention_weights = torch.softmax(scores, dim=-1)  # 计算注意力权重        output = torch.matmul(attention_weights, V)  # 加权求和        output = output.transpose(12).contiguous().view(batch_size, -1self.d_model)  # 合并多头        output = self.W_o(output)  # 输出变换        return output, attention_weights  # 返回输出和注意力权重class FeedForward(nn.Module):  # 前馈神经网络类    """前馈神经网络"""    def __init__(self, d_model, d_ff, dropout=0.1):  # 初始化方法        super().__init__()  # 调用父类初始化        self.linear1 = nn.Linear(d_model, d_ff)  # 创建第一线性层        self.linear2 = nn.Linear(d_ff, d_model)  # 创建第二线性层        self.dropout = nn.Dropout(dropout)  # 创建Dropout层        self.relu = nn.ReLU()  # 创建ReLU激活函数    def forward(self, x):  # 前向传播方法        x = self.linear1(x)  # 第一线性变换        x = self.relu(x)  # ReLU激活        x = self.dropout(x)  # Dropout正则化        x = self.linear2(x)  # 第二线性变换        return x  # 返回输出class TransformerBlock(nn.Module):  # Transformer块类    """Transformer块"""    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):  # 初始化方法        super().__init__()  # 调用父类初始化        self.attention = MultiHeadAttention(d_model, num_heads)  # 创建多头注意力层        self.norm1 = nn.LayerNorm(d_model)  # 创建第一层归一化        self.norm2 = nn.LayerNorm(d_model)  # 创建第二层归一化        self.feed_forward = FeedForward(d_model, d_ff, dropout)  # 创建前馈网络        self.dropout = nn.Dropout(dropout)  # 创建Dropout层    def forward(self, x, mask=None):  # 前向传播方法        attn_output, _ = self.attention(x, x, x, mask)  # 自注意力计算        x = self.norm1(x + self.dropout(attn_output))  # 残差连接和层归一化        ff_output = self.feed_forward(x)  # 前馈网络计算        x = self.norm2(x + self.dropout(ff_output))  # 残差连接和层归一化        return x  # 返回输出class PositionalEncoding(nn.Module):  # 位置编码类    """位置编码"""    def __init__(self, d_model, max_len=5000):  # 初始化方法        super().__init__()  # 调用父类初始化        pe = torch.zeros(max_len, d_model)  # 创建位置编码矩阵        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)  # 创建位置索引        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))  # 计算分母项        pe[:, 0::2] = torch.sin(position * div_term)  # 偶数位置用sin        pe[:, 1::2] = torch.cos(position * div_term)  # 奇数位置用cos        self.register_buffer('pe', pe.unsqueeze(0))  # 注册为缓冲区    def forward(self, x):  # 前向传播方法        return x + self.pe[:, :x.size(1), :]  # 添加位置编码class Transformer(nn.Module):  # Transformer模型类    """Transformer模型"""    def __init__(self, vocab_size, d_model=512, num_heads=8, num_layers=6, d_ff=2048, max_len=512, dropout=0.1):  # 初始化方法        super().__init__()  # 调用父类初始化        self.d_model = d_model  # 保存模型维度        self.embedding = nn.Embedding(vocab_size, d_model)  # 创建词嵌入层        self.pos_encoding = PositionalEncoding(d_model, max_len)  # 创建位置编码层        self.transformer_blocks = nn.ModuleList([  # 创建Transformer块列表            TransformerBlock(d_model, num_heads, d_ff, dropout)  # 创建单个Transformer块            for _ in range(num_layers)  # 创建多个Transformer块        ])        self.fc = nn.Linear(d_model, vocab_size)  # 创建输出全连接层        self.dropout = nn.Dropout(dropout)  # 创建Dropout层    def forward(self, x, mask=None):  # 前向传播方法        x = self.embedding(x)  # 词嵌入        x = self.pos_encoding(x)  # 添加位置编码        x = self.dropout(x)  # Dropout正则化        for block in self.transformer_blocks:  # 遍历Transformer块            x = block(x, mask)  # 通过Transformer块        output = self.fc(x)  # 输出变换        return output, None  # 返回输出class TextDataset(Dataset):  # 文本数据集类    """文本数据集"""    def __init__(self, data, seq_len):  # 初始化方法        self.data = data  # 保存数据        self.seq_len = seq_len  # 保存序列长度        self.all_tokens = []  # 创建所有token列表        for seq in data:  # 遍历数据序列            self.all_tokens.extend(seq)  # 拼接所有token        self.num_samples = max(0len(self.all_tokens) - seq_len)  # 计算样本数量    def __len__(self):  # 获取数据集长度        return self.num_samples  # 返回样本数量    def __getitem__(self, idx):  # 获取单个样本        x = self.all_tokens[idx:idx + self.seq_len]  # 获取输入序列        y = self.all_tokens[idx + 1:idx + 1 + self.seq_len]  # 获取目标序列        return torch.LongTensor(x), torch.LongTensor(y)  # 返回张量对def load_and_encode_data(file_paths, tokenizer, max_lines_per_file=None):  # 加载并编码数据的函数    all_lines = []  # 创建所有行列表    for file_path in file_paths:  # 遍历文件路径        if os.path.exists(file_path):  # 如果文件存在            print(f"加载文件: {file_path}")  # 打印文件名            with open(file_path, 'r', encoding='utf-8'as f:  # 打开文件                lines = [line.strip() for line in f.readlines() if line.strip()]  # 读取并清理行            if max_lines_per_file is not None:  # 如果限制了每个文件的行数                lines = lines[:max_lines_per_file]  # 每个文件截取前N行            all_lines.extend(lines)  # 添加到总行列表            print(f"读取了 {len(lines)} 行文本")  # 打印读取行数    print(f"总共读取了 {len(all_lines)} 行文本")  # 打印总行数    print("编码数据:")  # 打印编码开始信息    encoded_data = []  # 创建编码数据列表    for line in tqdm(all_lines, desc="编码进度"):  # 遍历所有行        seq = [tokenizer.vocab['<SOS>']]  # 添加开始标记        seq.extend(tokenizer.encode(line))  # 编码文本        seq.append(tokenizer.vocab['<EOS>'])  # 添加结束标记        encoded_data.append(seq)  # 添加到编码数据列表    print(f"编码完成,共 {len(encoded_data)} 个文本片段")  # 打印编码完成信息    return encoded_data  # 返回编码数据def train_model(model, train_loader, val_loader, device, num_epochs, learning_rate, save_dir):  # 训练模型的函数    optimizer = optim.AdamW(model.parameters(), lr=learning_rate, weight_decay=0.01)  # 创建AdamW优化器    criterion = nn.CrossEntropyLoss(ignore_index=0)  # 创建交叉熵损失函数    scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=num_epochs, eta_min=1e-6)  # 创建学习率调度器    best_val_loss = float('inf')  # 初始化最佳验证损失    train_losses = []  # 创建训练损失列表    val_losses = []  # 创建验证损失列表    print(f"\n开始训练,共 {num_epochs} 个epoch")  # 打印训练开始信息    print(f"训练批次数: {len(train_loader)}, 验证批次数: {len(val_loader)}\n")  # 打印批次信息    for epoch in range(1, num_epochs + 1):  # 遍历所有epoch        start_time = time.time()  # 记录开始时间        model.train()  # 设置为训练模式        train_loss = 0  # 初始化训练损失        for x, y in tqdm(train_loader, desc=f"Epoch {epoch}/{num_epochs} [训练]", leave=False):  # 遍历训练批次            x, y = x.to(device), y.to(device)  # 移动数据到设备            optimizer.zero_grad()  # 清零梯度            outputs, _ = model(x)  # 前向传播            B, S, V = outputs.shape  # 获取输出形状            outputs = outputs.reshape(-1, V)  # 重塑输出            y = y.reshape(-1)  # 重塑目标            loss = criterion(outputs, y)  # 计算损失            loss.backward()  # 反向传播            torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)  # 梯度裁剪            optimizer.step()  # 更新参数            train_loss += loss.item()  # 累加损失        train_loss /= len(train_loader)  # 计算平均训练损失        train_losses.append(train_loss)  # 保存训练损失        model.eval()  # 设置为评估模式        val_loss = 0  # 初始化验证损失        with torch.no_grad():  # 禁用梯度计算            for x, y in tqdm(val_loader, desc=f"Epoch {epoch}/{num_epochs} [验证]", leave=False):  # 遍历验证批次                x, y = x.to(device), y.to(device)  # 移动数据到设备                outputs, _ = model(x)  # 前向传播                B, S, V = outputs.shape  # 获取输出形状                outputs = outputs.reshape(-1, V)  # 重塑输出                y = y.reshape(-1)  # 重塑目标                loss = criterion(outputs, y)  # 计算损失                val_loss += loss.item()  # 累加损失        val_loss /= len(val_loader)  # 计算平均验证损失        val_losses.append(val_loss)  # 保存验证损失        scheduler.step()  # 更新学习率        epoch_time = time.time() - start_time  # 计算epoch时间        print(f"Epoch {epoch:3d}/{num_epochs} | Train Loss: {train_loss:.4f} | Val Loss: {val_loss:.4f} | Time: {epoch_time:.1f}s | LR: {optimizer.param_groups[0]['lr']:.6f}")  # 打印epoch信息        if val_loss < best_val_loss:  # 如果验证损失更优            best_val_loss = val_loss  # 更新最佳验证损失            torch.save({  # 保存模型                'model_state_dict': model.state_dict(),  # 保存模型参数                'optimizer_state_dict': optimizer.state_dict(),  # 保存优化器状态                'epoch': epoch,  # 保存epoch数                'train_loss': train_loss,  # 保存训练损失                'val_loss': val_loss,  # 保存验证损失            }, os.path.join(save_dir, 'best_model.pt'))  # 保存到文件            print(f"  ✓ 保存最佳模型 (Val Loss: {val_loss:.4f})")  # 打印保存信息        if epoch % 10 == 0:  # 每10个epoch            torch.save({  # 保存检查点                'model_state_dict': model.state_dict(),  # 保存模型参数                'optimizer_state_dict': optimizer.state_dict(),  # 保存优化器状态                'epoch': epoch,  # 保存epoch数                'train_loss': train_loss,  # 保存训练损失                'val_loss': val_loss,  # 保存验证损失            }, os.path.join(save_dir, f'checkpoint_epoch_{epoch}.pt'))  # 保存到文件    history = {  # 创建训练历史字典        'train_losses': train_losses,  # 保存训练损失列表        'val_losses': val_losses,  # 保存验证损失列表    }    history_path = os.path.join(save_dir, 'training_history.json')  # 创建历史文件路径    with open(history_path, 'w', encoding='utf-8'as f:  # 打开文件        json.dump(history, f, indent=2)  # 写入JSON    print(f"\n训练历史已保存到: {history_path}")  # 打印保存路径    return best_val_loss, train_losses, val_losses  # 返回训练结果def main():  # 主函数    print("=" * 60)  # 打印分隔线    print("Transformer模型训练")  # 打印标题    print("=" * 60)  # 打印分隔线    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')  # 选择设备    print(f"使用设备: {device}")  # 打印设备信息    if torch.cuda.is_available():  # 如果有CUDA        print(f"GPU数量: {torch.cuda.device_count()}")  # 打印GPU数量        for i in range(torch.cuda.device_count()):  # 遍历GPU            print(f"GPU {i}{torch.cuda.get_device_name(i)}")  # 打印GPU名称    data_files = ["斗破苍穹.txt""凡人修仙传.txt"]  # 设置数据文件列表    save_dir = "checkpoints"  # 设置保存目录    tokenizer_path = os.path.join(save_dir, "tokenizer.pkl")  # 设置分词器路径    os.makedirs(save_dir, exist_ok=True)  # 创建保存目录    if os.path.exists(tokenizer_path):  # 如果分词器已存在        print(f"\n加载已有分词器: {tokenizer_path}")  # 打印加载信息        tokenizer = BPETokenizer()  # 创建分词器实例        tokenizer.load(tokenizer_path)  # 加载分词器        print(f"分词器已加载,词表大小: {len(tokenizer.vocab)}")  # 打印词表大小    else:  # 如果分词器不存在        print("\n训练新的BPE分词器...")  # 打印训练信息        tokenizer = BPETokenizer(vocab_size=5000)  # 创建分词器实例        all_lines = []  # 创建所有行列表        for file_path in data_files:  # 遍历数据文件            if os.path.exists(file_path):  # 如果文件存在                with open(file_path, 'r', encoding='utf-8'as f:  # 打开文件                    lines = f.readlines()  # 读取所有行                all_lines.extend([line.strip() for line in lines if line.strip()])  # 清理并添加行        tokenizer.train(all_lines[:100000000], verbose=True)  # 训练分词器        tokenizer.save(tokenizer_path)  # 保存分词器        print(f"分词器已保存到: {tokenizer_path}")  # 打印保存路径    encoded_data = load_and_encode_data(data_files, tokenizer, max_lines_per_file=None)  # 加载并编码数据,读取所有行    split_idx = int(len(encoded_data) * 0.9)  # 计算划分索引    train_data = encoded_data[:split_idx]  # 获取训练数据    val_data = encoded_data[split_idx:]  # 获取验证数据    print(f"训练集: {len(train_data)}, 验证集: {len(val_data)}")  # 打印数据集大小    seq_len = 64  # 设置序列长度    train_dataset = TextDataset(train_data, seq_len)  # 创建训练数据集    val_dataset = TextDataset(val_data, seq_len)  # 创建验证数据集    print(f"训练样本数: {len(train_dataset)}, 验证样本数: {len(val_dataset)}")  # 打印样本数    batch_size = 128  # 设置批次大小    train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=0)  # 创建训练加载器    val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=0)  # 创建验证加载器    vocab_size = len(tokenizer.vocab)  # 获取词表大小    d_model = 256  # 设置模型维度    num_heads = 4  # 设置注意力头数    num_layers = 4  # 设置层数    d_ff = 512  # 设置前馈网络维度    print(f"\n创建Transformer模型...")  # 打印创建信息    model = Transformer(  # 创建模型        vocab_size=vocab_size,  # 设置词表大小        d_model=d_model,  # 设置模型维度        num_heads=num_heads,  # 设置注意力头数        num_layers=num_layers,  # 设置层数        d_ff=d_ff,  # 设置前馈网络维度        max_len=seq_len,  # 设置最大序列长度        dropout=0.1  # 设置dropout率    ).to(device)  # 移动到设备    num_params = sum(p.numel() for p in model.parameters())  # 计算参数数量    print(f"模型参数数量: {num_params:,}")  # 打印参数数量    best_val_loss = float('inf')  # 初始化最佳验证损失    current_round = 1  # 初始化当前轮数    while True:  # 训练循环        print(f"\n{'='*60}")  # 打印分隔线        print(f"第 {current_round} 轮训练")  # 打印轮数        print(f"{'='*60}")  # 打印分隔线        best_val_loss_round, train_losses, val_losses = train_model(  # 训练模型            model=model,  # 传入模型            train_loader=train_loader,  # 传入训练加载器            val_loader=val_loader,  # 传入验证加载器            device=device,  # 传入设备            num_epochs=50,  # 设置epoch数            learning_rate=1e-3,  # 设置学习率            save_dir=save_dir  # 传入保存目录        )        if best_val_loss_round <= 0.1:  # 如果达到目标            print(f"\n{'='*60}")  # 打印分隔线            print(f"✅ 训练成功!验证损失: {best_val_loss_round:.4f} <= 0.1")  # 打印成功信息            print(f"{'='*60}")  # 打印分隔线            break  # 退出循环        if len(val_losses) >= 10:  # 如果有足够的验证损失            recent_losses = val_losses[-10:]  # 获取最近10个损失            loss_decrease = recent_losses[0] - recent_losses[-1]  # 计算损失下降量            if loss_decrease < 0.01:  # 如果下降缓慢                print(f"\n{'='*60}")  # 打印分隔线                print(f"⚠️  Loss下降缓慢,调整参数继续训练")  # 打印警告信息                print(f"最近10个epoch loss下降: {loss_decrease:.4f}")  # 打印下降量                print(f"{'='*60}")  # 打印分隔线                new_lr = 1e-4  # 设置新学习率                print(f"调整学习率为: {new_lr}")  # 打印新学习率                best_val_loss_round, train_losses, val_losses = train_model(  # 继续训练                    model=model,  # 传入模型                    train_loader=train_loader,  # 传入训练加载器                    val_loader=val_loader,  # 传入验证加载器                    device=device,  # 传入设备                    num_epochs=50,  # 设置epoch数                    learning_rate=new_lr,  # 设置新学习率                    save_dir=save_dir  # 传入保存目录                )                if best_val_loss_round <= 0.1:  # 如果达到目标                    print(f"\n{'='*60}")  # 打印分隔线                    print(f"✅ 训练成功!验证损失: {best_val_loss_round:.4f} <= 0.1")  # 打印成功信息                    print(f"{'='*60}")  # 打印分隔线                    break  # 退出循环        print(f"\n当前验证损失: {best_val_loss_round:.4f} > 0.1,继续训练...")  # 打印当前损失        current_round += 1  # 轮数递增        if current_round > 5:  # 如果超过最大轮数            print(f"\n{'='*60}")  # 打印分隔线            print(f"❌ 训练未达到目标,已达到最大训练轮数")  # 打印失败信息            print(f"最佳验证损失: {best_val_loss_round:.4f}")  # 打印最佳损失            print(f"{'='*60}")  # 打印分隔线            break  # 退出循环    print(f"\n最终最佳验证损失: {best_val_loss:.4f}")  # 打印最终损失    if best_val_loss <= 0.1:  # 如果达到目标        print("✅ 训练成功!")  # 打印成功信息    else:  # 如果未达到目标        print("❌ 训练未达到目标")  # 打印失败信息if __name__ == "__main__":  # 如果作为主程序运行    main()  # 调用主函数
测试代码段:100%Trae实现
# -*- coding: utf-8 -*-"""测试脚本 - 验证"萧炎使用了"的生成功能"""import torchimport torch.nn as nnimport mathimport pickleclass PositionalEncoding(nn.Module):    """位置编码"""    def __init__(self, d_model, max_len=5000):        super().__init__()        pe = torch.zeros(max_len, d_model)        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))        pe[:, 0::2] = torch.sin(position * div_term)        pe[:, 1::2] = torch.cos(position * div_term)        self.register_buffer('pe', pe.unsqueeze(0))    def forward(self, x):        return x + self.pe[:, :x.size(1), :]class MultiHeadAttention(nn.Module):    """多头注意力机制"""    def __init__(self, d_model, num_heads):        super().__init__()        self.d_model = d_model        self.num_heads = num_heads        self.d_k = d_model // num_heads        self.W_q = nn.Linear(d_model, d_model)        self.W_k = nn.Linear(d_model, d_model)        self.W_v = nn.Linear(d_model, d_model)        self.W_o = nn.Linear(d_model, d_model)    def forward(self, query, key, value, mask=None):        batch_size = query.size(0)        Q = self.W_q(query)        K = self.W_k(key)        V = self.W_v(value)        Q = Q.view(batch_size, -1self.num_heads, self.d_k).transpose(12)        K = K.view(batch_size, -1self.num_heads, self.d_k).transpose(12)        V = V.view(batch_size, -1self.num_heads, self.d_k).transpose(12)        scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.d_k ** 0.5)        if mask is not None:            scores = scores.masked_fill(mask == 0, -1e9)        attention_weights = torch.softmax(scores, dim=-1)        output = torch.matmul(attention_weights, V)        output = output.transpose(12).contiguous().view(batch_size, -1self.d_model)        output = self.W_o(output)        return output, attention_weightsclass FeedForward(nn.Module):    """前馈神经网络"""    def __init__(self, d_model, d_ff, dropout=0.1):        super().__init__()        self.linear1 = nn.Linear(d_model, d_ff)        self.linear2 = nn.Linear(d_ff, d_model)        self.dropout = nn.Dropout(dropout)        self.relu = nn.ReLU()    def forward(self, x):        x = self.linear1(x)        x = self.relu(x)        x = self.dropout(x)        x = self.linear2(x)        return xclass TransformerBlock(nn.Module):    """Transformer块"""    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):        super().__init__()        self.attention = MultiHeadAttention(d_model, num_heads)        self.norm1 = nn.LayerNorm(d_model)        self.norm2 = nn.LayerNorm(d_model)        self.feed_forward = FeedForward(d_model, d_ff, dropout)        self.dropout = nn.Dropout(dropout)    def forward(self, x, mask=None):        attn_output, _ = self.attention(x, x, x, mask)        x = self.norm1(x + self.dropout(attn_output))        ff_output = self.feed_forward(x)        x = self.norm2(x + self.dropout(ff_output))        return xclass Transformer(nn.Module):    """Transformer模型"""    def __init__(self, vocab_size, d_model=256, num_heads=4, num_layers=4, d_ff=512, max_len=64, dropout=0.1):        super().__init__()        self.d_model = d_model        self.embedding = nn.Embedding(vocab_size, d_model)        self.pos_encoding = PositionalEncoding(d_model, max_len)        self.transformer_blocks = nn.ModuleList([            TransformerBlock(d_model, num_heads, d_ff, dropout)            for _ in range(num_layers)        ])        self.fc = nn.Linear(d_model, vocab_size)        self.dropout = nn.Dropout(dropout)    def forward(self, x, mask=None):        x = self.embedding(x) * math.sqrt(self.d_model)        x = self.pos_encoding(x)        x = self.dropout(x)        for block in self.transformer_blocks:            x = block(x, mask)        output = self.fc(x)        return output, Nonedef load_model():    """加载模型和分词器"""    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')    print(f"🚀 设备: {device}")    with open("checkpoints/tokenizer.pkl"'rb'as f:        data = pickle.load(f)        vocab = data['vocab']        inv_vocab = data['inv_vocab']        merges = data['merges']    print(f"📦 词表大小: {len(vocab)}")    checkpoint = torch.load("checkpoints/best_model.pt", map_location=device)    model = Transformer(len(vocab)).to(device)    model.load_state_dict(checkpoint['model_state_dict'])    model.eval()    print(f"✓ 模型已加载 (Epoch {checkpoint['epoch']}, Val Loss: {checkpoint['val_loss']:.4f})")    return model, vocab, inv_vocab, merges, devicedef encode(text, vocab, merges):    """编码文本"""    words = text.split()    encoded = []    for word in words:        word += '</w>'        symbols = list(word)        while len(symbols) > 1:            merged = False            for pair in merges:                for i in range(len(symbols) - 1):                    if symbols[i] == pair[0and symbols[i+1] == pair[1]:                        symbols[i:i+2] = [pair[0] + pair[1]]                        merged = True                        break                if merged:                    break            if not merged:                break        for symbol in symbols:            if symbol in vocab:                encoded.append(vocab[symbol])            else:                encoded.append(vocab['<UNK>'])    return encodeddef decode(ids, inv_vocab):    """解码ID序列"""    text = []    for idx in ids:        if idx in inv_vocab:            token = inv_vocab[idx]            if token not in ['<PAD>''<SOS>''<EOS>''<UNK>']:                text.append(token.replace('</w>'' '))    return ''.join(text)def generate_text(model, prompt, vocab, inv_vocab, merges, device, max_length=50, temperature=0.8):    """生成文本"""    tokens = [vocab['<SOS>']]    tokens.extend(encode(prompt, vocab, merges))    with torch.no_grad():        for _ in range(max_length):            input_seq = torch.LongTensor([tokens[-64:]]).unsqueeze(0).to(device)            outputs, _ = model(input_seq)            logits = outputs[0, -1, :] / temperature            if temperature == 0:                next_token = logits.argmax(dim=-1).item()            else:                probs = torch.softmax(logits, dim=-1)                next_token = torch.multinomial(probs, 1)[0].item()            tokens.append(next_token)            if next_token == vocab['<EOS>']:                break    generated_text = decode(tokens, inv_vocab)    return generated_textdef test_prompt():    """测试特定提示词"""    print("=" * 60)    print("🧪 测试提示词生成")    print("=" * 60)    model, vocab, inv_vocab, merges, device = load_model()    test_prompts = ["萧炎使用了"]    for prompt in test_prompts:        print(f"\n📝 提示词: {prompt}")        print(f"🔄 正在生成...")        try:            generated = generate_text(model, prompt, vocab, inv_vocab, merges, device, max_length=50, temperature=0.8)            print(f"✨ 生成结果: {generated}")            print("✅ 测试成功!")        except Exception as e:            print(f"❌ 生成失败: {str(e)}")            import traceback            traceback.print_exc()    print("\n" + "=" * 60)if __name__ == "__main__":    test_prompt()
还是想训练一个完整版本尝尝咸淡:
(TraeAI-6) C:\Users\wangh\Music\transformer [1:-1073741510] $ python train_complete.py   ============================================================Transformer模型训练============================================================使用设备: cudaGPU数量: 2GPU 0: NVIDIA GeForce GTX 1080 TiGPU 1: NVIDIA GeForce GTX 1080 Ti加载已有分词器: checkpoints\tokenizer.pkl分词器已加载,词表大小: 3108加载文件: 斗破苍穹.txt读取了 82618 行文本加载文件: 凡人修仙传.txt读取了 139946 行文本总共读取了 222564 行文本编码数据:编码进度: 100%|██████████████████████████████| 222564/222564 [00:04<00:00, 46478.34it/s] 编码完成,共 222564 个文本片段训练集: 200307, 验证集: 22257训练样本数: 12512255, 验证样本数: 1184140创建Transformer模型...模型参数数量: 2,087,652============================================================第 1 轮训练============================================================开始训练,共 20 个epoch训练批次数: 48876, 验证批次数: 4626Epoch 1/20 [训练]:   0%|                          | 103/48876 [00:10<1:06:09, 12.29it/s]

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-11 09:17:48 HTTP/2.0 GET : https://67808.cn/a/462449.html
  2. 运行时间 : 0.186658s [ 吞吐率:5.36req/s ] 内存消耗:4,771.00kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=53ca987c14ddd9faa4f100bd6b531551
  1. /yingpanguazai/ssd/ssd1/www/no.67808.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/no.67808.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/no.67808.cn/runtime/temp/6df755f970a38e704c5414acbc6e8bcd.php ( 12.06 KB )
  140. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000864s ] mysql:host=127.0.0.1;port=3306;dbname=no_67808;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000924s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000306s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000253s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000448s ]
  6. SELECT * FROM `set` [ RunTime:0.000195s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000502s ]
  8. SELECT * FROM `article` WHERE `id` = 462449 LIMIT 1 [ RunTime:0.000669s ]
  9. UPDATE `article` SET `lasttime` = 1770772668 WHERE `id` = 462449 [ RunTime:0.006922s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 65 LIMIT 1 [ RunTime:0.006391s ]
  11. SELECT * FROM `article` WHERE `id` < 462449 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.003800s ]
  12. SELECT * FROM `article` WHERE `id` > 462449 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000400s ]
  13. SELECT * FROM `article` WHERE `id` < 462449 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.014423s ]
  14. SELECT * FROM `article` WHERE `id` < 462449 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.003731s ]
  15. SELECT * FROM `article` WHERE `id` < 462449 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.000770s ]
0.188236s