Learning to Rank基于pairwise的算法(一)——Ranking SVM、MHR、IRSVM

1.概述

对于搜索任务来说,系统接收到用户查询之后,返回相关文档列表。所以问题的关键是确定文档之间的先后顺序,而pairwise则将重点转向对文档关系是否合理的判断。

在pairwise中,排序算法通常转化为对文档对的分类,分类输入是文档对,结果是哪个文章的相关度更好,学习的目标是减少错误分类的文档对,在完美的模型中,所有的文档对的顺序都被正确分类,于是可以得到某一query下完全正确合理的文档列表,即为完美的排序。

l2r-pairwise
图1 l2r-pairwise

在pairwise方法下,不同算法的最大不同之处在于对应机器学习方法的不同。对现有的大部分pairwise方法进行整理后发现,总结出以下4类:

(1)基于SVM算法的:基于SVM的pairwise算法最早的一种为R.Herbrich等人于2000年提出的Ranking SVM算法;后续基于此算法进行改进的有MHR(Multiple Hyperplanes Rank),该方法使用分而治之的策略,使用多个超平面对实例进行排序,最后聚合超平面给出的排序结果;IRSVM则是针对Ranking SVM中的位值误差和长度误差两大缺陷进行了改进。

(2)基于Boost类:基于Boost的pairwise算法最早的一种为Yoav Freund等人于2003年提出的RankBoost;基于Boost的另一个pairwise算法是GBRank,它是基于回归来解决pair对的先后排序问题。在GBRank中,使用的回归算法是GBT(Gradient Boosting Tree)。

(3)基于RankNet的

# 610ms user time, 70ms system time, 41.38M rss, 106.85M vsz # Current date: Tue Aug 26 19:28:02 2025 # Hostname: ECS-hostname-20240410059 # Files: /home/finance/Data/sika-dbslow.msxf.lo/1/20250826/10.246.6.191_3306.log # Overall: 2 total, 2 unique, 0.00 QPS, 0.00x concurrency ________________ # Time range: 2025-06-23T15:50:27 to 2025-07-24T09:06:04 # Attribute total min max avg 95% stddev median # ============ ======= ======= ======= ======= ======= ======= ======= # Exec time 20s 10s 10s 10s 10s 3ms 10s # Lock time 11us 5us 6us 5us 6us 0 5us # Rows sent 0 0 0 0 0 0 0 # Rows examine 0 0 0 0 0 0 0 # Query size 259 47 212 129.50 212 116.67 129.50 # Profile # Rank Query ID Response time Calls R/Call V/M Item # ==== ================== ============= ===== ======= ===== ====== # 1 0x402CFC71A27ECE45 10.0262 50.0% 1 10.0262 0.00 CREATE # 2 0x98B653F46BD7096E 10.0223 50.0% 1 10.0223 0.00 ALTER # Query 1: 0 QPS, 0x concurrency, ID 0x402CFC71A27ECE45 at byte 69755009 _ # This item is included in the report because it matches --limit. # Scores: V/M = 0.00 # Time range: all events occurred at 2025-06-23T15:50:27 # Attribute pct total min max avg 95% stddev median # ============ === ======= ======= ======= ======= ======= ======= ======= # Count 50 1 # Exec time 50 10s 10s 10s 10s 10s 0 10s # Lock time 45 5us 5us 5us 5us 5us 0 5us # Rows sent 0 0 0 0 0 0 0 0 # Rows examine 0 0 0 0 0 0 0 0 # Query size 81 212 212 212 212 212 0 212 # String: # Databases mysql # Hosts 127.0.0.1 # Users agentmsfinance # Query_time distribution # 1us # 10us # 100us # 1ms # 10ms # 100ms # 1s # 10s+ ################################################################ CREATE USER 'root'@'127.0.0.6' IDENTIFIED WITH 'caching_sha2_password' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT PASSWORD REQUIRE CURRENT DEFAULT\G # Query 2: 0 QPS, 0x concurrency, ID 0x98B653F46BD7096E at byte 69755009 _ # This item is included in the report because it matches --limit. # Scores: V/M = 0.00 # Time range: all events occurred at 2025-07-24T09:06:04 # Attribute pct total min max avg 95% stddev median # ============ === ======= ======= ======= ======= ======= ======= ======= # Count 50 1 # Exec time 49 10s 10s 10s 10s 10s 0 10s # Lock time 54 6us 6us 6us 6us 6us 0 6us # Rows sent 0 0 0 0 0 0 0 0 # Rows examine 0 0 0 0 0 0 0 0 # Query size 18 47 47 47 47 47 0 47 # String: # Databases mysql # Hosts 127.0.0.1 # Users agentmsfinance # Query_time distribution # 1us # 10us # 100us # 1ms # 10ms # 100ms # 1s # 10s+ ################################################################ ALTER USER 'guodong'@'%' IDENTIFIED BY <secret>\G 无法找到 Profile 数据
08-27
从你提供的日志输出和错误信息来看,`无法找到 Profile 数据` 的原因是因为程序在遍历 `# Profile` 段落时,**没有识别到有效的数据行**,这可能是以下几种原因导致的: --- ## ❗问题分析 ### 1. **`# Profile` 段落识别失败** 虽然你提供的日志中有 `# Profile` 段落,但代码中识别 `# Profile` 的逻辑是: ```go if strings.HasPrefix(line, "# Profile") { inProfile = true continue } ``` 但 **`# Profile` 后面可能有空格或其他内容**,例如: ``` # Profile # Rank Query ID Response time Calls R/Call V/M Item # ==== ================== ============= ===== ======= ===== ====== ``` 所以建议改为: ```go if strings.Contains(line, "# Profile") { inProfile = true continue } ``` 这样即使 `# Profile` 不在行首也能识别。 --- ### 2. **`# ====` 行未被跳过** 你当前的代码: ```go if strings.HasPrefix(line, "# ====") { continue // 跳过表头分隔线 } ``` 但如果这行是 `# ==== ================== ============= ===== ======= ===== ======`,那么 `strings.HasPrefix(line, "# ====")` 会失败。 建议改为: ```go if strings.Contains(line, "# ====") { continue } ``` --- ### 3. **段落结束条件判断错误** 你使用了: ```go if strings.HasPrefix(line, "# ") || line == "" { break } ``` 但 `# Query 1:` 或 `# Time range:` 这样的行不会以 `# ` 开头,而是以 `# Query` 开头,因此不会触发退出逻辑,导致程序跳过这些数据。 建议改为: ```go if inProfile && strings.HasPrefix(line, "#") && !strings.Contains(line, "# ====") { break // 遇到其他段落开头,结束提取 } ``` --- ## ✅ 修复后的完整代码(Go) ```go func parseAndAppendProfile(content *strings.Builder, out string) { lines := strings.Split(out, "\n") var profileLines []string inProfile := false for _, line := range lines { if strings.Contains(line, "# Profile") { inProfile = true continue } if inProfile { if strings.Contains(line, "# ====") { continue // 跳过分隔线 } if strings.HasPrefix(line, "#") && !strings.Contains(line, "# ====") { break // 遇到其他段落,结束 Profile 提取 } if line == "" { continue } profileLines = append(profileLines, line) } } if len(profileLines) == 0 { fmt.Println("无法找到 Profile 数据") return } // 构建 HTML 表格 content.WriteString("<br/><strong>慢查询详情:</strong><br/>\n") content.WriteString("<table border='1' style='border-collapse: collapse;'>\n") content.WriteString("<tr><th>Rank</th><th>Query ID</th><th>Response Time</th><th>Calls</th><th>R/Call</th><th>V/M</th><th>Item</th></tr>\n") // 正则表达式匹配每行数据 lineRegex := regexp.MustCompile(`\s*(\d+)\s+([0-9a-fA-Fx]+)\s+([\d.]+\s+\d+\.0%)\s+(\d+)\s+([\d.]+)\s+([\d.]+)\s+(.+)`) for _, line := range profileLines { line = strings.TrimSpace(line) if line == "" { continue } submatch := lineRegex.FindStringSubmatch(line) if len(submatch) < 8 { continue } content.WriteString("<tr>") content.WriteString("<td>" + submatch[1] + "</td>") content.WriteString("<td>" + submatch[2] + "</td>") content.WriteString("<td>" + submatch[3] + "</td>") content.WriteString("<td>" + submatch[4] + "</td>") content.WriteString("<td>" + submatch[5] + "</td>") content.WriteString("<td>" + submatch[6] + "</td>") content.WriteString("<td>" + submatch[7] + "</td>") content.WriteString("</tr>\n") } content.WriteString("</table>\n") } ``` --- ## ✅ 示例输出(成功提取) ```html <br/><strong>慢查询详情:</strong><br/> <table border='1' style='border-collapse: collapse;'> <tr><th>Rank</th><th>Query ID</th><th>Response Time</th><th>Calls</th><th>R/Call</th><th>V/M</th><th>Item</th></tr> <tr><td>1</td><td>0x402CFC71A27ECE45</td><td>10.0262 50.0%</td><td>1</td><td>10.0262</td><td>0.00</td><td>CREATE</td></tr> <tr><td>2</td><td>0x98B653F46BD7096E</td><td>10.0223 50.0%</td><td>1</td><td>10.0223</td><td>0.00</td><td>ALTER</td></tr> </table> ``` --- ## ✅ 总结 | 问题 | 原因 | 解决方式 | |------|------|----------| | 无法找到 Profile 数据 | `# Profile` 段落未被正确识别 | 使用 `strings.Contains(line, "# Profile")` | | 分隔线 `# ====` 未被跳过 | 使用了 `HasPrefix` | 改为 `Contains` | | 段落结束判断失败 | 没有正确识别下个段落 | 改为判断 `#` 开头且不是分隔线 | --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值