鱼C论坛

 找回密码
 立即注册
查看: 830|回复: 9

为什么我的惩罚函数没有变化

[复制链接]
发表于 2024-4-2 16:48:32 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
以下是我的代码,为什么我的punish以及init_weight等函数在循环中没有变化?
  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as f
  4. from torch.autograd import Variable
  5. import numpy as np
  6. import os
  7. import copy
  8. import random
  9. import pandas
  10. import matplotlib.pyplot as plt
  11. from torch.autograd import Function
  12. def ConvertTensor(vec,grad_flag=True):
  13.         return torch.tensor(vec,dtype=torch.float32,requires_grad=grad_flag)


  14. def Punishment(geom,generator,flag=1):
  15.         if flag==1:
  16.                 thresh1 = generator.thresh1 #smaller
  17.                 thresh2 = generator.thresh2 #larger
  18.                 w1      = generator.w1
  19.                 w2      = generator.w2
  20.        
  21.                 ThreeN = len(geom)
  22.                 NAtom  = int(ThreeN/3)
  23.                 geom = geom.view(NAtom,3)
  24.                 bond_length=[]
  25.                 for i in range(NAtom):
  26.                         for j in range(i+1,NAtom):
  27.                                 diff = geom[i,:]-geom[j,:]
  28.                                 diff = torch.linalg.norm(diff)
  29.                                 bond_length.append(diff)
  30.        
  31.                 bond_length = ConvertTensor(bond_length)
  32.                 dist_max = torch.max(bond_length)
  33.                 dist_min = torch.min(bond_length)
  34.                 #if all bond lengths are good
  35.                 res1 = 0
  36.                 res2 = 0
  37.                 #if any bond is too short or too long
  38.                 if dist_min < thresh1:
  39.                         res1 = -(dist_min - thresh1)
  40.                 if dist_max > thresh2:
  41.                         res2 =  dist_max - thresh2
  42.        
  43.                 return w1*res1 + w2*res2


  44. class Generator(nn.Module):
  45.         """ Architecture of the Generator, uses res-blocks """

  46.         def __init__(self):
  47.                 super().__init__()
  48.                
  49.                 self.counter = 0
  50.                 self.progress = []
  51.                 self.NAtom = 12
  52.                 self.model  = self.G_model()
  53.                 self.optimizer = torch.optim.Adam(self.parameters(),lr=0.001)

  54.                 self.thresh1 = 1.6
  55.                 self.thresh2 = 12.6
  56.                 #---------------------------------------------
  57.                 #the below are used for punishment function
  58.                 #for flag 1
  59.                 self.RandomInput  = self.Gen_Random_input()

  60.        
  61.                 #--------------------------------------------
  62.                 #self.model.apply(weight_init)
  63.                 print("Generator model")
  64.                 print(self.model)

  65.         ###################################################
  66.         def G_model(self):
  67.                 NAtom           = self.NAtom
  68.                 activation_func = nn.Sigmoid()
  69.                 inp_dim         = NAtom*3
  70.                 Normalizer      = nn.LayerNorm
  71.                 model_flag      =1
  72.         #-----------------------------------------------
  73.                 if model_flag ==1: #simple
  74.                         model = nn.Sequential(
  75.                                  nn.Linear(inp_dim, inp_dim),
  76.                                         activation_func
  77.                 )

  78.                 if model_flag ==101: #simple
  79.                         model = nn.Sequential(
  80.                                  nn.Linear(inp_dim, inp_dim),
  81.                                         activation_func,
  82.                                                  Normalizer(inp_dim)
  83.                 )

  84.                 return model
  85.         #-----------------------------------------------
  86.         #######################################################


  87.         def forward(self,input_tensor,weight,bias=0):

  88.                 out=self.model(input_tensor)
  89.                 out=torch.matmul(out,weight)+bias
  90.                 out=out+bias
  91.                 return out


  92.         def Gen_Random_input(self):
  93.                 NAtom = self.NAtom
  94.                 mean  = 0
  95.                 std   = 2
  96.                 inp   = torch.randn(NAtom*3)
  97.                 input_tensor = inp*std + mean
  98.                 input_tensor.requires_grad_(True)       
  99.                 return input_tensor
  100.        
  101.         def train(self,target_flag,maxit=1000):

  102.                 thresh1 = self.thresh1
  103.                 thresh2 = self.thresh2
  104.                 input_tensor = self.Gen_Random_input()
  105.                 init_bias = torch.zeros_like(input_tensor)
  106.                 init_weight = torch.ones(self.NAtom*3,self.NAtom*3)       
  107.                 self.w1=1
  108.                 self.w2=1

  109.                 for i in range(maxit):
  110.                
  111.                         #generate fake data       
  112.                         gen_tensor   = self.forward(input_tensor,init_weight,init_bias)
  113.                         self.gen_tensor = gen_tensor
  114.                         #calculate the punishment of the fake data
  115.                         punish  = Punishment(gen_tensor.detach(),self,flag=1)       
  116.                         print(punish)       
  117.                         #punish  = torch.max(punish,torch.zeros_like(punish))

  118.                         loss   = punish


  119.                         #get the network parameters
  120.                         params = self.state_dict()
  121.                         keys = list(params.keys())
  122.                         last_b = copy.deepcopy(params[keys[-1]])
  123.                         last_w = copy.deepcopy(params[keys[-2]])
  124.                         #do some check
  125.                         assert len(last_b) == self.NAtom*3,        print("error length of last_b not equal to threeN, len(keys)=",len(last_b))
  126.                         assert len(last_w) == self.NAtom*3,    print("error length of last_w not equal to threeN, len(keys)=",len(last_b))

  127.                         #update the init_bias and init_weight
  128.                         init_bias = ConvertTensor(last_b,True)
  129.                         init_weight = ConvertTensor(last_w,True)       

  130.                         #run the backward training
  131.                         self.optimizer.zero_grad()
  132.                         loss.backward()
  133.                         self.optimizer.step()

  134.                         if punish > 10:
  135.                                 init_weight = torch.zeros_like(init_weight)

  136.                         #plot stuff
  137.                         self.counter += 1
  138.                         if self.counter%10==0:
  139.                                 self.progress.append(loss.item())
  140.                 #                print(gen_tensor)


  141.                 return loss,gen_tensor


  142.         def plot_progress(self):
  143.                 df = pandas.DataFrame(self.progress,columns=['loss'])
  144. #                df.plot(ylim=[0,0.8],marker='.',grid=True,title="generator_loss" )
  145.                 df.plot(marker='.',grid=True,title="generator_loss" )
  146. #                plt.savefig("generator_loss.png")
  147.                 plt.show()



  148. #-----


  149. if __name__=="__main__":

  150.         generator = Generator()
  151.         NAtom=12
  152.         init_weight = torch.ones([NAtom*3, NAtom*3])
  153.         geom = generator.forward(generator.RandomInput,weight=init_weight)       
  154.         maxit=500
  155.         print("max iteration=",maxit)
  156.         loss,gen_tensor = generator.train(target_flag=1,maxit=maxit)
  157.        
  158.         generator.plot_progress()

复制代码

本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-4-2 16:48:45 | 显示全部楼层
问题出在以下几个地方:
1. 在你的`Punishment`函数中,你没有传入`generator`对象。所以在使用`generator.thresh1`和`generator.w1`等属性时会报错。你需要修改为`Punishment(geom,generator,flag=1)`。
2. 在你的`forward`函数中,你传入了`weight`参数,但是在调用`self.model`时并没有使用它。你可以尝试修改为`out = self.model(input_tensor) + torch.matmul(weight, input_tensor)`。
3. 在你的`Gen_Random_input`函数中,你没有将输入的`grad_flag`传递给`torch.randn()`方法。你需要修改为`inp = torch.randn(NAtom*3, dtype=torch.float32, requires_grad=True)`。
4. 在你的`train`函数中,在循环开始之前,你需要先调用`self.optimizer.zero_grad()`来清空之前计算的梯度。

以上是我发现的问题和建议修复的地方,你可以尝试将代码改进后再运行一下看看是否有更好的结果。希望能对你有所帮助!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-3 09:57:19 | 显示全部楼层
FishC 发表于 2024-4-2 16:48
问题出在以下几个地方:
1. 在你的`Punishment`函数中,你没有传入`generator`对象。所以在使用`generator ...

下面是我现在修改后的代码,但是punish在循环过程中还是没有变化。问题在哪里?
  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as f
  4. from torch.autograd import Variable
  5. import numpy as np
  6. import os
  7. import copy
  8. import random
  9. import pandas
  10. import matplotlib.pyplot as plt
  11. from torch.autograd import Function
  12. def ConvertTensor(vec,grad_flag=True):
  13.         return torch.tensor(vec,dtype=torch.float32,requires_grad=grad_flag)

  14. class Generator(nn.Module):
  15.         """ Architecture of the Generator, uses res-blocks """

  16.         def __init__(self):
  17.                 super().__init__()
  18.                
  19.                 self.counter = 0
  20.                 self.progress = []
  21.                 self.NAtom = 12
  22.                 self.model  = self.G_model()
  23.                 self.optimizer = torch.optim.Adam(self.parameters(),lr=0.001)
  24.                 self.loss_func = nn.MSELoss()

  25.                 self.thresh1 = 1.6
  26.                 self.thresh2 = 12.6
  27.                 #---------------------------------------------
  28.                 #the below are used for punishment function
  29.                 #for flag 1
  30.                 self.RandomInput  = self.Gen_Random_input()

  31.        
  32.                 #--------------------------------------------
  33.                 #self.model.apply(weight_init)
  34.                 print("Generator model")
  35.                 print(self.model)

  36.         ###################################################
  37.         def G_model(self):
  38.                 NAtom           = self.NAtom
  39.                 activation_func = nn.ReLU()
  40.                 inp_dim         = NAtom*3
  41.                 Normalizer      = nn.LayerNorm
  42.                 model_flag      =1
  43.         #-----------------------------------------------
  44.                 if model_flag ==1: #simple
  45.                         model = nn.Sequential(
  46.                                  nn.Linear(inp_dim, inp_dim),
  47.                                         activation_func,
  48.                                  nn.Linear(inp_dim, inp_dim),
  49.                                         activation_func


  50.                 )

  51.                 if model_flag ==101: #simple
  52.                         model = nn.Sequential(
  53.                                  nn.Linear(inp_dim, inp_dim),
  54.                                         activation_func,
  55.                                                  Normalizer(inp_dim)
  56.                 )

  57.                 return model
  58.         #-----------------------------------------------
  59.         #######################################################


  60.         def forward(self,input_tensor,weight,bias=0):
  61.                 out=self.model(input_tensor)
  62.                 out=torch.matmul(out,weight)+bias
  63.                 out=out+bias

  64.                 return out


  65.         def Punishment(self,geom):
  66.                 thresh1 = self.thresh1 #smaller
  67.                 thresh2 = self.thresh2 #larger
  68.                 w1      = self.w1
  69.                 w2      = self.w2
  70.                
  71.                 ThreeN = len(geom)
  72.                 NAtom  = int(ThreeN/3)
  73.                 geom = geom.view(NAtom,3)
  74.                 bond_length=[]
  75.                 for i in range(NAtom):
  76.                         for j in range(i+1,NAtom):
  77.                                 diff = geom[i,:]-geom[j,:]
  78.                                 diff = torch.linalg.norm(diff)
  79.                                 bond_length.append(diff)
  80.                
  81.                 bond_length = ConvertTensor(bond_length)
  82.                 dist_max = torch.max(bond_length)
  83.                 dist_min = torch.min(bond_length)
  84.                 #if all bond lengths are good
  85.                 res1 = 0
  86.                 res2 = 0
  87.                 #if any bond is too short or too long
  88.                 if dist_min < thresh1:
  89.                         res1 = -(dist_min - thresh1)
  90.                 if dist_max > thresh2:
  91.                         res2 =  dist_max - thresh2
  92.                
  93.                 return w1*res1 + w2*res2
  94.        


  95.         def Gen_Random_input(self):
  96.                 NAtom = self.NAtom
  97.                 mean  = 0
  98.                 std   = 2
  99.                 inp   = torch.randn(NAtom*3)
  100.                 input_tensor = inp*std + mean
  101.                 input_tensor.requires_grad_(True)       
  102.                 return input_tensor
  103.        
  104.         def train(self,target_flag,maxit=1000):

  105.                 thresh1 = self.thresh1
  106.                 thresh2 = self.thresh2
  107.                 input_tensor = self.Gen_Random_input()
  108.                 init_bias = torch.zeros_like(input_tensor)
  109.                 init_weight = torch.ones(self.NAtom*3,self.NAtom*3)       
  110.                 self.w1=1
  111.                 self.w2=1

  112.                 gen_tensor = input_tensor
  113.                 for i in range(maxit):
  114.                         self.optimizer.zero_grad()
  115.                         #generate fake data       
  116.                         gen_tensor   = self.forward(gen_tensor,init_weight,init_bias)
  117.                         self.gen_tensor = gen_tensor
  118.                         #calculate the punishment of the fake data
  119.                         punish  = self.Punishment(gen_tensor.detach())       
  120.                 #        print(gen_tensor)       
  121.                         print(punish)       
  122.                         #punish  = torch.max(punish,torch.zeros_like(punish))
  123.                         loss   = self.loss_func(punish,torch.zeros_like(punish))


  124.                         #get the network parameters
  125.                         params = self.state_dict()
  126.                         keys = list(params.keys())
  127.                         last_b = copy.deepcopy(params[keys[-1]])
  128.                         last_w = copy.deepcopy(params[keys[-2]])
  129.                         #do some check
  130.                         assert len(last_b) == self.NAtom*3,        print("error length of last_b not equal to threeN, len(keys)=",len(last_b))
  131.                         assert len(last_w) == self.NAtom*3,    print("error length of last_w not equal to threeN, len(keys)=",len(last_b))

  132.                         #update the init_bias and init_weight
  133.                         init_bias = ConvertTensor(last_b,True)
  134.                         init_weight = ConvertTensor(last_w,True)       

  135.                         #run the backward training
  136.                         loss.backward()
  137.                         self.optimizer.step()

  138.                         #plot stuff
  139.                         self.counter += 1
  140.                         if self.counter%10==0:
  141.                                 self.progress.append(loss.item())
  142.                 #                print(gen_tensor)


  143.                 return loss,gen_tensor


  144.         def plot_progress(self):
  145.                 df = pandas.DataFrame(self.progress,columns=['loss'])
  146. #                df.plot(ylim=[0,0.8],marker='.',grid=True,title="generator_loss" )
  147.                 df.plot(marker='.',grid=True,title="generator_loss" )
  148. #                plt.savefig("generator_loss.png")
  149.                 plt.show()



  150. #-----


  151. if __name__=="__main__":

  152.         generator = Generator()
  153.         NAtom=12
  154.         init_weight = torch.ones([NAtom*3, NAtom*3])
  155.         geom = generator.forward(generator.RandomInput,weight=init_weight)       
  156.         maxit=500
  157.         print("max iteration=",maxit)
  158.         loss,gen_tensor = generator.train(target_flag=1,maxit=maxit)
  159.        
  160.         generator.plot_progress()

复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-3 09:57:39 | 显示全部楼层
lqhenwunai 发表于 2024-04-03 09:57
下面是我现在修改后的代码,但是punish在循环过程中还 ...


问题在于在`train`函数中的循环中,你使用了`gen_tensor = input_tensor`,这导致在每次迭代中都使用相同的输入作为生成器的输入。因此,生成器的输出`gen_tensor`没有改变,所以惩罚函数`Punishment`的结果也不会改变。

你可以尝试将其修改为`gen_tensor = self.forward(gen_tensor, init_weight, init_bias)`,以便用上一次迭代的生成器输出作为下一次迭代的输入。这样,生成器的输出将随着迭代次数的增加而逐渐改变,从而使惩罚函数的结果也能够变化。

另外,你还需要将`punish`的计算方式修改为`loss = self.loss_func(punish, torch.zeros_like(punish))`,并且在每次迭代前调用`self.optimizer.zero_grad()`来清空之前的梯度计算。

修改后的代码如下:

  1. def train(self, target_flag, maxit=1000):
  2.     thresh1 = self.thresh1
  3.     thresh2 = self.thresh2
  4.     input_tensor = self.Gen_Random_input()
  5.     init_bias = torch.zeros_like(input_tensor)
  6.     init_weight = torch.ones(self.NAtom*3, self.NAtom*3)
  7.    
  8.     self.w1 = 1
  9.     self.w2 = 1
  10.    
  11.     gen_tensor = input_tensor
  12.    
  13.     for i in range(maxit):
  14.         self.optimizer.zero_grad()
  15.         
  16.         # 生成假数据
  17.         gen_tensor = self.forward(gen_tensor, init_weight, init_bias)
  18.         self.gen_tensor = gen_tensor
  19.         
  20.         # 计算惩罚值
  21.         punish = self.Punishment(gen_tensor.detach())
  22.         print(punish)
  23.         
  24.         loss = self.loss_func(punish, torch.zeros_like(punish))
  25.         
  26.         params = self.state_dict()
  27.         keys = list(params.keys())
  28.         last_b = copy.deepcopy(params[keys])
  29.         last_w = copy.deepcopy(params[keys])
  30.         
  31.         assert len(last_b) == self.NAtom*3, print("error length of last_b not equal to threeN, len(keys)=", len(last_b))
  32.         assert len(last_w) == self.NAtom*3, print("error length of last_w not equal to threeN, len(keys)=", len(last_b))
  33.         
  34.         init_bias = ConvertTensor(last_b, True)
  35.         init_weight = ConvertTensor(last_w, True)
  36.         
  37.         loss.backward()
  38.         self.optimizer.step()
  39.         
  40.         self.counter += 1
  41.         if self.counter % 10 == 0:
  42.             self.progress.append(loss.item())
  43.    
  44.     return loss, gen_tensor
复制代码


希望这次的修改能够解决你的问题!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-3 10:03:42 | 显示全部楼层
FishC 发表于 2024-4-3 09:57
问题在于在`train`函数中的循环中,你使用了`gen_tensor = input_tensor`,这导致在每次迭代中都使用相 ...

我就是这样修改的,但是punish还是没有变化
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-3 10:03:57 | 显示全部楼层
lqhenwunai 发表于 2024-04-03 10:03
我就是这样修改的,但是punish还是没有变化


抱歉,我在之前的回答中犯了错误。问题出在`Punishment`函数中。你在计算`bond_length`时,应该是对每一对原子计算差异,而不是对整个几何体进行计算。另外,在计算`diff`时,你需要使用索引来获取每对原子的坐标差异。以下是修改后的代码:

  1. def Punishment(self, geom):
  2.     thresh1 = self.thresh1 #smaller
  3.     thresh2 = self.thresh2 #larger
  4.     w1      = self.w1
  5.     w2      = self.w2
  6.    
  7.     ThreeN = len(geom)
  8.     NAtom  = int(ThreeN/3)
  9.     geom = geom.view(NAtom,3)
  10.    
  11.     bond_length = []
  12.     for i in range(NAtom):
  13.         for j in range(i+1,NAtom):
  14.             diff = geom[i] - geom[j]
  15.             diff = torch.linalg.norm(diff)
  16.             bond_length.append(diff)
  17.    
  18.     bond_length = ConvertTensor(bond_length)
  19.     dist_max = torch.max(bond_length)
  20.     dist_min = torch.min(bond_length)
  21.    
  22.     res1 = 0
  23.     res2 = 0
  24.    
  25.     if dist_min < thresh1:
  26.         res1 = -(dist_min - thresh1)
  27.     if dist_max > thresh2:
  28.         res2 = dist_max - thresh2
  29.         
  30.     return w1*res1 + w2*res2
复制代码


请尝试运行修改后的代码,看看是否能够得到正确的惩罚结果。抱歉给你带来了困扰!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-3 10:06:00 | 显示全部楼层
FishC 发表于 2024-4-3 10:03
抱歉,我在之前的回答中犯了错误。问题出在`Punishment`函数中。你在计算`bond_length`时,应该是对每 ...

这个punishment跟我的代码没有区别
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-3 10:06:14 | 显示全部楼层
lqhenwunai 发表于 2024-04-03 10:06
这个punishment跟我的代码没有区别


非常抱歉,我之前的回答仍然存在错误。在`Punishment`函数中,你需要计算每对原子之间的距离,并将其与阈值进行比较,以计算惩罚。以下是修改后的代码示例:

  1. def Punishment(geom, generator):
  2.     thresh1 = generator.thresh1
  3.     thresh2 = generator.thresh2
  4.     w1 = generator.w1
  5.     w2 = generator.w2
  6.    
  7.     ThreeN = len(geom)
  8.     NAtom = int(ThreeN/3)
  9.     geom = geom.view(NAtom, 3)
  10.    
  11.     res1 = 0
  12.     res2 = 0

  13.     for i in range(NAtom):
  14.         for j in range(i+1, NAtom):
  15.             diff = geom[i] - geom[j]
  16.             bond_length = torch.linalg.norm(diff)

  17.             if bond_length < thresh1:
  18.                 res1 += -(bond_length - thresh1)
  19.             
  20.             if bond_length > thresh2:
  21.                 res2 += bond_length - thresh2
  22.    
  23.     return w1 * res1 + w2 * res2
复制代码


请尝试使用修改后的`Punishment`函数,看看是否能够得到正确的惩罚结果。非常抱歉之前给你带来的困惑!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-4-3 10:07:18 | 显示全部楼层
FishC 发表于 2024-4-3 10:06
非常抱歉,我之前的回答仍然存在错误。在`Punishment`函数中,你需要计算每对原子之间的距离,并将其与 ...

不能得到正确的结果
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-3 10:07:34 | 显示全部楼层
lqhenwunai 发表于 2024-04-03 10:07
不能得到正确的结果


非常抱歉,我之前回答中的代码仍然有问题。在Punishment函数中,你使用了一个列表来存储每对原子之间的距离,但是你没有正确计算每对原子之间的距离,而是直接将整个geom张量减去自身,这是错误的。以下是修复后的代码示例:

  1. def Punishment(geom, generator):
  2.     thresh1 = generator.thresh1
  3.     thresh2 = generator.thresh2
  4.     w1 = generator.w1
  5.     w2 = generator.w2

  6.     ThreeN = len(geom)
  7.     NAtom = int(ThreeN/3)
  8.     geom = geom.view(NAtom, 3)

  9.     bond_length = []
  10.     for i in range(NAtom):
  11.         for j in range(i+1, NAtom):
  12.             diff = geom[i] - geom[j]
  13.             diff = torch.linalg.norm(diff)
  14.             bond_length.append(diff)

  15.     bond_length = torch.tensor(bond_length)
  16.     dist_max = torch.max(bond_length)
  17.     dist_min = torch.min(bond_length)

  18.     res1 = 0
  19.     res2 = 0

  20.     if dist_min < thresh1:
  21.         res1 = -(dist_min - thresh1)
  22.     if dist_max > thresh2:
  23.         res2 = dist_max - thresh2

  24.     return w1 * res1 + w2 * res2
复制代码


请尝试使用修复后的`Punishment`函数,看看是否能够得到正确的惩罚结果。非常抱歉之前给你带来的困惑!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-22 04:25

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表