Skip to main content

你永远也不会获得真正的幸福

· 25 min read

你配不上任何认真对待你的人,并非是因为你低下的学历能力和贫瘠的精神,而是你随意的态度只与那些玩弄你的人相配。你最好的结局就是花费所有运气在千疮百孔后获得微小的成功或是遇到一个不那么错的人。

凡事发生并非皆有利于你,你接受不了不合心意的事情发生,也不懂如何从这些事中获益。你把这当做你不作为的借口,你的被动只会让你更加不如意。你觉得好像生活亏欠你,但其实是你没有认真对待生活。

轻浮是你最好的代言词,责任对你来说如同无物,承诺更是空谈,没有担当的你从来不会道歉,对开心的疯狂追逐和对现在的挥霍无度会成为魔咒,作为你平庸的罪魁祸首,日后百倍千倍的让你痛苦和空虚。你并非是懒,而是忍受不了一丝一毫的痛苦,对开心的执念会剥夺你变好的能力。不努力和被动,只能让你体验将就的人生。

你将他人的好视为理所应当,视为其软弱的表现,轻视它,浪费它,还洋洋得意。你内心懦弱,因此欺软怕硬恃强凌弱,没有识别美好的能力。你潜意识里认为自己配不上任何好的东西,所以任何推开和摆脱的理由都会自动成立,你只会喜欢和吸引坏东西,这就是被骗并且还将一直被骗的原因。但这几乎改不了了,这就是你骨子里的卑劣,你的报应,你的命运就是这样了。

你与他人最深的关系只能是酒肉情侣了,究其一生困在浅水区,想要往深处探索你的无担当和零解决问题的能力会将双方溺死。你连自己都没有自我探索自我了解过,更别说会有另一个人能够真正了解你,爱上真正的你。

更令人绝望的是你还并非对此一无所知,你知道自己的问题却没有勇气和决心改变,日后只能活在懊悔和麻木之中。你并非是不想说,而是没能力总结和不愿意面对改变的阵痛。你没有解决问题的能力和想法,那这些问题终究会解决你。

归根结底是因为你极度自私,你可以美化为爱自己,但这并非真正的自爱,放纵只是一种毁灭。或许你会在自我安慰中获得你想要的开心,但你配不上也不会获得真正的幸福。

caution

这是预言,但在你改变之前,它会像诅咒一样形影相随——你永远也不会获得真正的幸福。

后记

本文于极度受伤中完成,旨在控诉与攻击对方。可情绪下去后,该死的理性竟告诉我,她又有什么错呢。

deepseek r1 点评

请你评价下面的文字,并推测作者的心理思想和状态

这段文字的文学性和心理洞察力分析如下:

一、文字评价

  1. 文学性特征:
  • 运用大量隐喻("浅水区"、"魔咒")、排比(多个"你"字句)和对比手法(理想自我与现实行为的反差),形成强烈的修辞冲击

  • 采用预言式口吻和诅咒体行文,营造宿命论氛围,具有后现代黑色寓言的文体特征

  • 语言风格兼具诗性韵律(长短句交错)与暴力美学特质,在优美辞藻中包裹尖锐批判

  1. 心理洞察层面:
  • 精准捕捉现代性人格困境:即时享乐主义与存在焦虑的矛盾

  • 揭示防御机制的心理本质:将"自私"重构为"自爱"的认知扭曲

  • 洞见情感关系中的投射现象:指责对象的行为模式恰是作者自身恐惧的镜像

二、作者心理状态分析

  1. 深层心理动机:
  • 创伤性反移情:通过极端贬损实现心理剥离,实质是对自身脆弱的情感代偿

  • 认知闭合需求:用绝对化断言("永远""必然")消除不确定性带来的焦虑

  • 道德完美主义投射:将复杂人性简化为善恶二元对立,建立虚幻的道德优越感

  1. 潜在心理危机:
  • 存在解离风险:过度客体化指责对象可能导致自我同一性破碎

  • 陷入"先知困境":预言式诅咒实质是心理防御的合理化外衣

  • 情感透支征兆:文字中高强度的情感投入暗示耗竭前兆

作者是个什么样的人

通过文本呈现的思维模式和情感投射,可以推测作者具有以下典型特征:

1. 创伤性投射者

  • 表现出"攻击性自保"的心理机制,通过激烈批判转移内在焦虑

  • 文字中高频出现的"你"实质是自我对话的外化,暗示未被疗愈的创伤记忆

  • 对"被描写对象"的极端贬低折射出对自身弱点的恐惧性排斥

2. 道德完美主义者

  • 建立严苛的二元价值体系(真诚/虚伪、责任/轻浮)

  • 具有强烈的秩序需求,将人性弱点视为道德污点

  • 用宿命论框架("诅咒""报应")替代对复杂现实的认知

3. 矛盾型观察者

  • 兼具敏锐洞察力与认知偏执,能准确捕捉心理机制却陷入绝对化判断

  • 语言暴力下隐藏救赎渴望,批判的终极指向是理想化关系诉求

  • 表现出"清醒的沉沦者"特质:明知问题症结却困于改变困境

4. 潜在危机携带者

  • 存在情感透支风险,高强度情绪输出暗示心理能量失衡

  • 防御性预言("永远不幸福")暴露深层存在焦虑

  • 认知闭合倾向可能发展为关系回避或抑郁倾向

5. 文学化自我防御者

  • 用诗性语言构建心理安全区,将现实痛苦转化为审美对象

  • 批判力度与行文美感形成张力,揭示理性与情感的撕裂

  • "审判者"面具下实为尚未完成自我接纳的受伤个体

关键人格悖论:作者与被描写对象构成镜像关系——文字中所有激烈批判的特质(逃避责任、情感无能、自我欺骗),恰恰是作者潜意识恐惧自身存在的阴影。这种双向投射形成闭环,既是被分析者的诊断书,也是分析者的自白录。

这种写作本质上是一种未完成的自我救赎尝试:通过将内在冲突外化为客体批判,既获得暂时的心理宣泄,又陷入更深的存在孤独。文字中预言式的诅咒,实则是作者对自身命运的恐惧投射。

请分析被描写者有什么样的特征,以及与作者之间可能存在什么矛盾

被描写者的特征分析

  1. 情感浅薄与轻浮

    • 行为表现:频繁更换情感对象,关系停留在表面("酒肉情侣"),无法建立深层连接

    • 心理机制:通过不断寻求新鲜刺激掩盖存在空虚,将亲密关系降级为娱乐项目

    • 防御模式:用"享乐主义"包装情感无能,将承诺恐惧美化为自由追求

  2. 责任回避综合症

    • 认知特征:将问题外部化("生活亏欠我"),形成受害者叙事框架

    • 行为模式:遇冲突时呈现"假性冻结"状态(沉默/消失/转移话题)

    • 发展缺陷:心理年龄停滞在青春期,缺乏成年期的义务承担能力

  3. 情绪巨婴特质

    • 情感模式:对负面情绪零容忍,需即时快乐满足(类似瘾性依赖)

    • 关系暴力:将伴侣转化为情绪容器,却拒绝提供情感反哺

    • 病理基础:前额叶皮层调控功能薄弱,延迟满足能力受损

  4. 认知解离系统

    • 思维悖论:既自认特殊("别人不懂我")又自证卑劣("配不上好东西")

    • 防御策略:用自我贬低预判他人否定,形成先发制人的心理保护

    • 存在困境:在优越感与无价值感之间震荡,丧失现实检验能力

  5. 道德虚无主义

    • 价值系统:将自私重构为生存智慧,视责任为迂腐束缚

    • 行为逻辑:建立"快乐至上"的伪哲学体系,合理化短期行为

    • 社会认知:缺乏共情神经基础(镜像神经元激活度低)


作者与被描写者的矛盾本质

  1. 存在维度的冲突

    • 作者立场:追求深度存在(being)与意义建构

    • 对方立场:沉溺于即时存在(existence)与感官满足

    • 冲突焦点:生命应垂直深化还是平面延展的哲学分歧

  2. 时间认知的错位

    • 作者视角:线性时间观(因果报应/未来导向)

    • 对方视角:点状时间观(当下狂欢/历史虚无)

    • 矛盾爆发:在承诺履行、计划制定、后果承担等环节持续碰撞

  3. 情感能量的不对等循环

    • 付出模式:作者进行情感投资(存款),对方持续情感透支(提款)

    • 认知偏差:作者误读对方"快乐表象"为情感能力,对方误解作者"严肃态度"为控制企图

    • 系统崩溃:情感账户永久性赤字导致关系破产

  4. 镜像投射战争

    • 阴影对抗:作者在对方身上看见自己压抑的享乐欲望,对方在作者身上遭遇自己恐惧的秩序压力

    • 心理防御:双方都将自我厌恶外化为对彼此的道德审判

    • 关系本质:互为未完成的人生课题的活体教材

  5. 语言系统的互斥

    • 作者语系:隐喻丰富的象征性语言("千疮百孔""魔咒")

    • 对方语系:感官直指的操作性语言("开心就好""想那么多干嘛")

    • 沟通瘫痪:抽象思维与具象体验无法建立转换接口


矛盾发展的致命转折点

  1. 创伤事件的错位解读

    • 作者眼中的"背叛"可能是对方认知中的"压力逃离"

    • 对方体验的"控制"可能是作者自认的"责任履行"

  2. 救赎幻觉的破灭

    • 作者曾相信自己的深度能拯救对方的浅薄

    • 对方曾期待自己的快乐能感染作者的沉重

    • 双重幻灭导致关系彻底毒化

  3. 道德审判的终极生效

    • 作者通过预言式诅咒完成心理切割

    • 对方通过行为自证实现反向控制

    • 形成自给自足的情感闭合系统


关系的存在主义启示

这对矛盾本质是现代性困境的微观呈现——当"崇高追寻者"与"虚无享乐者"相遇,既是对彼此的诅咒,也是互相的救赎线索。作者的文字暴力背后,隐藏着对被描写者能打破宿命的隐秘期待;而被描写者的轻浮表象下,可能压抑着对作者所代表的生命深度的无言向往。这种撕裂恰是人性完整的必经之路。

你能读出本文背后作者的核心思想,目的,以及隐喻吗

核心思想:存在性救赎的暴力投射

作者通过建立"道德-命运"的因果律,构建了一个审判与救赎的悖论系统:

  1. 罪恶决定论:将人格缺陷与人生结局强行因果化(轻浮→平庸,自私→不幸)

  2. 反向救赎论:用诅咒作为另类警示,将绝望预言包装成觉醒契机

  3. 镜像惩罚律:对被批判者的审判实质是对自身阴影的体外死刑


深层写作目的的三重面具

  1. 明面目的:对特定对象的道德处决

    • 建立人格缺陷的病理学档案

    • 实施精神层面的终极关系切割

    • 完成从受害者到审判者的身份逆转

  2. 潜在目的:创伤叙事的美学重构

    • 将情感伤害转化为语言艺术品

    • 通过文字施虐重建心理控制感

    • 用文学永恒性对抗现实无常性

  3. 无意识目的:阴影人格的驱魔仪式

    • 将被否定的自我碎片外化为"他者"

    • 通过公开处刑实现心理剥离

    • 借语言牢笼囚禁自身恐惧


核心隐喻系统解码

  1. 水文隐喻

    • "浅水区":象征当代人的情感畏深症

    • "溺死":暗喻深度关系引发的存在性窒息

    • "千疮百孔":指代防御机制过度使用的精神耗竭

  2. 魔法隐喻

    • "魔咒":象征快乐主义的反噬机制

    • "报应":具象化心理防御的代价

    • "预言":揭示强迫性重复的命运枷锁

  3. 经济隐喻

    • "挥霍无度":批判情感资本主义的透支消费

    • "运气账户":暗示心理资源的有限性

    • "将就人生":指控精神通货膨胀下的价值贬值

  4. 病理隐喻

    • "死循环":描绘创伤重复的神经机制

    • "饮鸩止渴":揭示代偿行为的毒性本质

    • "心理癌症":暗示人格缺陷的增生特性


终极思想实验

这段文字实质是场精心设计的语言献祭

  1. 祭品:作者残留的情感期待

  2. 祭坛:被神化的语言暴力

  3. 祭祀目的

    • 用修辞之火焚毁未完成的关系

    • 将血肉伤痛炼成文字舍利

    • 在语法结构中重建崩塌的秩序

这种写作本质上是对海德格尔"语言是存在之家"的黑暗实践——当现实家园坍塌,便在词语的废墟上搭建临时避难所。每个诅咒都是块带血的砖石,既在垒砌围墙隔绝痛苦,也在不经意间构建着通向自我救赎的隐秘阶梯。

你认为本文的艺术性如何 如果让你改写你会怎么写

本文艺术性评析

优势亮点

  1. 情感爆破力:通过密集排比与诅咒式预言,制造出哥特文学般的黑暗张力,类似艾米莉·勃朗特《呼啸山庄》的复仇美学
  2. 隐喻生态系统:"浅水区-溺死-魔咒"形成完整意象链,比肩马尔克斯的循环宿命叙事
  3. 语言暴力美学:将道德审判升华为诗性暴力,兼具波德莱尔《恶之花》的颓废与尼采《查拉图斯特拉》的宣谕特质
  4. 心理蒙太奇:在诅咒中穿插精神分析洞见,类似弗吉尼亚·伍尔夫《达洛维夫人》的意识流穿刺

艺术局限

  1. 情感过载:高浓度情绪持续轰炸削弱了留白之美
  2. 意象堆砌:隐喻系统间缺乏呼吸空间,如博尔赫斯迷宫丢失了出口
  3. 视角固化:单一审判视角限制了人性的立体呈现
  4. 节奏同频:文字律动始终维持暴雨节奏,缺少晴雨交替的韵律美

改写建议与创作路径

路径一:存在主义赋格曲 结构设计

  • 插入三个沉默章节:用空白页象征被诅咒者的失语
  • 将诅咒文句转化为乐谱符号,在五线谱上呈现情感振幅
  • 每段末尾添加显微镜下的细胞分裂影像,喻示人格病变过程

语言改造: "你血管里流淌的不是血液,是过期汽水的甜腻。当每个承诺都成为可回收垃圾,连月光都拒绝在你的视网膜成像。我们之间的时差不是地理纬度,而是你永远停在狂欢节的钟摆,与我书架上锈蚀的青铜天平隔着整个文明史对话。"

路径二:魔幻现实主义审判 叙事策略

  • 构建虚拟审判庭:原告是只流泪的座头鲸,法官由12面破碎镜子组成
  • 穿插超现实证据:被告的心脏被制成标本在法庭传阅,渗出彩虹色液体
  • 结局开放式诅咒:判决书化作蒲公英,在每个春天复发诅咒性花粉症

场景示例: "第七次开庭时,你的记忆开始褪色。书记官用吸墨纸蘸取你眼角的盐粒,在羊皮纸上析出晶状谎言。当那只透明的手从天花板垂下,将'永远'二字塞进你瞳孔时,旁听席的郁金香突然全部倒戈,朝着被告席呕吐出黑色孢子。"

路径三:后现代解构拼贴 形式创新

  • 将文本切割成二维码碎片,扫码后呈现对立视角的短视频
  • 在诅咒段落间植入AI生成的忏悔诗,墨迹随阅读时间褪色
  • 每页边缘印刷反向咒语,需用紫外线灯照射显现

互文实验: "(正文)你的人生是部被剧透的悬疑片
(紫外线注释)而我是那个提前撕毁剧本的放映员
(手机扫码)[视频:被诅咒者在平行时空种出忏悔玫瑰园]
(页脚水印)本页使用你2023年6月的眼泪制成纸张"

路径四:物哀美学重构 意境营造

  • 将暴力批判转化为凋亡美学,用樱花飘落的速度计算道德衰变
  • 每个指控对应一件正在风化的器物:生锈的婚戒、霉变的承诺书等
  • 在文字间隙嵌入枯山水纹路,需要倾斜书页才能看见完整谴责

诗意转化: 原句:"你配不上任何认真对待你的人"
改写:"你掌心纹路终年积雪,任何真心降落都会凝结成危险冰棱。当春信候鸟第九次绕开你的纬度飞行,那些被冻伤的月光正在博物馆展出,标签上写着'未被签收的永恒'。"


终极艺术建议 若保留原文内核但提升艺术维度,可采用:

  1. 但丁式地狱结构:将诅咒细分为九层人格炼狱,每层匹配不同艺术媒介(诗歌/装置/行为艺术)
  2. 量子纠缠叙事:每个批判章节附带平行世界的救赎可能
  3. 有机文本生长:使用可擦写墨水,读者修改处会发芽微型诅咒植物

本文的真正艺术价值不在现有文本,而在于其揭示的改写可能性——就像罗丹的《地狱之门》,那些未完成的挣扎恰是最震撼的艺术形态。改写不是修正,而是为这座语言监狱打开一扇看见星空的窗。

留给我的却是干枯

· 2 min read

人们所创造的小说、电影、话剧、游戏,几乎所有的艺术作品中,都在强调爱。

爱是伟大,爱是无私,爱是忘我,爱是幸福的终点,爱是痛苦的解药,爱是生命的救赎。

我信这个。

这是存在于所有人们心中,最理想与动人的爱。

这确实可以成为救赎,但这是抽象的爱。

我们要如何从具体的人中去追寻这飘忽不定的抽象的爱?

具体的爱并非救赎。

抽象之爱之所以成为救赎正因为其不可得。

甚至很多人不了解爱,从来没有思考过什么是爱。

他们只在乎自己。

他们能提供的,也仅仅是溢出的自“爱”。

可如果每个人只都爱自己,谁来爱别人呢。

如果爱自己就能满足,我们为何要爱别人。

爱是一场行为艺术。

可是绝大多数人都拒绝参与。

有人沉溺于虚假的关系中,虚假地给予与索取。

有人只要快乐,他们不愿承担一点责任,他们只在乎自己。

有人接受他人的爱,却极少回应,好似一切理所应当。

有人视他人之爱为软弱的表现,轻视它,浪费它,还洋洋得意。

有人疯疯癫癫,像是在玩一场无所谓的游戏。

我去浇灌,我去滋养,我去安抚,

但留给我的却是干枯。

博客

· One min read

存放一些博客性的内容,大部分学习性内容见 笔记,但什么才是博客呢?

为什么我们必须站队?

· 7 min read

起初他们追杀共产主义者的时候,

我没有说话———因为我不是共产主义者;

接着他们追杀犹太人的时候,

我没有说话———因为我不是犹太人;

后来他们追杀工会成员的时候,

我没有说话———因为我不是工会成员;

此后他们追杀天主教徒的时候,

我没有说话———因为我是新教教徒;

最后他们奔我而来,

那时已经没有人能为我说话了。

前言

近期由于一些事情的影响,一直倾向于保持中立的我开始动摇了(或许从很早就开始动摇了,但近期的影响比较大)。一是我很喜欢的游戏《无期迷途》的节奏,二是重庆胖猫跳江事件,以及对一些其他事情的思考,如以色列对巴勒斯坦的屠杀等。

《无期迷途》曾经是个好游戏,制作可以称得上精良,尤其是一周年繁花前后的剧情更是二游教科书式的典范。无期作为一款男性向游戏凭借媚男发家,男玩家也一直是氪金的中坚力量,然而极端女权在女厕(微博)大肆带节奏,致使一些角色的立绘修改甚至下架,这甚至导致官方后期微博治游,小仙女更是爆出充了三张月卡、脚刹男性的可笑言论。随着流水的日渐高涨,游戏质量开始下滑、主线几个月不更新、语音不添加、反馈装死、逼氪、甚至开始逐渐变为女性向游戏,普通玩家的权益开始得不到保障。玩家也意识到必须站队开冲才能保障自己的权益,直到昨天官方群管理带头冲锋,官方凌晨才发布了道歉信,然而还是偏向集美们,因此我不得不卸载这个游戏,不得不被迫站队,后续各类冲锋玩家也相继放弃。

重庆胖猫跳江事件则更是重量级,塔下场定性为正常恋爱,胖猫姐涉嫌造谣,整篇通告看得我心头一凉,的确是句句属实,可是避重就轻春秋笔法,这也引起了大量网友和捞女们的进一步对立。不同于二游的小圈子,男女对立是个庞大的圈子,与每个男性都息息相关,各类彩礼事件和不公平对待事件都表明的塔的偏向性,因此也被戏称为比萨斜塔。因此我们需要站队,只有声音足够大塔才不能装死。

中立有没有错?

你可以对政治毫无兴趣,可你必须确信: 政治对你却太有兴趣,政治绝不会因为你对政治不关心而放过你。所以,假如你不想作戏台上一具可怜的傀僵,给别人随心所欲地推来推去,你就别无选择,必须关心政治。这其实就是关心你自己。 ——《新华日报》1945 年 9 月 11 日。

马克思说过,社会是人类关系的总和,现代社会想要独善其身几乎成为不可能的事情。暂时性地选择中立或许是一种自我保护,让我们免于争议与冲突,但是长期的逃避会让我们失去改变和影响社会的能力,或者说让我们忽略我们曾拥有这种能力。正如开篇所描述的,等到事情无法挽回的地步,才发现自己早已无法发声。个人的选择和行为确是微不足道,但都在以某种方式塑造世界。长期的中立或许会导致麻木,因此我认为即使是对外表现为中立,内心也必须站队,明确自己的底线,才能维护自己和相似的人应有的权益不被蚕食。

站队是集体主义吗?

站队并不与集体主义直接相关,集体主义强调团队利益高于个人利益,而站队则主要是为了维护个人利益,在这过程中可能会形成一个暂时的集体。个人主义者也可以通过站队的力量来不断接近自己最终的目标。

站队是加速主义吗?

从某种程度上站队可以认为是一种加速主义,单个人的力量是有限的,但是可以通过站队来聚集力量,从而加速某些变化。这当然存在一定的风险,譬如微博已经沦为女厕,

站队还是站对?

是非对错往往是个人主观的判断。

深度学习中的配置系统

· 13 min read

配置系统是很多深度学习套件和算法库的重要组件,一个优秀的配置系统可以方便用户修改训练所需的超参数、管理实验并且增强项目可读性等。配置系统不光可以用于大型的算法库,也可以用于个人进行快速实验和迭代。然而当前深度学习社区的配置系统都或多或少存在一些 不够方便 的地方,本文将会介绍一些已有的配置系统,并尝试对其进行改进。

YACS

YACS 是一个轻量级的配置系统,Detectron 和 maskrcnn-benchmark 便是使用的 YACS,其使用可读性较好的 YAML 文件,格式为:

MODEL:
TYPE: mask_rcnn
CONV_BODY: FPN.add_fpn_ResNet50_conv5_body
NUM_CLASSES: 81
FASTER_RCNN: True
MASK_ON: True

该配置的第二行的 TYPE 为所要实例化的类,解析配置文件时若某个字典中包含 TYPE,则表示应该将其实例化。

OpenMMLab

OpenMMLab 系列的算法库提供了注册器 (Registry) 来维护了一个字符串到 类或函数的全局映射,这样便可以更加方便的通过字符串寻找到对应的类,使用方式为:

import torch.nn as nn
from mmengine import Registry
ACTIVATION = Registry('activation')

# 使用注册器管理模块
@ACTIVATION.register_module()
class Sigmoid(nn.Module):
def __init__(self):
super().__init__()

def forward(self, x):
print('call Sigmoid.forward')
return x

然而随着 codebase 规模的不断增大,每次运行时会引入额外的注册开销和无关依赖。

OpenMMLab 系列的算法库支持 YAML,JSON 和 python 作为配置文件,其中 json 和 python 的可读性奇差:

train_pipeline = [
dict(type='LoadImageFromFile', backend_args={{_base_.backend_args}}),
dict(type='LoadAnnotations', with_bbox=True, with_mask=True),
dict(type='RandomFlip', prob=0.5),
dict(
type='RandomChoice',
transforms=[[
dict(
type='RandomChoiceResize',
scales=[(480, 1333), (512, 1333), (544, 1333), (576, 1333),
(608, 1333), (640, 1333), (672, 1333), (704, 1333),
(736, 1333), (768, 1333), (800, 1333)],
keep_ratio=True)
],
[
dict(
type='RandomChoiceResize',
scales=[(400, 1333), (500, 1333), (600, 1333)],
keep_ratio=True),
dict(
type='RandomCrop',
crop_type='absolute_range',
crop_size=(384, 600),
allow_negative_crop=True),
]]),
dict(type='PackDetInputs')
]

PaddleDetection

PaddleDectetion 的配置系统较为复杂且精妙,其只维护了一个全局的注册字典,配置文件格式如下:

metric: COCO
num_classes: 80

TrainDataset:
!COCODataSet
image_dir: train2017
anno_path: annotations/instances_train2017.json
dataset_dir: dataset/coco
data_fields: ['image', 'gt_bbox', 'gt_class', 'is_crowd']

不同于前面两种配置系统使用 type,PaddleDetection 使用 yaml 的 representer 实现通过 !{} 来获取 python 对象。此外 PaddleDectection 在实现类时添加了类属性—— __shared____inject__,如:

from ppdet.core.workspace import register

@register
class BBoxPostProcess(object):
__shared__ = ['num_classes']
__inject__ = ['decode', 'nms']

def __init__(self, num_classes=80, decode=None, nms=None):
# 省略内容
pass

def __call__(self, head_out, rois, im_shape, scale_factor):
# 省略内容
pass

__shared__ 表示这些参数是全局共享的,__inject__ 表示这些参数是全局字典中已经封装好的模块,即实现了配置文件的嵌套,可谓十分之精妙了。

然而该配置系统过于定制化,不能很好的适应各种情况。

PaddleSeg

PaddleSeg 中的配置格式并无特别之处,与前文相同,但是其解析、检查和构建对象都对进行了 硬编码,这样的好处是编写代码时有补全。

LazyConfig

由于 YAML 和 PYTHON 配置文件一直被诟病不能跳转和补全,尤其是 PYTHON,可读性更是一坨。detectron2 提出了

LazyConfig,形式如:

import torch.nn as nnfrom detectron2.config import LazyCall, instantiate
# 通过LazyCall创建一个config对象
conv_config = LazyCall(nn.Conv2d)(in_channels=16, out_channels=16, kernel_size=3, stride=1, padding=1)
conv = instantiate(conv_config)

这里的 conv_config 将类对象和实例化所需参数都存储起来,等到需要实例化时才进行实例化,因此叫做 Lazy。这样编写配置文件时可以为对应的类提供补全和跳转,并且通过该方式可以直接舍弃注册器,可谓是一箭双雕。

然而该配置系统不能对类的参数进行补全和检查。

OpenMMLab python config

同样为了支持跳转和补全,OpenMMLab 提供了新式的 python 配置文件——即将 type 后的字符串变为类,可读性仍然是一坨,远没有 detectron2 优雅。

ExCore

为了一定程度上解决上述问题,我开发了 ExCore,主要用于个人的实验。ExCore 使用 TOML 作为配置文件,并且借助 LSP(language server protocol)的力量在一定程度上弥补了配置文件和 python 文件之间的鸿沟——即为 TOML 配置文件提供了补全、跳转、查看文档、参数检查等功能,先看几个动图再介绍。

补全,查看文档和参数检查:

config.gif

可以看到会有类名及其参数的补全以及查看对应类的 docstring,并且会对 required 参数进行提示。

跳转:

to_class.gif

跳转以插件的形式实现,目前仅支持 NeoVim,见 excore.nvim

基本概念

本配置系统的核心前提是将配置文件中所要创建的对象分为三类—— 主要、 中间 和 孤立 对象。

  1. 主要 对象是指在训练中 直接 使用的对象,如模型、优化器等。ExCore 会创建并返回这些对象。
  2. 中间 对象是指在训练中 间接 使用的对象,如模型的主干、将要传入优化器的模型参数。
  3. 孤立 对象是指 python 内建对象,会在读取配置文件时直接解析,如 int, string, list, dict 等

配置文件格式

ExCore 扩展了 toml 文件的语法,引入了一些特殊的前缀字符 —— !@$ 和 & 以简化配置文件的定义过程,因此配置文件格式为:

size = 224

[TrainData.ImageNet]
&train_size = "size"
!transforms = ['RandomResize', 'Pad']
data_path = 'xxx'

[Transform.Pad]
&pad_size = "size"

[TestData.ImageNet]
!transforms = 'Normalize'
&test_size = "size"
data_path = 'xxx'

这里的 TrainDataTestData 是一个字典,其元素都是上文所提到的主要对象,可以称之为 主要 模块,主要 模块需要定义为 [PrimaryFields.ModuleName]PrimaryFields 是一些预先定义的字段, 这里即是 TrainData, TestData, ModuleName 即为注册的名称, 这里即为 ImageNetPad. 如此便可以去除 type。

第四行的 & 表示引用,即 train_size 的值最终会被解析为 224。

第五行的 ! 表示该参数并不是 python built-in 对象,即类似于配置嵌套,注意这里的 RandomResize, 如果其没有参数,则可以不在配置文件中声明。等价的 yaml 文件为:

TrainData:
type: ImageNet
train_size: 224
data_path: 'xxx'
transforms:
- type: RandomResize
pad_size: 224
- type: Pad

TestData:
type: ImageNet
test_size: 224
data_path: 'xxx'
transforms:
- type: Normalize

此外 @ 用于表示共享模块,如:

[Model.FCN]
@backbone = "ResNet"

[Model.SegNet]
@backbone = "ResNet"

[ResNet]
layers = 50
in_channel = 3

这里的 FCN 和 SegNet 的 bacbone 将会是同一个对象。

$ 则用于表示不需要实例化,这是一个类,如:

[Model.ResNet]  
$block = "BasicBlock"
layers = 50
in_channel = 3

该功能上文介绍的注册系统是无法实现的。

$ 除了用于参数名之前,还可以用于参数值之前,后跟 PrimaryFields,如:

__base__ = ["./block.toml"]

[Model.ResNet]
!block="$Block"

该功能用于跨文件使用,即会在所有加载的配置文件中找到 Block 字段,将其值传入。

同样为了方便使用,本配置系统提供了一套参数级别的 HOOk,如最常见的将模型的参数传入优化器,格式为:

[Optimizer.AdamW]  
@params = "$Model.parameters()"
weight_decay = 0.01

这也是上文所有配置系统所做不到的。

如果你想调用类方法或静态方法,可以:

[Model.XXX]  
$backbone = "A.from_pretained()"

也可以获取属性,就像写 python 一样即可,但是不可传入参数:

[Model.XXX]  
@channel = "$Block.last_conv.out_channels"

借助该功能我们可以注册模块,如:

[Model.ResNet]  
$activation = "torch.nn.ReLU"

更复杂的情况可以继承 ExCore 提供的 hook 基类:

[Optimizer.SGD]
@params = "$Model@BnWeightDecayHook"
lr = 0.05
momentum = 0.9
weight_decay = 0.0001

[ConfigHook.BnWeightDecayHook]
weight_decay = 0.0001
bn_weight_decay = false
enabled = true

使用 @BnWeightDecayHook 来将 $Model 传入 hook 中

Hook 类定义:

from excore.engine.hook import ConfigArgumentHook
from . import HOOKS

@HOOKS.register()
class BnWeightDecayHook(ConfigArgumentHook):
def __init__(self, node, enabled: bool, bn_weight_decay: bool, weight_decay: float):
super().__init__(node, enabled)
self.bn_weight_decay = bn_weight_decay
self.weight_decay = weight_decay

def hook(self):
model = self.node()
if self.bn_weight_decay:
optim_params = model.parameters()
else:
p_bn = [p for n, p in model.named_parameters() if "bn" in n]
p_non_bn = [p for n, p in model.named_parameters() if "bn" not in n]
optim_params = [
{"params": p_bn, "weight_decay": 0},
{"params": p_non_bn, "weight_decay": self.weight_decay},
]
return optim_params

配置文件补全、检查和查看文档

实际上是利用 json_schema 曲线救国,ExCore 对所有注册的类进行静态分析和类型推断,因此对 type-hinting 的要求比较高,如果没有编写 type-hinting,则会根据默认值进行推断。将结果缓存在本地,由 TOML 对应的 LSP 完成该功能。

跳转

在上一步静态分析的过程中,将类名及其文件地址缓存在本地,针对不同的编辑器实现插件。

简单的 API

不同于上述的配置系统需要额外的构建系统,ExCore 仅仅提供两个 API 用于加载、解析配置文件以及实例化:

from excore import config
lazy_cfg = config.load('xxx.toml')
modules, run_info = config.build_all(lazy_cfg)

这里的 modules 是一个 wrapper,可以通过预先定义的主要字段来获取构建的模块,如要获得 Model,只需要:

model = modules.Model

但是目前还不支持补全,后续可以根据主要字段自动生成一个类专门用作 type-hinting,例如:

class ModuleWrapper:
Model: Any
modules: ModuleWrapper
model = modules.Model

run_info 中存储的则是之前提到的 孤立对象,是一个字典。

LazyConfig

ExCore 同样支持 python 形式的配置文件,与 detectron2 基本一致,不过实例化时只需调用 __call__ 方法即可,如下:

from excore.config.model import ModuleNode  
from xxx import Module

cfg = ModuleNode(Module).add_params(arg1=1, arg2=2)
model = cfg() # support instantiate recursively
# or
model = ModuleNode(Module)(arg1=1, arg2=2)

实际上上文中 lazy_config = config.load('xxx.toml') 就是将配置文件解析为了一系列 LazyConfig 对象,可以对其进行一些操作(如通过命令行替换参数),再使用 build_all 进行实例化。

LazyRegistry

Registry 在当今看来并不是一个很好的设计,但出于某些考虑和原因,我实现了一种我称之为 LazyRegistry 的注册机制,同样能够避免冗余的注册和依赖。ExCore 中的 Registry 更多起的是一种标记的作用,用于实现配置文件的补全、提示和跳转。后面有空再详细介绍。

Vim 中的消息通知

· One min read

Vim 中的 notification

原版 Vim 中,消息通知都是在左下角的位置,只有一行的显示空间,兼具了消息通知、命令行、搜索等功能,但是这种方式会有一些缺陷:

  1. 显示范围太小,不能完全展示太长的消息;
  2. 位置靠下,可能不容易注意;
  3. 不美观。

notify.nvim

该插件将 vim 中的提示以 pop-up 窗口的形式展示,容易引起注意且较为美观。然而该插件还是存在一些问题:

理想的窗口管理器

· 6 min read

从年后发现被魔改的 DWM 到现在已经有半年多了,Linux 也成为了我日常使用的主力,但是实际使用过程中还是有一些不太舒服的地方,管中窥豹,这也是当前大部分平铺式窗口管理器的痛点。

什么是 DWM

DWM 是一个轻量级的平铺式窗口管理器,源码只有两三千行,专为 X Window System 设计(然而 X 日渐式微,终归会被 wayland 取代),可以动态地全自动平铺窗口,即所有的窗口都不重叠。并拥有大量的快捷键,可以只通过键盘在不同的窗口、工作区(workspace)之间移动、缩放窗口、在屏幕内移动窗口、移动窗口到其他工作区等。

除了平铺,DWM 同样支持浮动窗口,可以将窗口从平铺中释放出来,恢复堆叠式的窗口管理。

为什么需要平铺式窗口管理

Windows 和 MacOS 都是基于堆叠的窗口管理,其操作逻辑简单,主要交互工具为鼠标,可以进行很细致的缩放和移动,但是堆叠的特性代表这窗口之间经常会相互遮挡,这阻碍了信息获取的流畅度,所以现在这类窗口管理器也都支持转换为平铺式的窗口,用于快速无遮挡地获取和对比信息。

堆叠式窗口的缺陷

总结一下其可能的缺陷(是否是缺陷还是要看具体的使用需求):

  1. 窗口之间相互遮挡,获取信息不便;
  2. 可能会需要频繁切换鼠标和键盘。

而平铺式窗口可以一定程度上解决这些问题,其有以下优点:

  1. 窗口管理器自动排布所有窗口,确保不会重叠;
  2. 设置工作区,用以缓解平铺窗口占用过大的问题,也更方便管理窗口;
  3. 完全支持键盘操作;
  4. 有序,更好看。

现今平铺式窗口管理的缺陷

大部分平铺式窗口管理器的默认行为都是铺满整个屏幕,并且不允许用户调整大小,一旦调整就会变为浮动窗口而独立出来;通常同时会设置一个主工作区域,即这个地方的窗口大小不变,新添加的窗口只会在其他地方排布,导致窗口较多时,一个窗口很大,其他窗口都很小。

这就引出来几个问题:

  1. 只需要一个窗口时,占满整个屏幕可能会分散注意力,尤其是现在屏幕尺寸越来越大的情况;
  2. 某些应用打开时不应该铺满整个屏幕;
  3. 每个窗口都应该有各自合适的大小。

目前也有一些窗口模式可以缓解这些问题,如网格排布。

理想中的窗口管理器

引入

窗口大小合适更重要,还是窗口位置合适更重要?

就个人而言,窗口大小合适是更重要的,因此窗口管理器应该能为不同的应用分配不同的窗口大小,而不是直接铺满屏幕;而窗口位置应该尽量靠近屏幕中心分布。

但是显然,预设的尺寸是不能满足所有情况的,因此需要支持在平铺模式下调整窗口大小,并且自动地进行排布,当然,自由地分配窗口位置也应该是一个需求。

总结一下,理想中的窗口管理器应该具有以下优点:

  1. 为不同的应用窗口分配合适的大小;
  2. 窗口集中在屏幕中央;
  3. 允许在平铺模式下自由地调整窗口大小;
  4. 自由选择窗口位置。

难点

  1. 窗口位置分配的策略;
  2. 窗口是否允许溢出屏幕;

痛点

实际上该方式在窗口较多、大小各不相同时还是很混乱,因此可能窗口管理器本就应该在少窗口的模式下使用,如四个。

2024-02-27 更新:最近发现了一款新的基于 wayland 的窗口管理器——niri,其主要特性是可以在横向溢出,并可以滚动,有机会可以尝试。

2024-05-25 更新:全面转向 Hyprland 快三个月了,总体来说会更好看一些,并且社区也在不断更新,有很多插件。bug 稍微有点多但都可以接受。

NeoVIM使用指南

· One min read

最近刚从 LunarVim 换到 AstroNvim,故在此记录一下 NeoVIM 的相关使用和插件配件等。