后向传播神经网络

一、原理

BP(Back Propagation)算法是通过将网络预测值与实际值做对比,不断修改权重从而尽量将他们之间的均方根误差降低到最小的算法。该算法由最后的节点向前不断传递信息,所以被称为后向传播算法。BP算法具有简单易行、计算量小和并行性强等优点,其实质是求解误差函数最小值的问题,但由于梯度下降本身的缺点,容易陷入局部最小值,且根据学习率,有可能会导致收敛速度慢,学习效率低等缺点。

整个BPNN可以划分为两个阶段:

第一阶段前向传播阶段

这一阶段,节点之间通过权重边相互映射到新的节点中,第iii层的节点信息由第i−1i-1i−1层映射得到,满足以下公式:
Ii=∑i=1nwijOj+θiI_i=\sum_{i=1}^nw_{ij}O_j+\theta_i Ii​=i=1∑n​wij​Oj​+θi​
其中,OjO_jOj​表示上一层节点的输出信息,wijw_{ij}wij​表示节点jjj和节点iii的权重边,θi\theta_iθi​表示偏置量(Bias)。

我们通过添加一个激活函数,将原先的线性映射变为非线性映射,例如使用Sigmoid函数:
Oi=11+e−IiO_i=\frac{1}{1+e^{-I_i}} Oi​=1+e−Ii​1​
第二阶段反向传播

BP算法基于梯度下降,每次对参数的迭代都是梯度最快下降的方向,其误差值的评估为:
E=12∑j=1l(y^jk−yjk)2E=\frac{1}{2}\sum_{j=1}^l(\hat y_j^k-y_j^k)^2 E=21​j=1∑l​(y^​jk​−yjk​)2
前面的0.5是为了化简求导,常数项影响不大。

对于某一权重参数whjw_{hj}whj​,给定一个学习率σ\sigmaσ,其变化率为对误差值(也称作损失函数)的求导:
△whj=δEδwhj\triangle w_{hj}=\frac{\delta E}{\delta w_{hj}} △whj​=δwhj​δE​
根据梯度下降算法,他的变化值应该是其梯度的反方向。再加上学习率做平滑,所以最后的更新量为:
△whj=−σδEδwhj\triangle w_{hj}=-\sigma\frac{\delta E}{\delta w_{hj}} △whj​=−σδwhj​δE​
注意Sigmoid函数的求导结果为:
O′=O(1−O)O'=O(1-O) O′=O(1−O)
这里不给出具体求导步骤,感兴趣的朋友可以自己试着推一推。

于是,根据链式求导规则,在输出层单元jjj,误差ErrErrErr的计算表达为:
Errj=Oj(1−Oj)(Tj−Oj)Err_j=O_j(1-O_j)(T_j-O_j) Errj​=Oj​(1−Oj​)(Tj​−Oj​)
TjT_jTj​表示在这个单元上的真实结果。

根据BP原理,对于单元jjj的误差,来源于与他相关的nnn个隐含层单元映射,而对于某个隐含层映射iii,也有可能对kkk个输出层节点产生影响(多对多关系),所以在隐层的更新中,需要考虑所有从输出层传播回来的信息。

对于隐层节点iii,有:
Erri=Oi(1−Oi)∗∑j=1kwijErrjErr_i=O_i(1-O_i)*\sum_{j=1}^kw_{ij}Err_j Erri​=Oi​(1−Oi​)∗j=1∑k​wij​Errj​
了解完误差传播的规则之后,我们就需要对参数进行更新啦!

那么每一层权重的更新可以表示为:
△wij=σErrjOiwij=wij+△wij\triangle w_{ij}=\sigma Err_jO_i\\ w_{ij}=w_{ij}+\triangle w_{ij} △wij​=σErrj​Oi​wij​=wij​+△wij​
偏置的更新可以表示为:
△θj=σErrjθj=θj+σ△θj\triangle \theta_{j}=\sigma Err_j\\ \theta_{j}=\theta_{j}+\sigma \triangle \theta_{j}\\ △θj​=σErrj​θj​=θj​+σ△θj​
BP算法迭代停止条件为:

  • 前一周期所有的权重变化率都小于给定阈值
  • 前一周期误差百分比小于给定阈值
  • 超出给定的迭代周期数

二、案例

给定一个前馈神经网络如下,共有九个输入节点,可以看做九个特征维度,两个隐层节点和一个输出层节点。

首先第一步,我们需要给定权重的初始值和学习率:

隐层

w11 w21 w31 w41 w51 w61 w71 w81 w91
0.1 0.2 0.3 -0.4 -0.1 -0.2 -0.3 0.4 0.5
w12 w22 w32 w42 w52 w62 w72 w82 w92
0.2 0.4 0.1 -0.2 -0.4 0.3 0.2 0.4 -0.2

输出层

w1c w2c
0.7 0.5

输入初始值为

1,0,1,0,0,1,0,0,0

学习率

lr=0.01

偏置

θh1\theta_{h1}θh1​ -0.1
θh2\theta_{h2}θh2​ 0.2
θc\theta_{c}θc​ 0.1

对于每个节点,净输入值表示为:
I=θ+∑i=1nwiviI=\theta+\sum_{i=1}^nw_iv_i I=θ+i=1∑n​wi​vi​
其中,θ\thetaθ 表示偏置量,wiw_iwi​表示分支权重,viv_ivi​表示节点值。

输出值表示为:
O=11+e−IO=\frac{1}{1+e^{-I}} O=1+e−I1​
输出结果

单元 净输入 输出
H1 −0.1+0.1∗1+0.3∗1−0.2∗1=0.1-0.1+0.1*1+0.3*1-0.2*1=0.1−0.1+0.1∗1+0.3∗1−0.2∗1=0.1 11+e−0.1=0.525\frac{1}{1+e^{-0.1}}=0.5251+e−0.11​=0.525
H2 0.2+0.2∗1+0.1∗1+0.3∗1=0.80.2+0.2*1+0.1*1+0.3*1=0.80.2+0.2∗1+0.1∗1+0.3∗1=0.8 11+e−0.8=0.690\frac{1}{1+e^{-0.8}}=0.6901+e−0.81​=0.690
C 0.1+0.7∗0.55+0.5∗0.65=0.810.1+0.7*0.55+0.5*0.65=0.810.1+0.7∗0.55+0.5∗0.65=0.81 11+e−0.81=0.692\frac{1}{1+e^{-0.81}}=0.6921+e−0.811​=0.692

输出层的误差值计算为
Err=O∗(1−O)∗(T−O)Err=O*(1-O)*(T-O) Err=O∗(1−O)∗(T−O)
其中,OOO表示节点输出,TTT表示真实值

隐层节点的误差计算为:
Err=O∗(1−O)∗∑i=1nErri∗wiErr=O*(1-O)*\sum_{i=1}^nErr_i*w_i Err=O∗(1−O)∗i=1∑n​Erri​∗wi​
其中,ErrErrErr表示从高层传过来的误差。

计算每个节点的误差

单元 误差
C 0.692∗(1−0.692)∗(1−0.692)=0.0660.692*(1-0.692)*(1-0.692)=0.0660.692∗(1−0.692)∗(1−0.692)=0.066
H1 0.525∗(1−0.525)∗0.066∗0.7=0.0120.525*(1-0.525)*0.066*0.7=0.0120.525∗(1−0.525)∗0.066∗0.7=0.012
H2 0.69∗(1−0.69)∗0.066∗0.5=0.0070.69*(1-0.69)*0.066*0.5=0.0070.69∗(1−0.69)∗0.066∗0.5=0.007

偏置量的更新方程表示为:
θnew=θold+lr∗(Err)\theta_{new}=\theta_{old}+lr*(Err) θnew​=θold​+lr∗(Err)
权重的更新方程表示为:
wnew=wold+lr∗Err∗Ow_{new}=w_{old}+lr*Err*O wnew​=wold​+lr∗Err∗O

更新权重和偏置

这里只给出了链式求导用到的节点和权重

w1cw_{1c}w1c​ 0.7+0.01∗(0.066∗0.525)=0.70034650.7+0.01*(0.066*0.525)=0.70034650.7+0.01∗(0.066∗0.525)=0.7003465
w2cw_{2c}w2c​ 0.5+0.01∗(0.066∗0.69)=0.50045540.5+0.01*(0.066*0.69)=0.50045540.5+0.01∗(0.066∗0.69)=0.5004554
w11w_{11}w11​ 0.1+0.01∗(0.012)∗1=0.100120.1+0.01*(0.012)*1=0.100120.1+0.01∗(0.012)∗1=0.10012
w12w_{12}w12​ 0.2+0.01∗(0.007)∗1=0.200070.2+0.01*(0.007)*1=0.200070.2+0.01∗(0.007)∗1=0.20007
w31w_{31}w31​ 0.3+0.01∗(0.012)∗1=0.300120.3+0.01*(0.012)*1=0.300120.3+0.01∗(0.012)∗1=0.30012
w32w_{32}w32​ 0.1+0.01∗(0.007)∗1=0.10070.1+0.01*(0.007)*1=0.10070.1+0.01∗(0.007)∗1=0.1007
w61w_{61}w61​ −0.2+0.01∗(0.012)∗1=−0.19988-0.2+0.01*(0.012)*1=-0.19988−0.2+0.01∗(0.012)∗1=−0.19988
w62w_{62}w62​ 0.3+0.01∗(0.007)∗1=0.30070.3+0.01*(0.007)*1=0.30070.3+0.01∗(0.007)∗1=0.3007
θc\theta_{c}θc​ 0.1+0.01∗(0.066)=0.100660.1+0.01*(0.066)=0.100660.1+0.01∗(0.066)=0.10066
θh1\theta_{h1}θh1​ −0.1+0.01∗(0.012)=−0.09988-0.1+0.01*(0.012)=-0.09988−0.1+0.01∗(0.012)=−0.09988
θ42\theta_{42}θ42​ 0.2+0.01∗(0.007)=0.200070.2+0.01*(0.007)=0.200070.2+0.01∗(0.007)=0.20007

三、代码实现

1️⃣ 导入需要的库,以及我们需要用的函数

import math
import random
# step 1. 构建常用函数# 激活函数
def sigmoid(x):return math.tanh(x)
def ReLU(x):return x if x>0 else 0
def derived_sigmiod(x):# (O)(1-O)(T-O)return x-x**2# 生成随机数
def getRandom(a,b):return (b-a)*random.random()+a# 生成一个矩阵
def makeMatrix(m,n,val=0.0):# 默认以0填充这个m*n的矩阵return [[val]*n for _ in range(m)]

2️⃣ 初始化参数

这个阶段我们需要做的工作有:

  • 初始化节点个数
  • 创建权重矩阵并给定初始值
  • 保存各种参数量
  • 创建数据容器保存各层输出结果
  • 也可以设置动量参数
# step 2. 初始化参数
# 这部分主要有:节点个数、隐层个数、输出层个数
# 可以类似于torch.nn.Linear
class BPNN:def __init__(self,n_in,n_out,n_hidden=10,lr=0.1,m=0.1):self.n_in=n_in+1 # 加一个偏置节点self.n_hidden=n_hidden+1 # 加一个偏置节点self.n_out=n_outself.lr=lrself.m=m# 生成链接权重# 这里用的是全连接,所以对应的映射就是 [节点个数A,节点个数B]self.weight_hidden=makeMatrix(self.n_in,self.n_hidden)self.weight_out=makeMatrix(self.n_hidden,self.n_out)# 对权重进行初始化for i,row in enumerate(self.weight_hidden):for j,val in enumerate(row):self.weight_hidden[i][j]=getRandom(-0.2,0.2)for i,row in enumerate(self.weight_out):for j,val in enumerate(row):self.weight_out[i][j]=getRandom(-0.2,0.2)# 存储数据的矩阵self.in_matrix=[1.0]*self.n_inself.hidden_matrix=[1.0]*self.n_hiddenself.out_matrix=[1.0]*self.n_out# 设置动量矩阵# 保存上一次梯度下降方向self.ci=makeMatrix(self.n_in,self.n_hidden)self.co=makeMatrix(self.n_hidden,self.n_out)

3️⃣ 正向传播

这个阶段,我们要做的有:

  • 将输入数据保存到数据容器中
  • 开始根据正向传播规则传播数据
  # step 3. 正向传播# 根据传播规则对节点值进行更新def update(self,inputs):if len(inputs)!=self.n_in-1:raise ValueError("Your data length is %d, but our input needs %d"%(len(inputs),self.n_in-1))# 设置初始值self.in_matrix[:-1]=inputs# 注意我们最后一个节点依旧是1,表示偏置节点# 隐层for i in range(self.n_hidden-1):accumulate=0for j in range(self.n_in-1):accumulate+=self.in_matrix[j]*self.weight_hidden[j][i]self.hidden_matrix[i]=sigmoid(accumulate)# 输出层for i in range(self.n_out):accumulate = 0for j in range(self.n_hidden - 1):accumulate += self.hidden_matrix[j] * self.weight_out[j][i]self.out_matrix[i] = sigmoid(accumulate)return self.out_matrix[:] # 返回一个副本

4️⃣ 反向传播

这一阶段,我们要做的工作有:

  • 反向计算误差
  • 反向更新参数量
    # step 4. 误差反向传播def backpropagate(self,target):if len(target) != self.n_out :raise ValueError("Your data length is %d, but our input needs %d" % (len(target), self.n_out))# 计算输出层的误差# 根据公式: Err=O(1-O)(T-O)=(O-O**2)(True-O)out_err=[derived_sigmiod(o:=self.out_matrix[i])*(t-o) for i,t in enumerate(target)]# 计算隐层的误差# 根据公式:Err=(O-O**2)Sum(Err*W)hidden_err=[0.0]*self.n_hiddenfor i in range(self.n_hidden):err_tot=0.0for j in range(self.n_out):err_tot+=out_err[j]*self.weight_out[i][j]hidden_err[i]=derived_sigmiod(self.hidden_matrix[i])*err_tot# 更新权重# 输出层:# w=bias+lr*O*Err+m*(w(n-1))# m表示动量因子,w(n-1)是上一次的梯度下降方向for i in range(self.n_hidden):for j in range(self.n_out):# 更新变化量 change=O*Errchange=self.hidden_matrix[i]*out_err[j]self.weight_out[i][j]+=self.lr*change+self.m*self.co[i][j]# 更新上一次的梯度self.co[i][j]=change# 隐含层for i in range(self.n_in):for j in range(self.n_hidden):change=hidden_err[j]*self.in_matrix[i]self.weight_hidden[i][j]+=self.lr*change+self.m*self.ci[i][j]self.ci[i][j]=change# 计算总误差err=0.0for i,v in enumerate(target):err+=(v-self.out_matrix[i])**2err/=len(target)return math.sqrt(err)

总的代码为:

import math
import randomdef sigmoid(x):return math.tanh(x)
def ReLU(x):return x if x>0 else 0
def derived_sigmiod(x):return x-x**2
def getRandom(a,b):return (b-a)*random.random()+a
def makeMatrix(m,n,val=0.0):return [[val]*n for _ in range(m)]class BPNN:def __init__(self,n_in,n_out,n_hidden=10,lr=0.1,m=0.1):self.n_in=n_in+1 self.n_hidden=n_hidden+1 self.n_out=n_outself.lr=lrself.m=mself.weight_hidden=makeMatrix(self.n_in,self.n_hidden)self.weight_out=makeMatrix(self.n_hidden,self.n_out)for i,row in enumerate(self.weight_hidden):for j,val in enumerate(row):self.weight_hidden[i][j]=getRandom(-0.2,0.2)for i,row in enumerate(self.weight_out):for j,val in enumerate(row):self.weight_out[i][j]=getRandom(-0.2,0.2)self.in_matrix=[1.0]*self.n_inself.hidden_matrix=[1.0]*self.n_hiddenself.out_matrix=[1.0]*self.n_outself.ci=makeMatrix(self.n_in,self.n_hidden)self.co=makeMatrix(self.n_hidden,self.n_out)def update(self,inputs):self.in_matrix[:-1]=inputsfor i in range(self.n_hidden-1):accumulate=0for j in range(self.n_in-1):accumulate+=self.in_matrix[j]*self.weight_hidden[j][i]self.hidden_matrix[i]=sigmoid(accumulate)for i in range(self.n_out):accumulate = 0for j in range(self.n_hidden - 1):accumulate += self.hidden_matrix[j] * self.weight_out[j][i]self.out_matrix[i] = sigmoid(accumulate)return self.out_matrix[:]def backpropagate(self,target):out_err=[derived_sigmiod(o:=self.out_matrix[i])*(t-o) for i,t in enumerate(target)]hidden_err=[derived_sigmiod(self.hidden_matrix[i])*sum(out_err[j]*self.weight_out[i][j] for j in range(self.n_out)) for i in range(self.n_hidden) ]for i in range(self.n_hidden):for j in range(self.n_out):change=self.hidden_matrix[i]*out_err[j]self.weight_out[i][j]+=self.lr*change+self.m*self.co[i][j]self.co[i][j]=changefor i in range(self.n_in):for j in range(self.n_hidden):change=hidden_err[j]*self.in_matrix[i]self.weight_hidden[i][j]+=self.lr*change+self.m*self.ci[i][j]self.ci[i][j]=changeerr=0.0for i,v in enumerate(target):err+=(v-self.out_matrix[i])**2err/=len(target)return math.sqrt(err)

5️⃣ 模型使用

在这阶段我们新加两个API,用于网络训练和拟合

    def train(self,data,epochs=1000):best_err=1e10for i in range(epochs):err=0.0for j in data:x=j[0]y=j[1]self.update(x)err+=self.backpropagate(y)if err<best_err:best_err=errprint(best_err)def fit(self,x):return [self.update(i) for i in x]

我们也可以创建一个随机数据生成器用来获取随机数据

def getData(m,n,c=None):# 随机生成一组大小为m*n,类别为c的数据if c!=None:data=[[[random.uniform(0.0,2.0)]*n,[random.randint(0,c)]] for i in range(m)]else:data=[[random.uniform(0.0,2.0)]*n for _ in range(m)]return data
d_train=getData(20,5,1)
d_test=getData(10,5)

不过我们这里使用固定的模式进行测试:

# 固定模式
d=[[[1,0,1,0,1],[1]],[[1,0,1,0,1],[1]],[[1,0,1,0,1],[1]],[[1,0,1,1,1],[0]],[[1,0,1,0,1],[1]],[[1,0,1,1,1],[0]],
]
c=[[1,0,1,0,1],[1,0,1,0,1],[1,0,1,1,1],[1,0,1,0,1],[1,0,1,1,1],[1,0,1,0,1],[1,0,1,0,1],[1,0,1,1,1],[1,0,1,0,1],[1,1,1,0,1],
]

输入数据是一个6*5大小的数据,label是一个一维数据,所以我们需要创建一个输入维度为5,输出维度为1BPNN

net=BPNN(5,1)net.train(d)
print(net.fit(c))

得到的结果为:

[[0.9831619856205059], [0.9831619856205059], [0.023029882403248512], [0.9831619856205059], [0.023029882403248512], [0.9831619856205059], [0.9831619856205059], [0.02302988]]

可以发现确实简单实现了二分类。

当然我们也可以设定输出维度为2,结果表示为:

net=BPNN(5,2)net.train(d)
print(["cat" if i[0]>i[1] else 'dog' for i in net.fit(c)])
Err: 0.10754377610345334
result:
['cat', 'cat', 'dog', 'cat', 'dog', 'cat', 'cat', 'dog', 'cat', 'cat']

80行代码轻松搞定反向传播神经网络(BPNN)相关推荐

  1. html 简繁文件转换器,几行代码轻松搞定网页的简繁转换

    几行代码轻松搞定网页的简繁转换以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 对网页进行简繁字体转换的方法一般有两种 ...

  2. 精选10个Python库,几行代码轻松搞定探索性数据分析!

    点击上方"菜鸟学Python",选择"星标"公众号 超级无敌干货,第一时间送达!!! 探索性数据分析是数据科学模型开发和数据集研究的重要组成部分之一.在拿到一个 ...

  3. 几行代码轻松搞定网页的简繁转换(转载)

    对网页进行简繁字体转换的方法一般有两种:一是使用<简繁通>这样的专业软件,另外一种是制作两套版本的网页.显然,这两种方法都较为麻烦,而且专业软件一般不能用于免费的空间.笔者在这里给大家提供 ...

  4. 几行代码轻松搞定网页的简繁转换

        对网页进行简繁字体转换的方法一般有两种:一是使用<简繁通>这样的专业软件,另外一种是制作两套版本的网页.显然,这两种方法都较为麻烦,而且专业软件一般不能用于免费的空间.笔者在这里给 ...

  5. 机器学习建模神器PyCaret已开源!提升效率,几行代码轻松搞定模型

    Datawhale干货 编译:张峰,Datawhale成员 寄语:PyCaret,是一款 Python中的开源低代码(low-code)机器学习库,支持在「低代码」环境中训练和部署有监督以及无监督的机 ...

  6. html自动给图片加上水印 代码_如何给一千张图片去水印?还好我会python,100行代码轻松搞定...

    写在前面 近期好多网友私信我,问我编程该怎么学习.怎么入门.我觉得编程学习,就像写文章一样,需要积累. 如果把代码每个字符拆开,大伙都认识,但是组合在一起,就是另外一回事了.所以我的建议是,学习编程, ...

  7. 几行代码轻松搞定跨系统传递 traceId

    同样是新项目开发的笔记,因为使用的是分布式架构,涉及到各个系统之间的交互 这时候就会遇到一个很常见的问题: 单个系统是集群部署,日志分布在多台服务器上: 多个系统的日志在多台机器,但是一次请求,查日志 ...

  8. android 蓝牙耗电量,安卓Android BLE低功耗蓝牙接受数据详解 只需100行代码轻松搞定...

    做了一个安卓手机通过蓝牙获取电子秤的重量的Demo,在此写下以供大家参考和讨论. 先上代码,着急用的可以迅速参考,后面再写说明 我跳过了扫描过程,直接根据蓝牙设备地址进行连接,可以运行官方Demo来获 ...

  9. 几行代码轻松搞定网页简繁转换

    首先在 http://www.chinahtml.com/d/file/programming/3/transform.js处下载用于简繁转换的js文件transform.js,复制到网站目录下,然后 ...

  10. jquery练习03_手写扫雷(全网最强jquery练习),扫雷游戏 150行代码轻松搞定

    5 扫雷 5.1 扫雷规则 展示信息:总雷数 剩余雷数(总雷数-插旗数) 用时 规格:难度增加=雷数增加+类概率增加: 10% 15% 20% 状态:被点击(触雷+周围0雷+周围n雷+插旗)+未点击 ...

最新文章

  1. leetcode 74 java_【LeetCode】74. Search a 2D Matrix
  2. 查看oracle会话和进程_带有Oracle Digital Assistant和Fn Project的会话式UI
  3. 安装redis并开启_如何安装Redis,以及对Redis配置文件的更改和测试
  4. 客服端与服务器之间传输信息,QT实现客服端和服务器之间消息和文件交互
  5. 金融数据分析与挖掘实战1.4.4-1.5.1
  6. Flutter学习 — 处理点击
  7. 这本值得你认真一读的畅销书特价了
  8. 原神ios android,原神ios和安卓数据互通吗 原神ios和安卓能一起玩吗
  9. 应急响应--记录一次漏洞紧急处理中意外发现的挖矿木马(Shiro反序列化漏洞和ddg挖矿木马)...
  10. pandas常用命令
  11. 物流管理python实践报告_Python程序设计 实验报告6
  12. Git和Repository简明理解(GitHub是什么?)
  13. 打造金融科技新生态,巨杉数据库与宇信科技完成产品兼容互认证
  14. vue 车牌号校验(含新能源)
  15. 蚂蚁金服对收购英支付企业不予置评,雷军忆当年“被迫”当金山CEO往事 | 雷锋早报...
  16. App渠道统计基础知识:App推广运营人员必备
  17. 苹果报告问题_苹果或因5G价格阉割iPhone 12电池规格,续航堪忧
  18. 千篇一律的Python爬虫,大神精心总结的爬虫套路!
  19. java 微信转账 ca_error_java,微信支付退款_微信支付退款接口调用证书出现错误,java,微信支付退款,ssl - phpStudy...
  20. what?spring已经解决循环依赖了,为啥还报循环依赖错误?

热门文章

  1. AXURE原型——移动端标签、菜单原型集合
  2. 头条搜集的Redis视频教程
  3. ios lottie动画_在iOS中使用Lottie动画
  4. 高频功率放大器的设计实现
  5. Google Earth Engine(GEE)——计算NDVI\EVI\RVI\DVI\SAVI归一化植被指数、比值植被指数、差值植被指数、土壤调节植被指数、增强型植被指数和绿度植被指数计算并下载
  6. aspose.pdf-17.8暴力破解,附破解过程和使用代码。仅供参考,不可用于商业用途
  7. igs无法分配驱动器映射表_CAD图纸字体不全怎么办?只要修改字体映射表就可以轻松解决了...
  8. java 随机生成姓名_生成随机中文姓名java程序.pdf
  9. 1.7亿,国家重点研发计划“综合交通运输与智能交通”2019年项目申报开始
  10. QMC解码-某音乐解码