TOP-K相关问题总结

相信这类帖子在网上已经很多了,本帖很基础,仅对两个问题进行总结与收录:

  1. 面试常考题:海量数据TOP-K
  2. 笔试题:词频或者数字等TOP-K
    须知,面试与笔试还是迥然不同的,面试侧重于基础知识是否扎实与知识体系是否完善,笔试则纯粹是要快刀斩乱麻,能得分就行。

1.面试题:海量数据TOP-K

参考
1)直接排序(不推荐,工程中不现实):如果内存足够,并且数据范围已知,可以使用计数排序(桶排序),否则通常不适用。
2)分治:分成若干份,分别局部排序(可使用快排,注意内存限制),然后进行归并。
3)局部淘汰:最小堆(或者优先队列)

2.笔试题:TOP-K

以下摘自leetcode中若干题目,我的解法。主要调用标准库函数

2.1 215. 数组中的第K个最大元素 middle

  1. 傻瓜方法:直接调用sort
  2. 基于快排分区函数
  3. 基于堆调整的局部淘汰

解法3:基于堆调整的局部淘汰

基本技能:堆调整max_heapify函数
代码如下:

2.2 前K个高频元素 middle


解法1:正则表达式排序(略)

解法2:优先队列

直接给出代码:

// 高频面试题:前K个高频元素
// Created by wbzhang on 2020/9/29.
// leetcode No.347

#include <vector>
#include <unordered_map>
#include <map>
#include <queue>

using namespace std;
typedef pair<int,int> pint;

bool cmp(const pint& pa,const pint& pb)
{
	return pa.second > pb.second;
}

class Solution {
public:
	vector<int> topKFrequent(vector<int>& nums, int k) {
		// 哈希表统计频率
		unordered_map<int,int> ump;
		for(auto ele:nums) ump[ele]++; //数字,频率

		// 使用优先队列
		priority_queue< pint,vector<pint>, decltype(&cmp) > pq(cmp); // 大顶堆,但是重载比较函数中的频率排序
		for(auto ele:ump){
			if(pq.size()<k ){
				pq.push(ele);
			}else{
				// 按频率局部淘汰
				if(ele.second > pq.top().second){
					pq.pop();
					pq.push(ele);
				}
			}
		}

		vector<int> res;
		while(!pq.empty()){
			res.push_back(pq.top().first);
			pq.pop();
		}

		return res;
	}

	//		vector<pint> hist(ump.begin(),ump.end() );
};

2.2 前K个高频单词 middle

leetcode No.692
在这里插入图片描述

解法1:库函数sort排序

笔试推荐写法,其中基础技巧包括正则表达式

代码如下:

class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        unordered_map<string,int> ump;
        for(auto str:words) ump[str]++;
        vector<pair<string,int> > vecstri(ump.begin(),ump.end() );
        sort(vecstri.begin(),vecstri.end(),[](const pair<string,int> &pa,const pair<string,int> &pb)
        {
            return pa.second > pb.second ? true : (pa.second ==pb.second && pa.first<pb.first);
        });

        vector<string> res;
        for(int i=0;i<k;++i){
            res.push_back(vecstri[i].first);
        }
        return res;
    }
};

解法2:使用优先队列priority_queue局部淘汰

同样调用库函数,使用优先队列(相当于最小堆),基础技巧:使用自定义优先队列时需要重载比较函数。此处自定义优先队列的比较函数注意要使用结构体内部重载operator()的形式,使用正则表达式或者直接定义比较函数均不够好用。

// 自定义比较函数(不推荐)
bool cmp(const pair<string,int> &pa,const pair<string,int> &pb)
{
	return pa.second>pb.second ? true : (pa.second == pb.second && pa.first < pb.first);
}
// 对应的pq声明写法如下,故不建议使用
priority_queue<strint,vector<strint>, decltype(&cmp) > pq(cmp);

代码如下:

#include<queue>

typedef pair<string,int> strint;

struct cmp{
    bool operator()(const strint &pa,const strint &pb){
        return pa.second>pb.second ? true : (pa.second == pb.second && pa.first < pb.first);
    } 
};
   

class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        unordered_map<string,int> ump;
        for(auto str:words) ump[str]++;

        // 优先队列
        priority_queue<strint,vector<strint>, cmp> pq;
        for(auto ele:ump){
            if(pq.size() < k){
                pq.push(ele);
            }else{
                if(ele.second>pq.top().second 
                || (ele.second==pq.top().second && ele.first <pq.top().first ) ){
                    pq.pop();
                    pq.push(ele);
                }

            }
        }

        vector<string> res; 
        while(!pq.empty() ){
            res.push_back(pq.top().first );
            pq.pop();
        }
        reverse(res.begin(),res.end());
        return res;
    }
};
### 概述 概率张量在深度学习任务中广泛用于表示分类的概率分布,尤其是在分类、检测和分割任务中。当用户在使用概率张量时遇到报错,通常可能与张量的维度、数据类型、内存布局(连续性)或框架特定的输入要求有关。 --- ### 常见错误与解决方案 #### 1. 张量不连续导致操作失败 某些操作如 `view` 要求张量在内存中是连续的,若原始张量通过转置或切片等操作生成而不重新分配内存,则会引发错误。此时应使用 `.contiguous()` 方法创建连续副本后再进行操作[^1]。 ```python # 原始张量可能是转置后不连续的 x = torch.tensor([[1, 4], [2, 5], [3, 6]]).t() print("是否连续:", x.is_contiguous()) # False # 使用 contiguous() 创建连续副本 x_cont = x.contiguous() print("连续副本是否连续:", x_cont.is_contiguous()) # True # 现在可以安全地使用 view print("view 后结果:", x_cont.view(-1)) # tensor([1, 4, 2, 5, 3, 6]) ``` #### 2. 维度或形状不匹配 在进行张量运算(如矩阵乘法、拼接等)时,若输入张量的维度不一致或形状不兼容,会导致运行时错误。应检查张量的 `shape` 属性并确保其符合操作要求。 ```python # 检查两个张量的形状是否匹配 a = torch.randn(3, 4) b = torch.randn(4, 5) try: c = torch.matmul(a, b) except RuntimeError as e: print("形状不匹配,请检查张量维度:", e) ``` #### 3. 数据类型不匹配 例如,在进行损失计算时,若概率张量为浮点数类型(如 `float32`),而标签张量为整数类型(如 `int64`)则通常不会报错;但如果标签张量误用浮点类型,则会触发类型错误。应确保标签使用整数类型(如 `torch.int64`)[^3]。 ```python # 确保标签为整数类型 labels = torch.tensor([0.5, 2.0, 1.0]) # 错误地使用了浮点数 labels = labels.long() # 转换为整数类型 torch.int64 ``` #### 4. 图像数据格式不兼容 在可视化或输入图像张量时,若张量形状为 `(H, W, C)` 而非框架要求的 `(C, H, W)`,或数据类型为 `uint8` 而非 `float32`,也可能导致错误。应使用 `permute` 或 `unsqueeze` 调整通道顺序,并确保使用 `.float()` 转换类型[^2]。 ```python # 假设 image 是从 PIL 图像转换来的 numpy 数组 (H, W, C) image_tensor = torch.from_numpy(image).permute(2, 0, 1).float() / 255.0 ``` --- ### Top-k 和 Top-p 采样方法 #### 1. Top-k 采样 Top-k 采样是一种固定候选集大小的采样方法,它从概率分布中选择概率最高的前 k 个元素,并基于这些元素重新计算概率分布。这种方法可以避免低概率元素对生成结果的干扰,同时保持一定的多样性。 - **优点**:控制候选集大小,避免低概率元素干扰。 - **缺点**:固定的 k 值可能导致在不同分布下采样效果不稳定。 ```python import torch import torch.nn.functional as F # 假设 logits 是模型输出的未归一化分数 logits = torch.randn(1, 1000) k = 50 # 选择概率最高的前 k 个元素 top_k_values, top_k_indices = torch.topk(logits, k=k) # 构建新的 logits,只保留 top-k 元素 new_logits = torch.full_like(logits, float('-inf')) new_logits.scatter_(1, top_k_indices, top_k_values) # 应用 softmax probs = F.softmax(new_logits, dim=-1) ``` #### 2. Top-p 采样(核采样) 核采样是一种自适应的采样方法,它选择的候选词集合 V(p) 是满足累计概率和大于或等于给定阈值 p 的最小词汇子集。与 Top-k 采样不同,核采样的候选词数量不是固定的,而是基于累计概率动态确定的。这使得它在面对不同分布时更具灵活性。 - **优点**:动态调整候选集大小,适应不同概率分布。 - **缺点**:实现相对复杂,计算累计概率需要额外开销。 ```python # 假设 probs 是归一化后的概率分布 probs = F.softmax(logits, dim=-1) p = 0.9 # 对概率进行排序 sorted_probs, sorted_indices = torch.sort(probs, descending=True) # 计算累计概率 cumulative_probs = torch.cumsum(sorted_probs, dim=-1) # 选择累计概率小于等于阈值的最小集合 sorted_indices_to_keep = cumulative_probs <= p # 构建新的概率分布 new_probs = torch.zeros_like(probs) new_probs.scatter_(1, sorted_indices[sorted_indices_to_keep], sorted_probs[sorted_indices_to_keep]) ``` --- ### 总结 概率张量的错误通常源于张量的连续性、形状、数据类型或图像格式的不匹配。解决这些问题需要检查张量的属性并进行适当的调整。在生成任务中,Top-k 和 Top-p 采样方法提供了不同的策略来平衡生成结果的多样性和稳定性,Top-k 更适合固定候选集的场景,而 Top-p 则能根据概率分布动态调整候选集大小。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值