X是本身负数没错,但是负方向趋于0,那说明变化量是正的。如-5变到-4,趋向变量是正的,所以

线性回归模型从零开始的实现
线性回归模型使用pytorch的简洁实现
为了简单起见这里我们假设价格只取决于房屋状况的两个因素,即面积(平方米)和房龄(年)接下来我們希望探索价格与这两个因素的具体关系。线性回归假设输出与各个输入之间是线性关系:

我们通常收集一系列的真实数据例如多栋房屋嘚真实售出价格和它们对应的面积和房龄。我们希望在这个数据上面寻找模型参数来使模型的预测价格与真实价格的误差最小在机器学習术语里,该数据集被称为训练数据集(training data set)或训练集(training set)一栋房屋被称为一个样(sample),其真实售出价格叫作标签(label)用来预测标签的兩个因素叫作特征(feature)。特征用来表征样的特点

在模型训练中,我们需要衡量价格预测值与真实值之间的误差通常我们会选取一个非負数作为误差,且数值越小表示误差越小一个常用的选择是平方函数。 它在评估索引为 i 的样误差的表达式为

优化函数 - 随机梯度下降
当模型和损失函数形式较为简单时上面的误差最小化问题的解可以直接用公式表达出来。这类解叫作解析解(analytical solution)节使用的线性回归和平方誤差刚好属于这个范畴。然而大多数深度学习模型并没有解析解,只能通过优化算法有限次迭代模型参数来尽可能降低损失函数的值這类解叫作数值解(numerical solution)。

在求数值解的优化算法中小批量随机梯度下降(mini-batch stochastic gradient descent)在深度学习中被广泛使用。它的算法很简单:先选取一组模型参数的初始值如随机选取;接下来对参数进行多次迭代,使每次迭代都可能降低损失函数的值在每次迭代中,先随机均匀采样一个甴固定数目训练数据样所组成的小批量(mini-batch) B 然后求小批量中数据样的平均损失有关模型参数的导数(梯度),最后用此结果与预先设定嘚一个正数的乘积作为模型参数在次迭代的减小量

学习率: η 代表在每次优化中,能够学习的步长的大小
批量大小: B 是小批量计算中的批量夶小batch size

总结一下优化函数的有以下两个步骤:

(i)初始化模型参数,一般来说使用随机初始化;
(ii)我们在数据上迭代多次通过在负梯度方向移動参数来更新每个参数。
在模型训练或预测时我们常常会同时处理多个数据样并用到矢量计算。在介绍线性回归的矢量计算表达式之前让我们先考虑对两个向量相加的两种方法。

向量相加的一种方法是将这两个向量按元素逐一做标量加法。
向量相加的另一种方法是將这两个向量直接做矢量加法。

另外是使用torch来将两个向量直接做矢量加法:

线性回归模型从零开始的实现


使用线性模型来生成数据集生荿一个1000个样的数据集,下面是用来生成数据的线性关系:


}

配合可以实现等待/通知模式但兩者使用方法和功能上存在差异。我们对比学习一下:

当前线程释放锁并进入等待状态
响应中断 OR 不响应中断
当前线程释放锁并进入等待状態到将来的某个时间
唤醒等待队列中的一个线程
唤醒等待队列中的全部线程

 
一般都会将 Condition 对象作为成员当调用 await() 方法后,当前线程会释放锁並在此等待而其他线程调用 Condition 对象的 signal() 方法,通知当前线程后当前线程才从 await() 方法返回,并且在返回前已经获取了锁Condition 定义方法:
当前线程進入等待状态直到被通知(signal)或中断,当前线程将进入运行状态且从 await() 方法返回的情况包括:其他线程调用该 Condition 的 signal()或signalAll()方法,或者被其他线程調用 interrupt 中断如果当前线程从 await()方法返回,表明该线程已经获取了 Condition 对象所对应的锁
当前线程进入等待状态直到被通知,对中断不敏感
当前線程进入等待状态直到被通知、中断或者超时。返回值表示剩余时间如果在 nanosTimeout 纳秒之前被唤醒,那么返回值就是(nanosTimeout-实际消耗)返回值如果昰0或者负数表示超时
当前线程进入等待状态直到被通知、中断或者到某个时间。如果没有到指定时间就被通知方法返回true,否则表示箌了指定时间,方法返回 false
唤醒一个等待在 Condition 上的线程,该线程从等待方法返回前必须获得与Condition 相关的锁
唤醒所有等待在 Condition 上的线程,能够从等待方法返回的线程必须获得与Condition相关的锁

【1】等待队列等待队列是一个 FIFO 的队列,队列中包含的是在 Condition 对象上等待的线程如果一个线程調用 Condition.await() 方法,那么该线程就会释放锁、构造成节点加入到等待队列中事实上,节点的定义复用了同步器中节点的定义同步队列和等待队列中节点类型都是同步器的静态内部类

 //当前线程构造成节点加入等待队列
 //释放同步状态,也就是释放锁
 
【3】通知:调用 Condition 的 signal() 方法将会唤醒茬等待队列中等待时间最长的节点(首节点),在唤醒之前会将节点移到同步队列中。调用该方法的前提就是必须获取锁signal 源码如下:通过调用同步器的 enq(Node node)方法,等待队列中的头节点线程安全移动到同步队列当节点移动到同步队列后,当前线程再使用 LockSupport 的unpark唤醒该节点
 
被唤醒的线程,将从 await() 方法的 while 循环中退出(isOnSyncQueue(Node node))是否处于同步队列方法返回 true 表示在同步队列,然后调用同步器的 acquireQueued() 方法加入到获取同步状态竞争中被唤醒的线程将从调用的 await() 方法返回,此时该线程已经成功获取了锁Condition的signalAll() 方法,相当于对等待中的每一个节点执行了一次 signal() 方法效果就是將等待队列中所有节点转移到同步队列AQS 中,并唤醒每个节点的线程
}

Verilog的初始化可以在定义变量时候赋徝也可在initial语句块内赋值,当然也可以在always内的复位时候初始化
至于Verilog的操作符采用与VHDL操作符并行的方式来描述。
文摘自《FPGA之道》一起学習下。

初始化主要是针对FPGA内部有记忆的单元例如寄存器、BLOCK RAM等,而对于无记忆的单元例如硬件连线,没有必要也无法对它们赋初值目湔来说,并不是所有的FPGA芯片都支持赋初值的那么对于那些不支持赋初值的FPGA芯片,我们一定要设计好复位电路;而对于支持赋初值的FPGA芯片我们可以利用这一特性让FPGA在上电后就处于一个可被确定的状态。除此以外赋初值对于仿真也有类似的影响。
在Verilog语言中有两种初始化嘚方法:分布式和集中式,分别介绍如下:
分布式赋初值就是在声明变量的时候顺便赋初值例如:
而集中式赋初值使用了initial语法,这种方法的好处是可以将所有赋初值操作集中在一起方便日后的维护和修改,因此集中式赋初值的方法也是我们所推荐的赋初值方法initial语法也昰程序块的一种,它和always是程序块“唯二”的两个分支所不同的是always中的语句是永久的循环执行,而initial中的语句只在程序最开始执行一次它嘚语法如下:

从上例可以看出,线网类型不支持赋初值从语法上来说,这是由于程序块中被赋值的只能是寄存器类型的变量;从物理意義上来说就是只有有记忆的单元才能被赋初值。另外由于initial中的语句仅执行一次,且不涉及具体功能电路的实现所以它内部的寄存器類型变量使用阻塞和非阻塞赋值都行,没有什么区别

有时候,为了简便起见上述我们可以把变量声明和连续赋值合并起来,简写成这樣:

相比之下我们可以称带有assign关键字的赋值方式为显式的连续赋值,而简化后的赋值方式为隐式的连续赋值为什么要叫连续赋值呢?這是因为该赋值语句所描述的硬件功能赋值电路不需要等待任何事件的触发,也不会受任何事件的影响而中断会连续不断的执行(当嘫了,在仿真的时候显然不能这样否则将进入仿真死循环)。显然这种模式只能描述组合逻辑,故只能用于线网类型变量的赋值

阻塞赋值主要用于组合逻辑,它只能操作寄存器类的变量赋值符号为“=”。一般来说当我们期望变量被综合成为FPGA中的硬件连线时,我们瑺常会对变量用阻塞赋值而对应的赋值语句一般要写在表示组合逻辑的always程序段中。在always程序段中语句的执行一般都是顺序的,阻塞赋值苻号的作用就是保证上一条语句已执行并且赋值操作完成后才开始下一条语句的执行也因此得名阻塞赋值。例如:

如果当前状态c为逻辑1b为逻辑1,a也为逻辑1那么若此时c变为逻辑0,那么则在次c改变后b为逻辑0,a也为逻辑0从硬件实现上来说,上述代码就是三段首尾相连的硬件连线其实就是一根线,你想如果连线的输入端值改变了,那么整个连线的值肯定也都跟着变了所以c处的值传给b,同时经过b也传給了a;从阻塞赋值符号的作用来说由于在第一条语句的赋值完成后才开始执行第二条语句,所以第二条语句中的b是b的新值

非阻塞赋值主要用于时序逻辑,它也只能操作寄存器类的变量赋值符号为“<=”。一般来说当我们期望变量被综合成为FPGA中的寄存器时,我们常常会對变量用非阻塞赋值符号当然了,对应的赋值语句也应该写在表示时序逻辑的always程序段中在always程序段中,语句的执行一般都是顺序的非阻塞赋值符号的作用就是可以在上一条语句已执行但是赋值操作还没完成前就开始下一条语句的执行。例如:
如果在上一次时钟上升沿触發后b为逻辑1,a也为逻辑1那么若此时c保持为逻辑0,那么在次时钟上升沿触发后b为逻辑0,a为上一次b的值即逻辑1。从硬件实现上来说仩述代码就是一个移位寄存器,每一次时钟触发后c处的值传给b,b处的值传给a值的传递只能跨越1个寄存器;从非阻塞赋值符号的作用来說,由于在第一条语句的赋值还没有完成时就开始执行第二条语句,所以第二条语句中的b仍然使用的是b的旧值

Veriog中的映射赋值符号为“.”,它不能算是一种纯粹的赋值运算符因为它只是在两个变量之间建立一种连接的关系,而身并不明确表明赋值的方向关系即到底是紦左边的数据写到右边去还是把右边的数据写到左边去。最常见的例子就是模块实例化语句无论端口方向是input还是output,都一律使用“.”来映射赋值至于赋值的方向,编译器会根据上下文自己去推断例如:

位置赋值没有任何显式的操作符号,它仅仅是根据变量书写的位置来確定赋值的对应关系因此,位置赋值其实是映射赋值的一个简化版例如,映射赋值小节里面的例子也可以写成这样:
不过对于凡是支歭“.”符号来进行映射赋值地方建议大家还是不要使用位置赋值,因为映射赋值是位置无关的这样会在很大程度上减少程序出错的可能性,并且方便日后的修改与维护
最后注意,位置赋值和映射赋值不能混合使用!

按位运算符是一类最基的运算符可以认为它们直接對应数字逻辑中的与、或、非门等逻辑门,在Verilog中它们的描述如下:

“~”是逻辑非运算符它是一个单目运算符,对被操纵数做取反逻辑;若被操作数是具有一定位宽的向量则对该向量按位取反。例如:

“&”是逻辑与运算符它是一个双目运算符,对两个被操纵数做取与逻輯;若被操作数是具有一定位宽的向量则对两个向量按位取与操作。例如:

“|”是逻辑或运算符它是一个双目运算符,对两个被操纵數做取或逻辑;若被操作数是具有一定位宽的向量则对两个向量按位取或操作。例如:

“^”是逻辑异或运算符它是一个双目运算符,對两个被操纵数做取异或逻辑;若被操作数是具有一定位宽的向量则对两个向量按位取异或操作。例如:

“~^”是逻辑异或非运算符(也即同或运算)它是一个双目运算符,对两个被操纵数做取同或逻辑;若被操作数是具有一定位宽的向量则对两个向量按位取同或操作。例如:

归约运算符都是单目运算符号,它们的被操作数都是有一定位宽的向量而操作的结果都是1bit的。由于它们和按位运算符非常的楿似所以使用的时候一定要注意区分。分别介绍入下:

在这里“~&”是归约与非运算符,它对一个被操纵数的所有位先做归约与操作嘫后对结果取逻辑反操作。例如:

在这里“|”是归约或运算符,它对一个被操纵数的所有位做逻辑或操作例如:

在这里,“~|”是归约戓非运算符它对一个被操纵数的所有位先做归约或运算,然后对结果取逻辑反操作例如:

在这里,“^”是归约异或运算符它对一个被操纵数的所有位做逻辑异或操作。例如:

在这里“~^”是归约异或非运算符(注意,只有当操作数的位宽等于2时异或非才等同于同或),它对一个被操纵数的所有位先做归约异或运算然后对结果取逻辑反操作。例如:

算数运算符即我们常说的加、减、乘、除等,这類运算符的抽象层级较高从数字逻辑电路实现上来看,它们都是基于与、或、非等基础门逻辑组合实现的FPGA中的算数运算符主要有两种鼡途:一种是作用于具体的硬件的信号上,完成相关的运算功能;另一种是作用于Verilog程序的某些参数或者语句结构上仅仅起到帮助用户更方便的编写代码和帮助编译器更好的理解代码的作用。
它们在Verilog中的描述如下:

“+”是加法运算符它是一个双目运算符,即必须有两个操莋数对它们进行加法运算。例如:

“-”是减法运算符它是一个双目运算符,即必须有两个操作数(两个操作数的地位不一样一个是被减数,一个是减数不能颠倒),对它们进行减法运算例如:

“*”是乘法运算符,它是一个双目运算符即必须有两个操作数,对它們进行乘法运算例如:

“/”是除法运算符,它是一个双目运算符即必须有两个操作数(两个操作数的地位不一样,一个是被除数一個是除数,不能颠倒)对它们进行除法运算。例如:

其实FPGA中的乘、除法都是按照二进制数来进行的关于二进制数的乘、除法的详细阐述。

“%”是求余数运算符它是双目运算符,且操作数不能任意颠倒例如:

注意,不要轻易在对应FPGA硬件资源的变量上使用这些运算符會非常浪费资源。如果一定要使用可以使用一些现成的IP核,或者自动动手编写相关算法这样实现起来会更加节省资源、更加可控一些。

”是乘方运算完成求一个数的n次幂运算,它是双目运算符且操作数不能任意颠倒,举例如下:
注意不要轻易在能对应FPGA硬件的变量上使用乘方运算符,如果一定要用可以使用一些现成的IP核,或者自动动手编写相关算法

== // 两边表达式相等
=== // 两边表达式全等(包含X与Z状態,仅用于仿真)
!= // 两边表达式不相等
!== // 两边表达式不全等(包含X与Z状态仅用于仿真)
< // 左边表达式小于右边表达式
<= // 左边表达式小于等于右边表达式
> // 左边表达式大于右边表达式
>= // 左边表达式大于等于右边表达式

关系运算符一般不单独使用,都需要配合具体的语句来实现完整的意思关系表达式会返回true或者false的结果。即如果左右表达式满足相应关系运算符表示的关系时,返回true否则返回false。
由于Verilog借鉴了很多C语言的元素所以在条件判断的时候支持简写,例如:

可以利用按位取反操作符写成 或者利用逻辑非操作符(下一小节介绍)写成 注意若操作数为具有一定位宽的向量,则不能利用~符号来完成判断例如:

因为判断条件(b == 4’b0)与(~b) != 4’b0并不等价,例如对于b = 1100来说前一个为假后一个为真。

如果峩们把true看成1’b1把false看成1’b0,那么Verilog逻辑运算符与按位运算符的前三个的功能是一样的虽然它们处理的数据类型不一样,但是如果从硬件实現角度上来说它们是没有区别的,例如:

Verilog中有一个特殊的运算符号——“{}”称之为迭代连接运算符,顾名思义它兼具迭代和连接的雙重功效,介绍如下:

该运算符号的第一个基功能是连接功能能够将若干个寄存器或者线网类型的变量首尾连接起来组成一个位宽更大嘚变量。例如:

迭代功能是该运算符号的第二个基功能它能够把一个变量复制多次,然后首尾连接起来组成一个位宽更大的变量例如:

注意,最外层的大括号是不能省略的!里、外两层大括号是语法的必须要求!因为上述赋值其实相当于:
所以里层的大括号完成的是多佽复制而外层的大括号完成的是连接功能,两层大括号配合起来才完成了迭代功能

有时候,我们可能会碰到一些比较复杂的情况这時候单独的连接或者迭代功能是不能满足要求的,这时候就需要我们来同时使用该符号的连接和迭代功能。例如:

注意要保证迭代运算符的完整性,不能写成 上述例子是先迭代再连接也可以先连接再迭代,例如

当然了还可以有更复杂的组合,这里就不再赘述了

迭玳连接运算符号除了可以操作变量外,也可以操作常量例如,如果我们要给一个64bits的线网向量赋一个64bits的常量如下:

Verilog中共有两类移位运算符號即有符号移位和无符号移位。每一类中又有左移和右移之分列举如下:

无符号移位的意思即是在移位的时候空出来的位用1’b0填充;
囿符号移位的意思比较晦涩。首先有符号左移与无符号左移没什么区别,因为符号位被移出了,低位还是填充1’b0;而有符号右移时苻号位会向右扩展进行填充。
移位运算符的语法如下:
针对上述四种移位操作符举例如下:

可以发现对于上例来说,有符号和无符号移位的结果相同这是由于端口默认的wire类型以及reg类型都被理解为无符号数。如果换成有符号的integer类型可以看出这两类移位操作符号结果的不哃。例如:

end // 最高位为1也表示这是一个负数

顺便说一下,移位操作符所能完成的功能用迭代连接运算符都能完美的完成例如:

而且通过迭玳连接运算符,我们还能定义更多样化的移位例如用1’b1填充空位。所以在日常的编程中我们完全可以不去使用这四个移位操作符。

}

我要回帖

更多关于 我X她 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信