CCF-CSP 202305-2 矩阵计算

本文详细介绍了如何将Transformer中softmax注意力模块的计算简化为点乘操作,包括矩阵乘法、转置的应用,以及提供了一个C++代码示例来处理给定输入格式的矩阵计算。还讨论了时间和空间复杂度优化的相关知识点。
摘要由CSDN通过智能技术生成

😸题目要求

🐈‍⬛题目背景

S o f t m a x ( Q × K T d ) × V Softmax(\frac{Q×{K^T}}{\sqrt{d}})×V Softmax(d Q×KT)×V 是 Transformer 中注意力模块的核心算式,其中 Q Q Q K K K V V V 均是 n n n d d d 列的矩阵, K T K^T KT 表示矩阵 K K K 的转置, × × × 表示矩阵乘法。

🐈‍⬛问题描述

为了方便计算,顿顿同学将 S o f t m a x Softmax Softmax 简化为了点乘一个大小为 n n n 的一维向量 W W W
( W ⋅ ( Q × K T ) ) × V \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad(W·(Q×{K^T}))×V (W(Q×KT))×V
点乘即对应位相乘,记 W ( i ) W^{(i)} W(i) 为向量 W W W 的第 i i i 个元素,即将 ( Q × K T ) (Q×K^T) (Q×KT) i i i 行中的每个元素都与 W ( i ) W^{(i)} W(i) 相乘。
现给出矩阵 Q Q Q K K K V V V 和向量 W W W,试计算顿顿按简化的算式计算的结果。

🐈‍⬛输入格式

从标准输入读入数据。
输入的第一行包含空格分隔的两个正整数 n n n d d d,表示矩阵的大小。
接下来依次输入矩阵 Q Q Q K K K V V V 。每个矩阵输入 n n n 行,每行包含空格分隔的 d d d 个整数,其中第 i i i 行的第 j j j 个数对应矩阵的第 i i i 行、第 j j j 列。
最后一行输入 n n n 个整数,表示向量 W W W

🐈‍⬛输出格式

输出到标准输出中。
输出共 n n n 行,每行包含空格分隔的 d d d 个整数,表示计算的结果。

🐈‍⬛样例输入

3 2
1 2
3 4
5 6
10 10
-20 -20
30 30
6 5
4 3
2 1
4 0 -5

🐈‍⬛样例输出

480 240
0 0
-2200 -1100

🐈‍⬛子任务

70% 的测试数据满足: n ≤ 100 n \leq 100 n100 d ≤ 10 d \leq 10 d10;输入矩阵、向量中的元素均为整数,且绝对值均不超过 30 30 30
全部的测试数据满足: n ≤ 1 0 4 n \leq 10^4 n104 d ≤ 20 d \leq 20 d20;输入矩阵、向量中的元素均为整数,且绝对值均不超过 1000 1000 1000

提示

请谨慎评估矩阵乘法运算后的数值范围,并使用适当数据类型存储矩阵中的整数。

😸问题解决

🐈满分代码(含逐行代码解释)

🍭C++

#include<bits/stdc++.h>
using namespace std;

long long Q[10001][21];
long long K[10001][21];
long long V[10001][21];
long long W[10001];
long long temp1[21][21]; //d*d的矩阵 
long long temp2[10001][10001]; //n*n的矩阵 
long long result[10001][21]; //n*d的矩阵 

int main(){
	//输入 
	int n, d;
	cin >> n >> d;
	for(int i = 0; i < n; i++){
		for(int j = 0; j < d; j++){
			cin >> Q[i][j];
		}
	}
	for(int i = 0; i < n; i++){
		for(int j = 0; j < d; j++){
			cin >> K[i][j];
		}
	}
	for(int i = 0; i < n; i++){
		for(int j = 0; j < d; j++){
			cin >> V[i][j];
		}
	}
	for(int i = 0; i < n; i++){
		cin >> W[i];
	}
	//计算
	/* 
		首先解释一下为什么没有按照正常计算顺序进行运算:
		·矩阵乘法可以使用结合律而不能使用交换律 
		·先算n*n大小的矩阵一是代码不好表示,二是时间和空间复杂度较高,所以不采用 
		·先算d*d大小的矩阵可以在获得正确答案的基础上简化计算过程,这个数学原理显而易见
		·这个就是三行二列乘二行三列和二行三列乘三行二列的区别
		·计算过程中尤其要主要三层循环中每一层的变量取值范围,看清楚是几行几列的矩阵,建议在纸上写一些
		·矩阵乘法牢记下标 c_ij = a_ik * b_kj 
	*/
	/*先算K^T* x V*/
	for(int i = 0; i < d; i++){
		for(int j = 0; j < d; j++){
			temp1[i][j] = 0; //初始一个空矩阵存放此步结果 
			for(int k = 0; k < n; k++){
				temp1[i][j] += K[k][i] * V[k][j]; //矩阵K的转置乘矩阵V 
				//结果是d*d的矩阵 
			}
		}
	} 
	/*再算(Q x K^T) x V*/
	for(int i = 0; i < n; i++){
		for(int j = 0; j < d; j++){
			temp2[i][j] = 0; //初始一个空矩阵存放此步结果 
			for(int k = 0; k < d; k++){
				temp2[i][j] += Q[i][k] * temp1[k][j]; //矩阵Q乘上一步的计算结果 
				//结果是n*d的矩阵
			}
		}
	} 
	/*最后算(W * (Q x K^T)) x V)*/
	for(int i = 0; i < n; i++){
		for(int j = 0; j < d; j++){
			result[i][j] = 0; //初始一个空矩阵存放最终结果 
			result[i][j] = W[i] * temp2[i][j];	//乘以一维向量W[i]
			//结果是n*d的矩阵	
		}
	} 
	//输出 
	for(int i = 0; i < n; i++){
		for(int j = 0; j < d; j++){
			cout << result[i][j] << " "; //输出一个n*d的矩阵		
		}
		cout << endl; //注意矩阵输出是n*d行 
	} 
	return 0;
} 

🐈场景拓展

本题代码用于求解矩阵相关的算法,该代码还可能涉及如下考点:

  • 矩阵乘法、矩阵转置,以及其余矩阵相关的数学计算
  • 针对矩阵计算过程中关于结合律等运算性质的考察
  • 关于时间复杂度和空间复杂度的优化过程
  • 22
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值