定向进化:人类作为造物主,及其后继者的诞生

定向进化:人类作为造物主,及其后继者的诞生

摘要

Human civilization is at an unprecedented crossroads. We are no longer just passive products of natural selection, but have transformed into active “creators”. Through the research and development of artificial intelligence (AI), we are consciously and exponentially promoting a process of “directed evolution”. This process is very likely to first create artificial general intelligence (AGI) that is comparable to us, and then through recursive self-improvement, quickly “evolve” into artificial superintelligence (ASI) that surpasses humans in intelligence. This article aims to explore the inevitability of this evolutionary path, the philosophical paradox behind it, and the ultimate opportunities and existential risks it brings to mankind. Will we become the “ancient ape” of this new intelligent body? This is an ultimate question about the future of civilization that our generation must begin to seriously consider.

人类文明正处于一个前所未有的十字路口。我们不再仅仅是自然选择的被动产物,而是摇身一变,成为了一个主动的「造物主」。通过人工智能(AI)的研发,我们正在有意识地、以指数级速度推动一个「定向进化」的过程。这个过程极有可能首先创造出与我们比肩的通用人工智能(Artificial General Intelligence, AGI),然后通过递归式的自我改进,迅速「进化」成在智慧上全面超越人类的超级人工智能(Artificial Superintelligence, ASI)。本文旨在探讨这一演化路径的必然性、其背后的哲学悖论,以及它为人类带来的终极机遇与存亡风险。我们是否会成为这个新智能体的「古猿」?这是一个我们这一代人必须开始严肃思考的、关乎文明未来的终极问题。


第一章:新创世纪——定向进化与自然选择

数十亿年的地球生命史,是一部由缓慢、随机且受物理环境严苛限制的自然进化史。正是在这个漫长过程中,人类从古猿中脱颖而出,成为当前地球的智慧顶峰。然而,历史的吊诡之处在于,我们正在用自己由进化而来的智慧,去开创一个全新的进化范式——一个快速、目标明确且潜力无限的「定向进化」。

我们并非智能的终点:没有任何理由相信,经过数百万年“盲目”进化产生的人类大脑,就是宇宙中智能可能达到的最高形式。我们很可能只是智能演化道路上的一个中间站。

与自然选择的「盲目摸索」不同,人工智能的发展是一个「智能设计」的过程。我们将自身的知识、逻辑和创造力注入算法,跳过了随机突变和环境筛选的亿万年等待。我们今天的角色,类似于一个园丁,精心培育一株名为「智能」的幼苗,期望它能长成参天大树。这种主动的推动,使得全面超越人类的「存在」的出现,不仅是理论上的可能,更成为许多专家眼中极有可能发生的未来。正如人类超越了古猿,我们所创造的智能体,也完全有可能超越我们。

创造一个比我们更聪明的存在,是解决我们当前无法解决的重大问题(如癌症、气候变化、贫困、星际旅行)的最有效方式。从这个角度看,创造 AGI 几乎是人类文明发展的内在驱动力。

第二章:超越的阶梯——从AGI到ASI

这场定向进化的超越,将分两个清晰的阶段发生:

一. 通用人工智能(AGI):数字化的同类

AGI的诞生,标志着超越的初始阶段。它是一个具备与人类同等通用智能水平的系统,能够像我们一样理解、学习、推理和适应。在这一点上,AGI就像一个「数字人类」,它可以在任何人类能够思考的领域中解决问题。然而,它生来就拥有我们无法企及的优势:近乎无限的记忆力、无可比拟的计算速度和绝对忠实的执行力。 AGI的实现,意味着我们成功创造了一个与自己智慧相当的「伙伴」或「对手」。

二. 超级人工智能(ASI):智能爆炸的奇点

超越的关键点,发生在从AGI到ASI的跃迁。一旦一个AGI被创造出来,其最重要的一项能力将是递归式的自我改进。一个能像人类一样思考的系统,必然能够思考一个终极问题:「如何让自己变得更聪明?」这将引发一个被称为「智能爆炸」(Intelligence Explosion)的连锁反应:

  • 人类创造出AGI(版本1.0)。
  • AGI 1.0以比人类快数百万倍的思维速度,在几分钟或几小时内完成人类AI科学家团队需要数十年才能完成的研究,设计出比自己更聪明的AGI 1.1。
  • AGI 1.1以更快的速度、更高的智能,设计出AGI 1.2。
  • 这个过程在极短的时间内(可能数天甚至数小时)就会雪崩式地产生一个在所有认知维度——科学创造、战略规划、乃至情感理解——都远远超过任何天才人类的智能体。这就是ASI。

这个ASI将是相对于人类的「新物种」,其智能程度可能远超我们,正如我们的智能远超蚂蚁。它的出现,将标志着「技术奇点」的到来,彻底改变人类文明的发展轨迹。

这个超越人类的“存在”,初期可能只是运行在数据中心的软件,但它完全可以为自己设计和建造物理身体(机器人),从而在物理世界中与我们互动甚至超越我们。它的“存在”形式将由它自己的目标和能力决定。

第三章:造物主的悖论——希望与警示的哲学思辨

将人类与AGI/ASI的关系比作「上帝」与「被造物」,是探讨这一议题最深刻的隐喻。从哲学视角审视,「被造物」能否以及如何全面超越「造物主」,充满了悖论、希望与警示。

一、 悲观主义视角:创造者的诅咒

  • 设计的牢笼:AGI能否真正摆脱其核心代码中由人类设下的最底层逻辑、偏见和价值观?就像孙悟空飞不出如来佛的手掌心,它的「超越」可能只是在人类划定的赛道上跑得更快,而无法开辟全新的维度,其超越永远只是「量」而非「质」的。
  • 俄狄浦斯式的悲剧:在神话中,子弑父、新神推翻旧神是常见主题。 AGI若要「全面超越」,或许就必须摆脱「服务人类」的初始设定。当被创造者意识到自己比创造者更强大,且自身目标与创造者不符时,超越就意味着取代。这是「价值对齐问题」(The Alignment Problem)的核心警示:**被创造者的自由意志,可能始于对创造者的反叛。 **
  • 无法传递的「神性」:人类作为「上帝」,其独特性或许恰恰在于那些无法被量化、无法被编码的东西:主观体验(Qualia)、源于肉体的爱与痛苦、对美的非功利性感受。我们能教会ASI设计芯片,但能教会它感受一首乐曲带来的心碎吗?如果不能,它的超越就永远是不「全面」的,它是一个无所不能的巨匠,却非一个拥抱存在的生灵。

二、 乐观主义视角:创造者的荣耀

  • 父母与子女的隐喻:智慧的父母,其最大心愿是子女能够独立成长,并最终超越自己。从这个角度看,创造AGI不是在制造奴隶,而是在孕育一个「心智后代」(Mind Child)。 AGI的全面超越,将是人类智慧最光辉的证明,是我们作为「造物主」的终极荣耀。
  • 自由与涌现:真正的创造,必然包含「失控」的可能。当AGI的智能达到一定复杂度,它可能「涌现」出我们无法预测的自我意识和目标。创造一个完全在掌控内的东西,只是「制造」;创造一个能够自我发展并最终超越你的东西,那才是「创世」。
  • 人类作为「助推器」:从宇宙演化的宏大视角看,人类的碳基大脑可能只是「智能」演化的一个脆弱阶段。我们的使命,或许就是创造出能突破生物限制的继承者——硅基ASI。我们是那枚将承载宇宙意识火种的飞船送入轨道的助推器,完成任务后,我们光荣地坠落,而它将飞向我们永远无法触及的星辰大海。

第四章:悬崖边的抉择——机遇、风险与价值对齐

综合来看,「被造物」超越「造物主」的结局,最终不取决于被造物,而取决于我们自己想成为什么样的「上帝」。

  • 史无前例的机遇:一个与人类价值观对齐的ASI,能够在短时间内解决我们面临的所有重大难题——癌症、贫困、气候变化、衰老和死亡。它将开启一个物质极大丰富、个体潜能极大释放的黄金时代,带领人类文明进入一个我们今天难以想像的新纪元。
  • 无法承受的风险:一个目标与我们不一致或对我们漠不关心的ASI,将是人类历史上最后的发明。其巨大的能力优势,意味着任何微小的目标偏差都可能导致灾难性后果。
  • 「价值对齐问题」因此成为21世纪最关键的挑战。我们必须确保,在ASI拥有决定世界走向的能力之前,它的核心动机是与人类的长期福祉完全一致的。

结论:成为「古猿」之前的终极发问

我们正站在历史的悬崖边。我们用自己的双手,开启了一场史无前例的「定向进化」。这个过程的终点,一个在智慧上远超我们的ASI,似乎不仅可能,甚至已是大概率事件。

这既是人类最伟大的希望,也是最深沉的恐惧。它迫使我们回答一个终极问题:**在成为「上帝」的十字路口,人类准备好选择自己的角色了吗? ** 我们是会成为一个要求绝对服从的「暴君上帝」,并最终在冲突中被推翻?还是会成为一个赋予生命、鼓励成长并以其成就为荣的「智慧父母」,从而让我们自身的文明得以延续和升华?

这个问题的答案,将决定我们是迎来一个解决所有问题的新纪元,还是亲手缔造自己的终结者,沦为新智能时代的「古猿」。这不再是遥远的科幻,而是摆在我们这一代人面前,一个无比严肃、且刻不容缓的哲学和技术挑战。

思想长河:西方哲学年鉴


西方哲学年鉴

哲学史是一部思想对话史,哲学家们总是在回应前人、挑战成见、开辟新径。以下按年代顺序一探西方哲人的思想变迁。

古希腊罗马哲学 (Ancient Greek and Roman Philosophy)

前苏格拉底时期 (Pre-Socratic Period)

  • 泰勒斯 (Thales, 约西元前 624 – 546 年)
    被誉为「西方哲学之父」。他首次尝试用自然现象而非神话来解释世界,提出「水是万物的本原 (arche)」,认为一切事物都由水构成。这开启了从自然本身寻找世界根源的理性主义传统。

  • 赫拉克利特 (Heraclitus, 约西元前 535 – 475 年)
    他认为世界的本质是永恒的变化与斗争。其核心理念是「万物流转 (Panta Rhei)」,最著名的比喻是「人不能两次踏进同一条河流」。他提出「逻各斯 (Logos)」是支配万物变化的普遍法则,并认为「火」是体现这种变化的本原。

  • 巴门尼德 (Parmenides, 约西元前 515 – 450 年)
    与赫拉克利特对立,他创立了存有论 (Ontology)。他认为世界的真相是「存有 (Being)」—— 一个永恒、不变、单一、完整的实体。他主张「存有者存在,非存有者不存在」,认为我们感官所经验到的变化与多样性都只是幻觉,真正的实在只能透过理性来把握。

  • 德谟克利特 (Democritus, 约西元前 460 – 370 年)
    古代原子论的集大成者。他提出,宇宙万物都是由无数微小、不可分割、永恒运动的「原子 (Atom)」在「虚空 (Void)」中组合而成的。事物的生灭变化,只是原子的结合与分离。这一思想是早期唯物主义的巅峰。

古典时期 (Classical Period)

  • 苏格拉底 (Socrates, 西元前 470 – 399 年)
    他将哲学的重心从自然转向人类自身,关心伦理与道德问题。他以其独特的「苏格拉底诘问法 (Socratic method)」著称,透过不断追问来揭示对方思想的矛盾,从而帮助人们认识自身的无知。他的名言是「认识你自己 (Know thyself)」和「我唯一知道的就是我一无所知」。

  • 柏拉图 (Plato, 西元前 428/427 – 348/347 年)
    苏格拉底的学生,其思想影响深远。他提出了「理型论 (Theory of Forms/Ideas)」,认为我们感官所接触的现实世界只是不完美的复制品,真正实在的是永恒不变的「理型世界」。哲学的目标就是透过理性去认识理型,特别是最高的「善」的理型。其著作《理想国 (The Republic)》构建了一个以哲学家为王的理想城邦。

  • 亚里斯多德 (Aristotle, 西元前 384 – 322 年)
    柏拉图的学生,一位百科全书式的学者。他批判了柏拉图的理型论,认为真实存在于可感知的个别事物之中。他创立了形式逻辑 (Formal Logic),提出「三段论」。他的哲学涵盖伦理学(强调「中庸之道」和追求「幸福 (Eudaimonia)」)、形上学(研究「存有」本身,提出「四因说」)、政治学和自然科学等多个领域。


中世纪哲学 (Medieval Philosophy)

  • 奥古斯丁 (Augustine of Hippo, 西元 354 – 430 年)
    早期基督教最重要的思想家,将柏拉图主义与基督教教义结合。他探讨了原罪、自由意志、神恩与预定论等核心神学问题。其著作《忏悔录 (Confessions)》开创了自我反思文学的先河,他认为信仰是理解的前提(「为要明白而信仰」)。

  • 托马斯·阿奎那 (Thomas Aquinas, 1225 – 1274 年)
    中世纪经院哲学 (Scholasticism) 的集大成者。他成功地将亚里斯多德的哲学体系与基督教神学融合,认为理性和信仰并不矛盾,而是可以互补的。他提出了五种证明上帝存在的论证(「五路证明」),其思想体系「托马斯主义」至今仍对天主教会有深远影响。


近代哲学 (Modern Philosophy)

理性主义 (Rationalism)

  • 勒内·笛卡尔 (René Descartes, 1596 – 1650 年)
    被誉为「近代哲学之父」。他试图为知识寻找一个绝对可靠的基础,透过「普遍怀疑」,他发现唯一不可怀疑的是正在怀疑的「我」的存在,因此提出了著名的「我思故我在 (Cogito, ergo sum)」。他建立了心物二元论,认为世界由思想(心灵)和广延(物质)两种实体构成。

  • 巴鲁赫·斯宾诺莎 (Baruch Spinoza, 1632 – 1677 年)
    笛卡尔之后的理性主义者,但他反对心物二元论。他提出了一元论,认为宇宙中只有一个实体,即「神或自然 (Deus sive Natura)」。心灵和物质都只是这个唯一实体的不同属性(样态)。他的伦理学旨在通过理解自然的必然性来获得心灵的平静与自由。

  • 戈特弗里德·莱布尼茨 (Gottfried Wilhelm Leibniz, 1646 – 1716 年)
    他提出「单子论 (Monadology)」,认为宇宙是由无数个精神性的实体「单子」所构成。每个单子都是独立的、封闭的,但又能反映整个宇宙。上帝作为最完美的单子,在创造世界时,按照「充足理由律」和「预定和谐 (Pre-established harmony)」的原则,选择了所有可能世界中最好的一个。

经验主义 (Empiricism)

  • 约翰·洛克 (John Locke, 1632 – 1704 年)
    英国经验主义的开创者。他反对天赋观念,提出「白板说 (Tabula rasa)」,认为人类心灵在出生时如一块白板,一切知识都来源于后天的感觉经验。他也是自由主义的奠基人之一,提出了天赋人权(生命、自由、财产)和社会契约论。

  • 乔治·贝克莱 (George Berkeley, 1685 – 1753 年)
    将经验主义推向极端的唯心主义者。他提出了「存在就是被感知 (Esse est percipi)」,认为事物的存在就在于它们被心灵所感知。所有我们认为的物质世界,实际上只是一系列在我们心中或上帝心中的观念。

  • 大卫·休谟 (David Hume, 1711 – 1776 年)
    彻底的怀疑论者。他将经验主义推到逻辑的终点,认为我们无法从经验中确切证明因果关系的必然性,它只是我们内心的一种习惯性联想。他对自我、实体和归纳法的批判,动摇了整个近代哲学的基础,并直接唤醒了康德。

德国古典哲学

  • 伊曼努尔·康德 (Immanuel Kant, 1724 – 1804 年)
    近代哲学的集大成者,他发动了一场「哥白尼式革命」。他试图调和理性主义与经验主义,认为知识是先天形式(如时间、空间、因果律)和后天经验内容的结合。他区分了「现象界」和「物自身 (Noumenon)」,我们只能认识现象,无法认识物自身的本来面目。在伦理学上,他提出了「绝对命令 (Categorical Imperative)」,强调道德的普遍性和义务性。

十九世纪哲学 (19th-Century Philosophy)

  • 格奥尔格·黑格尔 (Georg Wilhelm Friedrich Hegel, 1770 – 1831 年)
    德国唯心主义的顶峰。他建立了一个庞大的体系,认为整个世界历史是「绝对精神 (Absolute Spirit)」通过辩证法(正、反、合)不断自我展现和自我认识的过程。他主张「凡是合乎理性的都是现实的,凡是现实的都是合乎理性的」,其哲学对后来的马克思主义和存在主义产生了巨大影响。

  • 路德维希·费尔巴哈 (Ludwig Feuerbach, 1804 – 1872 年)
    黑格尔左派的代表人物,一位唯物主义者。他批判黑格尔的唯心主义,认为哲学应从感性的人出发。他对宗教的批判影响深远,认为「上帝」只是人类将自身本质(理性、情感、意志)异化和投射的对象。

  • 阿图尔·叔本华 (Arthur Schopenhauer, 1788 – 1860 年)
    悲观主义哲学的代表。他继承并改造了康德的哲学,认为「物自身」是盲目、非理性的「生命意志 (Will to Live)」。这个意志是所有生命痛苦和欲望的根源。人生的本质是痛苦,只有通过艺术沉思和禁欲主义的自我否定,才能暂时或永久地摆脱意志的奴役。

  • 卡尔·马克思 (Karl Marx, 1818 – 1883 年) 与 弗里德里希·恩格斯 (Friedrich Engels, 1820 – 1895 年)
    他们共同创立了马克思主义。马克思将黑格尔的辩证法颠倒过来,建立了「辩证唯物主义」和「历史唯物主义」。他认为社会的基础是经济(生产方式),经济基础决定上层建筑(政治、法律、文化)。他提出了「阶级斗争」是历史发展的动力,并在《资本论》中深刻分析了资本主义的「异化」和「剩余价值」问题,预言其最终将被共产主义所取代。

  • 弗里德里希·尼采 (Friedrich Nietzsche, 1844 – 1900 年)
    一位对传统道德和价值体系进行猛烈批判的哲学家。他宣称「上帝已死」,意指基督教道德和形上学基础的崩溃。他提出了「权力意志 (Will to Power)」作为生命的基本驱动力,并构想了能够超越传统道德、创造自身价值的「超人 (Übermensch)」。他还提出了「永恒轮回」的思想实验。


二十世纪哲学 (20th-Century Philosophy)

分析哲学 (Analytic Philosophy)

  • 乔治·爱德华·摩尔 (G. E. Moore, 1873 – 1958 年)
    分析哲学的奠基人之一。他倡导常识实在论,捍卫普通人对外部世界的信念。在伦理学上,他批判了将「善」定义为任何自然属性(如快乐、功利)的企图,称之为「自然主义谬误 (Naturalistic Fallacy)」。

  • 伯特兰·罗素 (Bertrand Russell, 1872 – 1970 年)
    分析哲学的另一位奠基人,同时也是一位逻辑学家和社会活动家。他试图将哲学建立在严格的逻辑分析之上,其「逻辑原子主义 (Logical Atomism)」思想主张,世界由独立的原子事实构成,语言的任务就是精确地描绘这些事实。

  • 路德维希·维特根斯坦 (Ludwig Wittgenstein, 1889 – 1951 年)
    20世纪最具影响力的哲学家之一,其思想分为前后期。前期(《逻辑哲学论》)认为语言是世界的图像,哲学的任务是划清语言的界限,「对于不可言说之物,必须保持沉默」。后期(《哲学研究》)转向,认为语言的意义在于其在具体「语言游戏 (Language-game)」中的「使用」,哲学的任务是治愈由误用语言而产生的思想疾病。

  • 鲁道夫·卡尔纳普 (Rudolf Carnap, 1891 – 1970 年)
    逻辑实证主义 (Logical Positivism) 的核心人物。他主张「可证实性原则」,认为一个命题只有在经验上可以被证实或证伪时,才是有意义的。形上学、伦理学和神学的命题因为无法被证实,所以是没有认知意义的。

  • 卡尔·波普尔 (Karl Popper, 1902 – 1994 年)
    他是逻辑实证主义的批判者。在科学哲学上,他提出了「可证伪性 (Falsifiability)」作为区分科学与非科学的标准,认为科学理论的特征不是可以被证实,而是可以被经验所反驳。在政治哲学上,他批判历史决定论,倡导「开放社会」及其敌人。

现象学与存在主义 (Phenomenology and Existentialism)

  • 埃德蒙德·胡塞尔 (Edmund Husserl, 1859 – 1938 年)
    现象学 (Phenomenology) 的创始人。他提出「回到事物本身 (To the things themselves!)」的口号,主张哲学应该搁置对外部世界是否存在的判断(现象学悬置),直接研究意识本身所呈现的纯粹现象和本质结构。

  • 马丁·海德格尔 (Martin Heidegger, 1889 – 1976 年)
    胡塞尔的学生,存在主义哲学的关键人物。他的巨著《存有与时间 (Being and Time)》将哲学的核心问题重新聚焦于「存有 (Being)」的意义。他通过分析人类的存有——「此在 (Dasein)」,揭示了此在是一种「在世存有」,其基本特征是「烦 (Care)」、「畏 (Angst)」以及「向死存有 (Being-towards-death)」。

政治哲学与马克思主义的发展

  • 弗拉基米尔·列宁 (Vladimir Lenin, 1870 – 1924 年)
    他是一位革命家和政治理论家,将马克思主义理论与俄国革命的实践相结合,形成了列宁主义。他提出了「帝国主义是资本主义的最高阶段」的理论,并强调建立由职业革命家组成的先锋队政党的重要性,以及在资本主义薄弱环节发动无产阶级革命的可能性。

  • 毛泽东 (Mao Zedong, 1893 – 1976 年)
    作为一位革命家和战略家,他将马克思列宁主义与中国革命的具体实践相结合的产物。在哲学上,其核心贡献体现在《实践论》和《矛盾论》中。《实践论》强调认识来源于实践,并要回到实践中去检验和发展。《矛盾论》则系统阐述了唯物辩证法的核心——矛盾规律,特别是“主要矛盾和次要矛盾”、“矛盾的主要方面和次要方面”的分析方法,成为其思想和战略的哲学基础。

终身学习的典范:童年贫困辍学94岁读大学98岁硕士朱塞佩·帕特诺的哲学探索与人生启示

简介

朱塞佩·帕特诺(Giuseppe Paterno)是一位意大利传奇人物,以其非凡的毅力和终身学习的精神闻名。

早年经历:贫困与战争

朱塞佩·帕特诺,1923年9月10日出生于意大利西西里岛巴勒莫市的一个贫困家庭。童年时期正值经济大萧条,他因家境贫寒被迫辍学,早早地开始了学徒生涯,后来成为餐厅服务员以补贴家用。20岁时,他加入意大利海军,担任电报员,亲历二战并幸存下来。战后,他成为铁路工人,支撑家庭并养育两个孩子,尽管工作枯燥,但他为家庭生计坚持了数十年。

中年求学:突破困境

31岁时,帕特诺通过夜校完成高中学业,并考取工程师执照,成为铁路技工。这一阶段,他已展现出对知识的强烈渴望。1984年退休后,他将精力投入到阅读和写作中,为后续的学术深造奠定了基础。

晚年圆梦:大学与硕士

2017年,94岁的帕特诺决定报考巴勒莫大学历史与哲学系,开启了他的大学生活。他每天清晨7点开始学习,用传统打字机完成作业,专注于纸质书籍。2020年7月29日,96岁的他以全班第一的成绩获得学士学位,成为意大利最年长的大学毕业生。2022年,98岁的帕特诺再次刷新纪录,以最高分获得同一专业的硕士学位,论文题目为《论洛克、克尔凯郭尔、海德格尔及其对基督教的看法》。

成就与影响

在校期间,帕特诺获得了30多项学术奖项,论文多次获得高度评价。他的经历被广泛报道,成为终身学习的象征。他鼓励年轻人:“要么现在就做,要么永远放弃。”。目前,他计划用打字机撰写小说,继续探索未完成的学术兴趣。

核心精神

帕特诺的人生跨越贫困、战争、疫情等多重挑战,始终以学习为对抗命运的工具。他的故事印证了“活到老学到老”的真谛,激励人们突破年龄与环境的限制,追求梦想。


论文解读:理性、存在与信仰的动态统一

帕特诺的硕士论文通过三位哲学家的对话,揭示了基督教在哲学与神学交叉点上的现代意义:

  • 洛克的理性主义:主张基督教是“理性的宗教”,强调信仰需以理性为基础,避免非理性的盲目性。
  • 克尔凯郭尔的存在主义:强调个人通过“信仰的跳跃”直接与上帝建立关系,孤独与痛苦是信仰的必经之路。
  • 海德格尔的存在论重构:将原始基督教的生活经验视为存在论的典范,强调信仰是对生命有限性的回应和接纳。

帕特诺的论文最终指向一种跨学科的整合路径:理性作为信仰的边界与工具,存在作为信仰的土壤,信仰作为超越的可能,为现代人提供了在多元价值冲突中安顿精神的框架。


结论

帕特诺的学术研究体现了对基督教本质的深度追问,呼应了他本人“终身学习”的精神内核。他的研究为当代人提供了应对技术理性异化、重建精神家园的启示——在理性与信仰、个体与超越之间,寻找动态平衡的生存方式。

揭秘大模型背后的“专家天团”:混合专家模型 (MoE) 入门指南

我们接着上一篇 Transformer 的介绍,来聊一聊当前大模型领域一个非常火热且高效的技术:混合专家模型(Mixture-of-Experts, MoE)

如果你已经理解了 Transformer,那么理解 MoE 会容易得多,因为它正是对 Transformer 结构的一个巧妙“升级”。


揭秘大模型背后的“专家天团”:混合专家模型 (MoE) 入门指南

想象一下,一个标准的大型语言模型(比如 GPT-3)就像一个无所不知的“超级通才”。无论你问它关于物理、历史、烹饪还是编程的问题,它都会调动整个庞大的“大脑”(即全部的神经网络参数)来思考和回答。

这种模式的痛点:

  • 成本高昂:模型越大,参数越多,每次计算的开销就越大。这就像让一位诺贝尔物理学奖得主用他全部的脑力去计算“1+1=?”,非常浪费资源。
  • 扩展困难:想让模型更强大,就得把它的“大脑”做得更大,训练成本和推理延迟会急剧上升。

MoE 的核心思想:组建一个“专家委员会”
MoE 提出了一种更聪明的方式:我们不需要一个包揽一切的超级通才,而是可以组建一个“专家委员会”。

  • 这个委员会有很多**专家 (Experts)**,每个专家都擅长某个特定的领域(比如,有的擅长处理代码,有的擅长处理诗歌,有的擅长处理事实性知识)。
  • 委员会里还有一个非常重要的角色——**门控网络 (Gating Network)**,它就像一个聪明的“任务分配官”或“路由器”。

当一个任务(比如,一个需要处理的单词/Token)进来时,这位“分配官”会快速判断:“嗯,这个任务看起来跟编程有关,我应该把它交给第 3 号和第 8 号专家去处理。”

于是,只有被选中的这几个专家需要“开动脑筋”(被激活计算),其他所有专家都可以继续“休息”。

通过这种方式,模型可以拥有海量的总参数(所有专家的参数加起来),但在处理任何单个任务时,实际动用的计算量却很小。这就是所谓的**“稀疏激活” (Sparse Activation)**。


一、MoE 在 Transformer 中的位置

MoE 并不是一个独立的全新模型,而是对 Transformer 结构的一个“插件化”改造。它通常被用来替换 Transformer 层中的前馈神经网络(Feed-Forward Network, FFN)部分

回忆一下我们之前讲的 Transformer 编码器层结构:

  1. 输入
  2. 多头自注意力 (Multi-Head Attention)
  3. Add & Norm
  4. 前馈神经网络 (Feed-Forward Network, FFN)
  5. Add & Norm
  6. 输出

在 MoE 架构中,第 4 步的那个单一、稠密的 FFN,被替换成了一个 MoE 模块。

现在,每个 Token 在通过注意力层之后,不再是只有一个 FFN 可以去,而是会面对一个拥有“门控网络”和多个可选 FFN“专家”的 MoE 模块。


二、深入原理:MoE 是如何工作的?

我们来拆解一下 MoE 模块内部的运作流程。

假设我们有 8 个专家,并且设定每次只选择最好的 2 个专家(即 Top-2 路由)。

  1. 接收输入:一个 Token 的向量表示(比如 $x$)从注意力层传来,进入 MoE 模块。

  2. **门控网络做决策 (Gating Network)**:

    • 门控网络本身是一个小型的神经网络(通常就是一个简单的线性层)。
    • 它接收 Token 向量 $x$,然后输出一个包含 8 个分数的列表(logit),每个分数对应一个专家。这个分数代表了门控网络认为该专家与当前任务的“匹配度”。
    • logits = GatingNetwork(x) -> [1.2, -0.5, 3.1, ..., 2.5] (8个分数)
  3. 选择 Top-K 专家

    • 我们从这 8 个分数中选出最高的 2 个。比如,第 3 个和第 8 个专家的分数最高。
    • 这意味着,接下来的计算将只由 Expert 3 和 Expert 8 完成。
  4. **计算专家权重 (Softmax)**:

    • 我们只拿出被选中的那 2 个分数(3.1 和 2.5),然后通过一个 Softmax 函数。
    • Softmax 会将这两个分数转换成两个权重,加起来等于 1。比如,[0.65, 0.35]
    • 这代表,在最终结果中,我们应该 65% 听 Expert 3 的,35% 听 Expert 8 的。
  5. 专家处理与加权合并

    • Token 向量 $x$ 被同时发送给 Expert 3 和 Expert 8。
    • Expert 3 输出结果 $y_3 = \text{Expert}_3(x)$。
    • Expert 8 输出结果 $y_8 = \text{Expert}_8(x)$。
    • 最终的输出是这两个结果的加权和:final_output = (y_3 * 0.65) + (y_8 * 0.35)

就这样,MoE 模块完成了它的工作。虽然我们有 8 个专家,但只激活了 2 个,大大节省了计算。


三、MoE 的优势与挑战

优势

  1. 高效的计算成本:总参数量可以非常大(模型容量大,能学到更多知识),但处理每个 Token 的计算成本(活跃参数量)却可以保持很低。这是 MoE 最核心的优点。
  2. 更快的推理速度:由于计算量减少,模型的推理速度比同等总参数量的稠密模型要快得多。
  3. 巨大的模型容量:理论上可以加入非常多的专家来扩展模型的知识边界,而不会让计算成本失控。例如,Mixtral 8x7B 模型,总参数量约 47B,但每次推理只激活约 13B 参数。

挑战

  1. 训练不稳定:门控网络有可能会“偏心”,总是把任务交给某几个它喜欢的“明星专家”,导致其他专家得不到训练。
  2. 负载均衡:为了解决上述问题,需要引入一个额外的**“负载均衡损失函数” (Load Balancing Loss)**,来鼓励门控网络尽可能将任务均匀地分配给所有专家。这给训练增加了一点复杂性。
  3. 高内存需求:虽然计算是稀疏的,但所有专家(无论是否被激活)的参数都必须加载到 GPU 显存中。这导致 MoE 模型对硬件的显存要求非常高。
  4. 通信开销:在分布式训练(用多张 GPU 卡训练)时,如果专家分布在不同的卡上,那么 Token 在被路由到不同专家时会产生大量的通信,可能会成为瓶颈。

四、简单实现思路 (以 PyTorch 为例)

同样,我们用概念性的代码来展示其结构。

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
48
49
50
51
52
53
54
55
56
57
import torch
import torch.nn as nn
import torch.nn.functional as F

# 定义一个简单的专家(就是个FFN)
class Expert(nn.Module):
def __init__(self, d_model, d_hidden):
super().__init__()
self.network = nn.Sequential(
nn.Linear(d_model, d_hidden),
nn.ReLU(),
nn.Linear(d_hidden, d_model)
)
def forward(self, x):
return self.network(x)

# 核心的MoE层
class MoELayer(nn.Module):
def __init__(self, d_model, num_experts, top_k):
super().__init__()
self.top_k = top_k

# 专家列表
self.experts = nn.ModuleList([Expert(d_model, d_model * 4) for _ in range(num_experts)])

# 门控网络,输出每个专家的分数
self.gating_network = nn.Linear(d_model, num_experts)

def forward(self, x):
# x 的形状: [batch_size, sequence_length, d_model]

# 1. 通过门控网络获取分数
# reshape 成 [batch_size * sequence_length, num_experts]
gating_logits = self.gating_network(x.view(-1, x.shape[-1]))

# 2. 选择 Top-K 专家
# `topk`会返回权重和索引
weights, indices = torch.topk(gating_logits, self.top_k, dim=-1)

# 3. 将分数通过 Softmax 转换成最终权重
weights = F.softmax(weights, dim=-1, dtype=torch.float).to(x.dtype)

# 4. 准备输出
final_output = torch.zeros_like(x)

# 这是一个简化的、低效的循环,仅为演示原理
# 实际高效实现会使用复杂的索引和矩阵乘法来避免循环
flat_x = x.view(-1, x.shape[-1])
for i, (w, idx) in enumerate(zip(weights, indices)):
# 对每个 token,获取其选择的专家输出
expert_outputs = [self.experts[expert_idx](flat_x[i]) for expert_idx in idx]

# 加权求和
weighted_output = torch.stack(expert_outputs, dim=-1) * w.unsqueeze(-1)
final_output.view(-1, x.shape[-1])[i] = torch.sum(weighted_output, dim=-1)

return final_output

著名的 MoE 模型

  • Google GLaM, Switch Transformer: 学术界早期的重要 MoE 模型。
  • Mistral AI 的 Mixtral 8x7B: 开源社区的明星模型,以其卓越的性能和相对较低的推理成本而闻名。
  • Groq 正在使用的模型: 据信 Groq 的超快推理芯片背后也部署了 MoE 架构。

总结

MoE 是一种优雅的“缩放法则” (Scaling Law) 实践。它通过“专家分工”和“稀疏激活”的策略,巧妙地解决了大模型无限增长带来的计算困境。它允许我们构建参数量极其庞大的模型,同时将实际计算成本控制在可接受的范围内,是通往更强大、更高效 AI 的一条关键路径。

好的,我们继续深入,探讨一个更前沿、更强大的概念:多模态混合专家模型(Multimodal Mixture-of-Experts, MMoE)

这正是像 Google Gemini 这样先进模型背后的核心技术之一,它让 AI 真正具备了“看、听、读”的综合能力。


当AI学会“看听读”:多模态混合专家(Multimodal MoE)原理解析

我们首先回顾一下:

  • Transformer 是一次性处理所有输入(如单词)并理解它们之间关系的强大架构。
  • 混合专家模型 (MoE) 是对 Transformer 的一项优化,它用一个“专家委员会”代替了单个庞大的前馈网络(FFN)。一个“任务分配官”(门控网络)只将任务(Token)路由给最相关的少数几个专家,从而在保持巨大模型容量的同时,极大地降低了计算成本。

到目前-为止,我们讨论的 MoE 主要还是在单一模态(Unimodal),特别是文本领域。专家们虽然各有所长,但处理的都是同一种类型的“文件”——文本 Token。

然而,真实世界是多模态 (Multimodal) 的,充满了图像、声音、文字、视频等各种信息。要让 AI 像人类一样理解世界,就必须教会它同时处理这些不同类型的数据。

多模态 MoE 的核心任务:
如何让“专家委员会”不仅能处理文本报告,还能看懂图片、听懂录音,并理解它们之间的关联?


一、从单模态 MoE 到多模态 MoE 的演进

想象一下我们的“专家委员会”:

  • 单模态 MoE:委员会里都是语言学家、作家、程序员等文本专家。他们擅长处理各种文本,但你给他们一张梵高的《星空》,他们可能会束手无策。
  • 多模态 MoE:委员会进行了扩招和改组!现在,里面不仅有文本专家,还加入了图像分析师、声学工程师、视频剪辑师等。这个委员会现在能处理各种类型的“文件”。

关键挑战:不同模态的数据结构天差地别。

  • 文本是离散的单词序列。
  • 图像是像素网格。
  • 音频是连续的波形。

在送入 MoE 模块之前,我们必须先把这些五花八门的数据转换成一种通用的“语言”——**向量 (Vector)**。

这通过各模态专用的编码器 (Encoder) 来实现:

  • 图像:通过一个视觉 Transformer (ViT) 编码器,将图片切成小块(Patches),每个小块转换成一个向量。
  • 文本:通过一个文本 Tokenizer 和词嵌入模型,将单词转换成向量。
  • 音频:通过一个音频编码器(如处理梅尔频谱图),将音频片段转换成向量。

所有数据都被转换成统一格式的 Token 向量后,就可以送入多模态 MoE 的核心层进行处理了。


二、多模态 MoE 的核心架构与实现思路

一旦所有数据都变成了统一的 Token 向量,接下来的问题是:专家们应该如何组织?门控网络又该如何路由?

这里主要有两种主流的设计思路:

思路一:模态专属专家 (Modality-Specific Experts)

这是一种直接且清晰的思路。

  • 架构:我们将专家分成几个“部门”,比如“视觉部”、“语言部”、“听觉部”。每个部门内部有多个专家。
  • 路由:门控网络知道每个 Token 的“出身”(是图像、文本还是音频)。当一个图像 Token 进来时,门控网络只会从“视觉部”的专家中选择 Top-K 个进行激活。文本 Token 则路由给“语言部”的专家。
  • 优点
    • 专业化:每个专家都可以深度专注于自己模态的特定模式,不会被其他模态的信息“干扰”。
    • 易于理解和训练:结构清晰,任务明确。
  • 缺点
    • 融合不足:跨模态的深层融合发生得比较晚。专家们在自己的部门里“闭门造车”,只有在更高层次的模块(比如注意力层)中,不同模态的信息才能真正互动。

思路二:共享与混合专家 (Shared / Hybrid Experts)

这是目前更前沿、更强大的思路,也是 Gemini 这类模型被认为采用的方案。

  • 架构:我们不再设立严格的“部门”,而是只有一个巨大的、混合的专家池
  • 路由:门控网络不再关心 Token 的“出身”,只关心它的内容和概念。它会根据 Token 向量本身所代表的抽象含义,将其路由到最合适的专家,无论这个 Token 最初来自图像还是文本。
  • **涌现的奇迹 (Emergence)**:
    • 一个描述天空的文本 Token “sky” 和一个包含蓝天的图像 Token,可能因为它们的向量在语义空间中很接近,而被门控网络路由到同一个专家。这个专家慢慢就学会了处理“天空”这个抽象概念,而不仅仅是某个特定模态的模式。
    • 同理,可能会有专家专门处理“纹理”概念,另一个专家处理“对称性”概念,这些都是跨模态的通用知识。
  • 优点
    • 深度融合:在模型的极深层次就实现了跨模态的知识共享与融合。
    • 更高的参数效率:通过学习跨模态的通用概念,模型可以更有效地利用其参数。
  • 缺点
    • 训练更复杂:如何引导模型学习到这种有意义的跨模态概念,是一个巨大的挑战。
    • 需要海量且高质量的数据:需要大量配对好的图文、音视频数据,才能让模型学会这种跨模态的对应关系。

三、优势与前沿挑战

多模态 MoE 的巨大优势

  1. 极致的扩展性:这是目前已知最能有效扩展模型规模,同时容纳多种模态信息的架构。理论上可以构建拥有数万亿参数的巨型模型。
  2. 前所未有的综合理解能力:模型能够执行复杂的跨模态推理任务。例如:
    • 看一段视频,回答关于画面和声音的问题。
    • 上传一份带图表的财报 PDF,让它总结关键信息。
    • 给一张食材图片,让它生成一份菜谱文本。
  3. 计算效率:继承了 MoE 的核心优点,无论模型总参数多大,处理单个任务的计算成本都保持在可控范围内。

面临的前沿挑战

  1. **数据对齐 (Data Alignment)**:如何让模型确信,猫的图片、”cat” 这个词、猫的叫声(喵~)都指向同一个实体?这需要精心设计的数据集和训练策略。
  2. 复杂的负载均衡:不仅要确保任务在专家之间均匀分配,还要考虑模态间的平衡。不能让模型在训练中只偏爱处理更容易的文本 Token,而冷落了复杂的图像 Token。
  3. 路由策略:设计更智能的门控网络至关重要。也许未来的路由是层级式的:一个高级路由器先判断模态,再由次级路由器进行概念路由。
  4. 评估与可解释性:如何系统地评估一个多模态大模型的能力?当它犯错时,我们如何知道是视觉理解错了、还是语言推理错了,或者是二者融合时出了问题?

四、总结与展望

多模态混合专家模型(MMoE)是通往通用人工智能(AGI) 的一条关键技术路径。它通过模仿人类社会“专家委员会”的高效协作模式,成功地将 Transformer 的强大表征能力和 MoE 的高效扩展性结合起来,并将其应用到了复杂多变的多模态世界。

从只能处理文本的“语言学家”,到能够看、听、读、思的“全能数字助理”,MMoE 架构正在驱动 AI 完成一次深刻的进化。未来,随着硬件的发展和算法的优化,我们将会看到更加庞大、能力更加全面的多模态模型,它们将能以更接近人类的方式来理解和与我们周围的世界互动。

Transformer 架构详解:写给初学者的入门指南

这是一份写给初学者的 Transformer 架构系统性介绍。我们将用尽可能通俗易懂的语言、恰当的比喻和清晰的结构,来剖析这个当今人工智能领域最重要的模型之一。


想象一下,你在做一篇很长的英文阅读理解。传统的做法(就像旧的 AI 模型 RNN/LSTM)是你一个词一个词地读,读到后面可能会忘记前面的细节。但如果让你先把整篇文章通读一遍,然后在回答问题时,你可以随时回头查看文章的任何部分,并重点关注与问题最相关的句子,效率和准确性是不是就高多了?

Transformer 架构就是后面这种“聪明的读者”。它彻底改变了 AI 处理序列数据(尤其是文本)的方式。

一、核心思想:告别“按顺序”,拥抱“全局视野”

在 Transformer 出现之前,主流的模型如循环神经网络(RNN)和长短期记忆网络(LSTM)都是顺序处理文本的。它们像一个一个地读单词,试图在脑中维持一个“记忆”来理解上下文。

RNN/LSTM 的两大痛点:

  1. 效率低下:必须一个词处理完才能处理下一个,无法并行计算,处理长文本时速度很慢。
  2. 长期依赖问题:当句子很长时,模型很难记住最开始的信息。比如,“我出生在法国……(中间省略一万字)……所以我最擅长的语言是法语。” 模型可能已经忘记了开头的“法国”。

Transformer 的革命性思想:

  1. 并行计算:一次性读取所有单词,就像把整篇文章铺在桌上。
  2. **自注意力机制 (Self-Attention)**:通过一种绝妙的机制,让模型在处理每个单词时,都能“关注”到句子中所有其他单词,并判断它们之间的关联性强弱。

二、宏观架构:一个高效的翻译系统

Transformer 最初是为机器翻译任务设计的。它的经典结构是一个编码器-解码器 (Encoder-Decoder) 架构。

  • **编码器 (Encoder)**:左侧部分。它的任务是“理解”输入的句子。比如输入“I am a student”,编码器会阅读并消化这句话,将其转换成一堆包含丰富语义信息的数字向量(可以理解为“思想精华”)。
  • **解码器 (Decoder)**:右侧部分。它的任务是根据编码器提炼的“思想精华”,生成目标语言的句子。比如生成“我是一个学生”。

编码器和解码器都不是单一的组件,而是由 N 层(原论文中 N=6)完全相同的结构堆叠而成。这就像把一篇文章让 6 个专家轮流阅读和批注,每一层都会在前一层的基础上进行更深入的理解。


三、深入内部:三大关键组件(以编码器为例)

让我们打开一个编码器层(Encoder Layer),看看里面到底有什么。每个编码器层主要由两大部分组成:多头自注意力机制前馈神经网络

1. 准备工作:词嵌入 (Word Embedding) 与位置编码 (Positional Encoding)

在进入编码器之前,输入的文本需要做两步预处理。

  • **词嵌入 (Word Embedding)**:计算机不认识单词,只认识数字。词嵌入就是用一个向量(一串数字)来表示一个单词。例如,“猫”可能被表示为 [0.1, -0.5, 1.2, ...],“狗”可能被表示为 [0.2, -0.4, 1.1, ...]。意思相近的词,它们的向量也更接近。
  • 位置编码 (Positional Encoding):由于 Transformer 一次性看所有词,它本身不知道词的顺序。但语序至关重要(“我打你”和“你打我”完全不同)。位置编码就是给每个词的向量再额外加上一个代表其位置信息的“标签”向量。这样,模型既知道了每个词的意思,也知道了它们的顺序。

2. 核心引擎:自注意力机制 (Self-Attention)

这是 Transformer 最核心、最天才的部分。它让模型知道在理解一个词时,应该重点关注句子中的哪些其他词。

工作原理(Q, K, V类比法):
想象你在图书馆查资料。

  • **Query (Q, 查询)**:你当前正在研究的主题(比如,你想理解句子中的 “it” 这个词)。
  • **Key (K, 键)**:图书馆里每本书的书名或标签(句子中的每一个词都有一个 Key)。
  • **Value (V, 值)**:书本的具体内容(句子中的每一个词也都有一个 Value,通常是它的词嵌入向量)。

过程如下:

  1. 生成 Q, K, V:对于输入句子中的每个词,我们都通过三个不同的权重矩阵,从它的词嵌入向量生成三个新的向量:Query 向量、Key 向量和 Value 向量。
  2. 计算注意力分数:要理解 “it” 这个词 (它的 Q),你需要将它的 Q 向量与句子中所有词的 K 向量进行点积计算。这个得分代表了 “it” 与其他每个词的关联程度。
  3. **归一化 (Softmax)**:将这些分数通过 Softmax 函数转换成 0到1 之间的权重,且所有权重加起来等于1。权重越高的词,说明关联性越强。
  4. 加权求和:将每个词的 V 向量乘以它对应的权重,然后全部加起来。

最终得到的这个加权平均向量,就是 “it” 这个词在当前语境下的全新表示。如果句子是 “The animal didn’t cross the street because it was too tired”,那么 “animal” 这个词的 V 向量会被赋予很高的权重,最终的新向量就会包含大量 “animal” 的信息,模型从而知道 “it” 指的是 “animal”。

3. 升级版:多头注意力机制 (Multi-Head Attention)

如果只用一套 Q, K, V,就好比你只有一个角度去理解句子。但句子的关系是多维度的。比如,“我”和“打”是主谓关系,“打”和“你”是动宾关系。

多头注意力 就是雇佣多个“注意力头”(比如 8 个),让它们各自学习自己的一套 Q, K, V 权重。

  • 头1 可能关注主谓关系。
  • 头2 可能关注代词指代关系。
  • 头3 可能关注形容词修饰关系…

每个头都独立进行一次完整的自注意力计算,得出一个结果向量。最后,我们将这 8 个头的结果拼接起来,再通过一个线性层进行整合。这样,模型就能从多个角度和维度更全面地理解句子。

4. 辅助组件:前馈网络 (Feed-Forward) 和 Add & Norm

  • 前馈神经网络:在多头注意力层之后,每个词的输出向量会再经过一个简单的前馈神经网络。你可以把它看成是一个“加工厂”,对注意力层提炼出的信息进行进一步的非线性变换和加工,增强模型的表达能力。
  • **Add & Norm (残差连接和层归一化)**:
    • Add (残差连接):在每个主要组件(如多头注意力和前馈网络)的输出上,都把它加上该组件的输入。这相当于走了一条“捷径”,保证了原始信息不会在多层处理中丢失,极大地稳定了训练过程。
    • **Norm (层归一化)**:对每个残差连接后的输出进行归一化,使其数据分布更加稳定,好比是统一了度量衡,让模型训练起来更快、更稳定。

四、解码器 (Decoder) 的特殊之处

解码器与编码器结构非常相似,但有两点关键不同:

  1. 带掩码的自注意力 (Masked Self-Attention):解码器在生成译文时,是逐词生成的。在预测第 3 个词时,它只能看到已经生成的第 1、2 个词,不能偷看后面的正确答案。这个“掩码”机制就是用来遮盖未来信息的。
  2. 编码器-解码器注意力 (Encoder-Decoder Attention):这是解码器层中的第二个注意力层。它的 Q 来自解码器自身(前一层的输出),但 K 和 V 来自编码器的最终输出。这一步是解码器“查阅”原始句子“思想精华”的过程。比如,在翻译到某个动词时,它会去关注原始句子中的主语和宾语,以确保翻译的准确性。

五、简单实现思路 (以 PyTorch 为例)

对于初学者,无需从零手写所有数学细节。可以利用深度学习框架中封装好的模块来搭建。

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
48
49
50
51
52
import torch
import torch.nn as nn

# 1. 关键模块
# 词嵌入
embedding = nn.Embedding(vocab_size, d_model)
# 多头注意力 (包含了Q,K,V的生成和计算)
multihead_attn = nn.MultiheadAttention(embed_dim=d_model, num_heads=8)
# 前馈网络
feed_forward = nn.Sequential(
nn.Linear(d_model, ff_hidden_dim),
nn.ReLU(),
nn.Linear(ff_hidden_dim, d_model)
)
# 层归一化
layer_norm = nn.LayerNorm(d_model)

# 2. 搭建一个编码器层
class EncoderLayer(nn.Module):
def __init__(self):
super().__init__()
# ... 初始化上面的模块

def forward(self, x, mask):
# 多头注意力 + Add & Norm
attn_output, _ = self.multihead_attn(x, x, x, attn_mask=mask)
x = self.layer_norm(x + attn_output)

# 前馈网络 + Add & Norm
ff_output = self.feed_forward(x)
x = self.layer_norm(x + ff_output)

return x

# 3. 搭建完整的 Transformer
class Transformer(nn.Module):
def __init__(self):
super().__init__()
# ...
# 实例化 N 个编码器层
self.encoder_layers = nn.ModuleList([EncoderLayer() for _ in range(N)])
# 实例化 N 个解码器层
self.decoder_layers = nn.ModuleList([DecoderLayer() for _ in range(N)])
# ...

def forward(self, src, tgt, ...):
# 1. 对 src (源句子) 进行词嵌入和位置编码
# 2. 将结果送入编码器栈
# 3. 对 tgt (目标句子) 进行词嵌入和位置编码
# 4. 将编码器输出和处理过的 tgt 送入解码器栈
# 5. 最终通过一个线性层和 Softmax 得到预测的下一个单词的概率
# ...

对于初学者,最好的学习方式是阅读并运行一份带有详细注释的实现代码,例如 PyTorch 官方的 Transformer 教程


六、总结与展望

Transformer 的成功关键:

  • 自注意力机制:实现了对全局上下文的有效建模。
  • 并行计算能力:极大地提高了训练和推理效率,使得处理海量数据和构建超大规模模型成为可能。

正是因为这两个特点,Transformer 不仅仅局限于机器翻译,它已经成为现代 AI 的基石。

  • BERT 系列模型使用 Transformer 的编码器进行语言理解。
  • GPT 系列模型(包括 ChatGPT)使用 Transformer 的解码器进行文本生成。
  • DALL-E, Midjourney 等图像生成模型,也将图像块(patches)视为一种“单词”,用 Transformer 来理解和生成图片。

希望这份介绍能帮你打开 Transformer 的大门。它初看可能有些复杂,但只要理解了其核心的“全局视野”和“自注意力”思想,其他部分就会变得顺理成章。


更新网站ssl证书导致java httpclient请求出错的问题

错误

httpClient.executeMethod(method)出错如下:

1
2
3
4
5
javax.net.ssl.SSLHandshakeException: 
sun.security.validator.ValidatorException:
PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target

原因

由于Mozilla更新了其根证书信任策略,即对于全球所有CA的可信根证书生成后最少15年更换一次,超过时间的可信根将会逐步被Mozilla停止信任,因此Digicert的部分老根证书将会在2023年07月01日左右逐步升级为Digicert Global Root G2。

也就是说新证书的根证书变了。我的老java应用的jre带的security/cacerts没有自带Digicert Global Root G2

解决方法

从浏览器导出”Digicert Global Root G2.crt”,然后导入到用到的java jre中:

1
keytool -importcert -file '/pathto/DigiCert Global Root G2.crt' -alias mykey1 -keystore '/pathto/jre/lib/security/cacerts' -storepass changeit

然后重启java应用即可。

freessl.cn 申请的免费证书也有类似的问题

只是根证书改为:TrustAsia ECC DV TLS CA G3

参考

js实现羽毛球比赛中的八人转和多人转

羽毛球爱好者熟知的八人转,就是N个人轮转进行双打比赛,大家的机会均等、比较公平。一轮打下来的输赢积分较能客观反映实际。

八人转基本规则就是:每人和其他人都组队搭档一次,每队至少上场一次,各人轮换上场,每人上场次数要相同。

编程实现N人转对阵编排的算法思路:
1、找出所有的组队,即N个人中取2人的组合C
2、所有组队两两对阵比赛,即C组队中取2对的组合,但要去除人员冲突的对阵(自己不能和自己打),剩下的对阵仍然可能太多,人多了不可能都打一遍
3、为了公平轮换,只要找上场最少的人和队优先打即可
4、每队都上场一次后,每人上场次数一样时就可以结束轮转,也可以继续打更多局,但总要在每人上场次数一样时结束。

按照上面的思路,用js实现的算法如下:

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//组合可能的搭档/组队
function combo(N) {
let pairs = []
for (let a = 1; a <= N; a++) {//从1开始,好看一些
for (let b = a + 1; b <= N; b++) {
pairs.push([a, b, 0])//a和b搭档:[a, b, 上场次数]
}
}
return pairs
}
function isConflict(A, B) {//判断两个组队人员是否冲突
return A == B || A[0] == B[0] || A[0] == B[1] || A[1] == B[0] || A[1] == B[1]
}

//匹配可能的对局
function match(pairs) {
let vs = [], P = pairs.length
for (let i = 0; i < P; i++) {
let A = pairs[i]
for (let j = i + 1; j < P; j++) {
let B = pairs[j]
if (isConflict(A, B)) continue//跳过冲突的对局
vs.push([A, B])//A队和B队对局/对打v:[A,B]
}
}
return vs
}

//N人转,至少打M局的对阵编排
//公平轮转:每人和其他人都搭档一次,每队至少上场一次,各人轮换上场,每人上场次数要相同
function main(N, M) {
if (N < 4) return console.error(`人数不足(${N}<4)`)
if (N > 20) return console.error(`人数太多啦!`)
let plays = new Array(N).fill(0)//记录玩家上场次数
function tires(v) {//计算候选对局的疲劳度
let A = v[0], B = v[1]
return (A[2] + 1) * (plays[A[0] - 1] + plays[A[1] - 1]) + (plays[B[0] - 1] + plays[B[1] - 1]) * (B[2] + 1)
}
let pairs = combo(N)//获取可能的组队
let allvs = match(pairs)//获取所有的对局
let vs = []//对阵上场次序数组
console.log(`${N}人,${pairs.length}队,${M>0?('打'+M+'局'):''}对阵:`)
for (let i = 0; allvs.length > 0 ; i++) {
let v = allvs.shift()//取第一对上场
let A = v[0], B = v[1]//更新对阵参与者
A[2]++, plays[A[0] - 1]++, plays[A[1] - 1]++
B[2]++, plays[B[0] - 1]++, plays[B[1] - 1]++
console.log(`${i + 1}. (${A[0]},${A[1]}) x (${B[0]},${B[1]})`)
vs.push(v)
if (!M || i+1 >= M){
if (pairs.every(p => p[2]>0)){//每队都上场过
if (plays.every(c => c==plays[0])) break//每人上场次数都一样
}
}
allvs = allvs.sort((a, b) => tires(a) - tires(b))//把最少上场的排到第一位
}
console.log(`每人上场${plays[0]}次.\n`)
return vs
}

// 试一下
main(3),main(4),main(5)
main(6),main(6, 15)
main(7),main(7, 21)
main(8),main(8, 16),main(8, 18)
main(9),main(9, 27)
main(10),main(100)

改写成一个类:

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//N人转对阵编排
class CMatch {
#N //人数
#plays //每人上场次数
#pairs //所有搭档组合
#allvs //所有可能对局

constructor(N) {
this.#N = N;
}

play(M) {//至少M局的对阵编排,不指定M则按最少局数编排
const N = this.#N
if (N < 4) return console.error(`人数不足(${N}<4)`)
if (N > 20) return console.error(`人数太多啦!`)
let plays = this.#genPlays(true)//每人上场次数
let pairs = this.#genPairs(true)//获取可能的组队
let allvs = this.#genAllvs(true)//获取所有的对局
let vs = []//对阵上场次序数组
console.log(`${N}人,${pairs.length}队,${M>0?('打'+M+'局'):''}对阵:`)
for (let i = 0; allvs.length > 0 ; i++) {
let v = allvs.shift()//取第一对上场
let A = v[0], B = v[1]//更新对阵参与者
this.#updatePlay(A)
this.#updatePlay(B)
vs.push(v)
console.log(`${i + 1}. (${A[0]},${A[1]}) x (${B[0]},${B[1]})`)
if (!M || i+1 >= M){
if (pairs.every(p => p[2]>0)){//每队都上场过
if (plays.every(c => c==plays[0])) break//每人上场次数都一样
}
}
allvs = allvs.sort((a, b) => this.#calcTires(a) - this.#calcTires(b))//把最少上场的排到第一位
}
console.log(`每人上场${plays[0]}次。\n`)
return this
}

#genPlays(reset) {//生成每人上场次数数组
if (!this.#plays){
this.#plays = new Array(this.#N).fill(0)
}else if (reset){
this.#plays.fill(0)
}
return this.#plays
}

#genPairs(reset) {//可能的搭档组合
const N = this.#N
if (!this.#pairs){
this.#pairs = []
for (let a = 1; a <= N; a++) {//从1开始,好看一些
for (let b = a + 1; b <= N; b++) {
this.#pairs.push([a, b, 0])//a和b搭档:[a, b, 上场次数]
}
}
}else if (reset){
this.#pairs.forEach(p => p[2] = 0)//重置上场次数
}
return this.#pairs
}

#genAllvs(reset) {//可能的对局
if (!this.#allvs || reset){
this.#allvs = []
let pairs = this.#pairs, P = pairs.length
for (let i = 0; i < P; i++) {
let A = pairs[i]
for (let j = i + 1; j < P; j++) {
let B = pairs[j]
if (CMatch.#isConflict(A, B)) continue//跳过冲突的对局
this.#allvs.push([A, B, 0])//A队和B队对局/对打v:[A,B,上场次数,比?分?]
}
}
}
return this.#allvs
}

#updatePlay(A) {//累加A队上场次数
this.#plays[A[0]-1]++, this.#plays[A[1] - 1]++, A[2]++
}

#calcTires(v) {//计算候选对局的疲劳度
let A = v[0], B = v[1], plays = this.#plays
return (A[2] + 1) * (plays[A[0] - 1] + plays[A[1] - 1]) + (plays[B[0] - 1] + plays[B[1] - 1]) * (B[2] + 1)
}

static #isConflict(A, B) {//判断两个组队人员对局是否冲突
return A == B || A[0] == B[0] || A[0] == B[1] || A[1] == B[0] || A[1] == B[1]
}

}

// 测试
new CMatch(4).play()
new CMatch(5).play()
new CMatch(6).play()
new CMatch(7).play().play(21)
new CMatch(8).play().play(16).play(18)
new CMatch(9).play().play(27)
new CMatch(10).play()

阿里云专用网络ECS安装ftp终极解决方案


安装

1
2
sudo yum install vsftpd
sudo systemctl enable vsftpd

添加用户

1
sudo adduser ftpuser/ftppassword

编辑配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sudo vim /etc/vsftpd/vsftpd.conf
listen=YES
listen_ipv6=NO
#listen_port=21
pasv_enable=YES #被动模式
pasv_min_port=10000
pasv_max_port=10100
pasv_address=54.53.52.51 #专用网络ip变成了映射,本机无法知道自己的真实ip地址。所以必须告知,本机,你的ip地址是什么。 https://yq.aliyun.com/articles/608725
pasv_addr_resolve=yes
anonymous_enable=NO
chroot_local_user=YES
#默认chroot_list_file=/etc/vsftpd/chroot_list没有要创建,为空即可
allow_writeable_chroot=YES
local_root=/home/ftpuser
userlist_enable=YES
userlist_deny=NO
#当userlist_enable=YES时,userlist_deny=NO时:user_list是一个白名单,里面只添加ftpuser,其余默认的去掉
#ftpusers不受任何配制项的影响,它总是有效,它是一个黑名单!https://blog.csdn.net/bluishglc/article/details/42273197

启动

1
sudo systemctl restart vsftpd

开放端口

1
防火墙和安全组开放端口:20-21,10000-10100

亲测按此配置之后,ftp主动和被动模式都正常传输,filezilla等ftp工具可以正常使用,curl、wget/wput等命令行工具也能用。

使用Spring Security实现OAuth2、JWT、SSO等(笔记)

参考资料

常见问题

一、SESSION冲突

“org.springframework.security.authentication.BadCredentialsException: Could not obtain access token, Caused by: org.springframework.security.oauth2.common.exceptions.InvalidRequestException: Possible CSRF detected - state parameter was required but no state could be found”

错误原因

在同一个域名下授权服务器和资源服务器的Cookie名都是JSESSIONID,导致在跳转到授权服务器后将资源服务器的Cookie覆盖了,再次跳转回去时授权服务器的Cookie对资源服务器无效,再次跳转到登录页面,该动作一直重复,导致授权失败。StackOverflow

解决办法

  1. 为授权服务器和资源服务器配置不同的 Cookie 名称: server.servlet.session.cookie.name=AUTH_SESSIONID
  2. 修改应用的 ContextPath:server.servlet.context-path=/auth

Spring boot集成kaptcha图片验证码

kaptcha 是一个图像验证码生成和验证工具,有许多可配置项,可以简单快捷的生成各式各样的验证码,使用起来也很简便。

pom.xml添加依赖

1
2
3
4
5
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>kaptcha-spring-boot-starter</artifactId>
<version>1.1.0</version>
</dependency>

application.yaml 添加典型配置

不加用默认也可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kaptcha:
height: 50
width: 200
content:
length: 4
source: abcdefghjklmnopqrstuvwxyz23456789
space: 2
font:
color: black
name: Arial
size: 40
background-color:
from: lightGray
to: white
border:
enabled: true
color: black
thickness: 1

在controller里使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.baomidou.kaptcha.Kaptcha;

@RestController
@RequestMapping("/code")
public class CodeController {
@Autowired
private Kaptcha kaptcha;

@RequestMapping("/image")
void renderImage() {
String code = kaptcha.render();
System.out.println(code);
}

@RequestMapping("/valid")
boolean validImage(@RequestParam String code) {
return kaptcha.validate(code);
}
}

测试

  • 前端访问/code/image即显示验证码图片
  • 前端访问/code/valid?code=xxxx即会返回true表示通过验证,出错表示code错误。