TowardsDataScience 2023 博客中文翻译(三百一十二)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

实体解析实现的复杂性

原文:towardsdatascience.com/the-complexities-of-entity-resolution-implementation-a2284e54171?source=collection_archive---------11-----------------------#2023-08-14

关于数据匹配时一些典型挑战的实用示例

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Stefan Berkner

·

关注 发布于 Towards Data Science ·11 min read·2023 年 8 月 14 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实体的艺术表现(图像由作者提供)

实体解析是确定数据集中两个或更多记录是否指向同一现实世界实体的过程,通常是一个人或公司。乍一看,实体解析可能看起来是一个相对简单的任务:例如,给出一个人的两张照片,即使是小孩也可以以相当高的准确率确定是否显示的是同一个人。计算机也是如此:比较包含姓名、地址、电子邮件等属性的两个记录是很容易做到的。然而,越深入探讨这个主题,挑战越大:需要评估各种匹配算法,处理数百万或数十亿条记录意味着平方复杂度,更不用说实时和数据删除的使用案例了。

模糊文本匹配

让我们开始比较著名艺术家 Vincent Van Gogh 的两个记录——还是 Van Gough?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第二条记录中有一些错误(除了出生在一个世纪之后和一个电子邮件地址):名字拼写错误,出生日期混淆,邮政编码缺失,电子邮件地址略有不同。

那么我们如何比较这些值呢?如果,假设姓名相同,那么对这些值进行简单的字符串比较就足够了。由于情况并非如此,我们需要一些更先进的模糊匹配方法。文本基础的模糊匹配有许多不同的算法,大致可以分为三组。语音算法关注文本发音的相似性。最著名的算法是 Soundex 和 Metaphone,这些算法主要用于英语文本,但也有适用于其他语言的变体,如适用于德语的 Kölner Phonetik(科隆语音)。文本距离算法通常定义了将一个文本更改为另一个文本需要改变多少个字符。Levenshtein 和 Hamming 距离是这一组中两个知名的算法。相似性算法,如余弦相似性或 Jaccard 指数,计算文本的结构相似性,并通常以百分比表示相似性。

出于本文的目的,我们将使用一种非常简单的方法,仅对姓名使用 Levenshtein 距离,对城市进行等值比较。此示例及所有后续示例将使用 golang 作为编程语言,并尽可能使用现有库。将其转换为 Python、Java 或任何其他语言应该是微不足道的。此外,它仅对姓名属性进行匹配。添加更多属性或使其可配置不是本文的目的。

package main

import (
    "fmt"

    "github.com/hbollon/go-edlib"
)

type Record struct {
    ID int
    Name string
    City string
}

func matches(a, b Record) bool {
    distance := edlib.LevenshteinDistance(a.Name, b.Name)
    return distance <= 3 && a.City == b.City
}

func main() {
    a := Record{
        Name: "Vincent Van Gogh",
        City: "Paris",
    }
    b := Record{
        Name: "Vince Van Gough",
        City: "Paris",
    }
    if matches(a, b) {
        fmt.Printf("%s and %s are probably the same person\n", a.Name, b.Name)
    } else {
        fmt.Printf("%s and %s are probably not the same person\n", a.Name, b.Name)
    }
}

在 Go Playground 中尝试:go.dev/play/p/IJtanpXEdyu

两个名字之间的 Levenshtein 距离为 3。原因是有三个额外的字符(名字中的“en”和姓氏中的“u”)。注意,这适用于这个特定输入。然而,距离仍然远未完美。例如,“Joe Smith”和“Amy Smith”之间的 Levenshtein 距离也是三,但显然不是同一个人。结合距离算法和语音算法可能解决这个问题,但超出了本文的范围。

使用基于规则的方法而不是基于机器学习的方法时,选择能够为你的用例提供最佳结果的算法是商业成功的关键方面。这是你应该花费大部分时间的地方。不幸的是,正如我们现在将发现的那样,如果你决定自己开发实体解析引擎,还有很多其他事情会让你分心,妨碍你优化这些规则。

天真的实体解析

既然我们知道了如何比较两个记录,我们需要找到所有匹配的记录。最简单的方法是将每条记录与所有其他记录进行比较。为了这个例子,我们使用随机选择的名字和城市。对于名字,我们强制引入最多三个错误(将任何字符替换为 x)。

var firstNames = [...]string{"Wade", "Dave", "Seth", "Ivan", "Riley", "Gilbert", "Jorge", "Dan", "Brian", "Roberto", "Daisy", "Deborah", "Isabel", "Stella", "Debra", "Berverly", "Vera", "Angela", "Lucy", "Lauren"}
var lastNames = [...]string{"Smith", "Jones", "Williams", "Brown", "Taylor"}

func randomName() string {
    fn := firstNames[rand.Intn(len(firstNames))]
    ln := lastNames[rand.Intn(len(lastNames))]
    name := []byte(fmt.Sprintf("%s %s", fn, ln))
    errors := rand.Intn(4)
    for i := 0; i < errors; i++ {
        name[rand.Intn(len(name))] = 'x'
    }
    return string(name)
}

var cities = [...]string{"Paris", "Berlin", "New York", "Amsterdam", "Shanghai", "San Francisco", "Sydney", "Cape Town", "Brasilia", "Cairo"}

func randomCity() string {
    return cities[rand.Intn(len(cities))]
}

func loadRecords(n int) []Record {
    records := make([]Record, n)
    for i := 0; i < n; i++ {
        records[i] = Record{
            ID:   i,
            Name: randomName(),
            City: randomCity(),
        }
    }
    return records
}

func compare(records []Record) (comparisons, matchCount int) {
    for _, a := range records {
        for _, b := range records {
            if a == b {
                continue // don't compare with itself
            }
            comparisons++
            if matches(a, b) {
                fmt.Printf("%s and %s are probably the same person\n", a.Name, b.Name)
                matchCount++
            }
        }
    }
    return comparisons, matchCount
}

func main() {
    records := loadRecords(100)
    comparisons, matchCount := compare(records)

    fmt.Printf("made %d comparisons and found %d matches\n", comparisons, matchCount)
}

在 Go Playground 中尝试:go.dev/play/p/ky80W_hk4S3

你应该看到一些类似这样的输出(如果没有匹配的随机数据,你可能需要运行多次):

Daisy Williams and Dave Williams are probably the same person
Deborax Browx and Debra Brown are probably the same person
Riley Brown and RxxeyxBrown are probably the same person
Dan Willxams and Dave Williams are probably the same person
made 9900 comparisons and found 16 matches

如果你运气好的话,你也会得到像“Daisy”和“Dave”这样的不匹配。这是因为我们使用了三的 Levenshtein 距离,而对于短名字来说,这个值太高了。欢迎你自己改进这个方法。

性能方面,真正的问题在于需要进行 9,900 次比较才能得到结果,因为输入量的增加会大约使所需的比较次数增加四倍。200 条记录需要 39,800 次比较。对于仅有 100,000 条记录的小数据量,这意味着需要近 100 亿次比较。不管你的系统多么强大,数据量增加到一定程度时,系统将无法在可接受的时间内完成。

一个快速但几乎无用的优化是不要将每个组合比较两次。A 与 B 或 B 与 A 的比较结果应无差别。然而,这只能将所需比较次数减少一半,由于二次增长,这种减少微不足道。

通过阻塞减少复杂性

如果我们查看创建的规则,会很容易发现,如果城市不同,我们将永远不会有匹配。所有这些比较都是完全浪费的,应该被避免。将你怀疑相似的记录放入一个公共桶,将其他记录放入另一个桶,在实体解析中称为阻塞。由于我们希望使用城市作为阻塞键,因此实现起来相当简单。

func block(records []Record) map[string][]Record {
    blocks := map[string][]Record{}
    for _, record := range records {
        blocks[record.City] = append(blocks[record.City], record)
    }
    return blocks
}

func main() {
    records := loadRecords(100)
    blocks := block(records)
    comparisons := 0
    matchCount := 0
    for _, blockRecords := range blocks {
        c, m := compare(blockRecords)
        comparisons += c
        matchCount += m
    }

    fmt.Printf("made %d comparisons and found %d matches\n", comparisons, matchCount)
}

在 Go Playground 中尝试: go.dev/play/p/1z_j0nhX-tU

结果现在将是相同的,但比较次数仅为之前的十分之一,因为我们有十个不同的城市。在实际应用中,由于城市的方差更大,这一效果会更为显著。此外,每个块可以独立处理,例如,在相同或不同的服务器上并行处理。

找到合适的阻塞键本身可能是一个挑战。使用像城市这样的属性可能会导致不均匀的分布,从而导致某些巨大块(例如大城市)所需的时间比其他块要长得多。或者城市中包含一个微小的拼写错误,从而不再被视为有效匹配。使用多个属性和/或使用语音键或 q-grams 作为阻塞键可以解决这些问题,但会增加软件的复杂性。

从匹配到实体

到目前为止,我们只能说记录是否匹配。对于非常基本的用例,这可能已经足够。然而,大多数情况下,你会想知道属于同一实体的所有匹配。这可能从简单的星形模式(例如 A 与 B、C 和 D 匹配),到链式模式(例如 A 与 B 匹配,B 与 C 匹配,C 与 D 匹配),再到非常复杂的图形模式。这种所谓的传递记录链接可以使用连通组件算法轻松实现,只要所有数据都适合在单台服务器的内存中。再次强调,在现实世界的应用中,这会更具挑战性。

func compare(records []Record) (comparisons int, edges [][2]int) {
    for _, a := range records {
        for _, b := range records {
            if a == b {
                continue // don't compare with itself
            }
            comparisons++
            if matches(a, b) {
                edges = append(edges, [2]int{a.ID, b.ID})
            }
        }
    }
    return comparisons, edges
}

func connectedComponents(edges [][2]int) [][]int {
    components := map[int][]int{}
    nextIdx := 0
    idx := map[int]int{}

    for _, edge := range edges {
        a := edge[0]
        b := edge[1]
        aIdx, aOk := idx[a]
        bIdx, bOk := idx[b]
        switch {
        case aOk && bOk && aIdx == bIdx: // in same component
            continue
        case aOk && bOk && aIdx != bIdx: // merge two components
            components[nextIdx] = append(components[aIdx], components[bIdx]...)
            delete(components, aIdx)
            delete(components, bIdx)
            for _, x := range components[nextIdx] {
                idx[x] = nextIdx
            }
            nextIdx++
        case aOk && !bOk: // add b to component of a
            idx[b] = aIdx
            components[aIdx] = append(components[aIdx], b)
        case bOk && !aOk: // add a to component of b
            idx[a] = bIdx
            components[bIdx] = append(components[bIdx], a)
        default: // create new component with a and b
            idx[a] = nextIdx
            idx[b] = nextIdx
            components[nextIdx] = []int{a, b}
            nextIdx++
        }
    }

    cc := make([][]int, len(components))
    i := 0
    for k := range components {
        cc[i] = components[k]
        i++
    }
    return cc
}

func main() {
    records := loadRecords(100)
    blocks := block(records)
    comparisons := 0
    edges := [][2]int{}
    for _, blockRecords := range blocks {
        c, e := compare(blockRecords)
        comparisons += c
        edges = append(edges, e...)
    }
    cc := connectedComponents(edges)

    fmt.Printf("made %d comparisons and found %d matches and %d entities\n", comparisons, len(edges), len(cc))
    for _, component := range cc {
        names := make([]string, len(component))
        for i, id := range component {
            names[i] = records[id].Name
        }
        fmt.Printf("found the following entity: %s from %s\n", strings.Join(names, ", "), records[component[0]].City)
    }
}

在 Go Playground 中尝试: go.dev/play/p/vP3tzlzJ2LN

连通组件函数遍历所有边,创建新的组件、将新 id 添加到现有组件中,或将两个组件合并为一个。结果看起来大致如下:

made 1052 comparisons and found 6 matches and 2 entities
found the following entity: Ivan Smxth, Ixan Smith, Ivax Smitx from Cairo
found the following entity: Brxan Williams, Brian Williams from Cape Town

保留这些边缘给我们带来了一些优势。我们可以利用它们使结果实体易于理解和解释,理想情况下,还能提供一个漂亮的用户界面,展示实体的记录是如何连接的。或者在使用实时实体解析系统时,我们可以利用这些边缘来拆分实体,特别是在数据被移除时。或者你可以在构建 图神经网络 (GNN) 时使用它们,从而获得比仅仅记录本身更好的机器学习结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实体的可视化表示(图像由作者提供)

边缘的一个问题可能会出现在有大量非常相似的记录时。例如,如果 A 与 B 匹配,B 与 C 匹配,那么 C 也可能与 A 匹配,具体取决于使用的规则。如果 D、E、F 等也与现有记录匹配,那么我们将回到平方增长的问题,很快会出现大量边缘,导致无法处理。

还记得我们如何构建阻塞桶吗?惊喜!对于非常相似的数据,最终都集中在几个巨大的桶中,计算性能会急剧下降——即使你遵循了之前创建多个属性桶的建议。

这种非相同重复项的典型例子是某人在同一家商店定期订购,但使用的是访客访问(抱歉,没有漂亮的客户 ID)。那个人可能几乎总是使用相同的送货地址,并且大多数情况下能够正确地写出自己的名字。因此,这些记录应该以特殊方式处理,以确保系统性能的稳定,但这是一个 单独的话题

在你对获得的知识感到过于自满并想开始实施自己的解决方案之前,让我迅速打破你的幻想。我们还没有谈到实时处理这些问题的挑战。即使你认为不需要始终保持最新的实体(这是明显的好处),实时方法仍然提供了额外的价值:你不需要一遍又一遍地进行相同的计算,只需对新数据进行计算。另一方面,实施起来复杂得多。想做阻塞吗?将新记录与所属桶的所有记录进行比较,但这可能需要一段时间,并且可以被视为增量批处理。直到最终完成之前,还有大量新记录等待处理。想使用连通组件计算实体?当然,将整个图保存在内存中并只添加新的边。但是不要忘记跟踪由于新记录而刚刚合并在一起的两个实体。

所以,你仍然愿意自己实施这个方案。你做出了(在这种情况下)明智的决定,即不存储边缘并且不支持实时处理。于是你成功地运行了第一次实体解析批处理作业,处理了所有数据。这花了一段时间,但你每月只需执行一次,所以没问题。可能就在这个时候,你看到数据保护官从拐角处跑来,告诉你因 GDPR 投诉而需要从数据集中删除那个人。所以你为了一个被删除的实体重新运行了整个批处理作业——太棒了。

结论

进行实体解析乍一看可能相当简单,但它包含了许多重要的技术挑战。这些挑战中的一些可以被简化和/或忽略,但其他的则需要解决以获得良好的性能。

最初发表于 https://tilores.io

时间序列分析中的移动平均综合指南

原文:towardsdatascience.com/the-comprehensive-guide-to-moving-averages-in-time-series-analysis-3fb2baa749a

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 Midjourney 创建

探索简单移动平均和指数加权移动平均的细微差别

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Eryk Lewinson

·发表于 Towards Data Science ·阅读时间 9 分钟·2023 年 10 月 30 日

移动平均是时间序列分析中的一种基本工具,广泛用于各种目的。在时间序列分析中,移动平均的一些最受欢迎的应用包括:

  • 噪声减少: 移动平均有效地过滤掉时间序列数据中的随机波动和噪声。使用移动平均平滑数据有助于分析师关注潜在的模式,而不是短期波动,从而帮助他们识别有意义的趋势。

  • 季节性分解: 将时间序列数据分解为其组成部分通常涉及使用移动平均。 季节性分解 技术利用移动平均从数据中提取季节性模式。此外,移动平均还可以帮助识别时间序列数据中的周期性成分,表示那些不完全是季节性的重复模式。

  • 预测: 移动平均是各种预测模型的基本组成部分。

  • 过滤异常值: 移动平均可以用于识别和过滤时间序列数据中的异常值。那些显著偏离移动平均的异常数据点可能被认为是异常值,并需进一步调查。

  • 可视化平滑: 在绘制时间序列数据时,移动平均通常被用来创建更平滑、更清晰的可视化效果。这可以使向更广泛的受众传达趋势和模式变得更容易。

这些应用展示了移动平均在时间序列分析中的多样性。根据时间序列数据的具体目标和特征,我们可能会选择不同类型的移动平均和参数。虽然移动平均的概念看似简单,但可用的参数和选项可能会让人感到困惑。因此,在本文中,我们将详细探讨它们。

简单移动平均与指数加权移动平均

移动平均(MA)和指数加权移动平均(EWMA)是最受欢迎的两种移动平均类型。它们的关键区别在于对数据点分配权重的方式。

MA 计算固定数量最近数据点的平均值,赋予它们相等的权重。这意味着窗口中的所有数据点对移动平均数有相同的影响,较旧的数据点与最新的数据点同样重要。

另一方面,EWMA 是一种加权移动平均,它对随着时间推移变得越来越远的数据点赋予指数递减的权重。因此,较新的数据点接收更高的权重,使其在计算移动平均时更具影响力。这种方法使 EWMA 对数据的近期变化更加敏感,特别适合于跟踪短期趋势和快速响应时间序列中的变化。

设置和数据

我们从导入库开始。

接下来,我们生成具有以下特征的合成时间序列数据:

  • 一年期的每日观察数据

  • 时间序列中存在重复模式(季节性)

  • 缺乏任何潜在的趋势

  • 在 6 月 18 日至 6 月 23 日之间出现的显著峰值

  • 最终步骤引入随机噪声

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

简单移动平均

在使用简单移动平均时,我们通常关注两个参数:窗口的大小以及窗口是否居中。

居中和无中心移动平均的区别在于移动平均相对于其计算的数据点的位置。

中心移动平均中,平均值是对以感兴趣的数据点为中心的数据点窗口进行计算的。通常,窗口中使用的是奇数个数据点。例如,使用中心 3 天移动平均时,平均值是基于当前天和前一天及后一天计算的。

无中心移动平均中,平均值是对向前移动的时间窗口的数据点进行计算,但窗口并不以感兴趣的数据点为中心。例如,要计算第t天的 3 天无中心移动平均,我们会取第t-2天、第t-1天和第t天的平均值。

在以下代码片段中,我们使用中心和无中心方法计算 7 天移动平均。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以清楚地看到这两种方法之间的差异,特别是在 6 月的峰值周围。

对于中心化的 MA,平滑曲线中的峰值和谷值与数据中对应峰值和谷值的中点更为一致。

未中心化的 MA 滞后于数据点,这意味着它跟随数据的一般趋势,但对短期波动的反应不如快速。因此,移动平均曲线中的峰值和谷值在时间上相对于数据中的峰值和谷值有所偏移。

在接下来的片段中,我们生成了使用不同窗口大小的移动平均。从下图中可以观察到的一般模式是,窗口大小越大,移动平均对短期波动的敏感性越低。窗口大小较大的曲线更加平滑,并倾向于过滤短期噪声,突显出长期趋势。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最后需要记住的是,默认情况下,我们需要 X 个观测值来创建 X 期的移动平均。这就是为什么 180 天的移动平均仅在年中开始的原因。我们可以通过使用rolling方法中的min_periods参数来调整这一行为。

指数加权移动平均

如前所述,EWMA 为数据点分配指数递减的权重,给予近期观测值更多的权重,对较早的观测值则较少。这使得 EWMA 相比于简单移动平均对数据中的近期变化更为敏感。

使用平滑参数 alpha 的指数加权移动平均(EWMA)的公式通常以以下递归表示形式表达:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中 x_t 是时间t的值,α是平滑因子,它控制在移动平均中最近观测值的权重。

在我们深入探讨选择平滑参数的各种方法之前,让我们澄清一个常见的混淆来源:调整公式与未调整公式。这一选择由ewm方法中的adjust参数决定。以下是供您参考的两种公式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

调整的目的是通过在初始时期除以衰减平滑因子来解决相对权重的不平衡问题。

数学细节: 在调整公式中,分母表示几何级数。因此,假设时间序列无限长,两种公式将得出相同的结果,即未调整值。*

为了说明实际差异,我们使用相同的平滑参数创建了两个 EWMA 线。区别在于公式中应用的调整。此外,我们将重点放在第一个月,因为那里的差异最为明显,尽管非常微小。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作为下一步,让我们深入探讨 alpha 值与计算移动平均时权重衰减速度之间的关系。为了计算随后的权重,我们使用源自调整版 EWMA 的公式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在图表中,我们观察到较小的 alpha 导致权重衰减更慢。当 alpha 取其极端值 0.99 时,EWMA 实质上依赖于最新的数据点而忽略较旧的数据点。

接下来,让我们使用三个不同的 alpha 值:0.1、0.4 和 0.7 来可视化 EWMA 曲线。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以观察到先前识别的模式在实际应用中的表现:alpha 越高,曲线对最近数据点的响应就越强。这种行为发生是因为更高的 alpha 值对近期观察值赋予了更大的权重,导致曲线紧随数据波动。相反,较低的 alpha 值对历史观察值赋予更多权重,结果是曲线变得更平滑,但滞后于数据的波动。

在使用指数加权移动平均时,选择负责指数衰减的参数会增加复杂性。虽然我们已经讨论了 alpha 参数,但它并不是唯一的选项。下面,我们描述了剩余的三个参数,并提供了选择正确值的一些直觉:

  • Half-life (halflife) — Half-life 实质上是衰减量减少到其初始值一半所需的时间。较小的 half-life 使最近的观察更具影响力,从而使 EWMA 对短期变化更为敏感。相反,较大的 half-life 对较旧的数据赋予更多的权重,导致 EWMA 更平滑且变化较慢。

  • Span (span) — Span 表示有效的窗口大小或在 EWMA 计算中考虑的周期数。换句话说,它表示有多少过去的观察值对平滑过程有贡献。较大的 span 包含更多的历史数据,导致 EWMA 更平滑且变化较慢。相反,较小的 span 更关注最近的数据,使其对短期变化更为敏感。

  • Center of Mass (com) — 类似于 span,center of mass 参数定义了 span 内的相对位置。它指示了加权函数质量的主要分布位置。center of mass 为 0.5 相当于 span 的中点,给予两侧观察值相等的权重。调整 center of mass 允许我们将加权偏向于近期或较旧的数据点。较小的 com 将质量中心移向当前,强调近期数据并增加对短期波动的敏感性。另一方面,较大的 com 将质量中心移向过去,导致 EWMA 更平滑且变化较慢。

以下公式展示了这三种替代参数与平滑因子 alpha 之间的关系。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为了更好地理解这个概念,让我们考虑一个处理季度数据的场景。在我们的指数加权移动平均中,我们希望纳入过去一年的数据。为此,我们选择了一个对应 alpha 值为 0.4 的跨度 4。下表展示了一些示例值。

最后,让我们看看半衰期参数。我们已经提到,它对应于一个量被减少到其初始值一半的时间周期数。考虑一个半衰期为 10 的情况,这对应于 alpha = 0.066967。如果我们使用该 alpha 计算以下权重,我们可以清楚地看到第 10 次观测的权重等于 0.5,正如我们预期的那样。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

总结

  • 移动平均在时间序列分析中有多种用途,如噪声减少、季节性分解、预测、异常值过滤和创建更平滑的可视化。

  • 简单移动平均将远期和近期的观测值赋予相等的权重。

  • 相比之下,指数加权移动平均通过赋予近期数据更高的权重来更快速地响应最新数据。我们可以通过以下参数之一来微调平滑程度:平滑因子(alpha)、半衰期、跨度或质心。

你可以在这里找到本文中使用的代码。像往常一样,任何建设性的反馈都非常欢迎。你可以通过LinkedInTwitter 或评论联系我。

喜欢这篇文章?成为 Medium 会员,继续通过阅读无限制地学习。如果你使用 这个链接 成为会员,你将支持我而不会额外增加你的成本。提前感谢,期待再见!

你也可能对以下内容感兴趣:

## 时间序列预测中的交互项全面指南

了解如何通过使线性模型对趋势变化更灵活来提高其拟合度

towardsdatascience.com ## 解锁线性回归中交互项的威力

了解如何通过包含交互项使你的线性模型更加灵活

towardsdatascience.com ## 指标层:所有 KPI 定义的单一真实来源

了解为什么实施指标层将使在组织中获取数据驱动的洞察更加稳健!

towardsdatascience.com

进一步阅读

所有图片,除非另有说明,均为作者(以及可能的一些人工智能)提供。

控制渐变

原文:towardsdatascience.com/the-controlled-fade-b972e11ab452

为什么正分割可能对你的下一场马拉松最为合适。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 barrysmyth

·发表于 Towards Data Science ·13 min 阅读·2023 年 9 月 3 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 Miguel A Amutio 提供,来源于 Unsplash

休闲跑者是否应该遵循传统智慧,即负分割(后半程更快)是跑马拉松的最佳方式?

关键结果

  1. 马拉松跑者通常建议在比赛中保持节奏,以便后半程(稍微)快于前半程——这是一个负分割——因为 这是精英获胜的方式 以及如何打破记录。然而,这项分析基于超过 400 万场马拉松 表明,几乎 90%的比赛后半程完成时间较(即正分割)。

  2. 当我们控制跑者能力时,节奏更加均匀的比赛与更快的完成时间和更多的个人最佳相关。然而,最佳的休闲表现与略微的正分割相关,表明 控制渐变 是一种有效的休闲跑者节奏策略。

  3. 证据还指出,休闲跑者对于负分割存在表现偏见——例如,10%的正分割与更好的结果(更多的个人最佳、改善的相对完成时间)相关,而不是 10%的负分割——因为休闲跑者通常对负分割的节奏把控过于保守。

  4. 这项分析建议休闲跑者应该将 1.5%的正分割作为其A 目标(即,目标是后半程比前半程长 1.5%),并将正分割低于约 3%作为B 目标

介绍

传统智慧建议马拉松比赛应以均匀或稍微负差距进行,并且认为“把时间存入银行”通过更快的前半程(即正差距)不是一个好主意。支持这一观点的证据包括常被引用的统计数据,即大多数 800 米以上的世界纪录都是通过负差距实现的,但我们是否应该期望精英的成功能很好地转移到休闲跑者身上?毕竟,精英马拉松选手在管理他们的配速、努力和能量方面有更多经验,而大多数休闲跑者可能只完成了几次马拉松全程。

在撰写时,一项《Runner’s World》调查(见下文)显示,仅有 11%的跑者在比赛中瞄准负差距。那么,休闲跑者是否正确地放弃传统智慧,选择正差距?或者我们是否应该采取更有纪律的方法来规划我们的配速?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

《Runners World》的一项调查结果显示,跑者通常是否计划以负差距或正差距完成比赛。

这是最近一篇《Runner’s World》的文章讨论负差距智慧的问题,并对故意的正差距,即所谓的受控减速进行了推测,其中跑者在比赛的后半程比前半程慢 30 秒至 3 分钟。例如,在受控减速中,一位 4 小时(240 分钟)的马拉松选手可能在 119 分钟内完成前半程,在 121 分钟内完成后半程,从而得到约+0.01 的相对半程分差(RHS)——1%的正差距——基于以下公式。相反,另一位跑者可能以 121 分钟的前半程和 119 分钟的后半程达到相同结果,得到-0.01 的负差距

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

接下来,我们将更详细地探讨这个话题,使用一个来自 2005 至 2019 年间全球大城市马拉松的公开比赛记录的大型数据集。该数据集包含超过 400 万条独特的比赛记录(包括 5 公里/~3 英里分裂时间和整体完成时间),涉及约 270 万名独特的跑者。这些比赛记录来自 38 个城市的 270 场不同马拉松。数据集的完整细节,包括可以访问数据的马拉松网址,已在其他地方提供。

负分裂和正分裂有多常见?

当然,跑者声称偏好正分裂并不意味着他们的比赛就是这样进行的。然而,在我们的数据集中,我们发现 89%的比赛确实是通过正分裂完成的——这与 Runner’s World 的调查结果非常接近——且男性和女性之间没有显著差异。值得注意的是,虽然负分裂的平均 RHS 在男性和女性之间非常相似(分别为-0.045 和-0.044),但男性的正分裂平均 RHS(0.16)大于女性(0.13),且男性的 RHS 变异性要大得多。

图 1(a)显示了具有各种 RHS 值的比赛比例。最常见的 RHS 约为 0.05(比赛的第二半程比第一半程长 5%)。对于不同程度的负分裂(RHS<0),男性和女性跑者的比例几乎相同,但在跑正分裂的男性和女性比例上存在一些差异。女性跑者比男性更有可能跑出适中的正分裂(0<RHS<0.2),而男性更有可能跑出极端的正分裂(RHS > 0.2);这与之前的研究一致,研究表明男性比女性更容易出现撞墙现象(即非常大的正分裂)。当我们根据比赛的每半程之间的分裂差异(即第二半程时间 - 第一半程时间)比较跑者时,在(b)中我们看到类似的模式,尽管这一衡量标准下男性和女性跑者之间的差距不那么明显。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1. 比较不同相对半程分裂和分裂差异(分钟)的比赛和完成时间的比例。

性能影响

分裂类型和程度如何影响完成时间?在图 1©和(d)中,我们根据相对分裂度(RHS)和分裂差异(分钟)分别绘制了男性和女性跑者的平均完成时间。当分裂变得不那么均匀(负向和正向)时,会有完成时间的成本,最快的完成时间与男性和女性跑者的略微正分裂相关。

还要注意的是,图 1©和(d)中的完成时间并不对称。负分配的时间似乎比正分配的对应时间增加得更快。我们可以在图 2(a)和(b)中更清楚地看到这一点。我们将男性和女性跑者的平均完成时间与其绝对RHS 进行对比,实际上是对负分配时间进行镜像处理,以便更直接地与正分配时间进行比较。在每种情况下,对于给定的绝对 RHS,正分配的完成时间比对应的负分配完成时间要快;例如,5%的正分配比 5%的负分配要快。而且,对于逐渐增加的绝对 RHS 值,正负完成时间之间的差异(阴影区域)也在增加,男性的增加幅度大于女性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2. 比较男性和女性的负分配和正分配的平均完成时间。

控制能力

上述分析的一个缺陷是没有考虑到较快的跑者往往进行更加均匀配速的比赛。因此,较快的跑者占据了更均匀 RHS 组的主要部分,从而导致更快的完成时间。解决这个问题的一种方法是寻找一种独立于跑者能力的性能评估方式。

为此,我们关注数据集中至少完成过 5 场马拉松的跑者——共有 101,120 名跑者,他们共完成了 680,921 场比赛——对于每位跑者,我们确定其最快完成时间作为其个人最佳(PB)时间。当然,这不能保证是他们真实的 PB,因为这些跑者中至少有一些会在我们的数据集之外完成马拉松。然而,这将作为他们真实 PB 时间的一个有用(保守)估计,并且我们可以用它来评估跑者相对于其 PB 的表现;我们定义非 PB 比赛的相对完成时间为其完成时间除以跑者的 PB 时间,因此相对完成时间为 1.1 意味着这场比赛比跑者的 PB 慢 10%。

超过 80%的跑者在个人最佳成绩(PB)中完成的成绩与他们非 PB 比赛的更均匀分配(RHS ~ 0.069)相比要好于平均分配(RHS ~ 0.13),这进一步支持了更均匀配速是一种良好策略的假设。在下图 3(a)中,我们可以看到这些相对完成时间也随着配速变得更加均匀而得到改善,而在图 3(b)中,这些更均匀配速的比赛中,PB 的比例也显著更高。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3. 比较男性和女性跑者基于配速的表现,使用(a)完成时间作为个人最佳时间的分数和(b)PB 比赛的比例。

因此,尽管更快的跑者的比赛配速更均匀,我们仍然发现跑者的最佳(PB)比赛比非 PB 比赛更均匀,无论能力如何。此外,非 PB 比赛的相对表现随着配速的均匀性提高而改善,并且,无论能力如何,最多 PB 和最佳相对完成时间都与略微的正向分裂相关。

再次在上面的图 3(a) 中,我们可以看到相对完成时间在正向分裂的比赛中有偏向。图 4(a) 和 (b) 展示了这一点,再次通过根据绝对 RHS 值绘制跑者的相对完成时间,分为男性和女性。在这两种情况下,正向分裂的相对完成时间优于负向分裂。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4. 比较男性和女性在负向和正向分裂下的相对完成时间。

为什么会有偶数和正向分裂偏差?

尽管更均匀的配速与更好的表现一致,但似乎是正向分裂,而非负向分裂,对休闲跑者更有益。这是为什么呢?我们的工作假设是,这归结于跑者是否能够以这样的方式控制比赛配速,以至于他们在最后阶段投入了最大的努力,刚好在储备完全耗尽时完成比赛。

虽然这听起来很明显,但并不是显而易见的结论。跑者可能会以均匀的配速完成比赛,但没有过度推动自己——放弃 PB——以在当前能力范围内完成比赛。或者跑者可能会跑出过于保守的负向分裂,即使第二阶段更快,他们在终点线前还有更多体力;这又是一个错失 PB 的机会。或者,像往常一样,计划中的正向分裂,如果第一阶段过快,会导致马拉松选手在终点线前很久就已经体力耗尽,并且减速过多,无法 PB。

我们认为,适度 正向分裂(0 < RHS < 0.25)似乎比其对应的 适度 负向分裂(-0.25 < RHS < 0)更好,至少在休闲跑者中,这是因为适度的正向分裂比适度的负向分裂更容易“做到正确”。换句话说,跑者进行适度的负向分裂似乎面临着更大的风险,即在比赛结束时还有剩余体力,导致表现不佳,而进行相应正向分裂的跑者则面临在结束前体力耗尽的风险。

我们可以通过观察跑者完成马拉松最后阶段(我们数据集中最后的 2.2 公里或约 1.37 英里)的情况来获得一些见解,因为他们在比赛这一部分的减速或加速程度将告诉我们很多关于他们当前状况的信息。在图 5(a)中,我们绘制了不同 RHS 范围内最后 2.2 公里的相对配速(实际最后配速除以平均比赛配速)。跑负分段的人通常会加速(相对最终配速<1),而跑正分段的人则趋向于减速(相对最终配速>1)。

重要的是,范围在-0.2<RHS<0 的负分段者的平均相对最终配速为 0.89(对应 11%的加速),而正分段(0<RHS<0.2)的平均相对配速仅为 1.07(7%的减速)。换句话说,在马拉松的最后阶段,适度的负分段者往往比对应的正分段者加速更多,而正分段者几乎可以肯定会感到疲惫(他们在终点临近时甚至无法加速),而负分段者可能不会出现这种情况。这种表现偏差与这种可能性一致。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5. 根据跑步者的分段成绩,马拉松最后阶段(2.2 公里/约 1.37 英里)的平均加速速度和加速跑者的比例。

在图 5(a)中,男女线条在 0.005 < RHS < 0.03 之间交叉于平均比赛配速线(相对最终配速=1),这表明这是一个配速甜点区,因为在这个狭窄的 RHS 范围内,跑者以没有剩余的状态到达终点——他们能够保持配速,但无法加速也不会减速——因此我们在本分析中早些时候看到了这个 RHS 范围内的最佳表现。换句话说,轻微的正分段在这个 RHS 范围内与更快的完成时间和更多的 PB 相关,因为它使跑者以没有剩余的状态到达终点。拥有相同或更负的分段跑者有足够的储备来加速,而更正的分段跑者(RHS>0.03)则过早减速,并在越过终点线之前耗尽了储备。

当然,这并不是说小的正配速没有风险。可以推测,许多以大正配速(RHS>0.2)完成马拉松的跑者在开始时心中设想的是较为温和的配速,但也许他们对早期的配速或比赛日的补给判断不准确。负配速的相应危险在于过于保守地配速早期阶段。图 5(b)显示了在马拉松最后 2.2 公里中加速(即使只是稍微加速)的跑者比例,我们看到女性跑者无论配速如何更可能会加速。例如,在那些跑 4%正配速(RHS=0.04)的跑者中,大约 45%的女性跑者在比赛结束时加速,而男性跑者只有 25%。

如果在比赛末尾加速等同于早期保守的配速,那么这可能表明女性跑者的配速比男性跑者更为保守,即使是在那些配速大致均匀的跑者中也是如此。男性配速更激进的想法并不新鲜,但通常被归因于极端的正配速实例,例如那些撞墙的情况。这里我们看到的证据表明女性跑者在整个 RHS 值范围内配速较为保守。在图 3(a)中也可以明显看出,男性在广泛的 RHS 范围内往往表现略好(相对于他们的 PB),这与女性跑者更为保守的配速观察一致。也许女性跑者可以通过调整配速稍微减少保守度来提高表现?

受控渐退

我们所呈现的略微正配速的证据与所谓的受控渐退配速模式非常接近。通常表现为适度(30 秒—3 分钟)的正配速,过去曾推荐这种模式,因为业余跑者可能会从听到他们不必跑负配速以实现个人最好成绩的正心态转变中受益。

这项分析进一步提供了支持受控渐退的具体证据。例如,图 6(a)放大了这一受控渐退区域的比赛相对完成时间,我们可以看到男性和女性的最佳表现与这一区域相关。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6. 与受控渐退相关的比赛相对完成时间。

图 6(b)使用相对半程配速而非分钟配速差异来突出相应区域。我们可以看到类似的结果,最佳相对完成时间对应于 0.005 到 0.03 之间的相对半程配速(0.5%—3%的正配速)。

在每种情况下,最佳的完成时间都与正向分裂相关,其中第二半程时间比第一半程时间长约 90 秒,或相对半程分裂约为 0.015。因此,可以认为,跑者的 A 目标可能是瞄准略微正向的分裂(90 秒或约 1.5%),B 目标则是将正向分裂保持在 3 分钟以下(或约 3%)。在这些分裂之外(无论是负分裂还是正分裂),表现往往会下降。

结论

本文开始时质疑了传统观念,即休闲跑者应计划在下一次马拉松中实现负分裂,主要因为精英跑者似乎都这样做。我们发现这种建议很少被遵循,也许有充分的理由,因为略微正向的分裂(0.5-3%)似乎为休闲跑者提供了一个理想的配速点,而最佳表现通常出现在第二半程比第一半程长 1.5%的比赛中。这与控制衰退的配速模式非常接近。

这一分析的重要局限性在于,虽然我们的数据集反映了比赛期间发生的情况,但它并未告诉我们跑者是否按计划或预期的方式进行比赛。如前所述,一些计划进行均衡或适度正向分裂的跑者可能最终会出现更大的正向分裂。这就是马拉松的本质。即使有最好的意图,一些负分裂也可能转变为正分裂,具体取决于训练、补给和比赛条件等因素。

当然,对于许多休闲跑者来说,马拉松并不只是为了打破个人最佳记录。马拉松的乐趣来自于完成比赛的“快乐”而非打破个人最佳的痛苦。尽管如此,数据表明,略微正向的分裂是一种明智的策略,无论你的比赛目标如何!

特别感谢 Amby Burfoot Padraig Cunningham 对本文早期草稿的宝贵反馈。

除非另有说明,本文中的所有图片均由作者创作。

概率关系的反直观性质

原文:towardsdatascience.com/the-counter-intuitive-nature-of-probabilistic-relationships-2589bdd2a763?source=collection_archive---------13-----------------------#2023-07-21

如果 y 可以被估计为 x 的线性函数,并不意味着 x 也可以被估计为 y 的线性函数

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Alireza Modirshanechi

·

关注 发表在 Towards Data Science · 10 分钟阅读 · 2023 年 7 月 21 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个概率关系的示例(与图 1A-B 中的可视化风格相同)—— 作者提供的图像

考虑两个实值变量 xy,例如,父亲的身高和儿子的身高。统计学中回归分析的核心问题是通过已知 x 来预测 y,例如,根据父亲的身高预测儿子的身高¹。

线性回归的想法是使用x的线性函数作为对y的猜测。正式地,这意味着将ŷ(x) = α₁x + α₀作为我们的猜测,并通过最小化yŷ之间的均方误差来找到α₀α₁。现在,假设我们使用一个巨大的数据集并找到α₀α₁的最佳值,这样我们就知道了如何根据x找到y的最佳估计。那么我们如何利用这些最佳的α₀α₁来根据y找到关于x的猜测*x̂(y)*呢?例如,如果我们总是能根据父亲的身高来猜测儿子的身高,那么我们可以根据儿子的身高猜测父亲的身高吗?

这些问题是“我们如何使用ŷ(x)来找出x̂(y)?”的特殊情况。虽然这听起来可能很简单,但这个问题实际上非常难以解决。在本文中,我研究了*ŷ(x)x̂(y)在确定性和概率性环境下的联系,并表明我们对ŷ(x)x̂(y)*在确定性环境下的关系的直觉不能推广到概率性环境中。

问题的正式表述

确定性环境

所谓确定性环境,我指的是(i)没有随机性和(ii)每个x的值总是对应于相同的y的值。正式地,在这些环境中,我写作y = f(x),其中f: R → R是某个函数。在这种情况下,x以完全确定的方式决定y(即,没有随机性或噪声),*ŷ(x)的最佳选择是f(x)*本身。例如,如果一个儿子的身高总是其父亲身高的 1.05 倍(暂时忽略这个例子的不可行性!),那么我们对儿子身高的最佳猜测就是将父亲的身高乘以 1.05。

如果f是一个可逆函数,那么x̂(y)的最佳选择等于f的逆。在上面的例子中,这意味着对父亲身高的最佳猜测总是儿子身高除以 1.05。因此,确定性情况下ŷ(x)x̂(y)之间的联系是直接的,可以简化为找到函数f及其逆。

概率性环境

在概率性环境中,xy是随机变量XY的样本。在这种情况下,单个x的值可以对应多个y的值,为了最小化均方误差,ŷ(x)的最佳选择是条件期望E[Y|X=x]—见脚注²。用更容易理解的话来说,这意味着如果你训练一个非常有表现力的神经网络来预测给定xy(使用一个足够大的数据集),那么你的网络将会收敛到E[Y|X=x]

类似地,x̂(y) 的最佳选择是 E[X|Y=y] — 如果你训练你的表达能力强的网络来预测 x 给定 y,那么它原则上会收敛到 E[X|Y=y]。因此,在概率设置中,ŷ(x) 如何与 x̂(y) 相关的问题可以重新表述为条件期望 E[Y|X=x]E[X|Y=y] 之间的关系。

本文的目标

为了简化问题,我专注于线性关系,即 ŷ(x)x 上是线性的。线性确定性关系具有线性逆,即 y = αx(对于某个 α≠0)意味着 x = βyβ = 1/α — 见脚注³。与确定性关系 y = αx 类似的概率线性关系为

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

公式 1

其中 Z 是一个附加的随机变量,通常称为“噪声”或“误差项”,其条件平均值假定为零,即 E[Z|X=x] = 0 对所有 x 都成立;注意,我们并不总是假设 ZX 是独立的。利用公式 1,给定 X=xY 的条件期望为(见脚注⁴)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

公式 2

公式 2 说明条件期望 ŷ(x)x 上是线性的,因此可以看作是线性确定性关系 y = αx 的概率对应。

在本文剩余部分,我将提出两个问题:

  1. 公式 2 是否意味着 x̂(y) := E[X|Y=y] = βy 对某个 β≠0?换句话说,公式 2 中的线性关系是否有线性逆?

  2. 如果确实存在 x̂(y) = βy,那么我们可以像在确定性情况下那样写 β = 1/α 吗?

我使用了两个反例,表明尽管听起来有些反直觉,但两个问题的答案都是否定的!

示例 1:当 β 不是 α 的逆时

作为第一个例子,考虑线性回归问题的最典型设置,总结为以下三个假设(除了公式 1;见图 1A进行可视化):

  1. 误差项 ZX 独立。

  2. X 服从均值为零、方差为 1 的高斯分布。

  3. Z 服从均值为零、方差为 σ² 的高斯分布。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1. 可视化示例 1 和示例 2。 面板 AB 可视化了示例 1 (A;α = 0.5,固定 σ² = 3/4) 和示例 2 (B;α = 0.5,σ² 依赖于 x) 的 Y 在给定 X 条件下的分布。给定随机变量 X 的一个值,随机变量 Y 在这两个示例中都服从高斯分布:黑色线条显示条件期望 E[Y|X=x],阴影区域显示高斯分布的标准差。点显示了 (X, Y) 的 500 个联合分布样本。面板 C 显示了 Y 的边际分布(X 服从标准正态分布),示例 1 (蓝色) 和示例 2 (红色):示例 1 中 Y 的边际分布是均值为零、方差为 α² + σ² 的高斯分布,但我们只能数值评估示例 2 中 Y 的边际分布。

经过几行代数计算,很容易证明,这些假设意味着 Y 服从均值为零、方差为 α² + σ² 的高斯分布。此外,这些假设还意味着 XY 是联合高斯分布,均值为零,协方差矩阵等于

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由于我们拥有 XY 的完整联合分布,我们可以推导它们的条件期望(见脚注⁵):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,给定我们第一个示例的假设,方程 2 具有形式为 x̂(y) = βy 的线性逆,但 β 不等于其确定性对应的 1/α ——除非我们有 σ = 0,这 等同于 确定性情况!

这一结果表明,我们对确定性线性关系的直觉不能推广到概率线性关系。为了更清楚地看到这一结果所暗示的真正疯狂程度,让我们首先考虑 α = 0.5 的确定性情况 (σ = 0图 2A 和 2B 中的蓝色曲线):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这意味着,给定一个 x 值,y 的值是 x 的一半;给定一个 y 值,x 的值是 y 的两倍,这看起来是直观的。重要的是,我们总是有 x < y。现在,让我们再次考虑 α = 0.5,但这次 σ² = 3/4图 2A 和 2B 中的红色曲线)。这种噪声方差的选择意味着 β = α = 0.5,结果是

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这意味着,给定一个 x 值,我们对 y 的估计是 x 的一半;然而,给定一个 y 值,我们对 x 的估计也是 y 的一半!奇怪的是,我们总是有 x̂(y) < y ŷ(x) < x ——如果变量是确定性的,这将是不可能的。看似反直觉的是,方程 1 可以重写为

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

方程 3

然而,这只能意味着(与 方程 2 相对)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

方程 4

问题在于,虽然我们设计时有E[Z|X=x]=0,但我们不能对E[Z|Y=y]及其对y的依赖性做出任何说明!换句话说,使得x̂(y)y/α不同的是观察值y还包含关于误差Z的信息,例如,如果我们观察到一个非常大的y值,则意味着很可能误差Z也具有一个较大的值,这应在估计X时考虑进去。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2. 示例 1 和示例 2 中的线性关系及其逆关系。 面板A展示了在示例 1 和示例 2 的概率设置下*ŷ(x)与 x 之间的线性关系(红色;α = 0.5)和 y 与 x 之间的等效确定性关系(蓝色);注意ŷ(x)作为函数 x 在两个示例中是相同的。面板BC展示了在示例 1(面板B中的红色;固定σ² = 3/4)和示例 2(面板C中的红色;σ² 依赖于 x)的概率设置下x̂(y)*与 y 之间的逆关系。蓝线显示了参考的等效确定性关系的逆关系。在所有面板中,虚线黑色显示了 y=x 线。

这是对看似矛盾的陈述的简单解释,例如“高个子父亲的儿子(平均而言)也很高,但没有父亲那么高,同时,高个子的儿子有(平均而言)高个子父亲,但没有儿子那么高”!

总结,我们的示例 1 显示,即使概率线性关系ŷ(x) = αx具有形式为x̂(y) = βy的线性逆关系,斜率β不一定等于其确定性对等物1/α

示例 2:当x̂(y) 是 非线性时

只有当方程 4中的E[Z|Y=y]也是y的线性函数时,形式为x̂(y) = βy的逆关系才是可能的。在第二个示例中,我对示例 1 进行了一些小修改,以打破这一条件!

特别地,我假设误差项Z的方差依赖于随机变量X——与示例 1 中的假设 1 相对。正式地,我假设(除了方程 1;有关可视化见图 1B):

  1. X服从均值为零、方差为 1 的高斯分布(与示例 1 中的假设 2 相同)。

  2. 给定X=x,误差Z服从均值为零、方差为*σ² = 0.01 + 1/(1 + 2x²)*的高斯分布。

这些假设实际上意味着,给定X=x,随机变量Y服从均值为αx、方差为0.01 + 1/(1 + 2x²) 的高斯分布(见图 1B)。与示例 1 中XY的联合分布是高斯分布不同,示例 2 中XY的联合分布没有优雅的形式(见图 1C)。然而,我们仍然可以使用贝叶斯规则,找到给定Y=yX=x的相对复杂的条件密度(见图 3获取一些数值评估的示例):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

方程 5

其中曲线 N 表示高斯分布的概率密度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3 . 示例 2 中给定 Y=y 的 X 的条件分布。先验分布 p(x)(蓝色曲线)、似然 p(y|x)(橙色曲线)和后验分布 p(x|y)(黑色曲线;使用 方程 5 进行数值评估),从左到右,y = 0.5、1.5 和 2(假设所有情况下 α = 0.5)。

我们可以使用数值方法来评估条件期望

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

方程 6

对于给定的 yα图 2C 显示了 x̂(y)y 变化的函数,当 α = 0.5 时。尽管听起来可能违反直觉,但这种反向关系是高度非线性的——这是由于 图 1B 中显示的 x 依赖的误差方差所致。这表明,即使 y 可以很好地作为 x 的线性函数进行估计,并不意味着 x 也可以很好地作为 y 的线性函数进行估计。这是因为 方程 4 中的 E[Z|Y=y] 在我们超出类似于示例 1 的标准假设时,可能会对 y 产生任何奇怪的函数依赖。

总结,我们的示例 2 显示了概率线性关系 ŷ(x) = αx一定具有 x̂(y) = βy 的线性逆关系。重要的是,x̂(y)y 之间的反向关系依赖于误差项 Z 的特性。

结论

在我们的教育过程中,我们中的大多数人对确定性关系建立了丰富的直觉——基于我们在微积分、分析等领域看到的所有有趣结果。然而,至关重要的是要认识到这种直觉的局限性,并且在考虑概率关系时不能信任这种直觉。特别是,示例 1 和 2 表明,即使是极其简单的概率关系也可能违背我们的直觉。

致谢

我感谢 Johanni Brea、Mohammad Tinati、Martin Barry、Guillaume BellecFlavio Martinelli 和 Ariane Delrocq 对本文内容的有益讨论和宝贵反馈。

代码:

所有用于分析的代码(使用 Julia 语言)可以在 这里 找到。

脚注:

¹ 有兴趣的读者可以在“父亲的身高如何影响儿子的身高”中查看这个问题的易懂处理。

² 详见维基百科上的 “最小均方误差” 页面。

在不失一般性的情况下,我们总是假设 xy 的平均值为零。因此,在父亲和儿子的身高示例中,xy 表示它们的身高与父亲和儿子的平均身高之间的差异

方程 1方程 2 之间的关系是可逆的,即,如果 方程 2XY 的唯一约束,那么我们总是可以将 Y 表示为 方程 1 中的形式,且存在一个满足 E[Z|X=x] = 0 的随机变量 Z

请参阅维基百科上的 ‘多元正态分布’ 页中的“二元条件期望”部分。

文字数据的创造性、偶尔混乱的世界

原文:towardsdatascience.com/the-creative-occasionally-messy-world-of-textual-data-9d0ae46fc71a?source=collection_archive---------12-----------------------#2023-11-16

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 TDS Editors

·

跟踪 发表在 Towards Data Science · 作为 Newsletter · 3 分钟阅读 · 2023 年 11 月 16 日

几年来,文本与数据的交集(多多少少)仅限于自然语言处理(NLP)领域——利用文本数据进行预测、分类和推荐工具的广泛机器学习任务。

大型语言模型的崛起为该领域引入了一系列令人兴奋的新可能性,各种新用例和创新工作流程如雨后春笋般涌现。本周的亮点代表了对这一新兴领域深入挖掘的一系列概念和方法。从提示工程到文本转图像和文本转语音应用,我们很高兴分享探索这些强大模型的文本数据作为输入和输出的创造性可能性的作者的工作。让我们深入了解。

  • 迷失在 DALL-E 3 翻译中当你在非英语语言中使用 DALL-E 3 等文本转图像工具时会发生什么?Yennie Jun继续探索在使用资源匮乏语言的用户中,模型性能存在的差异以及性别和其他偏见如何渗透到生成的图像中。

  • 如何将任何文本转换为概念图在他的最新文章中,Rahul Nayak深入探讨了知识图增强生成的世界,引导我们通过将文本语料库转化为概念图(GC),然后可视化来检测模式并得出有意义的洞见。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由Jas Min拍摄,来源于Unsplash

  • RAG:如何与您的数据交谈在过去几个月中,我们多次涉及检索增强生成技术,但Mariya Mansurova对这一话题的贡献仍然非常值得您花时间阅读:她提出了一个引人入胜的实用工作流程,用于分析使用 ChatGPT 的客户反馈。

  • FastSpeech:论文概述与实现近年来,文本转语音工具取得了重大进展。要深入了解它们的工作原理以及如何利用变压器来提高它们的性能,请不要错过Essam Wisam对 2019 年 FastSpeech 论文的易于理解的介绍,这为我们在该领域看到的许多进展提供了便利。

  • 释放文本数据的力量与 LLMs如果你是一个初学者,想要开始尝试尖端的文本数据技术,索非亚·罗莎的分步指南将帮助你迅速入门。它将带领我们完成从下载数据到使用 GPT-3 和分析结果的整个工作流程。

  • **通用提示工程路线图:情境支架框架(CSF)**提示工程已成为人类直觉与大型语言模型能力之间互动中的关键组成部分。朱塞佩·斯卡拉莫尼亚超越了基本的提示技巧,介绍了情境支架框架(CSF),这是一个“有效提示工程的通用心理模型”。

我们希望你这周能有时间涉猎其他主题——以下是我们最近在数据可视化、生成内容检测等方面的一些突出成果:

  • 人工智能能否帮助我们理解大脑的工作原理?斯蒂芬妮·申通过绘制生物学习与人工神经网络之间的相似性来探讨这个重要问题。

  • Matplotlib 是一个无处不在且强大的可视化工具,但也有其独特的怪癖。李·沃恩的初学者友好指南将帮助你踏上学习之旅的正确起点。

  • 对于所有关注营销的数据科学家:不要错过竹田一关于客户生命周期价值预测的清晰且详细介绍。

  • 区分人类生产的内容与模型生成的内容的能力从未如此重要——也从未如此困难。斯蒂芬妮·基尔默解读了当前围绕这一难题的风险和挑战。

  • 本周想做些实际操作吗?Amanda Iglesias Moreno的教程将引导你构建六边形地图,使用 H3 和 Plotly。

  • 在他最新的深度分析中,Jeffrey Näf详细探讨了随机森林中的变量重要性,涵盖了传统方法和较新的发展。

感谢你支持我们作者的工作!如果你喜欢你在 TDS 上阅读的文章,可以考虑成为 Medium 会员——这将解锁我们的整个档案(以及 Medium 上的所有其他帖子)。

直到下一个 Variable,

TDS 编辑团队

选择损失函数在创建准确的时间序列预测中的关键作用

原文:towardsdatascience.com/the-critical-role-of-loss-function-selection-in-creating-accurate-time-series-forecasts-77cf69dc9d2f

通过机器学习掌握时间序列预测

你的损失函数选择如何影响时间序列预测的准确性

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 John Adeojo

·发布在 Towards Data Science ·8 分钟阅读·2023 年 3 月 14 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 Dan Asaki 提供,来自 Unsplash

介绍

在这篇文章中,我将向你展示我认为在机器学习中常被忽视的一个重要方面,即损失函数的选择。我将通过介绍我在Driven Data主办的登革热竞赛中的方法来实现这一点。

我已经建立了一个岭回归模型作为基准模型,并使用不同损失函数构建了几种“风味”的 XGBoost 回归模型。

竞争者被要求预测伊基托斯和圣胡安每周时间间隔内的登革热总病例。每位竞争者根据其模型在测试数据集上的平均绝对误差(MAE)进行排名。要了解更多关于挑战、登革热或自己参与竞赛的信息,可以访问挑战的 主页

笔记本与代码库

我已经通过下面的链接提供了我的工作 Jupyter 笔记本和 GitHub 代码库。笔记本可能需要几分钟才能加载,请耐心等待。

📒 Jupyter 笔记本

📁Github 代码库

请随意在笔记本中尝试你自己的损失函数。

数据

数据以 csv 格式提供,包括训练特征、标签和测试特征(类似于 Kaggle 比赛格式)。数据特征包括天气预报、气候预报和植被指数。

标签(登革热总病例数)具有长尾特征,极端值较少。这在两个城市中都成立。这些分布的形状应该能给你一些线索,关于什么可能是适合模型的损失函数。

本项目使用的数据由 Driven Data 策划和提供,按照 Driven Data¹ 的规定可以在比赛之外免费使用。数据的原始来源可以从 国家海洋和大气管理局获得,数据公开且可以免费使用,具体根据 NOAA 的条款和条件

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图片:登革热总病例数 Iquitos 的分布

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图片:时间序列 Iquitos

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图片:登革热总病例数 San Juan 的分布

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图片:时间序列 San Juan

数据预处理

除了拆分数据外,我进行了三个数据预处理步骤。第一步是填补缺失值。我简单地用这些缺失特征的均值进行填补,这是一种快速且粗略的方法,避免了过多的分布偏移。

第二步是对数据进行标准化。标准化是一种特征缩放的形式,将数值特征的方差设置为 1,均值设置为 0。虽然 XGBoost 不需要缩放,但在拟合 Ridge 回归模型之前缩放特征是有用的。

我应用的第三个操作是独热编码。这将类别变量转换为其数值表示,以便可以用来训练模型。

注意:为了防止数据泄漏,所有预处理步骤都在训练集、验证集和测试集上分别进行。

特征工程

我构建了一些滞后特征。这些特征对于提高时间序列模型的预测性能特别有用,因为它们能够利用自相关性。

数据中存在一些限制,这限制了我能够创建的滞后期。首先,测试数据比训练数据提前了 3 年,并且没有标签。这意味着我能够创建的最短滞后期是 3 年,这感觉相当长。接下来,病例数在周级别上记录得不均匀。我不得不通过在月级别上聚合病例数,并基于这些聚合创建滞后特征来克服这个问题。滞后特征被设计为均值、最小值和最大值。

机器学习方法

有两个独立的时间序列跟踪登革热的总病例数,按周记录。其中一个时间序列用于 Iquitos (Iq),另一个用于 San Juan (Sj),我分别为每个时间序列训练了模型。

这帮助我将机器学习概念化为一个优化问题,在这个问题中,你试图最小化一个目标函数。

目标函数 = 损失函数 + 正则化函数

损失函数决定了如何“评分”模型在预测标签方面的整体表现,在这个案例中就是登革热的总病例数。正则化函数惩罚模型复杂性,有助于减轻过拟合。

数据拆分

在深入讨论超参数调整之前,我应该简要地讲一下数据拆分。对于时间序列问题,随机拆分是不合适的。随机拆分数据会导致未来的数据点被包含在测试集中,从而给出虚假的(且可能过于乐观的)性能结果。我按日期拆分了数据,以防止任何泄漏,并设立了准确的验证。

  • 对于 Iquitos,训练数据集是截至 2008 年 9 月 30 日的所有观测数据。对于 San Juan,拆分点设置在 2004 年 7 月 30 日。

  • 为了进一步减少过拟合,我在训练模型时使用了 5 折时间序列交叉验证。 阅读更多关于时间序列交叉验证的详细信息

超参数调整

我使用了均匀分布上的随机搜索来调整我的正则化参数。我发现这是一个明智的方法,可以快速探索广泛的超参数空间。阅读更多关于随机搜索的详细信息

注意,我只调整了 l2 正则化。这是 Ridge 和 XGBoost 的 alpha 和 lambda 参数。

如果你想尝试不同的正则化参数,看看它们如何影响模型拟合,可以查看这个 我之前制作的 Streamlit 应用

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图像:Iquitos 的 Ridge 模型超参数调整过程示例。平均测试分数为 MAE

损失函数

我选择的损失函数包括泊松回归、平均绝对误差 (MAE) 和均方误差 (MSE)。

泊松回归

对于登革热传播,独立假设是合理的,因为病例不会从一个人传到另一个人。然而,恒定速率假设可能不适用于本案例研究,我认为登革热病例的发生率会根据多维因素显著变化。然而,泊松分布确实以平均速率作为模型参数。

当目标变量是遵循泊松分布的计数数据时,使用泊松损失函数。这种分布类型假设事件独立发生并以恒定速率发生。

平均绝对误差

平均绝对误差(MAE)仅仅测量预测值与实际值之间的绝对差异。MAE 对异常值的敏感性低于一些其他损失函数,尤其是 MSE。

我认为许多人将损失函数与评分指标混淆,假设最佳的损失函数是 MAE,因为这是比赛评分的依据,你将看到事实并非如此。

均方误差

均方误差(MSE)计算模型输出与期望输出之间的平方差。由于平方差异,MSE 对异常值敏感。它对预测值和实际值之间的大差异惩罚比小差异更严重。

模型性能

模型性能在验证集上进行评估。评分(与损失函数不同)是基于竞赛中的 MAE 评分和易解释性(简单地衡量登革热病例)。你将从下面的图表中看到选择损失函数如何显著影响每个模型的数据拟合情况。在分析这些图表时,考虑一下选择的损失函数如何导致模型以这种方式拟合数据。

岭回归

伊基托斯

伊基托斯模型 MAE:7

该模型捕捉到了一些季节性变化,但未能有效预测异常值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图像:伊基托斯的岭回归模型预测值与实际值

圣胡安

圣胡安模型 MAE:23.7

该模型能够捕捉到一些季节性变化,但未能有效预测异常值。岭回归的预测值通常比实际病例略高。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图像:圣胡安的岭回归模型预测值与实际值

XGBoost 回归器:泊松损失

伊基托斯

伊基托斯模型 MAE:7.8

该模型捕捉到了一些季节性变化,但未能有效预测异常值。模型预测到的峰值往往被低估。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图像:伊基托斯的泊松 XGBoost 模型预测值与实际值

圣胡安

圣胡安模型 MAE:18.9

模型似乎能够很好地预测训练数据中的峰值。模型尝试对验证数据进行预测,但可能会出现过度预测。使用泊松损失训练使模型尝试预测峰值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图像:Poisson XGBoost 模型预测与 San Juan 实际数据对比

XGBoost 回归器:MAE 损失

Iquitos

Iquitos 模型 MAE: 7

尽管 MAE 较低,但该模型似乎只是通过数据绘制了一条直线。它对异常值和季节性完全不敏感。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图像:MAE XGBoost 模型预测与 Iquitos 实际数据对比

San Juan

San Juan 模型 MAE: 16.6

该模型在预测季节性方面似乎有效,但在预测峰值方面表现不佳,尽管它确实尝试了这方面的预测。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图像:MAE XGBoost 模型预测与 San Juan 实际数据对比

XGBoost 回归器:MSE 损失

Iquitos

Iquitos 模型 MAE: 7.3

该模型能够捕捉到一定的季节性,但在捕捉异常值方面表现不佳。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图像:MSE XGBoost 模型预测与 Iquitos 实际数据对比

San Juan

San Juan 模型 MAE: 18.48

该模型捕捉了季节性,但对异常值的捕捉效果不是很好。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图像:MSE XGBoost 模型预测与 San Juan 实际数据对比

提交结果

Poisson 模型在测试数据上获得了最高的整体准确率,MAE 为 27.6,MSE 模型次之,为 27.8,MAE 模型最后,为 29。

Poisson 损失 XGBoost 在竞争中排名前 27%。对于最小的超参数调整和特征工程,这个成绩还不错。

最后的思考

选择最佳模型完全依赖于预测目标。严格来说,如果我们只是讨论在验证集上最小化 MAE 的模型,那么整体最佳的是具有 MAE 损失的 XGBoost 回归器。然而,表现出最佳建模能力的模型似乎是 Poisson 损失变体。

感谢阅读。

[1] Bull, P., Slavitt, I. 和 Lipstein, G. (2016). 利用群众的力量增加社会部门数据科学的能力。 [在线] 可用: https://arxiv.org/abs/1606.07781 [访问日期:2023 年 3 月 13 日].

[## 通过我的推荐链接加入 Medium - John Adeojo

我分享数据科学项目、经验和专业知识,以帮助你在旅程中前行。你可以通过……

medium.com](https://medium.com/@johnadeojo/membership?source=post_page-----77cf69dc9d2f--------------------------------) [## 主页 | John Adeojo

关于我 欢迎来到我的专业作品集!我是一名经验丰富的数据科学家和机器学习(ML)专家……

www.john-adeojo.com

《人工智能中的持续学习现状》

原文:towardsdatascience.com/the-current-state-of-continual-learning-in-ai-af4a05c42f3c?source=collection_archive---------1-----------------------#2023-10-18

为什么 ChatGPT 只训练到 2021 年?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Jon Flynn

·

关注 发表在 Towards Data Science ·23 分钟阅读·2023 年 10 月 18 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由作者使用 DALL-E 3 生成

知识前提:

几年前,我通过 StatQuest 视频、Lena Voita 的 NLP 博客以及《深度学习实践者》和《谈谈网络》等书籍学习了深度学习的基础。我现在希望了解深度学习中的持续学习的现状。我发现很少有信息能以简单的术语总结这个主题,并且需要筛选专家研究论文。因此,这篇文章是为那些对该主题有基本了解但发现研究难以阅读且可能不是专家的读者准备的。它集中在聊天机器人上,因此了解 chatGPT 的训练阶段也很有帮助。

简介

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ChatGPT 告诉用户它的训练只到 2021 年 9 月(作者截图)

如果像 ChatGPT 这样的语言模型能够不断用新数据更新,它们将加速从软件开发到法律程序到学习的一系列任务。这也会使像这样的一些文章变得过时。

持续学习是暂停模型训练过程,保存模型当前状态,然后稍后在新数据上重新开始训练的能力。模型应该能够很好地泛化到新数据,同时仍保持对旧数据的泛化能力。有关更正式的定义,请参阅这篇论文

目前,行业内增强聊天机器人的数据趋势是使用 RAG,将查询的向量与提示工程相结合来回答问题,而不是继续用新数据训练 LLM。ChatGPT 的零-shot 学习能力,使其能够回答关于新、未见数据的问题,这种方法非常有吸引力。例如,你可以教它一种新的编程语言,然后用少量提示询问有关该语言的问题,尽管性能会随着输入的 tokens 数量增加而略微下降。基于新主题不断训练模型回答问题需要大量计算资源,更重要的是,需要广泛的相关主题数据。此外,如果一个主题在训练集中出现频率很低,它的泛化能力会很差。例如:拿一个不受欢迎的公共仓库,尽管它在训练过程中曾见过,但可能对它了解甚少,还可能出现幻觉。上下文窗口(模型能接收的 tokens 数量)正在迅速变大,使 RAG 更加吸引人。然而,理想情况下,我们是否不希望有一个智能的全知模型,而不需要任何外部数据库?

持续学习是通向 AGI 的重要一步,有人怀疑我们是否能够在没有深度学习网络架构重大变革的情况下实现这一目标。杰夫·霍金斯在他的书《千脑》中提到,他认为当前的 ANN 不具备有效的持续学习能力,并且认为未来的模型可能需要更类似于人脑的架构,利用他关于新皮质皮层柱中的参考框架的理论。

语言模型的预训练阶段与微调阶段的持续学习

今年早些时候,发布了一篇名为《LIMA:少即是多的对齐》的研究论文。该论文介绍了一个没有使用来自人类反馈的强化学习(RLHF)进行训练的聊天机器人,而是仅在 1,000 个精心标注的问答样本上进行了微调。令人惊讶的是,研究人员表示,在 43%的情况下,“该聊天机器人的回应与 GPT-4 相当”。我没有深入了解这些回应是如何评估的,但尽管如此,普遍认为模型的大部分知识和能力是在预训练阶段获得的,这项研究进一步证明了这一点。

像 ChatGPT 和 Llama-chat 这样的模型已经经历了广泛的微调,以生成更对齐和有效的响应。OpenAI 目前提供一个 API 来进一步微调模型,该 API 接受问答数据作为输入用于进一步训练。然而,这不应被用来教模型新数据,而是用来定制语气和可调性。尝试通过微调模型来教它新数据可能会导致灾难性遗忘,即模型会忘记已学到的内容。本文将介绍一些旨在减轻这一问题的技术。

这也引出了关于持续学习的可行性和策略的一些关键问题:

  • 在哪个阶段引入持续学习最为有利且最为容易?

  • 鉴于微调和 RLHF 都会改变整个模型的参数,是否还有可能回到预训练阶段进行进一步修改?

注:我为以下讨论的部分论文提供了一些类似 PyTorch 的伪代码。这些伪代码未经过测试,可能无法正常工作,用于逐步解析技术并翻译任何混淆的数学符号,以帮助读者理解。

持续学习技术的 5 个子类别

持续学习的综合概述论文指出,持续学习的训练策略可以分为 5 个子类别:

  1. 基于正则化的方法:这种方法在训练过程中添加约束或惩罚。

  2. 基于优化的方法:这种技术侧重于修改优化算法。

  3. 基于表示的方法:这旨在学习跨不同任务的共享特征表示,帮助模型更好地推广到新的但相关的任务。

  4. 基于回放的方法:这涉及存储先前任务的一些数据或学习特征,并在训练新任务时重播它们,以维持对早期学习任务的性能。换句话说,在训练新任务时混合旧数据集和新数据集。

  5. 基于架构的方法:在这种方法中,网络架构是动态调整的,通常通过增长或分区来委派网络的不同部分给不同的任务。

1. 基于正则化的方法

参数的软掩蔽

下面的软掩蔽技术在训练过程中对每个参数的梯度进行掩蔽和调整。接下来的基于优化的方法也会操纵梯度以实现持续学习。记住,梯度不只是在训练期间出现和消失的临时数字,它们是指导权重演变的信号。

SPG

论文 提出了一种名为 SPG(Soft-masking of Parameter-level Gradient flow)的技术,旨在:

  1. 在每个任务上训练模型直至收敛。

  2. 训练后,计算每个任务的每个参数的“重要性”。

  3. 根据其累积重要性软掩蔽参数,使重要参数在学习新任务时更不可能改变。

让我们逐步分解这个方法:

1. 训练第一个任务

如常在第一个任务的数据集上训练模型。

2. 计算第一个任务的参数重要性

在第一个任务的训练完成后,我们计算每个模型参数的重要性。这里的直觉很简单,我们使用每个参数的梯度来计算其重要性。较大的梯度意味着该参数的微小变化将导致损失更大的变化,这意味着模型的性能可能会更显著地变化,因此该参数是重要的。

梯度也被归一化,因为第一层的梯度可能很小,而最后一层的梯度可能很大。如果你基于这些原始梯度值计算重要性,最后一层的参数会显得更重要,因为其梯度的规模,并非因为它们在任务中真正更关键。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

计算 SPG(详见论文 第 3.1 节)中模型参数重要性的方程式

让我们将这个计算翻译成类似于 PyTorch 的伪代码:

import torch

def compute_final_importance(model, loss_function, data_loader):
    # Get a single batch from the data loader
    inputs, labels = next(iter(data_loader)) 

    # Forward and backward pass to calculate the gradients for all parameters
    outputs = model(inputs)
    loss = loss_function(outputs, labels)
    loss.backward()

    importances = []

    # Calculate importance based on the gradients
    for param in model.parameters():
        if param.grad is not None:  # Gradients may be None for some unused parameters
            normalized_grad = (param.grad - torch.mean(param.grad)) / torch.std(param.grad)
            importance = torch.tanh(normalized_grad)
            importances.append(importance)

    return torch.stack(importances).mean(dim=0)

3. 跨任务积累重要性

每个参数在任务之间的累计重要性是通过在任何阶段取最大值来简单计算的。

4. 训练后续任务、组合损失和软掩码机制:

在新任务上进行训练时,研究人员使用一个由两个部分组成的组合损失函数。其中一个是标准损失函数,用于新任务和数据,另一个是额外的损失函数,它涉及将数据通过模型(即上一个任务之后的收敛模型检查点),并汇总生成的 logits。在分类网络中,logits 通常是模型在通过类似 softmax 函数之前生成的原始非标准化预测。这些 logits 的总和作为一种损失形式。其理由是,如果在模型参数发生变化时,总和 logits 受到显著影响,那么这些参数对之前学习任务的性能至关重要。

这些额外损失生成的梯度在反向传播过程中作为指导,推动共享参数朝着一个不容易损害第一个任务性能的方向改变。因此,它充当了一种惩罚项,以强制确保对模型所做的任何更新不会导致与先前任务相关的重要信息的显著丧失。

在下一个任务上训练模型。使用标准训练循环,但在反向传播过程中,根据梯度的累计重要性来修改梯度。这就是软掩码机制:

import torch

accumulated_importance = # calculated at the end of each task

for epoch in range(num_epochs):
  for x, y in train_loader:

    # Forward Pass: Calculate the loss for the current task using the proper loss function
    logits = new_model(x)
    loss_current_task = nn.CrossEntropyLoss()(logits, y)

    # Forward Pass: Calculate the additional losses for previous tasks (CHI mechanism)
    loss_previous_tasks = 0
    for prev_task_id in range(task_id):
        logits_prev = old_model(x, prev_task_id)
        loss_previous_tasks += logits_prev.sum()

    # Combine the losses
    combined_loss = loss_current_task + loss_previous_tasks

    # Backward Pass
    optimizer.zero_grad()
    combined_loss.backward()

    # Update the accumulated importance
    for param, acc_imp in zip(model.parameters(), accumulated_importance):
        grad = param.grad
        acc_imp = torch.max(acc_imp, torch.abs(grad)) 

    # Soft-masking the gradients before taking an optimization step
    for param, imp in zip(model.parameters(), accumulated_importance):
        param.grad *= (1 - importance)

    optimizer.step()

5. 软掩码特殊情况

  • 特征提取器:共享特征提取器中参数的梯度根据其特定的累计重要性进行修改。

  • 分类头:对于分类头,梯度根据特征提取器的平均重要性进行修改。

将其应用于大型语言模型(LLMs)

请注意,这篇论文没有用语言模型进行实验,但我假设在语言模型中,你可以将变换器层视为类似于“特征提取器”,将最终分类层(预测序列中的下一个词或标记)视为“分类头”。

应用于语言模型持续预训练的软掩码

接下来我们将讨论一篇将类似软掩码应用于语言建模中的预训练阶段的论文。

这篇论文介绍了一种称为 DAS(带软掩码的持续 DA 预训练)的方法,用于大型语言模型预训练阶段的持续学习。它应用了一种类似于刚才讨论的软掩码技术,并结合了其他几种技术,试图在不遇到灾难性遗忘的情况下继续对 LLM 进行预训练。

让我们一步步分解:

初始预训练阶段

像正常一样预训练 LLM。

在新领域的进一步预训练

准备新领域数据:

准备一个来自不同领域的新数据集。

计算每个神经元的重要性

SPG 使用梯度来确定每个参数的重要性,然后将计算得到的重要性值应用于训练过程中参数梯度调整的掩码。本文试图确定每个单元/神经元的重要性,而不是参数,然后在训练过程中以相同的方式使用这种重要性,通过掩盖梯度来进行训练。

本文使用两种不同的方法来计算神经元的重要性,具体取决于任务。其一是基于梯度的重要性检测方法(最初在这篇论文中概述),其二是定制的“代理损失函数”。

首先介绍的是不是用于第一个新领域的持续学习。为什么?它需要来自训练数据集的数据才能工作,而作者表示用户“无法访问庞大的原始预训练数据集”,这是一个合理的假设。因此,在持续学习的第一个阶段使用的是代理损失函数,然后在随后的每个阶段使用其他方法。

代理损失函数(“代理 KL 散度损失”):

我最初觉得这个术语令人困惑,但它之所以这样称呼,是因为原始的基于梯度的重要性检测方法本身被定义为一种损失函数,你可以用它来运行网络的输出以获得每个神经元的梯度,然后可以用这些梯度来推导重要性,就像 SPG 技术一样。计算方法如下:

  • 取出我们希望训练的新领域的一个子集,并将其通过模型输入两次,以获得两个不同的表示。这些表示会有所不同,因为 Transformer 架构中存在的 dropout 掩码。

  • 计算这两个表示之间的 KL 散度。

使用代理和综合损失的修改版反向传播流程

  1. 前向传播: 数据通过神经网络进行前向传播。

  2. 反向传播:

应用代理损失以调整梯度: 代理损失函数的单元级重要性用于软掩盖原始梯度。这可以表示为:

adjusted_grad *= (1 − unit_level_importance)

计算综合损失(MLM + 对比损失): 使用 MLM 和对比损失来计算综合损失。

在更多领域进行进一步预训练

  1. 直接重要性计算: 对于每个新领域,现在可以直接使用来自新领域的数据通过方程 3 中概述的基于梯度的方法来计算每个单元的重要性,从而消除了在初始预训练后仅使用一次的代理损失函数的需要。

  2. 神经元的重要性在每个新任务学习时逐步更新。 这种更新使用逐元素最大值。“逐元素最大(EMax)操作”指的是逐个元素比较两个向量,并取每个对应元素的最大值来创建一个新向量。例如:如果你有两个相同长度的向量 A 和 B,逐元素最大值将产生一个新向量 C,其中每个元素 C[i] 是 A[i] 和 B[i] 之间的最大值。

2. 基于优化的方法

我们将参考综合调查论文第 3.1 节中概述的两种技术。

梯度方向保持

论文讨论了操控基于梯度的优化过程,以使新训练样本的梯度方向接近旧训练样本的梯度。公式

⟨ ∇θ Lₖ(θ; Dₖ), ∇θ Lₖ(θ; Mₜ) ⟩ ≥ 0

强制要求学习新任务时不会增加旧任务的损失。本质上,鼓励新任务和旧任务的梯度对齐。

分解公式时,我们取新任务的损失梯度(∇θ Lₖ(θ; Dₖ))与旧任务的损失梯度(∇θ Lₖ(θ; Mₜ))的点积,应该是非负的。在这种情况下,正的点积意味着旧任务和新任务的梯度通常指向相同的方向,这两个向量之间的角度小于或等于 90 度。

前向/反向传播:

前向传播:

你需要将新任务的输入数据 Dₖ 和旧任务的 Mₜ​ 通过同一个模型来计算每个任务的损失。

反向传播:

  1. 计算旧任务和新任务的网络参数的损失梯度。

  2. 对齐检查:计算两个梯度的点积。然后使用这些信息以使新任务的梯度的点积为非负。

  3. 更新权重:使用这些“对齐”的梯度更新模型参数。

 import torch

# Forward pass for the new task
output_k = model(D_k)
loss_k = criterion(output_k, y_k)

# Forward pass for the old task
output_t = model(M_t)
loss_t = criterion(output_t, y_t)

# Compute gradients for both tasks
loss_k.backward(retain_graph=True)  # Compute gradients for new task but keep computation graph
grad_k = torch.cat([p.grad.view(-1) for p in model.parameters()])  

optimizer.zero_grad() 

loss_t.backward()  # Compute gradients for old task
grad_t = torch.cat([p.grad.view(-1) for p in model.parameters()]) 

# Compute dot product and modify gradients if they don't align
dot_product = torch.dot(grad_k, grad_t)
if dot_product < 0:
    # I'm not sure how you modify the gradients here if they don't align, I'm not sure the paper specifies it

# Use the modified gradient to update model parameters
index = 0
for p in model.parameters():
    num_params = p.numel()
    # Update using modified gradients
    p.grad = grad_k[index: index + num_params].view(p.shape)
    index += num_params

optimizer.step()

无需旧训练样本的梯度方向保持

文本还强调,即使不存储旧样本,也可以进行梯度投影。NCL(自然连续学习,论文链接)是这里总结的技术。请注意,这可以归类为正则化和优化两种方法。

训练过程逐步:

前向传播:

你需要将新数据通过网络并计算损失。

反向传播:

目标: 目的是在遵守距离约束 d(θ,θ+δ)≤r. 的同时,最小化任务特定的损失 ℓk(θ)

算法逐步

  1. 如常,计算模型参数∇θ​ℓk​(θ)的损失梯度。

  2. δ 是使用更新规则计算的。这为你提供了基于新任务要求的模型参数 θ 的“建议”更改。

  3. 然后,将这个 δ 插入到距离约束公式中:*d(θ,θ+δ)=squareroot(δ⊤Λ_k-1​δ)*​。约束在当前参数 θ 周围起到边界作用,由距离度量 d(θ,θ+δ) 和半径 r 定义。我很难理解为什么叫它“半径”,而不是“约束值”或其他什么。我认为这是因为研究人员在高维空间中可视化梯度和训练过程。当你基于距离度量应用约束时,本质上是在高维空间中定义了一个围绕当前参数值的“球体”。这个球体的“半径” r 设置了在学习新任务时参数可以移动的限制。

  4. 如果提议的 δ 根据这个距离度量将 θ 移动得过远,即超出了这个边界,你就将其缩小,以使其保持在由半径 r 定义的允许区域内。

让我们更深入地查看每一部分:

更新规则: 更新规则提供了 θ 应该移动的方向。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来自 持续学习论文的综合概述 第 3.1 节的 NCL 更新规则

分解如下:

  • ∇θ ℓk(θ) 表示由损失函数计算的所有参数 (θ) 的梯度。

  • 参数重要性计算 (Λ^(k-1)_(-1)): 这个术语表示一个 精度矩阵,它是计算网络中参数重要性的另一种方式。更多细节见下

  • 正则化项 (θ — μ_(k-1)): 这个项将更新后的参数拉近到来自先前任务的最佳参数 μ_(k-1)。像之前的方法一样,它作为正则化器以避免偏离已经学到的内容。

  • 学习率 (λ)

距离约束: 在应用这个更新之前,你通常会检查这个变化 δ 是否会违反距离约束 d(θ,θ+δ)≤r。如果违反了,你通常会缩小 δ,以使其满足约束。

精度矩阵解释: 在之前的软掩蔽方法中,我们通过所有神经元的输出或它们的梯度来计算重要性。在这个方法中使用了一个精度矩阵。这有点复杂,所以我会尽力解释:

我们首先计算网络参数的 协方差矩阵。在神经网络的上下文中,梯度矩阵 G 的列对应于模型的参数(权重和偏差)。G 的每一行代表一个训练样本的梯度向量,相对于所有这些参数。

如果你有一个具有 P 个参数的神经网络(这包括所有层的所有权重和偏置),那么每个梯度向量将有 P 个元素,每个元素对应一个参数。因此,G 将是一个形状为 N × P 的矩阵,其中 N 代表每个批次,因此每行代表在给定批次中的所有训练样本的平均梯度向量。

当你从 G 计算协方差矩阵 Σ 时,得到的矩阵将具有 P × P 的维度。对角线条目 Σii​ 将指示与 ith 参数相关的梯度的方差,而非对角线条目 Σij​ 将指示与 ithjth 参数相关的梯度之间的协方差。这让你了解这些参数在训练过程中如何相互作用或协变。该矩阵的逆矩阵是 精度矩阵,我们用它来确定重要性。

为什么选择 精度矩阵 而不是 协方差矩阵?虽然协方差矩阵 Σ 确实捕捉了参数在训练过程中如何相互作用,但它没有特别指示每个参数在考虑所有其他参数时对当前任务的关键程度。相比之下,精度矩阵允许我们评估 条件独立性(这是概率论中的一个概念,查阅一下)。精度矩阵中的大值表示,在给定所有其他参数的情况下,知道一个参数对另一个参数的了解具有很高的信息量。我不会进入如何运作的示例,所以请让 ChatGPT 生成一些示例,使用一个非常小的神经网络来看看这些值如何被解释。

我们之前看到的计算重要性的方法关注于单个神经元或参数,忽略了它们之间的关系。另一方面,精度矩阵可以捕捉这些关系。像深度学习中的一切一样,这是否是一种更好的计算网络重要性的方法,还需要通过经验来验证,并且可能会根据任务和网络规模的不同而有所不同。

PyTorch 中的算法步骤:

import torch

# Constraint radius
radius = 0.1

for epoch in range(num_epochs):  
    for batch_idx, (data, target) in enumerate(data_loader):
        optimizer.zero_grad()

        # Forward pass
        output = model(data)
        loss = loss_function(output, target)

        # Backward pass to get gradients for params
        loss.backward()
        model_grad = torch.cat([p.grad.data.view(-1) for p in model.parameters()])

        # Compute δ using the NCL method
        # δ = Λ^(-1) * grad - (θ - µ)
        delta = torch.matmul(torch.inverse(covarianceMatrix), model_grad) - (torch.cat([p.data.view(-1) for p in model.parameters()]) - parametersForPrevTask)

        # Check constraint
        if torch.norm(delta) > radius:
            delta = radius * delta / torch.norm(delta)

        # Update model parameters (θ) using δ
        idx = 0
        for p in model.parameters():
            length = p.data.numel()
            p.data += delta[idx: idx + length].view(p.data.shape)
            idx += length

        # Update Λ and µ for the next task, probably going to be task-specific and non-trivial

3. 基于表示的方法

首先,需要注意的是,对 LLM 进行预训练以便进一步在下游任务中进行微调,是这个子类别中的持续学习的一个例子。我认为 ChatGPT 在处理从未见过的数据时的推理能力也是这种方法的一个例子。虽然我们从技术上称之为零样本学习,而“持续学习”这个术语要求更新模型参数,但它超越了我们以前见过的任何东西。正如介绍中所讨论的,提示工程可能是持续学习的未来,而不是不断更新参数。

接下来我们将探讨使用知识蒸馏进行持续学习。我不太确定这属于哪个子类别,但我猜这可能是表示、架构和重放方法的混合。尽管我们正在审查的一些技术可能看起来随意且在大规模上未经验证,但这一领域的突破往往是不可预测的。因此,保持广阔的视角非常重要。

知识蒸馏用于持续学习

你可以将一个网络的知识(或“蒸馏”)转移到另一个网络中,第二个网络能够合理地近似原始网络所学习的功能。

蒸馏模型(学生)被训练来模仿较大网络(教师)的输出,而不是直接用原始数据进行训练。例如,假设你想训练一个较小的学生模型来模仿一个大型预训练语言模型(教师)。将原始预训练数据集通过教师模型生成“软目标”。这些是潜在输出的概率分布,例如:下一词预测任务中,教师可能会提供像 90% 为“cat”、5% 为“kitten”、3% 为“feline”等概率。

这通常是为了将知识转移到更小的模型上,并且尽管模型较小,但效果非常好。

让我们看看一些研究人员是如何成功应用到一个 NER(命名实体识别)模型上的。训练过程非常简单:

训练过程一步步进行

论文中概述了两种主要方法:AddNER 和 ExtendNER。

AddNER 模型

请注意,NER 模型通过接收一系列标记(通常是一个句子)作为输入,然后为每个标记输出一个概率分布(针对不同类型的实体)。IOB 标记法通常用于 NER 模型,每个标记可以被标记为‘O’,或作为实体类型 X 的开始(‘B-’)或内部(‘I-’)。‘O’表示‘Outside’,这意味着当前标记不属于任何实体。因此,对于 n 个实体类型,你将在分类层中得到 2n 个输出神经元:n 个用于‘B-’ 标签(每种实体类型一个),n 个用于‘I-’ 标签(同样,每种实体类型一个)。加上‘O’ 标签,表示一个标记不属于任何实体,你将得到 2n + 1 个可能的标签。最终的维度可以写成 h × (2n + 1),其中 h 是隐藏层输出的大小。请注意,这仅适用于标记只能属于一个实体的模型。例如:“Apple” 可以被标记为“FOOD”和“COMPANY”。

架构和师生设置

在这种情况下,学生模型是教师模型的一个副本,额外增加了一个输出分类层,以学习模型需要识别的每种新实体类型。在训练过程中,新的输出层从新的标注数据中学习,而旧的层则通过教师模型的输出进行指导,以减少遗忘。

训练后,旧的输出层不会被丢弃。它接着使用冲突解决器部分*(第 3.3 节末尾)*中描述的算法和启发式方法,将这些输出组合成每个序列中的单一最终预测。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

AddNER 模型的示意图来自第 3.2 节

前向传播

  1. 旧实体类型:将输入句子通过教师模型,以获得旧实体类型的概率分布(在此背景下为“软目标”)。

  2. 新实体类型:相同的句子也会通过新学生模型,该模型具有专门针对新实体类型的额外输出层。

后向传播

组合损失函数:

  1. KD 损失:通过比较新模型(学生)对旧实体类型的输出概率与旧模型(教师)的匹配程度来计算。它使用 KL 散度来计算。它可能是逐个标记计算的,然后在句子或批次中的所有标记上求和或取平均,但我认为论文没有详细说明这一点。

  2. 交叉熵损失:这是常用的损失函数,用于比较模型对新实体类型的预测与来自新数据集的实际标签。

  3. 两者结合:这两种损失通过加权求和的方式结合成一个组合损失。结合这些损失的权重由超参数 alpha 和 beta 设定,像调整其他超参数一样,根据实验结果优化性能。

# Hyperparameters alpha and beta for weighting the two loss functions
alpha = 0.5
beta = 0.5

for epoch in range(num_epochs):
    for sentence, labels in D_new:
        # Forward pass in teacher model for old entity types
        teacher_probs_Ei = teacher_model(sentence)

        # Forward pass in student model for old and new entity types
        # Note: the new entity types must go through the new output layer (not shown in this pseudocode)
        student_probs_Ei, student_probs_Enew = student_model(sentence)

        # Compute KD loss
        kd_loss = KL_divergence(teacher_probs_Ei, student_probs_Ei)

        # Compute CE loss for new entity types
        ce_loss = cross_entropy(labels, student_probs_Enew)

        # Combined loss
        total_loss = alpha * kd_loss + beta * ce_loss

        # Backward pass
        total_loss.backward()

        # Update student model parameters
        optimizer.step()

ExtendNER 模型

架构和师生设置

ExtendNER 模型扩展了输出层的维度,以适应新的实体类型,而不是添加新的输出层。论文相当简单地解释了这些维度应如何设置:

“假设Mi能够识别n个实体类型,它的最终层可以被视为一个*h×(2n+1)的矩阵。然后,Mi+1的输出层将扩展为一个h × (2n + 2m + 1)*的矩阵,以适应新的实体类型。”

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ExtendNER 模型的示意图来自第 3.4 节

前向传播

与 AddNER 相同,但维度扩展。

后向传播

损失计算使用KL 散度损失或交叉熵损失,具体取决于以下因素:

  • 当 NER 类别标签y为“O”(来自 IOB 标注模式)时,使用 KL 散度损失。

  • 当类别标签y不是“O”时,使用交叉熵损失。

最终预测

Viterbi 算法用于解码最终的实体类型。

AddNER 和 ExtendNER 模型在持续学习中表现良好,结果之间差异不大

4. 基于回放的方法

“微调的语言模型是持续学习者”

论文链接

论文中的模型不是像 GPT 这样仅用于对话回应的通用单任务模型。相反,它是为一系列专业任务进行微调的,从文本简化到俳句生成。这些任务各自有独特的要求、评估标准和专业训练数据集。

研究人员将旧数据集的部分与新数据集混合,通过在微调新任务时只混入 1%的旧任务数据集,取得了很好的效果。这是在多个任务上顺序进行的(8)。该模型在零-shot 学习设置中也表现良好,意味着它可以很好地推广到未经过训练的任务。例如,当给定一个未见过的主题时,它可以生成具有正确音节数的俳句,显示出其推广能力。研究人员还提到,他们的方法是任务顺序不变的,意味着学习任务的顺序不会影响模型的表现。实验发现,与新数据集混合的旧数据集的数量不会显著影响主要任务的表现。然而,它会影响零-shot 学习。在 0%复习时,模型倾向于忘记零-shot 任务,而在 1%复习时,模型在这些任务中的表现保持得很好。

这些都显得积极,我们可以只添加 1%的旧数据集并解决持续学习的问题,但当然,将其应用于像 chatGPT 这样的聊天机器人,将是经验性的,并且可能完全不同。即使假设 chatGPT 可以在微调和 RLHF 阶段持续训练,也需要大量标记的对话数据。

5. 基于架构的方法

我不会在这里详细讨论具体的论文或实现,但我会简要概述这种方法和几种不同的技术。我建议阅读这篇综合调查论文的第 4.5 节。它比其他章节更容易阅读。

  1. 参数分配:在这里,网络参数的一个子集被分配给每个任务。这可以通过屏蔽无关的神经元或明确识别当前任务的重要神经元来实现。

  2. 模块化网络:这涉及为每个任务使用独立的子网络或模块。

子网络可以以各种方式连接,以形成一个集成体或更复杂的架构。以下是几种常见的子网络连接方法:

输出的连接:

在这种方法中,多个子网络的输出被串联成一个张量,然后可以通过额外的层处理以生成最终输出。

投票机制:

在某些模型中,每个子网络对可能的结果进行“投票”,最终决定通过多数投票或加权投票来做出。这有生物学启发,因为它类似于新皮层中不同皮层柱进行投票的方式。

跳跃连接:

一些架构允许子网络具有跳跃连接到模型的其他部分,从而允许信息在模块之间流动。

顺序:

在这种情况下,一个子网络的输出作为下一个子网络的输入。

回到聊天机器人,如果能创建一个由两个子网络组成的架构,我觉得特别有趣。第一个是预训练模型,它包含通用的“知识”。第二个包含对齐模型所需的知识。一旦模型对齐,它将不再需要标记的对话数据。相反,它可以通过以无监督的方式训练预训练子网络来不断更新。

结论

总之,深度学习中持续学习的子领域具有挑战性,并且大部分仍未被了解。这是因为我们尚未完全理解大规模语言模型中的神经元如何工作,正如引言中所述,当前的网络架构或深度学习本身可能不适合此任务。

我注意到上个月 ChatGPT(仅 GPT-4)已经更新,现在它显示“自我的训练截止日期是 2022 年 1 月”,所以我想知道 OpenAI 的团队是如何实现这一点的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ChatGPT(GPT-4 变体)告知用户其训练数据截至到 2022 年 1 月(作者截图)

《维度灾难揭秘》

原文:towardsdatascience.com/the-curse-of-dimensionality-demystified-2fc9b0bb1126

理解维度灾难背后的数学直觉

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Reza Bagheri

·发布于 Towards Data Science ·23 分钟阅读·2023 年 10 月 6 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源: pixabay.com/illustrations/ancient-art-background-cosmos-dark-764930/

维度灾难 指的是在分析高维数据时出现的问题。数据集的维度维数指的是数据集中线性独立特征的数量,因此高维数据集就是特征数量众多的数据集。这个术语首次由贝尔曼在 1961 年提出,他观察到,为了以一定的准确性估计一个任意函数,所需的样本数量会随着函数所取参数数量的增加而呈指数增长。

在这篇文章中,我们详细探讨了在分析高维数据集时出现的数学问题。尽管这些问题可能看起来违反直觉,但可以用直观的方式来解释它们。我们不进行纯理论讨论,而是使用 Python 创建并分析高维数据集,观察维度灾难在实践中的表现。在这篇文章中,所有图片,除非另有说明,均由作者提供。

数据集的维度

如前所述,数据集的维度定义为其具有的线性独立特征的数量。一个线性独立的特征不能表示为数据集中其他特征的线性组合。因此,如果数据集中的一个特征或列是其他特征的线性组合,它不会增加数据集的维度。例如,图 1 显示了两个数据集。第一个数据集有两个线性独立的列,因此其维度为 2。在第二个数据集中,一列是另一列的倍数,因此我们只有一个独立特征。正如该数据集的图示所示,尽管有两个特征,但所有数据点都在一条 1 维的线上。因此,这个数据集的维度是 1。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1

维度对体积的影响

维度诅咒的主要原因是维度对体积的影响。在这里,我们关注数据集的几何解释。通常,我们可以假设数据集是从一个群体中抽取的随机样本。例如,假设我们的群体是图 2 所示的正方形上的点集合。这个正方形是所有点(x₁, x₂)的集合,使得 0≤x₁≤1 和 0≤x₂≤1,且这个正方形的边长为 1。

每个群体都有其自己的分布,这里我们假设这个正方形上的点是均匀分布的。这意味着如果我们试图从这个群体中随机选择一个点,所有的点都有相等的被选中机会。现在,如果我们从这个群体中抽取大小为n的随机样本(即从这个正方形中随机选择n个点),这些点形成了一个具有n行和两个特征的数据集。因此,这个数据集的维度是 2。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2

同样地,我们可以通过从一个边长为 1 的立方体中抽取随机样本来创建一个维度为 3 的数据集(图 2)。这个立方体是所有点(x₁, x₂, x₃)的集合,使得 0≤x₁≤1, 0≤x₂≤1, 0≤x₃≤1,并且这个立方体上的点是均匀分布的。

最终,我们可以扩展这个想法,通过从边长为 1 的d维超立方体中随机抽取大小为n的样本来创建一个d维数据集。超立方体定义为所有点(x₁, x₂,…, x_d)的集合,使得 0≤xᵢ≤1(对于 i=1…d)。同样,我们可以假设这些点在这个超立方体中是均匀分布的。生成的数据集包含n个示例(观测值)和d个特征。

边长为Ld维超立方体的体积是L^d。(请注意,如果d=2,该公式给出的就是边长为L的正方形的面积。然而,这里我们假设面积是二维对象的体积的特例)。因此,如果L=1,不管d的值是什么,体积都是 1。但重要的不是超立方体的总体积,而是体积在超立方体内部的分布。这里我们用一个例子来解释它。

我们可以将单位正方形分成 10 个壳体,如图 3 所示。第一个壳体实际上是单位正方形中心的小正方形,边长为 0.1。这个正方形的右下角位于x₁=0.55。第二个壳体的右下角位于x₁=0.6,因此其厚度为 0.05。所有其余的壳体都有相同的厚度,最外层壳体的右下角位于x₁=1。因此,它覆盖了单位正方形的边缘。图 3 展示了其中一个壳体作为示例,其右下角位于x₁=0.8。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3

我们可以轻松计算出底部右角位于x₁=c的壳体的体积(面积):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在我们可以将此过程扩展到d维超立方体。我们将超立方体分成 10 个厚度相同的超立方壳体。

角落之一位于x₁=c的壳体的体积由以下公式确定:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

列表 1 计算了不同d值下所有这些壳体的体积,并创建了体积的柱状图。结果如图 4 所示。

# listing 1

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import uniform, norm
from scipy.spatial import distance_matrix
from sklearn.neighbors import KNeighborsRegressor
%matplotlib inline

num_dims=[2, 3, 9, 15, 50, 100]
x_list = np.linspace(0.5, 1, 11)
edge_list = 2*(x_list - 0.5)
fig, axs = plt.subplots(3, 2, figsize=(14, 19))
plt.subplots_adjust(hspace=0.3)

for i, d in enumerate(num_dims):
    vols = [(edge_list[i+1])**d-(edge_list[i])**d \
            for i in range(len(x_list)-1)]
    axs[i//2, i%2].bar(x_list[1:], vols, width = 0.03)
    axs[i//2, i%2].set_title("d={}".format(d), fontsize=20)
    axs[i//2, i%2].set_xlabel('Shell corner $x_1$ coordinate', fontsize=18)
    axs[i//2, i%2].set_ylabel('Volume', fontsize=18)
    axs[i//2, i%2].set_xticks(x_list)
    axs[i//2, i%2].tick_params(axis='both', labelsize=12)

plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4

正如你所见,随着维度的增加,内层壳体的总体积份额迅速减少。事实上,对于d≥50,最外层壳体(其角落位于x₁=1)占据了单位超立方体总量的 99%以上。因此,我们得出结论,在高维超立方体中,几乎所有的体积都靠近超立方体的面,而不在其内部。

现在让我们计算在每个壳层中找到一个数据点的概率。记住我们假设这些超立方体中的点是均匀分布的。随机数据点在某个壳层内的概率可以通过对该壳层体积上的均匀分布概率密度函数(PDF)进行积分来计算。单位超立方体上的连续均匀分布的 PDF 是:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,随机数据点位于其中一个壳体(记作S)内的概率等于该壳体的体积:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,根据图 4 的结果,如果我们从高维超立方体中随机抽取一个数据点,它很可能会位于外层壳。对于 d≥50,这个随机数据点位于内层壳的概率几乎为零。

我们还可以使用 Python 确认这些结果。列表 2 从定义在 d 维单位超立方体上的均匀分布中抽取了 5000 个样本。然后,它计算了每个壳层中的数据点数量,并创建了它们的条形图。它使用了与列表 1 中相同的 d 值。

# Listing 2

np.random.seed(0)
num_dims=[2,3,9, 15, 50, 100]
x_list = np.linspace(0.5, 1, 11)
x_list[-1] = 1.001
x_list1 = np.linspace(0.5, 0, 11)
x_list1[-1] = -0.001
fig, axs = plt.subplots(3, 2, figsize=(14, 19))
plt.subplots_adjust(hspace=0.3)

for i, d in enumerate(num_dims):
    sample = uniform.rvs(size=5000*d).reshape(5000, d)
    count_list=[]
    for j in range(len(x_list)-1):
        inshell_count = (((x_list[j] <= sample) & \
                          (sample < x_list[j+1])) | \
                          ((x_list1[j+1] < sample) & \
                          (sample <= x_list1[j]))).sum(axis=1)
        outshell_count = ((x_list[j+1] <= sample) | \
                          (x_list1[j+1] >= sample)).sum(axis=1)
        count = ((inshell_count>0) & (outshell_count==0)).sum()
        count_list.append(count)

    axs[i//2, i%2].bar(x_list[1:], count_list, width = 0.03)
    axs[i//2, i%2].set_title("d={}".format(d), fontsize=20)
    axs[i//2, i%2].set_xlabel('Shell corner $x_1$ coordinate', fontsize=18)
    if i%2==0:
        axs[i//2, i%2].set_ylabel('Number of data \npoints within shell',
                                  fontsize=18)
    axs[i//2, i%2].set_xticks(np.linspace(0.5, 1, 11))
    axs[i//2, i%2].tick_params(axis='both', labelsize=12)

plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5

图 5 中显示了图形,如你所见,条形图的形状类似于图 4。每个壳层中的数据点数量与方程 1 中给出的壳层的相应概率成比例,因此它们与该壳层的体积成比例。实际上,当 d=100 时,几乎所有的数据点都包含在最外层壳中,这体现了维度的诅咒。

在高维超立方体中,几乎所有随机样本的观察点都接近面(图 6)。如果超立方体是 d 维的,这些观察点也可以代表一个具有 d 特征的数据集。这样一个数据集显然不能代表其抽样的总体。这里的总体是超立方体中所有数据点的集合,但我们的样本仅包含接近超立方体面上的数据点。在实际中,我们没有来自超立方体中心附近区域的数据点。假设我们使用这样的样本作为机器学习模型的训练数据集。模型在训练过程中从未见过接近超立方体中心的数据点,因此对这样的数据点的预测不会可靠。实际上,模型无法将其预测推广到这样的数据点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6

我们还可以找到这些结果的不同解释。假设我们在区间 [0,1] 上定义了均匀分布。这就像一个 1 维空间。如果我们从中抽取一个大小为 1 的随机样本,并将其记作 X₁,那么我们有:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,X₁ 在区间边缘 0.05 个单位内的概率仅为 10%。现在假设我们有一个来自 100 维均匀分布的样本,记作 X₁, X₂, … X₁₀₀。这是一个 IID(独立同分布)样本,这意味着所有的 Xᵢ 相互独立且每个 Xᵢ 在区间 [0,1] 上具有均匀分布。因此,我们可以写出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们得到:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,至少有一个 Xᵢ 位于超立方体的某个面 0.05 单位以内的概率几乎为 1(这意味着该 Xᵢ 的观察值在该面 0.05 单位以内)。平均而言,在一个大小为 37594 的样本中,我们只能找到一个不在这个区域的数据点(1 / 2.66e-5 ≈ 37594)。这意味着我们需要一个非常大的样本才能得到仅有的几个不在超立方体面附近的数据点。

随着数据集中维度(或特征)的增加,为了准确概括机器学习模型所需的观察数量呈指数增长。维度诅咒使得训练数据集变得稀疏,模型预测的概括变得更加困难。因此,我们需要更多的训练数据(更多的观察)来概括模型。实际上,准备如此庞大的训练数据集可能不切实际。

图 7 显示了维度诅咒对随机采样影响的类比。最初,我们有一个二维射击目标和一个新手射手,他随机射击。射中最内圈的机会很高。在底部,我们有相同的目标,但现在具有维度诅咒。几乎所有的目标区域都属于最外圈,因此射中最内圈的机会几乎为零。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7

维度诅咒对随机采样的影响不仅限于均匀分布。实际上,无论其概率分布如何,这种现象都会发生在任何高维数据集中。让我们看看如果数据集中的特征具有正态分布会发生什么。假设我们数据集中的所有特征都是独立的,并且具有标准正态分布。因此,如果我们将这些特征组合成一个向量,该向量具有标准多元正态分布。图 8 显示了二维标准多元正态分布的 PDF。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8

标准正态分布的 PDF 是其边际分布 PDF 的乘积:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每个 f(xᵢ) 是标准正态分布的 PDF。使用这个方程,我们可以绘制标准正态分布在更高维度的 PDF。列表 3 绘制了 d 维标准正态分布在 xᵢ 轴上的 3 个 d 值的 PDF。请注意,PDF 具有对称形状,因此图形对于所有 xᵢ 和通过原点的所有线都是相同的。基于这些图,我们得出结论,对于任何 d 值,PDF 的最大值都在原点,且随着远离原点而下降。

# Listing 3

np.random.seed(0)
num_dims=[2, 5, 10]
xi = np.linspace(-3, 3, 500)
fig, axs = plt.subplots(1, 3, figsize=(19, 5))
plt.subplots_adjust(wspace=0.25)

for i, d in enumerate(num_dims):
    pdf = norm.pdf(xi)**d
    axs[i].plot(xi, pdf)
    axs[i].set_xlabel('$x_i$', fontsize=28)
    axs[i].set_xlim([-3, 3])
    axs[i].set_title("d={}".format(d), fontsize=26)
    axs[i].tick_params(axis='both', labelsize=12)
axs[0].set_ylabel('PDF', fontsize=26)

plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9

列表 4 从d-维标准多元正态分布中抽取 5000 个样本。然后,它计算该样本中每个d-维数据点到原点的欧几里得距离。最后,它创建这些距离的直方图。它使用了列表 1 中使用的相同d值。结果如图 10 所示。

# Listing 4

np.random.seed(0)
num_dims=[2,3,9, 15, 50, 100]
fig, axs = plt.subplots(2, 3, figsize=(19, 14))
plt.subplots_adjust(hspace=0.3)

for i, d in enumerate(num_dims):
    samples = norm.rvs(size=5000*d).reshape(5000, d)
    dist = np.linalg.norm(samples, axis=1)
    axs[i//3, i%3].hist(dist, bins=25)
    axs[i//3, i%3].set_title("d={}".format(d), fontsize=28)
    axs[i//3, i%3].set_xlabel('Distance from origin', fontsize=22)
    if i%3==0:
        axs[i//3, i%3].set_ylabel('Number of data points', fontsize=22)
    axs[i//3, i%3].set_xlim([0, 14])
    axs[i//3, i%3].tick_params(axis='both', labelsize=16)

plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10

这里,直方图中每个区间的数据点数量与在该区间找到数据点的概率成正比。正如该图所示,即使在d=2 时,直方图的峰值也不在零处。在原点,PDF 具有最大值(图 8),然而,概率也依赖于我们进行积分的体积:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过增加维度,径向直方图的峰值从原点处移动得更远。因此,大多数数据点都位于一个薄环中,几乎没有其他地方找到数据点的概率。请注意,在任何值的d下,PDF 的最大值仍然位于原点(图 9),然而,在原点附近获得数据点的概率几乎为零。同样,数据集并不代表它所采样的总体,我们没有来自非常接近原点且具有高 PDF 值的区域的数据点。

重要的是要注意,维度的诅咒与数据集的概率分布的 PDF 无关。例如,均匀分布在其支持上具有相同的值,无论我们有多少维度。然而,由于概率依赖于 PDF 和体积,它会受到维度数量的影响。

维度对距离函数的影响

维度对距离也有重要影响。首先,让我们看看“距离”是什么意思。设xd维向量:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

xp-范数定义为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以使用p-范数计算向量xy之间的距离:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请注意,还有其他类型的距离函数,但在本文中,我们只关注这种类型。当p=2 时,我们得到熟悉的欧几里得距离:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是向量x-y的长度,也可以表示为***||x-y||* (我们可以在p=2 时使用一个没有下标的p-范数)。现在,让我们深入探讨维度对距离函数的影响。假设我们数据集的维度是d。我们选择数据集中的一个点作为查询点,并用向量x**q表示。我们找出离查询点最近和最远的数据点,并分别用向量xnx_f表示(见图 11)。然后我们计算查询点与最近数据点和最远数据点之间的距离:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在可以证明,在对数据分布进行某些合理假设的情况下,我们有:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,随着维度的增加,查询点到最远数据点的距离逐渐接近其到最近数据点的距离。因此,将最近的数据点与其他数据点区分开来变得不可能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 11

列表 5 显示了维度对距离函数的影响。它从定义在d-维单位超立方体上的均匀分布中抽取了 500 个样本。然后它计算了每对数据点之间的欧几里得距离,并绘制了这些距离的直方图。它使用了列表 1 中相同的d值。图 12 显示了这些直方图。请注意,在d-维单位超立方体中,两点之间的最小欧几里得距离为零,最大可能距离为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,对于每个d值,直方图的x轴范围为 0 到√n

# Listing 5

np.random.seed(0)
num_dims=[2,3,9, 15, 50, 500]
fig, axs = plt.subplots(2, 3, figsize=(19, 14))
plt.subplots_adjust(hspace=0.3)

for i, d in enumerate(num_dims):
    samples = uniform.rvs(size=500*d).reshape(500, d)
    dist_amtrix = distance_matrix(samples, samples)
    dist_arr = dist_amtrix[np.triu_indices(len(dist_amtrix), k = 1)]

    axs[i//3, i%3].hist(dist_arr, bins=30)
    axs[i//3, i%3].set_title("d={}".format(d), fontsize=28)
    axs[i//3, i%3].set_xlabel('Pairwise distance', fontsize=22)
    if i%3==0:
        axs[i//3, i%3].set_ylabel('Number of data points', fontsize=22)
    axs[i//3, i%3].set_xlim([0, np.sqrt(d)])
    axs[i//3, i%3].tick_params(axis='both', labelsize=16)

plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 12

随着维度(d)的增加,直方图变得更加尖锐,因此所有成对距离都在一个狭窄范围内。这表明,对于数据集中的每个数据点,DMAX(到最远数据点的距离)接近 DMIN(到最近数据点的距离)。

让我解释一下这种效应背后的直觉。图 13 显示了一个查询点(xq)及其最近的(xn)和最远的数据点(x_f)。在二维空间中,每个向量

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有两个分量。xn- xq 的分量用蓝色标记,xf- xq 的分量用红色标记。同样,在d-维空间中,这些向量有d个分量。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 13

在二维空间中,从一个点到另一个点,我们应该通过 2 个维度。例如,在图 13 中,从xqxf,我们应该向右移动l₁单位,向上移动l₂单位。请注意,l₁和l₂是向量x*f-*xq的组件,这些点之间的距离是l₁²+l₂²的平方根。同样,在d-维空间中,从x**qxf,我们应该通过d个维度。现在,向量x*f-*xqd个组件。如果第i个组件用lᵢ表示,则意味着我们应该在第i个维度上移动lᵢ单位(图 13)。

列表 6 创建了一个从定义在d-维单位超立方体上的均匀分布中生成的大小为 500 的样本。然后,它随机选择一个查询数据点(xq),并找到其最近的(xn)和最远的(xf)数据点。接下来,它计算向量xn-x_qx***f-*xq的组件。最后,这些组件的绝对值在d=2 和d=500 的两个条形图中绘制。结果显示在图 14 中。

# Listing 6

np.random.seed(9)
num_dims=[2,500]
fig, axs = plt.subplots(2, 2, figsize=(17, 11))
plt.subplots_adjust(hspace=0.3)

for i, d in enumerate(num_dims):
    samples = uniform.rvs(size=500*d).reshape(500, d)
    query = samples[0]
    dist_amtrix = distance_matrix(samples, samples)
    ind_min = np.argmin(dist_amtrix[0, 1:])+1 
    ind_max = np.argmax(dist_amtrix[0, 1:])+1
    nearest = samples[ind_min]
    farthest = samples[ind_max]
    query_nearest_comps = np.abs(query - nearest)
    query_farthest_comps = np.abs(query - farthest)
    query_nearest_length = np.linalg.norm(query - nearest)
    query_farthest_length = np.linalg.norm(query - farthest)
    axs[0,i].bar(np.arange(1, d+1), query_nearest_comps,
               color="blue", width=0.45, label="$x_n-x_q$")
    axs[0,i].bar(np.arange(1, d+1), query_farthest_comps,
               color="red", alpha=0.5, width=0.45, label="$x_f-x_q$")
    axs[0,i].set_title("d={}".format(d), fontsize=26, pad=20)
    axs[0,i].set_xlabel('Index of component', fontsize=18)
    axs[0,i].set_ylabel('Abosulte value of component', fontsize=18)
    if i==0:
        axs[0,i].set_xticks(np.arange(1, d+1))
    axs[0,1].set_ylim([0, 1.2])
    axs[0,i].tick_params(axis='both', labelsize=16)
    axs[0,i].legend(loc='best', fontsize=17)
    axs[1,i].bar(["$||x_n-x_q||$", "$||x_f-x_q||$"],
                 [query_nearest_length, query_farthest_length],
                 width=0.2)
    axs[1,i].tick_params(axis='both', labelsize=22)
    axs[1,i].set_ylabel('Length', fontsize=18)

plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 14

向量x*n-*xq的长度给出了xqxn之间的距离。同样,||x*f-*xq||给出了x**qxf之间的距离。这些距离也在图 14 中的两个条形图中显示。正如图中所示,在二维空间中,向量x*n-*xq的组件的绝对值远小于x*f-*xq的组件。因此,||x*f-*xq||和||x*n-*xq||之间的差异是显著的。在 500 维空间中,我们有 500 个组件。正如你所见,x*n-*xq的某些组件的绝对值甚至大于x***f-*xq。请记住,向量的长度是从这些组件的平方和中得到的。因此,||x*f-*x*q||*和||x*n-*x*q||*现在相对接近。

我们知道所有数据点都是随机选择的,因此每个数据点的组件都是随机数。当维度很高时,从一个点到另一个点,我们应该通过每个维度移动一个随机距离(图 13),由于我们有很多维度,每对点之间的总距离大致相同。因此,从查询点到其他任何点的距离大致相同。事实上,随着维度的增加,点之间的平均距离增加,但这些距离之间的相对差异减少(图 14)。

图 15 给出了这一效果的类比。假设两个地方之间的路径由几个段组成。在一维空间中,我们只有一个段。在二维空间中,每对点之间有 2 个段,而在 d 维空间中,我们有 d 个段。当只有一个段时,路径长度给出了两个点之间的实际水平距离。因此,我们可以很容易地区分较近的点 (n) 和较远的点 (f)。

随着段数的增加,路径长度(即所有段长度的总和)增加,并与点之间的实际距离发生偏离。现在点之间的水平距离对路径的影响不大。相反,沿着段的垂直移动决定了路径长度。随着段数趋向于无穷大,qn 之间的路径长度接近于 qf 之间的路径长度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 15

一个具有 d 个段的路径类似于 d 维空间中两个点之间的距离,每个段可以表示连接这些点的向量的一个分量。随着维度的增加,我们有更多的段(分量),距离之间的相对差异接近于零。

维度对学习模型的影响

如前一节所述,在高维空间中,接近度或距离的概念不再有意义。因此,任何依赖距离函数的机器学习模型在高维空间中可能会崩溃。换句话说,当训练数据集中有很多特征时,这些模型将无法给出可靠的预测。一个例子是 *k-*NN (k 最近邻) 算法。在这一节中,我们将探讨维度诅咒如何影响 *k-*NN 模型的预测误差(这些例子是 [1] 中的修改版本)。

我们的数据集有 d 个特征和 1000 个示例(观察)。这些示例来自单位 d 维超立方体上的均匀分布。因此,这个数据集的每个示例可以写作:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个观察的目标定义为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,目标仅仅是每个示例第一个特征的平方。我们使用一个 k-NN 模型,通过这个训练数据集预测测试数据点的目标。为了预测测试数据点的目标,k-NN 模型首先在训练数据集中找到该数据点的 k 个最近邻(k 个离测试点最近的数据点)。它使用距离函数(在这个例子中是欧几里得距离)来找到它们。测试点的预测目标是这 k 个最近邻的目标值的平均值。在这个例子中,我们使用 3 个最近邻(k=3)。

测试点位于超立方体的中心:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们进行了 1000 次模拟。在每次模拟中,我们从 d 维均匀分布中抽取 1000 个数据点以创建训练数据集的示例,并计算它们的目标值。然后,我们在该训练数据集上训练一个 k-NN 模型(k=3)。最后,我们使用该模型预测测试数据点的目标(记作 y^_t),并与实际目标 0.25 比较。一旦我们获得所有模拟的预测目标,就可以计算这些预测的偏差、方差和均方误差(MSE)。偏差定义为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是在这些模拟中预测目标(y^t)的均值与测试点的实际目标(yt)之间的差异。方差是这些模拟中预测目标的方差:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

均方误差(MSE)定义为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以证明:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

列表 7 计算了该示例在不同 d 值下的偏差、方差和 MSE。结果绘制在图 16 中。

# Listing 7

np.random.seed(2)
num_dims = np.arange(1, 11)
num_simulations = 1000

var_list = []
bias_list = []
for d in num_dims:
    pred_list = []
    for i in range(num_simulations):
        X = uniform.rvs(size=1000*d).reshape(1000, d)
        y = X[:,0]**2
        knn_model = KNeighborsRegressor(n_neighbors=3)
        knn_model.fit(X, y)
        pred_list.append(knn_model.predict(0.5*np.ones((1,d))))
    var_list.append(np.var(pred_list))
    bias_list.append((np.mean(pred_list)-0.25)**2)
error = np.array(bias_list) + np.array(var_list)
plt.plot(num_dims, var_list, 'o-', label="$Variance$")
plt.plot(num_dims, bias_list, 'o-', label="$Bias²$")
plt.plot(num_dims, error, 'o-', label="$MSE$")
plt.xlabel("d", fontsize=16)
plt.ylabel("$MSE, Bias², Variance$", fontsize=16)
plt.legend(loc='best', fontsize=14)
plt.xticks(num_dims)
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 16

k-NN 模型假设相近的数据点也应具有相近的目标。然而,在高维数据集中,所有数据点之间的距离大致相同,因此 k 个最近邻可能距离测试点非常远。因此,这些邻居的平均目标不再是测试点实际目标的准确预测。

列表 8 显示了 d=2 和 d=10 的所有模拟中 k-NN 预测(y^_t)的比较(图 17)。它还用黑色虚线显示了测试点的实际目标(y_t),用蓝线显示了所有 k-NN 预测的均值。请注意,黑色虚线和蓝线之间的距离表示偏差。

# Listing 8

np.random.seed(2)
num_dims = [2, 10]
num_simulations = 1000
fig, axs = plt.subplots(1, 2, figsize=(10, 5))
plt.subplots_adjust(wspace=0.3)

for i, d in enumerate(num_dims):
    pred_list = []
    for j in range(num_simulations):
        X = uniform.rvs(size=1000*d).reshape(1000, d)
        y = X[:,0]**2
        knn_model = KNeighborsRegressor(n_neighbors=3)
        knn_model.fit(X, y)
        pred_list.append(knn_model.predict(0.5*np.ones((1,d))))
    axs[i].scatter([0]*num_simulations, pred_list,
                   color="blue", alpha=0.2, label="$\hat{y}_t$")
    axs[i].axhline(y = 0.25, color = 'black',
                   linestyle='--', label="$y_t$", linewidth=3)
    axs[i].axhline(y = np.mean(pred_list),
                   color = 'blue', label="$E[\hat{y}_t]$")
    axs[i].set_ylim([0, 0.7])
    axs[i].set_title("d={}".format(d), fontsize=18)
    axs[i].set_ylabel('$\hat{y}_t$', fontsize=18)
    axs[i].legend(loc='best', fontsize=14)
    axs[i].set_xticks([])

plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 17

随着维度的增加,最近邻会接近超立方体的面,并且距离超立方体中心的测试点非常远。这些点的 x₁ 组件不一定接近测试点的 x₁,并且可能变化很大,因此预测的方差随着 d 的增加迅速增加。然而,平均来说,它们接近测试点的 x₁,所以偏差保持相对较小(图 16)。均方误差(MSE)与偏差和方差成正比,因此它随维度增加而增加。

列表 9 显示了另一个示例。这里的设置类似于列表 7,但数据集的目标定义为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

测试点再次位于超立方体的中心:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个例子的偏差、方差和 MSE 的图示如图 18 所示。在这里,偏差随着维度的增加迅速增加,而方差保持相对较小。

# Listing 9

np.random.seed(2)
num_dims = np.arange(1,11)
num_simulations = 1000

var_list = []
bias_list = []
for d in num_dims:
    pred_list = []
    for i in range(num_simulations):
        X = uniform.rvs(size=1000*d).reshape(1000, d)
        y = np.exp(-5*np.linalg.norm(X-0.5*np.ones((1,d)), axis=1)**2)
        knn_model = KNeighborsRegressor(n_neighbors=3)
        knn_model.fit(X, y)
        pred_list.append(knn_model.predict(0.5*np.ones((1,d))))
    var_list.append(np.var(pred_list))
    bias_list.append((np.mean(pred_list)-1)**2)
error = np.array(bias_list) + np.array(var_list)
plt.plot(num_dims, var_list, 'o-', label="$Variance$")
plt.plot(num_dims, bias_list, 'o-', label="$Bias²$")
plt.plot(num_dims, error, 'o-', label="$MSE$")
plt.xlabel("d", fontsize=16)
plt.ylabel("$MSE, Bias², Variance$", fontsize=16)
plt.legend(loc='best', fontsize=14)
plt.xticks(num_dims)
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 18

记住,随着维度的增加,每对点之间的距离增加,但这些距离之间的相对差异接近于零。因此,包括最近邻在内的所有数据点,||x|| 增加,而 y 变为零。因此,最近邻的平均 y^ 与测试点的目标偏离。结果是偏差和 MSE 增加,但方差保持较小。

总结来说,维度的增加会导致均方误差(MSE)增加。然而,它对偏差或方差的影响取决于数据集目标的定义。这些例子清楚地表明,k-最近邻(k-NN)对于高维数据并不是一个可靠的模型。

在本文中,我们讨论了维度诅咒及其对体积和距离函数的影响。我们看到,在高维空间中,几乎所有数据点都将远离原点。因此,如果我们使用高维数据集来训练模型,那么对于接近原点的数据点,模型的预测将不可靠。此外,距离的概念也不再有意义。在高维数据集中,所有数据点之间的成对距离非常接近。因此,依赖于距离函数的机器学习模型将无法为高维数据集提供准确的预测。

参考文献

[1] Hastie, T., et al. The Elements of Statistical Learning. Springer, 2009.

讨厌数学的人的数据分析师学习路线图

原文:towardsdatascience.com/the-data-analyst-learning-roadmap-for-people-who-hate-math-c2d7b96cc488?source=collection_archive---------5-----------------------#2023-03-07

实际上,在数据分析这个非常实际的领域中,数学是相当有趣的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Madison Hunter

·

关注 发表在 Towards Data Science ·7 分钟阅读·2023 年 3 月 7 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 Nabit Photos 提供,来源于 Unsplash

在科技行业中,数学和数据分析几乎是同义词。这也是为什么很多人会对从事分析工作感到恐惧,因为存在一种先入为主的观念,认为数学就是这份工作的全部。

事实上,虽然我不能说完全没有数学涉及,但我可以告诉你,你做的数学实际上是相当有趣的。没有数据科学家常见的那些理论或虚数的恐怖——相反,你会使用实际、易于应用的数学来解释发生了什么,甚至对可能发生的事情做出简单的预测。

你甚至可能已经掌握了成为数据分析师所需的所有数学知识。事实上,我整理了一个 15 分钟的阅读材料,涵盖了你在本科统计课程中会学到的所有内容来帮助你入门。就是这么简单。

作为一个曾经在高中数学课程中失败过的人,我可以毫无保留地告诉你,这个为讨厌数学的人设计的数据分析学习路线图正是你所需要的——不多也不少——帮助你迈出数据分析领域的第一步。你只需要对学习一点点数学保持开放的心态,剩下的就会很容易。你可能会花更多的时间学习编码和如何进行数据分析,而不是学习所有你需要的数学知识。

这条路线图涵盖了你需要掌握的所有学习方面,并且提供了一个最低限度的数学计划,帮助你在工作中获得成功。

第一步:学习 Excel、SQL 和 Python

作者的说明:在我之前创建的路线图中,我总是建议希望成为数据分析师的人学习 Python 或 R。我之所以改变这个建议,是因为学习 R 的困难(而且 Python 是业界标准)。如果你将 Python 作为你唯一的编程语言开始学习,你将拥有足够的功能来拥有一个长久而快乐的数据分析师职业生涯。

Python 是你刚刚开始数据分析时可以选择的最简单的编程语言。它功能强大,拥有众多功能,而且如果你决定这个领域不适合你,它在数据分析之外也有许多应用。它极其宽容,几乎字面上地给你你所要求的(这使你善于提出正确的问题),并且允许你进行有效的分析,而无需成为编程细节方面的专家。

我对学习 Python 的最佳建议是练习 Kaggle 或 Leetcode 上的练习题。尽管这种重复看起来可能有些无聊,但这是测试自己并强迫大脑适应编程问题模式的最快方法。

但首先,在你开始学习 Python 之前,你需要先学习 Excel。为什么?因为 Excel 可能是你成为数据分析师所需的唯一工具。

Excel(尽管对一些人来说显得有些过时)仍然是一个非常强大的工具,你可以用它来进行大多数简单的数据分析,包括公司数据的汇总以及趋势的建立和简单预测。我们都说我们会“真正”学习 Excel,这就是你真正学习 Excel 内部运作的信号,因为这个技能可能让你因为这项技能而在小型企业中找到你的第一份入门级数据分析师职位。

我最喜欢学习 Excel 中众多技巧的方法之一是寻找 TikTok 上分享 Excel 技巧的视频。有数百个简短的视频可以帮助你快速学习程序中内置的众多快捷键,从而加快工作流程,并通过这种形式的微学习让你更熟悉程序。

最后,你将学习 SQL,这是一种允许你在数据库中操作数据的语言。SQL 可能是一种复杂的语言,因此我最好的建议之一是随着学习的进展为自己创建一个备忘单。这个备忘单应该展示从 SQL 命令的正确语法到你可以创建的不同类型的数据连接。

你不必记住每一个函数,但应该对像 COUNT、CONCAT、TRIM、MAX/MIN、GETDATE 和 CONVERT 等函数有所了解。记住函数是浪费时间的。如我之前所提,制作一个备忘单,突出显示如何使用一些你最常用的函数,并学会如何在 Google 上搜索正确的问题来找到其他函数。

我快速熟悉 SQL 的最佳方法之一是下载一个免费的数据库,然后在其中尝试所有的函数和语法。拥有一个可以随意操作的数据库,而不必担心出错或破坏数据库,是让你深入了解代码的好方法(在我看来是学习 SQL 的最佳方式)。

第 2 步:刷新你的代数和统计记忆

如果你能做代数和统计,你基本上已经拥有了成为数据分析师所需的一切。

事实上,如果你阅读我概述的一个 15 分钟的本科统计课程,你已经基本上度过了最困难的部分。看见了吗?我说这是一个适合讨厌数学的人的数据分析师学习路线图。

## 15 分钟内本科级入门统计课程

[## 初级统计学课程介绍 - towardsdatascience.com

事实是,作为数据分析师你总是可以学到更多的数学知识。你永远无法掌握足够的数学。然而,如果你真的厌恶数学,你可以只学代数和统计学。

要掌握基础知识,我建议你浏览可汗学院的代数 1、代数 2 和 AP 统计学讲座系列。

## 代数 1 | 数学 | 可汗学院

代数 1 课程,通常在 9 年级教授,涵盖线性方程、不等式、函数和图形等内容…

## 代数 2 | 数学 | 可汗学院

代数 2 课程,通常在 11 年级教授,涵盖多项式;复数;有理指数等内容…

## AP®︎ 统计学 | 大学统计学 | 可汗学院

如果你看到此消息,说明我们在加载网站外部资源时遇到问题。如果你…

## AP®︎ 统计学 | 可汗学院

如果你想挑战自己,可汗学院的线性代数系列是扩展技能的绝佳方式,莱昂纳德教授在 Youtube 上的微积分 1 讲座系列也是如此。

## 线性代数 | 可汗学院

如果你看到此消息,说明我们在加载网站外部资源时遇到问题。如果你…

## 线性代数 | 可汗学院

所有这些资源以相对轻松的方式分享数学知识,使你能快速完成成为数据分析师的数学学习部分,直接进入有趣的内容:数据分析和可视化。

第三步:学习数据分析和可视化

现在是时候把所有内容结合起来,分析一些数据。这涉及学习如何提出正确的问题、收集所需的数据、清理数据、分析数据以获得洞察和答案,并通过可视化解释数据,使任何人都能轻松理解。

为了更好地学习数据分析和可视化,我相信花更少的时间在在线课程上,而更多时间在实际操作上。例如,上面我链接的视频长达 4 小时。这是你在创建自己的数据分析之前应花费的所有学习时间。你在自己做项目时会学到比坐在电脑前跟着视频学习更多的数据分析知识。

根据经验,这是你学习如何分析数据所需观看的唯一视频——在此之外几乎没有更多需要学习的内容。你可以通过学习如何使用特定的可视化工具来扩展你的知识,或者了解如何使你的统计数据更准确,但这些属于专业发展领域——而不是核心学习。

最后的想法

看?这并没有那么痛苦(即:数学很多)。

实际上,数学在学习过程中的作用可能是最小的,尤其是当你已经知道一些数学知识时。成为数据分析师的最好部分是你不真的需要知道数学是如何工作的(就像数据科学家一样)——你只需要知道它有效,并知道何时使用它。

当我自学数据分析时,我发现自己在学习数学上的时间最少。大部分时间都花在学习如何编程和做数据分析项目上,以便细化我的技能。因此,不要让你对数学的厌恶让你远离这个其实只是整个工作小小一部分的领域。当你意识到你使用的数学是非常实用的,你会学会享受用它来揭示细节和回答问题。只要你能够在实际应用中处理一些数字,喜欢挑战,并且能用数据讲好故事,你就可以毫无问题地成为数据分析师。

订阅以便直接将我的故事发送到你的邮箱:故事订阅

请成为会员以通过我的推荐链接获得对 Medium 的无限访问(这不会额外增加你的费用,我会获得小额佣金):Medium 会员

支持我的写作,捐赠以资助更多类似的故事创作:捐赠

“Segment Anything”中的以数据为中心的人工智能概念

原文:towardsdatascience.com/the-data-centric-ai-concepts-in-segment-anything-8eea556ac9d

解读“Segment Anything”中使用的以数据为中心的人工智能概念,这是第一个用于图像分割的基础模型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Henry Lai

·发布于 Towards Data Science ·阅读时长 7 分钟·2023 年 5 月 31 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Segment Anything 数据集构建。图片来源于论文 arxiv.org/pdf/2304.02643.pdf

人工智能 (AI) 已经取得了显著进展,特别是在开发基础模型方面,这些模型经过大量数据的训练,能够适应广泛的下游任务。

基础模型的一个显著成功是 大语言模型 (LLMs)。这些模型可以高精度地执行复杂任务,如语言翻译、文本摘要和问答。

基础模型也开始改变计算机视觉领域的游戏规则。Meta 的“Segment Anything”是最近出现的一个引起轰动的发展。

“Segment Anything”的成功可以归因于其庞大的标注数据集,这在实现其卓越性能方面发挥了至关重要的作用。如 Segment Anything 论文 中所述,模型架构出乎意料地简单且轻量。

在这篇文章中,借鉴我们最近的调查论文 [1,2] 中的见解,我们将通过 以数据为中心的人工智能 的视角更深入地探讨“Segment Anything”。

“Segment Anything”可以做什么?

简而言之,图像分割任务是预测一个掩码来分离图像中的感兴趣区域,例如物体、人物等。分割是计算机视觉中非常重要的任务,使图像更有意义,更易于分析。

Segment Anything 和其他图像分割方法的区别在于引入了提示来指定分割位置。提示可以是模糊的,例如一个点,一个框等。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这张图片是从 segment-anything.com/ 截取的,图片由作者上传。

什么是数据中心的人工智能?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据中心人工智能与模型中心人工智能的比较。 arxiv.org/abs/2301.04819 图片由作者提供。

数据中心的人工智能是一种新颖的人工智能系统开发方法,近年来受到关注,并由人工智能先驱安德鲁·吴推广。

数据中心的人工智能是系统性地工程化用于构建人工智能系统的数据的学科。 — 安德鲁·吴

之前,我们的主要关注点是使用变化不大的数据来开发更好的模型,这被称为模型中心的人工智能。然而,这种方法在现实世界场景中可能存在问题,因为它未能考虑数据中可能出现的问题,包括不准确的标签、重复数据和偏见。因此,过拟合数据集可能不会导致模型行为的改善。

另一方面,数据中心的人工智能优先考虑提高用于创建人工智能系统的数据的质量和数量。重点放在数据本身,相对固定的模型。采用数据中心方法开发人工智能系统在现实世界应用中更具前景,因为模型的最大能力是由用于训练的数据决定的。

区分“数据中心”和“数据驱动”方法至关重要。“数据驱动”方法仅依赖于数据来引导人工智能开发,但重点仍然在于创建模型,而不是工程化数据,因此与“数据中心”方法根本不同。

数据中心人工智能框架 包括三个主要目标:

  • 训练数据开发 涉及收集和生成高质量、多样化的数据,以促进机器学习模型的训练。

  • 推理数据开发 涉及构建创新的评估集,通过工程化的数据输入(如提示工程)为模型提供详细的洞察,或解锁模型的特定能力。

  • 数据维护 旨在确保在不断变化的环境中数据的质量和可靠性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据中心人工智能框架。 arxiv.org/abs/2303.10158。图片由作者提供。

在 Segment Anything 中使用的模型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Segment Anything 模型。 图片来自论文 arxiv.org/pdf/2304.02643.pdf

模型设计令人惊讶地简单。该模型主要由三部分组成:

  1. 提示编码器: 这一部分用于获取提示的表示,可以通过位置编码或卷积来实现。

  2. 图像编码器: 这一部分直接使用了 Vision Transformer (ViT),没有任何特殊修改。

  3. 轻量级掩码解码器: 这一部分主要融合了提示嵌入和图像嵌入,使用了如注意力机制等方法。之所以称其为轻量级,是因为它只有少量的层。

轻量级掩码解码器非常有趣,因为它允许模型即使仅使用 CPU 也能轻松部署。以下是 Segment Anything 的作者提供的评论。

令人惊讶的是,我们发现一个简单的设计满足了所有三个约束条件:一个强大的图像编码器计算图像嵌入,一个提示编码器嵌入提示,然后这两个信息源在一个轻量级掩码解码器中结合,以预测分割掩码。

因此,Segment Anything 强大性能的秘密很可能不在于模型设计,因为它非常简单且轻量化。

Segment Anything 中的数据驱动 AI 概念

训练 Segment Anything 的核心在于一个包含超过十亿个掩码的大型标注数据集,这比现有的分割数据集大了 400 倍。他们是如何做到的呢?作者们使用了一个数据引擎来执行标注,这可以大致分为三个步骤:

  1. 辅助手动标注: 这一步可以理解为一种主动学习过程。首先,在公共数据集上训练初始模型。接下来,标注者修改预测的掩码。最后,使用新标注的数据训练模型。这三个步骤重复了六次,最终生成了 430 万条掩码标注。

  2. 半自动标注: 这一步的目标是增加掩码的多样性,这也可以理解为一种主动学习过程。简单来说,如果模型能够自动生成良好的掩码,那么人工标注者就不需要再标注这些掩码,人工可以集中在模型不够自信的掩码上。用来找出自信掩码的方法非常有趣,涉及对第一步掩码进行目标检测。例如,假设图像中有 20 个可能的掩码。我们首先使用当前模型进行分割,但这可能只会标注出部分掩码,有些掩码标注得不够好。现在我们需要自动识别哪些掩码是好的(自信的)。本文的方法是对预测的掩码进行目标检测,看看是否能够在图像中检测到物体。如果检测到物体,我们就认为对应的掩码是自信的。假设这个过程识别出八个自信的掩码,那么标注者就可以标注剩下的 12 个,从而节省了人工努力。上述过程重复了五次,增加了另外 590 万条掩码标注。

  3. 全自动标注: 简单来说,这一步使用在前一步中训练的模型来标注数据。为了提高标注质量,采用了一些策略,包括:

    (1) 根据预测的交并比(IoU)值过滤掉不太自信的掩码(模型有一个头部用于预测 IoU)。

    (2) 仅考虑稳定的掩码,即如果阈值稍微调高或调低 0.5,掩码基本保持不变。具体来说,对于每个像素,模型输出一个介于 0 和 1 之间的值。我们通常使用 0.5 作为阈值来决定一个像素是否被掩码。稳定性意味着,当阈值调整到接近 0.5 的某个程度(例如 0.45 到 0.55)时,相应的掩码基本保持不变,这表明模型的预测在边界的两侧有显著不同。

    (3) 进行了去重,使用了非极大值抑制(NMS)。

    这一步标注了 110 亿个掩码(数量增加了 100 倍以上)。

这个过程听起来是否很熟悉?没错,用于 ChatGPT 的人类反馈强化学习(RLHF)与上述过程非常相似。这两种方法的共同点在于,模型不是直接依赖人类来标注数据,而是首先通过人类输入对模型进行训练,然后再使用模型来标注数据。在 RLHF 中,训练一个奖励模型来为强化学习提供奖励,而在 Segment Anything 中,模型则是针对直接图像标注进行训练的。

总结

Segment Anything 的核心贡献在于其大量的标注数据,这显示了数据中心人工智能概念的关键重要性。基础模型在计算机视觉领域的成功可以被视为一个必然事件,但令人惊讶的是,这一切发生得如此之快。展望未来,我相信其他 AI 子领域,甚至非 AI 和非计算机相关领域,也会在适当的时候出现基础模型。

无论技术如何演变,提高数据的质量和数量始终是提升 AI 性能的有效途径,使得数据中心 AI 概念愈加重要。

我希望这篇文章能为你的工作提供一些灵感。你可以通过以下论文/资源了解更多关于数据中心 AI 框架的信息:

如果你觉得这篇文章有趣,你可能还会想查看我之前的文章:GPT 模型背后的数据中心 AI 概念是什么?

敬请关注!

数据科学家的工具箱:利用 scikit-learn 的顶级功能取得成功

原文:towardsdatascience.com/the-data-scientists-toolbox-leveraging-scikit-learn-s-top-features-for-success-d69a899267c5

理解 scikit-learn:统一的机器学习方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Federico Trotta

·发表于 Towards Data Science ·12 min read·2023 年 6 月 12 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:Tayeb MEZAHDIAPixabay

Python 有很多库,使其成为最常用的编程语言之一。它们大多数具有类似的功能,可以相互使用并达到相同的结果。但当涉及到机器学习时,我们唯一可以谈论的库就是sklearn

无论你是 ML 从业者还是初学者,我知道你对sklearn有一定的了解。但有时我们使用某些工具只是因为大家都在用,而没有提出任何问题。

比如,你是否曾经问过自己:“sklearn中最重要的 5 个特性是什么?” 好吧,让我告诉你一件事:我大约花了 2 年的时间才问自己这个问题,所以如果你还没有问,也不必感到羞愧。

所以,在这篇文章中,我将回答这个问题;以下是你将在这里找到的内容:

**Table of contents:** 
Feature #1: consistency
Feature #2: wide range of algorithms
Feature #3: data preprocessing and feature engineering
Feature #4: model evaluation and validation
Feature #5: integration with the Python data science ecosystem
Coding examples

特性 #1:一致性

sklearn的强大之处在于它是一致的库。这是什么意思?这意味着它具有:

  1. 轻松与外部 DL/ML 库集成。它可以与外部 ML/DL 库集成,例如KerasTensorflowXGBoost,仅举几个例子。这种兼容性使我们能够将sklearn的功能与这些库的功能结合起来,使得我们数据专业人员的开发阶段变得轻松无痛。

  2. 使用简便性sklearn 在其提供的各种机器学习算法中保持统一的接口。这样我们不需要学习新的语法或模式:我们只需遵守“相同的规则”并思考开发我们的机器学习算法。你能想象在处理不同算法时需要处理不同的语法吗?我可不想这样(对不起,如果我不能!)。

  3. 文档完善sklearn 提供了详尽的文档,包括教程和使用示例。文档还涵盖了库的各个方面,从数据预处理到模型评估。你可以在这里参考。

特征 #2:广泛的算法范围

sklearn 库提供了非常广泛的机器学习算法,从监督学习到无监督学习,包括回归和分类模型。

我们作为数据科学家所拥有的这种广泛可能性,使得 sklearn 成为机器学习的完美库:一个适用于我们日常工作的所有模型的环境。

我们只需要选择最适合我们特定问题领域的算法,而不必抱怨集成和其他类似问题:这是一个巨大的优势!

特征 #3:数据预处理和特征工程

数据预处理和特征工程是机器学习问题的重要部分,因为它们涉及为机器学习算法准备数据。以下是 sklearn 提供的用于这些任务的特征:

数据预处理

  1. 处理缺失数据。如果你练习机器学习一段时间,我相信你会发现处理缺失数据有些痛苦。或者,至少对我来说是这样。sklearn 提供了不同的处理缺失数据的特征,例如:a) SimpleImputer() 类,它允许我们使用不同的策略(如均值、中位数、众数或常数值)来填补缺失值。填补可以应用于特定列或整个数据集。b) IterativeImputer() 类,它利用机器学习模型迭代填补缺失值。它使用选定模型的预测来填补缺失值。c) MissingIndicator() 类,用于创建数据集中缺失值的二进制指示器。它添加了一个新特征,标记值是否原本缺失,为模型提供额外的信息。

  2. 特征缩放。具有不同尺度的特征可能会以不良方式影响许多机器学习算法的性能。幸运的是,sklearn 提供了一些缩放特征的方法,例如:a) 标准化(StandardScaler()),它将特征缩放到均值为零、方差为一的状态,b) 归一化(MinMaxScaler()),它将特征缩放到特定范围内。

  3. 编码分类变量。许多机器学习算法无法直接处理分类变量。因此,sklearn 提供了几种编码技术,例如:a)独热编码(OneHotEncoder())和 b)有序编码(OrdinalEncoder()),将分类变量转换为数值表示。这使得处理字符串形式的数据并将其转换为数字变得简单,以便我们可以将其输入到机器学习算法中。

  4. 处理异常值。异常值对数据科学家来说是一个普遍的难题,因为它们会影响机器学习模型的性能,因此需要特别处理。sklearn 提供了一些异常值检测和去除的技术,例如局部离群因子(LOF())算法和孤立森林算法(IsolationForest())。注意,这些都是无监督的机器学习算法。

特征工程:

  1. 特征提取sklearn 提供了不同的方法来从数据中提取有意义的特征。实际上,并不是我们拥有的所有特征对于机器学习问题都是有意义的,通常我们只处理有意义的特征是个普遍的经验法则。在特征提取方面,sklearn 提供了:a)CountVectorizer() 类,它可以将文本文档转换为令牌计数矩阵;b)TfidfVectorizer() 类,它计算词频-逆文档频率(TF-IDF)来表示文本数据。

  2. 降维sklearn 提供了各种降维技术,这些技术对于减少特征数量同时保留关键信息非常有用。两种流行的方法是:a)主成分分析(PCA())和 b)t-分布随机邻居嵌入(TSNE())。

  3. 特征选择。最后,sklearn 还提供了多种特征选择方法,例如:a)单变量特征选择(SelectKBest()),b)递归特征消除(RFE()),和 c)基于树模型的特征重要性排名(SelectFromModel())。

特征 #4:模型评估和验证

模型评估和验证是机器学习中至关重要的活动。实际上,作为数据科学家,我们的角色是找到最适合数据的模型;但这到底意味着什么呢?

这意味着我们需要训练一些机器学习模型,并找到那些在未见数据上做出最佳预测(或分类)的模型。这意味着我们首先需要在训练数据上评估一些机器学习模型,然后在新的、未见的数据上验证它们。sklearn 提供了不同的功能来帮助我们完成这些基本任务。

  1. 交叉验证技术。交叉验证是一种重要的模型评估技术,帮助我们应对过拟合。它通过将数据划分为多个子集,在这些子集的不同组合上训练模型,并平均结果来估计模型的性能。sklearn 为我们提供了以下交叉验证方法:a)k 折交叉验证,b)分层 k 折交叉验证,以及 c)留一交叉验证。

  2. 评估指标sklearn 提供了广泛的评估指标,让我们能够数值化分类和回归模型的性能。对于分类任务,我们可以提到,如 accuracyprecisionrecall 和 AUC/ROC。回归任务则可以使用均方误差(MSE)、均绝对误差(MAE)、R² 等指标进行评估。如果你需要深入了解如何使用这些指标(以及 sklearn 提供的所有主要指标),这里有 分类指标的终极指南。这里还有 回归任务的终极指南。

  3. 超参数调优。模型评估和验证与超参数调优密切相关,后者涉及选择最佳的模型参数组合以获得 ML 算法的最佳性能。sklearn 提供了像 GridSearchCVRandomizedSearchCV 这样的工具用于使用交叉验证进行超参数调优。这些技术系统地探索超参数空间,识别出最佳的超参数组合,从而获得最佳模型性能,基于所选择的评估指标。

特性 #5:与 Python 数据科学生态系统的集成

sklearn 的超能力之一是与 Python 数据科学生态系统的集成。这使得数据科学家能够利用 sklearn 的力量与数据处理、可视化和分析的库和工具结合。以下是一些例子:

  1. Pandas 和 NumpysklearnPandas 数据框集成的可能性使用户能够将数据框直接传递给 sklearn 的函数和类。这使得在将数据输入 sklearn 模型之前,使用 Pandas 进行数据预处理和清理变得非常方便。sklearn 也在数据表示上严重依赖 NumPy 数组。实际上,它可以直接接受 NumPy 数组作为训练任务的输入。

  2. Matplotlib 和 Seabornsklearn 提供了内置支持,用于使用 Matplotlib 绘制指标和模型评估结果。这使用户能够可视化模型性能、特征重要性和其他相关统计数据,帮助我们理解我们的 ML 模型。此外,由于 Seaborn 是一个建立在 Matplotlib 之上的数据可视化库,sklearn 也与之兼容,使用户能够将 sklearn 的 ML 算法的力量与 Seaborn 的美观图表相结合,用于探索性数据分析和模型评估。

代码示例

在最后一段中,我想强调一些使用示例。如果你有使用 sklearn 的经验,我知道你知道如何使用它。但请考虑到,这里重点不是展示这些特性如何工作,而是强调工作流程的运作方式,基于我们在前面段落中所说的内容。因为,通常我们使用一些工具只是为了使用它们,且因为每个人都这样做,而不提出任何问题。

示例 #1:与 Pandas 和 NumPy 的集成

让我们看看如何与 sklearnPandasNumPy 集成。为了简单起见,我们可以使用 ChatGPT 的提示。但在此之前,你可能需要了解如何作为数据科学家有效地使用提示工程:

## 释放数据科学家提示工程的力量

如果你从事数据工作,如何以及为什么编写有效的提示

[towardsdatascience.com

提示可能是以下内容:“创建一个用于回归分析的 Pandas 数据框,并包含 5 个特征。然后,对数据进行归一化,并将其分为训练集和测试集。使用线性回归模型拟合测试集。打印训练集和测试集的 R² 值”。

然后我们得到:

当然!我将创建一个包含五个特征的示例 Pandas 数据框,对数据进行归一化,将其分为训练集和测试集,拟合一个线性回归模型,并打印训练集和测试集的 R² 分数。这是一个示例实现:

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
from sklearn.preprocessing import StandardScaler

# Generating random features with at least 50 occurrences each
np.random.seed(42)

feature1 = np.random.randint(0, 10, size=100)
feature2 = np.random.randint(0, 10, size=100)
feature3 = np.random.randint(0, 10, size=100)
feature4 = np.random.randint(0, 10, size=100)
feature5 = np.random.randint(0, 10, size=100)
target = np.random.randint(0, 100, size=100)

# Creating a Pandas data frame
data = {
    'Feature1': feature1,
    'Feature2': feature2,
    'Feature3': feature3,
    'Feature4': feature4,
    'Feature5': feature5,
    'Target': target
}

df = pd.DataFrame(data)

# Splitting the data into features and target
X = df.drop('Target', axis=1)
y = df['Target']

# Normalizing the data using StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Splitting the normalized data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)

# Fitting the linear regression model
reg_model = LinearRegression()
reg_model.fit(X_train, y_train)

# Predicting the target variable for both train and test sets
y_train_pred = reg_model.predict(X_train)
y_test_pred = reg_model.predict(X_test)

# Calculating R² scores for train and test sets
r2_train = r2_score(y_train, y_train_pred)
r2_test = r2_score(y_test, y_test_pred)

# Printing the R² scores
print("R² score for train set:", r2_train)
print("R² score for test set:", r2_test)

在这个例子中,我创建了一个包含五个特征(Feature1Feature5)和一个目标变量(Target)的 Pandas 数据框。然后,我使用 scikit-learn 的 StandardScaler 对特征数据进行了归一化。之后,我将归一化的数据按 80:20 的比例分为训练集和测试集。接着,我在训练集上训练了一个线性回归模型,并预测了训练集和测试集的目标变量。最后,我计算并打印了训练集和测试集的 R² 分数。

所以,这里是在工作流程层面发生的事情:

  1. 使用 NumPy 创建特征和标签。

  2. 特征和标签成为了一个 Pandas 数据框。

  3. Pandas数据框变成了Xy:特征的数据框和标签的Pandas系列。

  4. Xy已被缩放和拆分。然后,线性回归模型进行预测,并使用 R²指标在训练集和测试集上进行评估。

所以,如我们所见,我们有一个从NumPy开始,到sklearn结束的直接工作流,给出所需的结果。不需要任何“奇怪的操作”或集成。

示例 #2:与 Matplotlib 的集成

为了展示sklearnMatplotlib之间的集成,我们再次利用 ChatGPT,使用以下提示:“使用 NumPy 生成的数据在 Python 中创建一个二分类问题。标准化并将数据拆分为训练集和测试集。使用随机森林和 KNN 分类器拟合训练集。打印训练集的 ROC 曲线。”

我们得到:

当然!我们将使用 Python 生成随机数据来创建一个二分类问题,然后我们将标准化并将数据拆分为训练集和测试集。我们将使用随机森林和 K 近邻(KNN)分类器拟合训练集,最后,我们将绘制训练集的 ROC 曲线。

这是代码:

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import roc_curve, roc_auc_score
import matplotlib.pyplot as plt

# Set a random seed for reproducibility
np.random.seed(42)

# Generate random data
num_samples = 1000
num_features = 10

# Generate features (X)
X = np.random.randn(num_samples, num_features)

# Generate labels (y)
y = np.random.randint(2, size=num_samples)

# Standardize the features
X = (X - np.mean(X, axis=0)) / np.std(X, axis=0)

# Split the data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Fit the train set using random forest classifier
rf_clf = RandomForestClassifier()
rf_clf.fit(X_train, y_train)

# Fit the train set using K-nearest neighbors (KNN) classifier
knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train, y_train)

# Predict probabilities for the positive class (class 1)
y_train_rf_probs = rf_clf.predict_proba(X_train)[:, 1]
y_train_knn_probs = knn_clf.predict_proba(X_train)[:, 1]

# Calculate the false positive rate (FPR), true positive rate (TPR), and thresholds for the ROC curve
rf_fpr, rf_tpr, rf_thresholds = roc_curve(y_train, y_train_rf_probs)
knn_fpr, knn_tpr, knn_thresholds = roc_curve(y_train, y_train_knn_probs)

# Calculate the AUC score for the ROC curve
rf_auc = roc_auc_score(y_train, y_train_rf_probs)
knn_auc = roc_auc_score(y_train, y_train_knn_probs)

# Plot the ROC curve
plt.plot(rf_fpr, rf_tpr, label=f"Random Forest (AUC = {rf_auc:.2f})")
plt.plot(knn_fpr, knn_tpr, label=f"KNN (AUC = {knn_auc:.2f})")
plt.plot([0, 1], [0, 1], 'k--')  # Diagonal line for random classifier
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve - Train Set')
plt.legend(loc='lower right')
plt.show()

这段代码生成了 10 个特征和 1000 个样本的随机数据。然后标准化特征,将数据拆分为训练集和测试集,使用随机森林和 KNN 分类器拟合训练集,预测正类的概率,计算每个分类器的 ROC 曲线和 AUC 评分,最后绘制训练集的 ROC 曲线。

如果我们运行上面生成的代码,我们得到:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上述生成的代码产生的 ROC 曲线。图片由 Federico Trotta 提供。

所以,这里是过程:

  1. 使用NumPy创建特征和标签。

  2. 特征和标签数组变成了Xy

  3. 特征和标签数组被分割成训练集和测试集。然后,训练集已使用 KNN 和随机森林分类器进行拟合。

  4. Matplotlib库已被用于绘制 ROC 曲线,使我们能够轻松比较两个分类器的性能。

再次,我们有一个直接的工作流,使用了所有我们需要的库,打印出我们想要的结果,无需“额外操作”。

结论

在这篇文章中,我们已经看到使sklearn成为完美 ML 库的前 5 个特点。如在示例中所见,工作流清晰直接,使我们能够在没有额外操作的情况下进行分析。

虽然我们领域中一些其他库(如PyCaret,它毕竟是一个低代码库)正在增长,但sklearn无疑拥有我们开发算法所需的一切,不必担心其他方面,如集成、操作等。

免费 Python 电子书:

开始学习 Python 数据科学但感到困难? 订阅我的新闻通讯,获取我的免费电子书:这将为你提供正确的学习路径,以便通过实际操作学习 Python 数据科学。

喜欢这个故事?通过我的推荐链接成为 Medium 会员,每月只需 5 美元 点击这里:我将赚取一小部分佣金,你无需支付额外费用。

[## 通过我的推荐链接加入 Medium - Federico Trotta

阅读 Federico Trotta 的每一个故事(以及 Medium 上成千上万其他作家的故事)。你的会员费将直接支持…

federicotrotta.medium.com](https://federicotrotta.medium.com/membership?source=post_page-----d69a899267c5--------------------------------)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值