Self-study Python Fish-C Note14 P50to51

函数 (part 4)

本节主要讲函数 递归

递归 (recursion)

递归就是函数调用自身的过程
示例1:

def fun1(i):
    if i > 0: 
        print('something')
        i-=1
        fun1(i)
fun1(5) # 这样就会打印五遍 'something'
something
something
something
something
something

要让递归正常工作,一定要有一个结束条件,并且每次调用都会向着这个结束条件去推进。比如上面的例子,加上一个 if 条件语句,让递归在恰当的时候进行回。归小心无限调用的死循环(可以使用 Ctrl + C 强制结束)
有这样一句话“天才程序员用递归,普通程序员用迭代”。接下来我们用几个例子来批判比较一下相同任务递归与迭代的区别:
示例2,求一个数的阶乘:
在数学中,正整数的阶乘(英语:factorial)是所有小于等于该数的正整数的积,记为 n! 例如5的阶乘表示为 5!,其值为120

# 使用迭代
def fact1(n):
    result = n
    for i in range(1,n):
        result*=i
    return result
print(fact1(5))
print(fact1(10))
print(fact1(1))
120
3628800
1
# 使用递归
def fact2(n):
    if n == 1:
        return 1
    else:
        return n*fact2(n-1)
print(fact2(5))
print(fact2(10))
print(fact2(1))  
# print(fact2(-1))  这个如果运行会进入无限循环,挂掉
120
3628800
1

以 fact2(5) 为例,代码中实际做的如下:

递归过程结果
fact2(5) = 5 * fact2(4)——> fact2(5) = 5 * 4 * 3 * 2 * 1
fact2(4) = 4 * fact2(3)——> fact2(4) = 4 * 3 * 2 * 1
fact2(3) = 3 * fact2(2)——> fact2(3) = 3 * 2 * 1
fact2(2) = 2 * fact2(1)——> fact2(2) = 2 * 1
fact2(1) = 1——> fact2(1) = 1

示例3,斐波那契数列:
斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称“兔子数列”,其数值为 1,1,2,3,5,8,13,21,34,55,89……这个数列从第3项开始 ,每一项都等于前两项之和。

# 迭代法
def fib1(n):
    a = 1
    b = 1
    c = 1
    while n > 2:
        c = a + b 
        a = b
        b = c
        n-=1
    return c
print(fib1(1))
print(fib1(2))
print(fib1(3))
print(fib1(4))
print(fib1(5))
print(fib1(6))
1
1
2
3
5
8
# 递归法
def fib2(n):
    if n==1 or n==2:
        return 1
    else:
        return fib2(n-1)+fib2(n-2)
print(fib2(1))
print(fib2(2))
print(fib2(3))
print(fib2(4))
print(fib2(5))
print(fib2(6))
1
1
2
3
5
8

此时:

调用代码过程结果
fib2(1)直接去到 return 11
fib2(2)直接去到 return 21
fib2(3)fib2(3)=fib2(2)+fib2(1)=22
fib2(4)fib2(4)=fib2(3)+fib2(2)=fib2(2)+fib2(1)+fib2(2)3
fib2(5)fib2(5)=fib2(4)+fib2(3)=fib2(3)+fib2(2)+fib2(2)+fib2(1)=fib2(2)+fib2(1)+2*fib2(2)+fib2(1)=55
fib2(6)8

如果我们使用递归计算第 120 个数 即 fib2(120), 我们会发现计算了很长时间依然无法计算出结果。反而,使用迭代 fib1(120) 结果计算的很快。这就体现了递归和迭代之间的效率问题。每一次调用递归函数,他并不会立刻返回,而是要等到最底层的那个函数返回,然后在一层一层往上走,这个过程是非常耗费资源的。

汉诺塔

有三根杆子A,B,C。A杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:
(1) 每次只能移动一个圆盘;
(2) 大盘不能叠在小盘上面。
提示:可将圆盘临时置于 B 杆,也可将从 A 杆移出的圆盘重新移回 A 杆,但都必须遵循上述两条规则
在这里插入图片描述

def hanoi(n,x,y,z):
    if n==1:
        print(x,'-->',z) # 如果只有1层,直接将金片从 x 移动到 z
    else:
        hanoi(n-1,x,z,y) # 将 x 上的 n-1 个金片移动到 y
        print(x,'-->',z) # 将放在最下面的金片从 x 移动到 z
        hanoi(n-1,y,x,z) # 将 y 上的 n-1 个金片移动到 z

hanoi(5,'A','B','C')
A --> C
A --> B
C --> B
A --> C
B --> A
B --> C
A --> C
A --> B
C --> B
C --> A
B --> A
C --> B
A --> C
A --> B
C --> B
A --> C
B --> A
B --> C
A --> C
B --> A
C --> B
C --> A
B --> A
B --> C
A --> C
A --> B
C --> B
A --> C
B --> A
B --> C
A --> C

总结递归函数的执行逻辑:递归函数在调用的时候不会直接立刻的去返回,而是经过层层调用,直到遇到符合条件的时候再触发返回机制,接着再逐层的返回。注意,递归函数一定要有一个结束条件

附言:
题目:Self-study Python Fish-C Note-14 P50-P51
本文为自学B站上鱼C的python课程随手做的笔记。一些概念和例子我个人为更好的理解做了些查询和补充
因本人水平有限,如有任何问题,欢迎大家批评指正!
原视频链接:https://www.bilibili.com/video/BV1c4411e77t?p=8

  • 15
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 以下是一个 Python 代码示例,用于实现 multi-head self-attention: ```python import torch import torch.nn as nn class MultiHeadAttention(nn.Module): def __init__(self, d_model, num_heads): super(MultiHeadAttention, self).__init__() self.num_heads = num_heads self.d_model = d_model self.depth = d_model // num_heads self.query_linear = nn.Linear(d_model, d_model) self.key_linear = nn.Linear(d_model, d_model) self.value_linear = nn.Linear(d_model, d_model) self.output_linear = nn.Linear(d_model, d_model) def forward(self, query, key, value, mask=None): batch_size = query.size() # Linear transformations query = self.query_linear(query) key = self.key_linear(key) value = self.value_linear(value) # Split into heads query = query.view(batch_size * self.num_heads, -1, self.depth) key = key.view(batch_size * self.num_heads, -1, self.depth) value = value.view(batch_size * self.num_heads, -1, self.depth) # Transpose for matrix multiplication query = query.transpose(1, 2) key = key.transpose(1, 2) value = value.transpose(1, 2) # Calculate scores scores = torch.matmul(query, key.transpose(-2, -1)) scores = scores / torch.sqrt(torch.tensor(self.depth).float()) # Apply mask (if provided) if mask is not None: mask = mask.unsqueeze(1) scores = scores.masked_fill(mask == , -1e9) # Softmax attention_weights = nn.Softmax(dim=-1)(scores) # Dropout attention_weights = nn.Dropout(p=.1)(attention_weights) # Multiply by values context = torch.matmul(attention_weights, value) # Reshape and concatenate context = context.transpose(1, 2).contiguous().view(batch_size, -1, self.num_heads * self.depth) # Linear transformation output = self.output_linear(context) return output ``` 希望对你有所帮助! ### 回答2: 下面是使用Python语言实现multi-head self-attention的一个示例代码: ``` import torch import torch.nn as nn import torch.nn.functional as F class MultiHeadSelfAttention(nn.Module): def __init__(self, d_model, num_heads): super(MultiHeadSelfAttention, self).__init__() self.num_heads = num_heads self.d_head = d_model // num_heads self.fc_query = nn.Linear(d_model, d_model) self.fc_key = nn.Linear(d_model, d_model) self.fc_value = nn.Linear(d_model, d_model) self.fc_concat = nn.Linear(d_model, d_model) def forward(self, x): batch_size, seq_len, d_model = x.size() h = self.num_heads # Split input into multiple heads query = self.fc_query(x).view(batch_size, seq_len, h, self.d_head) key = self.fc_key(x).view(batch_size, seq_len, h, self.d_head) value = self.fc_value(x).view(batch_size, seq_len, h, self.d_head) # Compute attention scores scores = torch.matmul(query, key.transpose(-2, -1)) / (self.d_head ** 0.5) attn_weights = F.softmax(scores, dim=-1) # Apply attention weights to value vectors attended_values = torch.matmul(attn_weights, value) attended_values = attended_values.transpose(1, 2).contiguous().view(batch_size, seq_len, -1) # Concatenate and linearly transform attended values output = self.fc_concat(attended_values) return output # 使用示例 d_model = 128 num_heads = 8 seq_len = 10 batch_size = 4 input_tensor = torch.randn(batch_size, seq_len, d_model) attention = MultiHeadSelfAttention(d_model, num_heads) output = attention(input_tensor) print("Input Shape: ", input_tensor.shape) print("Output Shape: ", output.shape) ``` 上述代码定义了一个`MultiHeadSelfAttention`的类,其中`forward`函数实现了multi-head self-attention的计算过程。在使用示例中,我们输入一个大小为`(batch_size, seq_len, d_model)`的张量,经过multi-head self-attention计算后输出一个大小为`(batch_size, seq_len, d_model)`的张量。其中`d_model`表示输入的特征维度,`num_heads`表示attention头的数量。 ### 回答3: 下面是使用Python实现multi-head self-attention示例的代码: ```python import torch import torch.nn as nn class MultiHeadSelfAttention(nn.Module): def __init__(self, embed_size, num_heads): super(MultiHeadSelfAttention, self).__init__() self.embed_size = embed_size self.num_heads = num_heads self.head_size = embed_size // num_heads self.query = nn.Linear(embed_size, embed_size) self.key = nn.Linear(embed_size, embed_size) self.value = nn.Linear(embed_size, embed_size) self.out = nn.Linear(embed_size, embed_size) def forward(self, x): batch_size, seq_len, embed_size = x.size() # Split the embedding into num_heads and reshape x = x.view(batch_size, seq_len, self.num_heads, self.head_size) x = x.permute(0, 2, 1, 3) # Apply linear transformations to obtain query, key, and value query = self.query(x) key = self.key(x) value = self.value(x) # Compute scaled dot product attention scores scores = torch.matmul(query, key.permute(0, 1, 3, 2)) scores = scores / self.head_size**0.5 # Apply softmax to obtain attention probabilities attn_probs = nn.Softmax(dim=-1)(scores) # Apply attention weights to value and sum across heads attended = torch.matmul(attn_probs, value) attended = attended.permute(0, 2, 1, 3) attended = attended.contiguous().view(batch_size, seq_len, self.embed_size) # Apply output linear transformation output = self.out(attended) return output ``` 上述代码中定义了一个名为MultiHeadSelfAttention的类,继承自nn.Module,可以通过指定嵌入大小(embed_size)和头部数量(num_heads)来创建多头自注意力层。在前向传播方法forward中,先通过线性变换将输入张量分别变换为查询(query)、键(key)和值(value)张量。然后计算缩放点积注意力得分,将其作为注意力概率经过softmax函数进行归一化。通过注意力概率权重对值进行加权求和,并应用线性变换得到最终的输出张量。最后返回输出张量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值