Jiahong 的个人博客

凡事预则立,不预则废


  • Home

  • Tags

  • Archives

  • Navigation

  • Search

Linux——jq命令详解


整体说明

  • jq 是 Linux 下轻量、强大的命令行 JSON 处理器
  • 包含功能:
    • JSON 数据的解析、过滤、转换、格式化与流式处理
  • 是平时工作时,日志查看抽取,Json 字段修改,构造新的 Json 等常用的工具

jq 基本语法

  • 从文件读取:

    1
    jq [参数] 'filter_statement' [json_file_name]
    • 参数 用于指定输出形式等
    • 'filter_statement' 表示过滤器表达式,可以用于访问 Json 对象内部的字段
    • json_file_name 表示 Json 文件名
  • 或从标准输入读取

    1
    2
    cat data.json | jq 'filter_statement'
    echo '{"key":"value"}' | jq 'filter_statement'

jq 常用参数:

  • -c/--compact-output:
    • 单行显示 JSON(紧凑输出,不做美化),常用于压缩过长的输出(注:输出是有颜色的,所以相比普通的纯文本会更易读取)
  • -r/--raw-output:
    • 输出原始字符串,自动去除 JSON 引号,主要适合脚本直接使用文本内容
  • -C:
    • 启用彩色高亮输出(当前默认终端都启用这个参数的)

jq 常用过滤器

基础访问语法

  • .:输出整个输入 JSON,用于格式化美化
    • 示例:echo '{"name":"Alice"}' | jq '.'
    • 注:
      • 省略过滤器时默认跟 . 效果一致
      • 使用是记得加双引号
  • .字段名:提取对象字段值
    • 嵌套时可用 .父字段.子字段 链式访问
    • 示例:echo '{"user":{"name":"Bob"}}' | jq '.user.name'
  • .字段名?:
    • 安全访问字段,字段不存在时返回 null,不报错
  • .[索引]:
    • 访问数组元素,索引从 0 开始
    • .[起始:] 取切片
    • .[起始:结束] 取区间
    • 示例:echo '["a","b","c"]' | jq '.[1]'(取第二个元素)
  • .[]:
    • 迭代数组/对象所有元素,展开为独立输出
    • 示例:echo '["x","y"]' | jq '.[]'(每行输出一个元素)
  • .[]?:
    • 安全迭代,空数组/对象时不报错

jq 管道与组合

  • |:

    • 管道符,可将前一个过滤器的输出作为后一个的输入
    • 示例:echo '{"users":[{"name":"A"},{"name":"B"}]}' | jq '.users[] | .name'
      • 输出为两行
        1
        2
        "A"
        "B"
  • ,:

    • 多表达式组合,依次执行并输出多个结果
    • 示例:echo '{"a":1,"b":2}' | jq '.a, .b'
      • 输出为两行
        1
        2
        1
        2
  • A // B:

    • 空值合并,A 为 null 或 空时,取 B
    • 示例:echo '{"name":null}' | jq '.name // "Unknown"'

jq 常用内置函数

  • keys:获取对象的键名列表,或数组的索引列表

    • 示例:echo '{"x":1,"y":2}' | jq 'keys'
      • 输出:
        1
        2
        3
        4
        [
        "x",
        "y"
        ]
  • length:返回数组长度、字符串长度或对象键的数量

    • 示例:echo '{"name":null}' | jq 'length'
      • 输出
        1
        1
  • map(过滤器):对数组每个元素应用过滤器,返回新数组

    • 示例:echo '[1,2,3]' | jq 'map(. * 2)'(每个元素的值翻倍)
      • 输出:
        1
        2
        3
        4
        5
        [
        2,
        4,
        6
        ]
  • select(条件):按条件过滤元素,保留满足条件的结果

    • 示例:echo '[{"age":20},{"age":30}]' | jq '.[] | select(.age > 25)'
      • 输出:
        1
        2
        3
        {
        "age": 30
        }
  • sort_by(字段):按指定字段对数组排序

    • 示例:echo '[{"age":30},{"age":20}]' | jq 'sort_by(.age)'
      • 输出:
        1
        2
        3
        4
        5
        6
        7
        8
        [
        {
        "age": 20
        },
        {
        "age": 30
        }
        ]

jq 的一些常用用法示例

  • 构造新 JSON

    1
    2
    3
    # 提取指定字段并重组
    echo '{"name":"Alice","age":30,"city":"Beijing"}' | jq '{userName:.name, userAge:.age}'
    # 输出:{"userName":"Alice","userAge":30}
  • 条件判断与修改

    1
    2
    # 年龄大于25则标记为成年
    echo '[{"name":"A","age":20},{"name":"B","age":30}]' | jq '.[] | . + {adult:(.age > 25)}'

NLP——词嵌入的前世今生

词嵌入的发展,NNLM,CBOW,skip-gram
词嵌入(Word Embedding)也称为词向量(Word2Vec): 指的是将词转换成向量的形式


基本问题

  • 用语言模型做预训练
    • 语言模型: 如何计算一段文本序列在某种语言下出现的概率?
  • 如何表示文本中的一个词?

文本表示模型

词袋模型

  • One-Hot: 向量维度与词的数量相同,某个维度为1,其他都为0
  • TF-IDF: 计算词对文档的贡献(权重)
  • TextRank: 类似于PageRank的思想
    • 如果一个单词出现在很多单词后面的话,那么说明这个单词比较重要
    • 一个TextRank值很高的单词后面跟着的一个单词,那么这个单词的TextRank值会相应地因此而提高
优缺点
  • OneHot:
    • 维度灾难
    • 语义鸿沟
  • TF-IDF和TextRank
    • [个人理解]也无法表示语义,所以存在语义鸿沟

主题模型

  • LSA: 矩阵分解(SVD)的方式,又名 Latent Semantic Indexing
  • pLSA: 简单的概率隐式语义模型
  • LDA: 无监督聚类常用的模型,有Gibbs Sampling和变分推断等实现,网上有很多资料,也有很多实现
  • L-LDA: LDA的有监督版本,核心思想就是在LDA吉布斯实现的版本基础上加上采样限制,使得每次采样只能控制在对应主题上,效果比较好,网上没有官方的实现,大部分实现不完全,感兴趣可以试试我的实现Labeled-LDA-Python
    • L-LDA理论上可以得到比LDA质量更高,效果更好的词向量
优缺点:
  • LSA:
    • 利用全局语料特征
    • 但SVD求解计算复杂度大

基于词向量的固定表征

  • Word2vec: 基于上下文训练, 拥有相同上下文的词相近,基于分布式假设(相同上下文语境的词有似含义)
  • FastText: 通过对”词-词”共现矩阵进行分解从而得到词表示的方法,基于分布式假设(相同上下文语境的词有似含义)
  • GloVe: 基于全局预料,结合了LSA和word2vec的优点, 论文: GloVe: Global Vectors for Word Representation
优缺点
  • 目前提到的以上所有都是静态词向量,无法解决一次多义等问题
  • Word2vec和FastText:
    • 优化效率高,但是基于局部语料
    • 本质上二者也都是语言模型
  • GloVe:
    • 基于全局语料库、并结合上下文语境构建词向量, 结合了LSA和word2vec的优点
    • 可以看作是更换了目标函数和权重函数的全局word2vec

基于词向量的动态表征

  • ELMo:
    • 采样 LSTM 提取特征
    • 采用双向语言模型
    • 实际上是两个单向语言模型(方向相反)的拼接,这种融合特征的能力比BERT一体化融合特征方式弱
  • GPT:
    • 采样 Transformer 进行特征提取
    • 采用单向语言模型
  • BERT:
    • 采用Transformer进行特征提取
    • 采用双向语言模型
优缺点
  • 三者都是基于语言模型的动态词向量,能解决一次多义问题
  • ELMo:
    • 采用1层静态向量+2层 LSTM,多层提取能力有限
  • GPT和BERT:
    • Transformer 可采用多层,并行计算能力强
  • 很多任务表明:
    • Transformer 特征提取能力强于 LSTM
  • GPT和BERT都采用 Transformer,Transformer 是 encoder-decoder 结构,GPT 的单向语言模型采用 decoder 部分,decoder 的部分见到的都是不完整的句子;BERT 的双向语言模型则采用 encoder 部分,采用了完整句子

NNLM/RNNLM

  • 词向量为副产物,存在效率不高等问题;

不同模型的对比

word2vec vs NNLM

  • 本质上都可以看做是语言模型
  • 对 NNLM 来说,目不是词向量,是语言模型
  • word2vec 虽然本质上也是语言模型,但是更关注词向量本身,因此做了很多优化来提高计算效率
    • 与 NNLM 相比,词向量直接 sum(CBOW),而不是拼接,并取消隐藏层
    • 考虑到 Softmax 需要遍历整个词汇表,采用 Hierarcal Softmax和 Negative Sampling 进行优化
    • word2vec 所做的一切都是为了一切为了快速生成词向量

word2vec vs fastText

  • 都可以进行无监督学习, fastText 训练词向量时会考虑 subword(子词)
    • 在 fastText 中,每个中心词被表示成子词的集合,下面我们用单词 “where” 作为例子来了解子词是如何产生的
    • 首先,我们在单词的首尾分别添加特殊字符“<”和“>”以区分作为前后缀的子词
    • 然后,将单词当成一个由字符构成的序列来提取 n 元语法
      • 例如,当n=3时,我们得到所有长度为3的子词:“<wh>” “whe” “her” “ere” “<re>”以及特殊子词“<where>”
  • fastText还可以进行有监督学习进行文本分类:
    • 结构与CBOW类似,但学习目标是人工标注的分类结果, 而不是中心词 \(w\)
    • 采用hierarchical softmax对输出的分类标签建立哈夫曼树,样本中标签多的类别被分配短的搜寻路径;
    • 引入N-gram,考虑词序特征;
    • 引入subword(子词)来处理长词,处理未登陆词问题

word2vec vs GloVe vs LSA

  • word2vec vs GloVe
    • 语料库:
      • word2vec是局部语料库训练的,特征提取基于滑动窗口,可以在线学习
      • GloVe的滑动窗口是为了建立”词-词”共现矩阵,需要统计共现概率,不能在线学习
    • 无监督学习:
      • word2vec是无监督学习
      • GloVe通常被认为是无监督学习(无需标注),但实际上GloVe还是有label的,即共现次数 \(log(X_{ij})\)
    • 损失函数:
      • word2vec损失函数实质上是带权重的交叉熵,权重固定;
      • GloVe的损失函数是最小平方损失函数,权重可以做映射变换
    • 总结:
      • word2vec的损失函数是带权重的交叉熵 , 权重是固定的
      • GloVe可以看作是目标函数为MSE ,权重函数可变的全局word2vec
  • GloVe vs LSA
    • LSA(Latent Semantic Analysis)可以基于co-occurance matrix构建词向量,实质上是基于全局语料采用SVD进行矩阵分解,然而SVD计算复杂度高;
    • GloVe可看作是对LSA一种优化的高效矩阵分解算法,采用Adagrad对最小平方损失进行优化;

ELMo、GPT、BERT

  • 三者使用了两种不同的NN组件提取特征
    • ELMo: LSTM
    • GPT, BERT: Transformer
  • 三者使用了两种不同的语言模型:
    • GPT: 单向语言模型
    • ELMo, BERT: 双向语言模型
    • ELMo实际上是两个方向相反的单向语言模型的拼接, 融合特征的能力比BERT那种一体化的融合特征方式弱
    • GPT和BERT都采用Transformer,Transformer是 Encoder-Decoder结构
      • GPT为单向语言模型,采用的是Decoder部分, Decoder部分见到的都是不完整的句子
      • BERT为双向语言模型,采用的是Encoder部分,采用了完整的句子

下面是一些模型的详细介绍


NNLM

模型目标

  • 给定一段长度为m的序列时,预测下一个词(第m+1)个词出现的概率
  • 参考博客DL——NNLM

word2vec

  • 此处只做简单介绍,更详细的讲解请参考博客word2vec学习笔记
  • 一个比较好的手写笔记版:通俗易懂讲解Word2vec的本质 - 对白的文章 - 知乎
  • 损失函数: 带权重的交叉熵损失函数(权重固定)

两类实现

  • 一般实现: 每次训练一个样本 \((w_{in}, w_{out})\),需要计算所有词和当前词一起对应的 softmax, 很耗时间,需要 \(O(V)\) 复杂度的时间
  • 优化实现: Hierarcal Softmax 和 Negative Sampling 两种方式
    • Hierarcal Softmax: 每次训练一个样本 \((w_{in}, w_{out})\),只需要更新平均 \(O(log V)\) 个非叶子节点(每个非叶子结点就是一个词向量)即可
    • Negative Sampling: 每次训练一个样本 \((w_{in}, w_{out})\),只需要更新 \({w_j|w_j\in {w_O}\bigcup W_{neg}}\) 个词向量, 通常负样本的集合大小取 \(log(V)\) 量级甚至更小
层次 Softmax 模型框架
  • 层次 Softmax(Hierarchical Softmax),也称为层次化 Softmax
  • 对于词表大小为 \(V\) 的场景,如果使用普通的 Softmax,则每次需要在分母上计算 \(V\) 次指数运算并求和,使用 层次 Softmax 后转化为 \(\log(V)\) 次二分类任务
  • 核心思想: 输出层由语料库中词出现的频数当作权值构造出的哈夫曼树作为输出
  • 引入哈夫曼树 , Hierarcal Softmax是一种有效计算Softmax的方式,使用二叉树来表示
  • 其中Hierarchical Softmax模型的输出层由语料库中词出现的频数当作权值构造出的哈夫曼树作为输出
  • CBOW
    • 输入层: \(2c\)个词向量
    • 投影层: \(2c\)个词向量的累加
    • 输出层: 哈夫曼树(重点是词w所在的叶子节点, 以及w到根节点的路径)
      • 所有单词没有输出向量表示形式,每个内部结点有一个输出向量 \(v\)
      • 输出层共有 \(V - 1\) 个非叶节点, 也就是要学习 \(V - 1\) 个输出向量
      • 看起来并没有什么优化,但是每次迭代训练一个样本 \(w_{in}, w_{out}\) 时,我们只需要优化从根节点到输出单词 \(w_{out}\) 的路径上的输出向量即可, 平均共 \(O(log V)\) 个向量
      • 每个单词的概率计算需要考虑叶子节点来求出
    • 传统softmax公式 :在传统的softmax函数中,计算目标词与所有词汇之间的概率分布公式为\(P ( w_t | C ) = \frac{e^{v_{w_t}^T v_{w_c} } }{\sum_{w’} e^{v_{w’}^T v_{w_c} } }\),其中\(w_t\)是目标词,\(w_c\)是上下文词,\(v_{w_t}\)和\(v_{w_c}\)是目标词和上下文词的词向量
      • 其中:\(v_{w_c}\) 和 \(v_{w_t}\) 就是我们要学习的词向量
    • 层次softmax公式 :给定一个目标词\(w_t\),上下文词\(w_c\),通过霍夫曼树计算从目标词到上下文词的路径概率。假设从根节点到叶节点的路径上有多个节点,对于目标词\(w_t\),路径\(P\)到达目标词,要最大化的概率公式为\(P ( w_t | C ) = \prod_{k = 1}^{L} \sigma(v_{w_t}^T v_{n_k})\)。其中\(L\)是路径的长度(即路径上的节点数),\(n_k\)是路径上的第\(k\)个节点(非叶子节点),\(v_{w_t}\)是目标词的词向量,\(v_{n_k}\)是路径节点\(n_k\)的词向量。每一个路径的概率通过sigmoid函数计算:\(\sigma(x) = \frac{1}{1 + e^{-x} }\)
      • 其中:\(v_{w_t}\) 就是我们要学习的词向量
  • Skip-gram
    • 输入层:词 \(w\) 的向量
    • 投影层:依旧是词 \(w\) 的向量
    • 输出层:哈夫曼树(重点是词 \(w\) 的上下文窗内 \(2c\) 个词所在的叶子节点,以及各自到根节点的路径)
Negative Sampling模型框架
  • 实际上就是简单的对每一个样本中每一个词都进行负例采样(本质上就是用负采样代替了层次softmax的哈弗曼树)
  • 负采样是噪声对比估计的一个简化版本,目的是提高训练速度并改善所得词向量的质量
  • 核心思想: 在每次迭代过程中, 有大量的输出向量需要更新,为了解决这一困难, Negative Sampling的做法是只更新其中一部分输出向量
  • 利用不同词在语料中出现的频次多少来决定被采样到的概率(单词出现的频次越大,越可能被选做负样本)
  • 负采样: 利用不同词在语料库中出现的频次多少来决定该词被采样到的概率
  • 每次训练一个样本 \((w_{in}, w_{out})\),只需要更新 \({w_j|w_j\in {w_O}\bigcup W_{neg}}\) 个词向量, 通常负样本的集合大小取 \(log(V)\) 量级甚至更小
  • CBOW
    • 输入层: \(2c\)个词向量
    • 投影层:\(2c\)个词向量的累加
    • 输出层:负采样词集(重点是词w的负词词集的参数 \(\theta\),负词的概率永远是1-Sigmoid函数值)
  • Skip-gram模型:
    • 输入层:词w的向量
    • 投影层:依旧是词w的向量
    • 输出层:每个上下文词u的负采样(重点是词w的负词词集的参数 \(\theta\),负词的概率永远是1-Sigmoid函数值)
总结
  • 层次Softmax模型:
    • CBOW模型的一次更新是:
      • 输入 \(2c\) 个词向量的累加(简单的sum,而不是拼接)
      • 对中心词 \(w\) 上的路径节点系数进行更新(输出层)
      • 对所有的上下文词的词向量进行整体一致更新(输入层到隐藏层的参数, 修改多个词向量)
    • Skip-gram模型的一次更新是:
      • 输入中心词 \(w\) 的词向量(直接输入,无其他操作)
      • 对每个上下文词 \(u(i)\) 所在的路径上的节点系数进行更新(输出层,修改多个上下文的结点)
      • 对词w的词向量进行单独更新(输入层到隐藏层的参数,只修改一个词向量)
    • CBOW一次训练更新计算量小,Skip-gram一次训练计算量大(更新的系数更多?)
  • CBOW看起来更新的更平滑,适合小量文本集上的词向量构建,Skip-gram每次更新都更加有针对性,所以对于大文本集上表现更好

word2vec的缺陷

  • 不能解决多义词问题(只能把几种意思同时编码进向量里)
    • 这是所有固定词向量的通病
    • 解决方案是动态词向量(允许不同上下文使用不同词向量表达同一个词),包括ELMo, GPT, BERT

GPU——显卡相关知识

  • 参考链接:
    • 大模型GPU性能与性价比天梯图详解!各类GPU的大模型训练与推理性能对比,以及主流GPU性价比分析!

显卡性能详细天梯图

  • 显卡性能天梯图(截止到 24 年底):
  • H100 SXM 和 H100 性能不一致的原因主要是接口不同导致的,SXM 英伟达设计的一种集成到主板上的接口,支持更大功率,能发挥的显卡性能也更好
  • 图中没有显示的常见显卡简单说明:
    • H800 是 H100 的替代卡,通过降低互联带宽(从 H100 的 900GB/s 降至 450GB/s)规避出口限制,但算力仍达行业顶尖水平
      • 23 年腾讯开始使用
    • H20 是 25年新发布的 H100 限制版芯片,目前有 96G 和 141G 版本,带宽 4TB/s (高于 H100 的 3.35TB/s) 性能大概只有

AI 相关显卡对比

  • 注意:本图来自网络(不一定符合真实情况)
  • 文字总结:
    • 华为
        1. Ascend 910C:A100 的 2.56 倍
        1. Ascend 950DT:A100 的 1.6 倍
        1. Ascend 910B:A100 的 1.026 倍
        1. Ascend 910:A100 的 0.82 倍
    • 寒武纪
        1. Siyuan 690:A100 的 1.8倍
        1. Siyuan 590:A100 的 0.9 倍
        1. Siyuan 290:A100 的 0.82倍
    • 海光
        1. BW100(DCU3):A100 的 1.12 倍
        1. K100(DCU2):A100 的 0.32 倍
        1. Z100(DCU1):A100 的 0.26倍
    • 沐曦
        1. C500:A100 的 0.77 倍
        1. MXN100:A100 的 0.26 倍
    • 壁仞
        1. BR106M/BR106B:A100 的 0.54 倍
        1. BR106C:A100 的 0.41 倍
    • 摩尔线程
        1. MTT S4000:A100 的 0.31 倍
        1. MTT S3000:A100 的 0.1倍。

主要显卡对照记录

  • 主要显卡对照记录表格
    型号 发布时间 架构 主要身份 / 卖点 备注
    H100 2022 年 Q3 Hopper 首款 Hopper 架构旗舰,取代 A100 80 GB HBM3,989 TFLOPS FP16,是 2022 年的“卡皇”
    H800 2023 年 Q1 Hopper 中国特供“缩水版 H100” 带宽砍到 2 TB/s,算力基本保留
    H200 2023-11-13 Hopper H100 的“显存升级版” 141 GB HBM3e + 4.8 TB/s,推理速度大约 2×H100
    H20 2023-11 Ampere(部分文献亦标为 Hopper 降规) 中国特供“再缩水版”,算力只有 H100 的 1/7 96 GB HBM3、148 TFLOPS FP16,对标昇腾 910B
  • 注:H20 虽官方 PPT 仍写 Hopper,但算力/显存规格与 H100/H200 差距过大,业内多视为“Ampere 时代”最后一款中国特供卡
  • H910 系列显卡
    • 亲自测试结论:
      • 910B 约是 H800 的 1/4
      • 910C 约是 H800 的 1/3?

附录:GPU、NPU 和 TPU 辨析

  • NPU(Neural Processing Unit,神经网络处理器)是专门为加速人工智能和深度学习任务设计的硬件芯片
    • NPU 采用针对神经网络算法优化的指令集和硬件结构,如高效矩阵乘法单元、低精度数据处理能力,能以低功耗实现高性能计算,支持边缘智能,可在本地处理数据
    • NPU 主要应用于智能手机、智能安防、自动驾驶、医疗等领域,如手机 AI 拍照、实时人脸识别等
    • 华为的升腾 910B 就是 NPU
  • TPU(Tensor Processing Unit,张量处理单元)是谷歌为加速机器学习工作负载,特别是 TensorFlow 框架下的深度学习任务而定制开发的专用集成电路
    • TPU 采用定制化架构,针对张量运算优化,有高效矩阵乘法单元和专用内存结构,能在较低功耗下实现极高计算性能
    • TPU 主要应用于大规模数据中心的深度学习训练和推理任务
  • GPU(Graphics Processing Unit,图形处理器)是一种专门用于处理图形和图像相关运算的微处理器
    • GPU 拥有大量流处理器,可并行处理多个线程,最初用于图形渲染,如游戏中的3D场景渲染等
    • 因 GPU 强大的并行计算能力,也广泛应用于深度学习、科学计算、影音编辑和渲染等领域

GitHub——主题推荐算法

Topic Suggestions for Millions of Repositories

Github官网原文


总体流程图


分为七个步骤:


Readme 预处理与清除

(Readme preprocessing and cleanup)

  • 移除不要的文本块(Remove unwanted blocks)
    • 去除没用文本:一些块是没用的,比如code, table 和image链接等
  • 文本划分(Text segmentation)
    • 提取有用的文本:一个启发式的README tagger,借助格式分析:分析缩进(indentation),空格(spacing),和反撇号( `, backtick)等决定是否是有用信息【说明:这里语法分析是没必要的,我们只关心有用的文本,其他的都是为噪音(noise)】
    • 提取到有用文本后,删除拓展部分:HTML标签,路径和其他处理出来的部分等
    • 最后将剩下的文本进行粗粒度分割【用标点符号(punctuation marks)和README中的一些符号,比如临近哈希符号(contiguous hash symbols)】

生成候选主题

(Generate candidate topics)

  • 用自定义的停用词去将词单元划分出来(Use custom stop words to break text into word units)
    • 停用词去除
    • n-gram分段小于等于4(测试发现小于4的比较好,太长的主题过于具体了,不合适)

移除噪音主题

(Eliminate noisy topics)

  • 用逻辑回归模型删减“bad”主题(Use a logistic regression model to prune “bad” topics)
    • 监督逻辑回归模型(supervised logistic regression model)主要针对除了频数比较小的外,一些不好的,比如”great”, “cool”等,这里的模型是一个分类模型,分为两类(good[positive] and bad(negative)) , 我们称之为关键词过滤模型
      • 手动收集大约300个数据集作为训练集
      • 单个动词一般都是Bad类请教师兄
      • 其他的(Other features we used were occurrence of user names, n-gram size of a phrase, length of a phrase, and numeric content within a phrase.)
      • 以后打算加入回馈机制(来自用户的)去更新这个关键词过滤模型:接受度高的词作为positive的,接受度低的作为停用词或者negative的
  • 移除不满足最小频数的主题(Eliminate topics not satisfying minimum frequency count)

给主题评分

(Score Topics)

  • 用混合tf-idf分数,以主题频率和n元词作为打分标准(Use combination of tf-idf scores, topic frequency and n-gram size for scoring)
    • 评估多种指标后,选择了点互信息指标(PMI): Pointwise Mutual Information
    • 考虑另一种指标tf-idf:参考论文Using TF-IDF to Determine Word Relevance in Document Queries.pdf或者Python的实现
    • The second approach we tried uses the average tf-idf scores of individual words in a phrase weighted by the phrase frequency (if it’s more than one word long) and n-gram size.
    • 两种方法比较:
      • PMI: 强调独特性,越特殊的短语评分越高,但有些可能只是拼写错误(Typo)或者是没有被删除的代码片段
      • tf-idf: 不强调独特性,最终选择是tf-idf,原因是这个指标较好的平衡了独特性和主题与仓库(repository)的相关程度
    • 在tf-idf的基础上还添加了一下其他的比如boosting scores等方法
    • 下面是TF-IDF的说明*

规范化主题

(Canonicalize topics)

  • 使用内部词典规范主题形式(Use an in-house dictionary to suggest canonical form of topics)

    • 解决文字层面的差别和变化等,比如下面四个主题

      neural-network
      neural-networks
      neuralnetwork
      neuralnetworks


移除相似的主题

(Eliminate near similar topics)

  • 用基于Jaccard相似性评分的贪心算法(Greedy approach using Jaccard similarity scoring)

    • motivation:在得到Top-N的主题后,有些主题其实很相似,虽然都有用,但是他们只是在不同粒度描述了同一个主题而已,因此我们需要删除一些重复的,比如下面的例子

      machine learning
      deep learning
      general-purpose machine learning
      machine learning library
      machine learning algorithms
      distributed machine learning
      machine learning framework
      deep learning library
      support vector machine
      linear regression

    • method: 两个短语的相似性计算使用的是基于词的Jaccard相似性(两个短语的差集与并集的比值,因为它对较短的短语很有效,而且分数是[0-1]的,很方便设置阈值(thresholds)),用贪心算法,如果两个主题相似,去除分数较低的那一个,上面的例子去除相似主题后的结果是:

      machine learning
      deep learning
      support vector machine
      linear regression


返回Top-K主题作为最终结果

Go语言——String与Slice深度解析

Note: 他们都是struct类型的


String

type StringHeader struct {
    Data uintptr
    Len  int
}

Slice

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

把Slice转换为String的语法为

[]byte("string")
string([]byte{1,2,3})

注意:这种实现会有拷贝操作


如何避免拷贝操作呢?

答案是自己实现指针转换(也可用反射实现头部转换),省去复制数据部分,同时注意这种实现后底层的数据不能再更改了,不然容易引发错误

直接修改指针类型并构建相应头部

func String2Slice(s string) []byte {
    sp := *(*[2]uintptr)(unsafe.Pointer(&s))
    bp := [3]uintptr{sp[0], sp[1], sp[1]}
    return *(*[]byte)(unsafe.Pointer(&bp))
}

func BytesToString(b []byte) string {
    return *(*string)(unsafe.Pointer(&b))
}

使用反射机制获取到头部再进行转换

func Slice2String(b []byte) (s string) {
    pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b))
    pstring := (*reflect.StringHeader)(unsafe.Pointer(&s))
    pstring.Data = pbytes.Data
    pstring.Len = pbytes.Len
    return
}


func String2Slice(s string) (b []byte) {
    pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b))
    pstring := (*reflect.StringHeader)(unsafe.Pointer(&s))
    pbytes.Data = pstring.Data
    pbytes.Len = pstring.Len
    pbytes.Cap = pstring.Len
    return
}

Go语言——切片和数组的区别

关于切片与数组的区别,这里给出定义和传值等使用上的区别,最后给出总结


数组定义

  • 申明类型定义
1
2
var arr [2]byte
arr[0] = 10
  • 直接赋值定义
1
arr := [2]byte{1,2}

切片定义

  • 申明类型定义
1
var sli []byte
  • 直接赋值定义
1
sli := make([]byte, 5)

作为参数传入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package main

import "fmt"

func changeSlicePoint(p *[]byte){
(*p)[0] = 100
}

func changeSlice(a []byte){
a[4] = 100
}

func changeArrayPoint(p *[5]byte){
(*p)[0] = 100
}

func changeArray(a [5]byte){
a[4] = 100
}

func main() {
array := [5]byte{1,2,3,4,5}
var sli1 []byte
sli1 = make([]byte, 5)
slice := []byte{1,2,3,4,5}

changeSlicePoint(&sli1)
changeSlice(sli1)
changeArray(array)
changeArrayPoint(&array)
changeSlicePoint(&slice)
changeSlice(slice)

fmt.Printf("slice[0]: %d\nslice[4]: %d\n", slice[0], slice[4])
fmt.Printf("sli1[0]: %d\nsli1[4]: %d\n", sli1[0], sli1[4])
fmt.Printf("array[0]: %d\narray[4]: %d\n", array[0], array[4])
}


//output:
//slice[0]: 100
//slice[4]: 100
//sli1[0]: 100
//sli1[4]: 100
//array[0]: 100
//array[4]: 5
//

总结

  • 切片传入时是按照引用传递的,加上指针甚至可以修改引用(内存地址)本身

    比如下面的代码会是的传入的引用重新指向nil指针:

1
2
3
4
func changeSlicePoint(p *[]byte){
(*p)[0] = 100
*p = nil
}
  • 数组的传递是按值传递的,使用了指针可实现传入地址,从而实现对数组的修改

Go语言——struct类型作为函数参数

本文先给出Go语言中struct类型作为函数参数传入的例子,然后给出总结


首先看下面代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func changeValue(person Person){
person.name = "zhoujiahong"
person.age = 22
}

func changePoint(person *Person){
person.name = "zhoujiahong"
person.age = 22
}
func main() {
person := Person{
name:"origin",
}
changeValue(person)
fmt.Printf("Person:\n person.name: %s---person.age: %d\n", person.name, person.age)
changePoint(&person)
fmt.Printf("*Person:\n person.name: %s---person.age: %d\n", person.name, person.age)
}

//output:
//Person:
// person.name: origin---person.age: 0
//*Person:
// person.name: zhoujiahong---person.age: 22

总结

  • struct类型的传递是按值传递的,与数组[5]byte等的传递相似,也可以理解为和C++一样
  • struct类型传递与切片不同,切片事实上是一个指针(栈)指向一块数组(堆)这样的数据结构,所以无论如何都是传入指针或者是指针(栈)的指针(指向栈)
  • 可以理解为: 切片类似于Java(切片还多了个特色,通过传入切片可以修改指针(栈)本身,而Java是做不到的), 而数组类似于C++

注意:struct类型与切片传值方式不同而与数组相同

Hello World

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

ML——LightGBM概述

本文介绍LightGBM的一些特性和并行实现方法

  • 参考博客: https://www.cnblogs.com/ldt-/p/10206356.html

LightGBM vs XGBoost

  • 树结点切分方式不同:
    • XGBoost树节点切分是 Level-wise
    • LightGBM树节点切分是 Leaf-wise
  • LightGBM直接支持类别特征 ,对类别特征不必进行 One-Hot 处理
  • 实现方面:
    • 直方图算法(近似切分算法)最初由LightGBM提出,后来的XGBoost算法也实现了直方图算法
  • XGBoost的近似搜索算法和LightGBM的直方图算法不同
    • XGBoost的近似搜索算法是保存所有样本的二阶梯度,用分位数确定划分方法,他的分割点是可以直接通过计算总的样本梯度和和分位数点得到的.
        * LightGBM算法是将所有样本放进对应的桶中,并以当前桶作为划分点,计算左右桶的最大增益,它的最优点是遍历所有的桶才能得到的.
  • LightGBM 通信优化 比 XGBoost 做得好
    • 这里有亲身体验: XGBoost使用多个处理器时,有时候处理器数量增加训练速度不增加,甚至反而变慢,xgboost.XGBClassifier
  • LightGBM 使用了 GOSS(Gradient-based One-Side Sampling) 来做采样算法
    • GOSS 是通过区分不同梯度的实例,保留较大梯度实例同时对较小梯度随机采样的方式减少计算量,从而达到提升效率的目的
    • GOSS 算法流程 :
      • 根据样本的梯度将样本降序排序
      • 保留前 \(a \ (0 < a < 1)\) 比例大小的数据样本,作为数据子集 \(Z_1\),也就是保留 \(a * len(all\_samples)\) 的数据样本
      • 对于剩下的数据的样本,随机采样获得大小为 \(b \ (0 < b < 1)\) 的数据子集 \(Z_2\),也就是采样 \(b * len(all\_samples)\) 的数据样本
      • 计算信息增益时对采样的 \(Z_2\) 样本的梯度数据乘以 \((1-a)/b\) (目的是不改变原数据的分布)
    • GOSS的思想是,梯度大的实例正常使用,梯度小的实例就通过采样实现部分拟合总体的方法(拟合时不改变原来的分布,除以采样率就行了)
  • LightGBM 使用了 EFB (Exclusive Feature Bundling)
    • EFB 通过特征捆绑的方式减少特征维度(其实是降维技术)的方式,提升计算效率
    • 通常被捆绑的特征都是互斥的(一个特征值为0,一个特征值不为0), 这样两个特征捆绑起来也不会造成信息丢失
    • 当两个特征不是完全互斥时,可以用一个指标对特征不互斥程度进行评估,称为冲突比率
    • 冲突比率较小时,我们可以将他们捆绑而不太影响训练结果
    • EFB 算法流程:
      • 将特征按照非零值的个数进行排序
      • 计算不同特征之间的冲突比率
      • 遍历每个特征并尝试合并特征,使冲突比率最小化
  • LightGBM 的内存优化
    • XGBoost 中 需要对每个特征进行预排序(注意:这里不能在算是XGBoost的缺点了,后来的XGBoost也实现了这个直方图算法,不需要预排序了)
    • LightGBM使用直方图算法替代了预排序

LightGBM的优点

  • 相对XGBoost:
    • 速度快 (GOSS, EFB, Histogram等)
    • 内存少 (XGBoost中排序)
    • 精度高(效果不明显, 与XGBoost相当, 本人测试, 实际使用中有时候不如XGBoost, 可能是参数调节的问题)

缺点

  • 虽然官方一再强调LightGBM的精度不比XGBoost低,而且可能超过XGBoost,但是实践中, LightGBM除了比XGBoost快以外, 精度方面没什么优势, 甚至精度还不如XGBoost(注意: 也可能是我调参技术不行)
    • 问题: 为什么某些数据集上出现LightGBM比XGBoost精度差的情况?
    • 回答: (个人理解)因为GOSS和EFB会带来一定的精度损失

总结

  • LightGBM = XGBoost + Histogram + GOSS + EFB
    • XGBoost: 不同于XGBoost的, 树节点切分不同, LightGBM使用了 Leaf-wise 的生长策略
    • Histogram:
      • Histogram方法牺牲了一定的精度,但是作者强调了在实验中精度降低并不多
      • 开始的XGBoost使用的是预先排序的方式, 后来在 XGBoost 中也实现了Histogram
      • LightGBM 对 Histogram 算法进一步加速
      • 一个叶子节点的 Histogram 可以直接由父节点的Histogram和兄弟节点的Histogram做差得到, 一般情况下,构造Histogram需要遍历该叶子上的所有数据,通过该方法,只需要遍历Histogram的k个捅, 速度提升了一倍
    • GOSS: 对于梯度小的样本, 使用采样部分代替总体的方法省时间
    • EFB: 互斥特征捆绑,提升计算效率
  • LightGBM 真正做到了把并行做到极致
    • 特征并行: 在不同的机器在不同的特征集合上分别寻找最优的分割点, 然后再机器间同步最优的分割点.
    • 数据并行: 让不同的机器先在本地构造直方图, 然后进行全局的合并,然后在合并的直方图上寻找最优的分割点.
  • LightGBM 支持类别特征
    • 无需将类别特征 One-Hot
  • 问题: 为什么XGBoost也使用了直方图,但是速度依然比较慢?
    • 直方图算法的实现有两种:
      • 1)全局构造 ,在树的构造过程中只建立一次直方图, 每次分裂都从缓存的直方图中寻找分裂点
      • 2)局部构造 ,每次树分裂到一层的时候就建立一次直方图
      • XGBoost使用的是局部构造的方式, 所以速度会较慢

ML——LR-逻辑回归


手动推导流程

  • 假设有m样本: \(X = (x_{1}, x_{2}, x_{3},\dots x_{m})\)
    • 样本点为: \(((x_{1}, y_{1}), (x_{2}, y_{2}), (x_{3}, y_{3}), \dots (x_{m}, y_{m}))\)
  • 假设 \(w, \theta, x_{i}\) 等所有向量都为列向量

确定分类决策函数

  • 线性回归模型
    $$f(x) = w^{T}x + b$$
  • 令
    $$
    \begin{align}
    \theta &= (w; b) \\
    x_{i} &= (x_{i}; 1)
    \end{align}
    $$
  • 有
    $$f(x) = \theta^{T} x$$
  • 逻辑回归决策函数在线性回归模型上加一个sigmoid函数
    $$
    \begin{align}
    h_{\theta}(x) &= sigmoid(f(x)) \\
    &= \frac{1}{1+e^{-f(x)}} \\
    &= \frac{1}{1+e^{-\theta^{T} x}} \\
    \end{align}
    $$
  • 即
    $$
    \begin{align}
    p(y=1|x) &= h_{\theta}(x) = \frac{1}{1+e^{-\theta^{T} x}} = \frac{e^{\theta^{T} x}}{1+e^{\theta^{T} x}}\\
    p(y=0|x) &= 1-h_{\theta}(x) = \frac{e^{-\theta^{T} x}}{1+e^{-\theta^{T} x}} = \frac{1}{1+e^{\theta^{T} x}} \\
    \end{align}
    $$
  • 对数几率(log odds, 也称为logit) 定义为: \(ln \frac{p}{1-p}\),在LR中有:
    $$
    \begin{align}
    ln\frac{h_{\theta}(x)}{1-h_{\theta}(x)} = \theta^T x
    \end{align}
    $$
  • 分类超平面不确定,与最终的阈值有关, \(\alpha\) 的值与最终阈值相关
    $$w^{\star}x + b^{\star} = \alpha$$
    • 分类超平面由 \((w, b)\) 和阈值唯一确定,(注意: SVM的分类超平面由 \((w, b)\) 唯一确定)
    • 这一点和SVM不同,SVM的分类超平面是确定的,详情参看ML——SVM-支持向量机

确定优化目标

  • LR中使用极大似然法
    $$
    \begin{align}
    L(\theta) &= p(Y|X;\theta) \\
    &= \prod_{i=1}^{m}p(y_{i}|x_{i}; \theta) \\
    &= \prod_{i=1}^{m}(p(y_{i} = 1|x_{i};\theta))^{y_{i}}(p(y_{i} = 0|x_{i};\theta))^{1-y_{i}}
    \end{align}
    $$
  • 上面的式子不易求导优化,我们使用与上面的式子单调性和最优点等价的对数似然函数为
    $$
    \begin{align}
    LL(\theta) &= \log L(\theta) \\
    &= \log \prod_{i=1}^{m}(p(y_{i} = 1|x_{i};\theta))^{y_{i}}(p(y_{i} = 0|x_{i};\theta))^{1-y_{i}} \\
    &= \sum_{i=1}^{m}\left (y_{i}\log(p(y_{i} = 1|x_{i};\theta)) + (1-y_{i})\log(p(y_{i} = 0|x_{i};\theta)) \right ) \\
    &= \sum_{i=1}^{m}\left (y_{i}\log\frac{p(y_{i} = 1|x_{i};\theta)}{p(y_{i} = 0|x_{i};\theta)} +\log(p(y_{i} = 0|x_{i};\theta))\right ) \\
    \end{align}
    $$
    • 上面的式子中:
      $$\sum_{i=1}^{m}\left (y_{i}\log(p(y_{i} = 1|x_{i};\theta)) + (1-y_{i})\log(p(y_{i} = 0|x_{i};\theta)) \right )$$
    • 加个负号即为为交叉熵损失函数
      $$-\sum_{i=1}^{m}\left (y_{i}\log(p(y_{i} = 1|x_{i};\theta)) + (1-y_{i})\log(p(y_{i} = 0|x_{i};\theta)) \right )$$
    • 所以交叉熵损失函数与逻辑回归的对数似然损失函数(=逻辑回归对数似然函数的负数)是等价的
  • 由前面的推导有
    $$
    \begin{align}
    \log \frac{p(y_{i} = 1|x_{i};\theta)}{p(y_{i} = 0|x_{i};\theta)} = \log\frac{\frac{e^{\theta^{T} x}}{1+e^{\theta^{T} x}}}{\frac{1}{1+e^{\theta^{T} x}}} = \log e^{\theta^{T} x} = \theta^{T}x\\
    \end{align}
    $$
    • 且:
      $$\log(p(y_{i} = 0|x_{i};\theta)) =\log(\frac{1}{1+e^{\theta^{T} x}}) = -\log(1+e^{\theta^{T} x})$$
  • 故而有
    $$
    \begin{align}
    LL(\theta) &= \sum_{i=1}^{m}\left (y_{i}\log\frac{p(y_{i} = 1|x_{i};\theta)}{p(y_{i} = 0|x_{i};\theta)} +\log(p(y_{i} = 0|x_{i};\theta))\right )\\
    &= \sum_{i=1}^{m}\left ( y_{i}\theta^{T}x - \log(1+e^{\theta^{T}x}) \right)
    \end{align}
    $$

损失函数

  • 最大化似然函数等价于最小化似然函数的负数
  • LR中使用极大似然法 ,所以对应的损失函数自然为对数似然损失函数
    $$loss(\theta) = -LL(\theta) = \sum_{i=1}^{m}\left (- y_{i}\theta^{T}x +\log(1+e^{\theta^{T}x}) \right)$$

梯度下降法优化

注意: 这里优化目标也可以用牛顿法

  • 目标,求一个 \(\theta^{\star}\) 满足似然函数最大化或者损失函数最小化
    $$\theta^{\star} = \mathop{\arg\max}_{\theta} LL(\theta) = \mathop{\arg\min}_{\theta} -LL(\theta) = \mathop{\arg\min}_{\theta} loss(\theta)$$
  • 对数似然函数对参数 \(\theta\) 求导有
    $$
    \begin{align}
    \frac{\partial loss(\theta)}{\partial\theta} &= \sum_{i=1}^{m}\left ( -y_{i}x_{i} + \frac{x_{i}e^{\theta^{T}x}}{1+e^{\theta^{T}}x}\right ) \\
    &= \sum_{i=1}^{m}x_{i}\left ( -y_{i} + \frac{e^{\theta^{T}x}}{1+e^{\theta^{T}}x}\right ) \\
    &= \sum_{i=1}^{m}x_{i}\left ( -y_{i} + h_{\theta}(x_{i})\right ) \\
    \end{align}
    $$
  • LR模型的梯度下降参数迭代公式
    $$
    \begin{align}
    \theta^{t+1} &= \theta^{t} - \alpha\sum_{i=1}^{m}x_{i}\left ( -y_{i} + h_{\theta^{t}}(x_{i})\right ) \\
    &= \theta^{t} + \alpha\sum_{i=1}^{m}x_{i}\left ( y_{i} - h_{\theta^{t}}(x_{i})\right )
    \end{align}
    $$
    • 其中 \(\alpha\) 为步长
  • 线性回归和LR模型的梯度下降法参数迭代公式表达式看似相同,但是不同的模型对应的 \(h_{\theta}\) 函数并不相同

其他总结

参考:https://www.cnblogs.com/ModifyRong/p/7739955.html

  • LR中使用极大似然法 ,所以对应的损失函数自然为对数似然损失函数(对数损失函数)
    • 对数似然损失函数定义为:
      $$Loss(\theta) = -P(Y|X; \theta)$$
    • 《统计学习方法》P213 中定义 LR 的损失函数为逻辑斯蒂损失函数,在LR模型和最大熵模型中,逻辑斯蒂损失函数本质上与对数似然损失函数等价,可推导得到
  • 一句话概括逻辑回归:
    • 逻辑回归是假设数据服从伯努利分布,通过极大似然函数的方法,运用梯度下降法或者牛顿法来求解参数,来达到将数据二分类的目的
    • 逻辑回归的假设: 数据服从伯努利分布 , \(p(y=1|x) = 1-p(y=0|x)\)
    • 逻辑回归的损失函数: 对数似然损失函数(交叉熵损失函数) ,也就是对数似然函数的负数
    • 逻辑回归的求解方法: 梯度下降法或牛顿法
    • 逻辑回归的目的: 将数据二分类
    • 逻辑回归如何分类: 预测结果是连续的[0-1]的数 ,我们一般选择0.5作为阈值来分类,但是这个值可能是可以变化的,因为损失函数最小并不意味着0.5时分类精度最高
  • 为什么要用极大似然法?等价于为什么要用对数似然损失函数作为损失函数?
    • 损失函数一般有平方损失函数,对数损失函数,合页损失函数,绝对值损失函数等,极大似然函数取对数后等同于对数损失函数,在逻辑回归这个模型中,推导可以得到,对数损失函数训练求解参数的迭代函数只与 \(x_{i}, y_{i}\) 相关,与sigmoid函数的梯度等无关.这样的参数更新自始至终都比较稳定
    • 为什么不选平方损失函数的呢?其一是因为如果你使用平方损失函数,你会发现梯度更新的速度和sigmod函数本身的梯度是很相关的,sigmod函数在它在定义域内的梯度都不大于0.25, 这样训练会非常的慢
  • 逻辑回归中,如果某些特征高度相关,甚至某些特征完全相同,会造成什么影响?
    • 损失函数收敛后,没有影响,因为特征的相关性并不影响分类器效果,重复特征会分化特征的权重(10个重复特征和单个特征训练结果差别在于前者每个特征的权重是后者的十分之一),本质上最终结果不变的
    • 但是训练时由于特征重复,参数增多,模型复杂度增加,训练时长,内存等都会增加
  • 为什么需要去掉高度相关的特征?
    • 去掉高度相关的特征使得模型可解释性更好
    • 提高训练时间,节约内存,减少参数数量
    • 特征的提取本身也需要时间,实际工程项目中可以少提取一个特征往往能节约很多时间
  • logistic 与 logit 的区别?
    • logit: 又名 log adds , 指的是”对数几率”, 定义为 \(ln\frac{p}{1-p}\)
    • logistic: 又叫Sigmoid函数, 指的是”对数几率函数”, 本质上是一种”Sigmoid”函数, 定义为 \(f(x) = \frac{1}{1+e^{-x}}\)
  • 简单介绍LR模型的优缺点:
    • 优点:
      • 模型简单,可解释性好,(如果对数据特征进行了归一化处理的话)可以从特征的权重看到不同特征对最终结果的影响
      • 模型效果往往不错(特征工程做得好的话)
      • 训练速度快, 成熟的SGD优化方法(SGD可以分布式)等
      • 内存占用小
      • 输出结果可以作为概率值,然后可以对阈值根据实际进行划分,不一定是确定的0.5,只是一般选择0.5而已
    • 缺点:
      • 难以处理非线性数据,本质上是线性分类面
      • 难以处理数据不平衡问题 , 这里如果正例远远多于负例,那么全都预测为正例,整体损失函数也不会太大
      • LR 本身无法筛选特征 ,有时候会用GBDT和XGBoost来筛选特征,然后再用LR模型
  • 扩展:逻辑回归可像SVM一样引入核函数处理非线性分类问题吗?
    • 一般来说不可以
    • [存疑]理论上通过对原始样本非线性映射,似乎也可以,如果将 \(f(x) = \theta^{T}x\) 中的 \(f(x)\) 看做 \(\theta\) 看做变量,然后类比SVM的核函数,定义一个关于 \(x_{i}\) 的非线性映射
      • 这里 \(x_{i}\) 表示第 \(i\) 个样本, 用 \(x_{i}^{j}\) 表示第 \(i\) 个样本的第 \(j\) 个维度
        $$x_{i}^{j} = \phi_{j}(x_{i}^{j})$$
      • 基于上述非线性映射函数的定义,我们对每个样本都进行线性映射,每个维度用不同的映射函数(不同样本相同维度映射函数相同)
      • 这里的非线性映射与SVM的核函数不同,SVM不使用核函数的话,也可以通过相同的非线性映射的方式实现非线性分类
      • 使用核技巧后的LR模型将变得很慢,SVM与kernels是相配的,而LR与kernels会十分慢(来源SVM核技巧)
  • LR 模型训练完成后,输出概率多少的样本应该评估为正样本?【以下分析为个人理解,暂无严格证明】
    • LR模型的损失函数本质上是交叉熵损失函数,交叉熵损失函数本质是最小化预估分布与训练样本分布之间的差距,故而预估均值与真实训练样本均值应该相等 ,即LR模型的预估值均值理论上与训练样本标签均值相同(这里LR的预估值是Sigmoid的输出值,训练样本负样本标签为0,正样本标签为1)。【PS,一种辅助理解思考:假设训练集中的样本特征完全相同,但其中30%是正样本,另外70%为负样本,那么优秀的LR模型在预估该训练样本时应该输出约为0.3】
    • 进一步来说,当预估的平均值大于训练样本的均值时,即可判断为正样本
      • 举例,假设训练样本的均值为0.4,那么预估值大于0.4的样本均可视为正样本(思考:这种判定下模型的准确率 \( \text{Accuracy} = \frac{TP+TN}{TP+TN+FP+FN}\) 应该是最高的?)

附录:多分类任务重的 logits

  • TLDR:多分类任务中模型输出的 logits 和 LR 中的 logit 含义不完全相同
    • 二者是同源但适用场景和维度不同的概念
  • LR 的 logit 是二分类专属的标量对数几率;
  • 多分类模型的 logits 是多分类任务的向量原始得分 ,是 logit 概念在多类别场景下的推广,需通过 Softmax 转换为概率分布

同源:基于对数几率的定义

  • 二者的本质都源于对数几率(log-odds) ,即事件发生概率与不发生概率的比值的自然对数,公式为:
    $$\text{logit}(p) = \ln\left(\frac{p}{1-p}\right)$$
    • 这个公式是连接线性得分和概率的桥梁

二分类场景下的等价性

  • 当多分类任务退化为二分类时,模型的 logits 向量为 \([\text{logit}_0, \text{logit}_1]\)
    • 若满足 \(\text{logit}_0 = -\text{logit}_1\)
    • 则 \(2\text{logit}_1\) 就完全等价于 LR 中的 logit(正例的对数几率)
  • 此时 Softmax 激活等价于 Sigmoid 激活:
    $$ p(y=1) = \frac{e^{\text{logit}_1}}{e^{\text{logit}_0}+e^{\text{logit}_1}} = \frac{e^{\text{logit}_1}}{e^{-\text{logit}_1}+e^{\text{logit}_1}} = \frac{1}{1+e^{-2\text{logit}_1}} = \frac{1}{1+e^{-\theta^T x}} = \frac{1}{1+e^{-\text{logit}}}$$
    • 若令 \(\text{logit} = 2\text{logit}_1\),则与 LR 的 Sigmoid 输出完全一致

多分类 LR 的 logits

  • 多分类逻辑回归(Softmax 回归)的输出 logits 就是上述多分类模型的 logits 向量,每个元素对应一个类别的线性得分,这是 logit 概念从标量到向量的扩展
1…545556…66
Joe Zhou

Joe Zhou

Stay Hungry. Stay Foolish.

659 posts
53 tags
GitHub E-Mail
© 2026 Joe Zhou
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4