From 0bda6b0c1cd632c49a9ec508c58d08fca1adf0e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B3=E6=9D=89=E6=9D=89?= <467638484@qq.com> Date: Sun, 22 Dec 2024 23:35:23 +0800 Subject: [PATCH] update about --- content/about/index.md | 4 + public/about/index.html | 120 +++++++++++++++--- .../index.html" | 4 +- .../index.html" | 4 +- .../index.html" | 4 +- public/articles/index.html | 6 +- .../index.html" | 4 +- .../index.html" | 4 +- .../index.html" | 4 +- .../index.html" | 4 +- .../index.html" | 12 +- .../index.html" | 4 +- .../index.html" | 4 +- .../index.html" | 4 +- .../index.html" | 6 +- .../index.html" | 4 +- .../index.html" | 4 +- .../index.html" | 4 +- .../index.html" | 4 +- .../index.html" | 4 +- .../index.html" | 2 +- public/guide/index.html | 6 +- public/index.html | 2 +- public/index.json | 2 +- public/roadmap/index.html | 4 +- public/tags/ai/index.html | 2 +- public/tags/llm/index.html | 2 +- .../index.html" | 2 +- .../index.html" | 2 +- 29 files changed, 159 insertions(+), 73 deletions(-) diff --git a/content/about/index.md b/content/about/index.md index 2dc9b59..5bd5d69 100644 --- a/content/about/index.md +++ b/content/about/index.md @@ -89,6 +89,10 @@ Studying at School of Electronic and Information Engineering, majoring in commun I am a contributor to many open source projects on GitHub, which are shown below. +{{< github repo="vllm-project/vllm" >}} + +
+ {{< github repo="ggerganov/llama.cpp" >}}
diff --git a/public/about/index.html b/public/about/index.html index c1215ed..6558e31 100644 --- a/public/about/index.html +++ b/public/about/index.html @@ -106,7 +106,7 @@ "mainEntityOfPage": "true", - "wordCount": "582" + "wordCount": "596" }] @@ -600,7 +600,7 @@

- ·582 words· + ·596 words· @@ -1046,7 +1046,7 @@

Open Source Contribution

I am a contributor to many open source projects on GitHub, which are shown below.

- +
@@ -1064,20 +1064,20 @@

Open Source Contribution
- ggerganov/llama.cpp + vllm-project/vllm

- LLM inference in C/C++ + A high-throughput and memory-efficient inference and serving engine for LLMs

+ style="background-color: #3572A5">
- C++ + Python
@@ -1090,7 +1090,7 @@

Open Source Contribution
- 68755 + 32277
@@ -1103,14 +1103,14 @@

Open Source Contribution
- 9872 + 4917


- +
@@ -1144,20 +1144,20 @@

Open Source Contribution
- 2noise/ChatTTS + ggerganov/llama.cpp

- A generative speech model for daily dialogue. + LLM inference in C/C++

+ style="background-color: #f34b7d">
- Python + C++
@@ -1170,7 +1170,7 @@

Open Source Contribution
- 32444 + 68201
@@ -1183,14 +1183,14 @@

Open Source Contribution
- 3523 + 9780

+
+ +
+ +
+ + + + + + + + + + +
+ 2noise/ChatTTS +
+
+ +

+ A generative speech model for daily dialogue. +

+ +
+ + +
+ Python +
+ + + + + + + + + + +
+ 32525 +
+ + + + + + + + + + +
+ 3530 +
+ +
+ +
+ +

@@ -1553,8 +1633,8 @@

Contact me diff --git "a/public/articles/ascend-aclnn-\347\256\227\345\255\220\345\274\200\345\217\221\345\205\245\351\227\250/index.html" "b/public/articles/ascend-aclnn-\347\256\227\345\255\220\345\274\200\345\217\221\345\205\245\351\227\250/index.html" index 0daa054..4f62060 100644 --- "a/public/articles/ascend-aclnn-\347\256\227\345\255\220\345\274\200\345\217\221\345\205\245\351\227\250/index.html" +++ "b/public/articles/ascend-aclnn-\347\256\227\345\255\220\345\274\200\345\217\221\345\205\245\351\227\250/index.html" @@ -2221,8 +2221,8 @@

Related

diff --git "a/public/articles/ascend-npu-\346\236\266\346\236\204-cann-\345\271\263\345\217\260\345\205\245\351\227\250\345\255\246\344\271\240/index.html" "b/public/articles/ascend-npu-\346\236\266\346\236\204-cann-\345\271\263\345\217\260\345\205\245\351\227\250\345\255\246\344\271\240/index.html" index 2046554..30c352d 100644 --- "a/public/articles/ascend-npu-\346\236\266\346\236\204-cann-\345\271\263\345\217\260\345\205\245\351\227\250\345\255\246\344\271\240/index.html" +++ "b/public/articles/ascend-npu-\346\236\266\346\236\204-cann-\345\271\263\345\217\260\345\205\245\351\227\250\345\255\246\344\271\240/index.html" @@ -1897,8 +1897,8 @@

Related

diff --git "a/public/articles/git-\345\256\236\350\267\265\346\241\210\344\276\213-\345\220\210\345\271\266\345\244\232\344\270\252\345\210\206\346\225\243\347\232\204-commit-\350\212\202\347\202\271/index.html" "b/public/articles/git-\345\256\236\350\267\265\346\241\210\344\276\213-\345\220\210\345\271\266\345\244\232\344\270\252\345\210\206\346\225\243\347\232\204-commit-\350\212\202\347\202\271/index.html" index 62f1fbf..8b8ffe2 100644 --- "a/public/articles/git-\345\256\236\350\267\265\346\241\210\344\276\213-\345\220\210\345\271\266\345\244\232\344\270\252\345\210\206\346\225\243\347\232\204-commit-\350\212\202\347\202\271/index.html" +++ "b/public/articles/git-\345\256\236\350\267\265\346\241\210\344\276\213-\345\220\210\345\271\266\345\244\232\344\270\252\345\210\206\346\225\243\347\232\204-commit-\350\212\202\347\202\271/index.html" @@ -1536,8 +1536,8 @@

Related

diff --git a/public/articles/index.html b/public/articles/index.html index 914aa45..3652126 100644 --- a/public/articles/index.html +++ b/public/articles/index.html @@ -542,8 +542,8 @@

Arti @@ -956,7 +956,7 @@

- ·1146 words + ·1157 words diff --git "a/public/articles/maven-\351\241\271\347\233\256\347\274\226\350\257\221\346\212\245\351\224\231\350\247\243\345\206\263\346\226\271\346\263\225/index.html" "b/public/articles/maven-\351\241\271\347\233\256\347\274\226\350\257\221\346\212\245\351\224\231\350\247\243\345\206\263\346\226\271\346\263\225/index.html" index 9685faa..5cad418 100644 --- "a/public/articles/maven-\351\241\271\347\233\256\347\274\226\350\257\221\346\212\245\351\224\231\350\247\243\345\206\263\346\226\271\346\263\225/index.html" +++ "b/public/articles/maven-\351\241\271\347\233\256\347\274\226\350\257\221\346\212\245\351\224\231\350\247\243\345\206\263\346\226\271\346\263\225/index.html" @@ -1595,8 +1595,8 @@

Related

diff --git "a/public/articles/nvidia-gpu-\346\236\266\346\236\204-cuda-\345\271\263\345\217\260\345\205\245\351\227\250\345\255\246\344\271\240/index.html" "b/public/articles/nvidia-gpu-\346\236\266\346\236\204-cuda-\345\271\263\345\217\260\345\205\245\351\227\250\345\255\246\344\271\240/index.html" index 5402384..d174b21 100644 --- "a/public/articles/nvidia-gpu-\346\236\266\346\236\204-cuda-\345\271\263\345\217\260\345\205\245\351\227\250\345\255\246\344\271\240/index.html" +++ "b/public/articles/nvidia-gpu-\346\236\266\346\236\204-cuda-\345\271\263\345\217\260\345\205\245\351\227\250\345\255\246\344\271\240/index.html" @@ -1958,8 +1958,8 @@

Related

diff --git "a/public/articles/\345\237\272\344\272\216-euleros-ascend-npu-\346\220\255\345\273\272-pytorch-\350\277\234\347\250\213\345\274\200\345\217\221\347\216\257\345\242\203/index.html" "b/public/articles/\345\237\272\344\272\216-euleros-ascend-npu-\346\220\255\345\273\272-pytorch-\350\277\234\347\250\213\345\274\200\345\217\221\347\216\257\345\242\203/index.html" index 217d406..07a239a 100644 --- "a/public/articles/\345\237\272\344\272\216-euleros-ascend-npu-\346\220\255\345\273\272-pytorch-\350\277\234\347\250\213\345\274\200\345\217\221\347\216\257\345\242\203/index.html" +++ "b/public/articles/\345\237\272\344\272\216-euleros-ascend-npu-\346\220\255\345\273\272-pytorch-\350\277\234\347\250\213\345\274\200\345\217\221\347\216\257\345\242\203/index.html" @@ -2163,8 +2163,8 @@

Related

diff --git "a/public/articles/\345\244\247\346\250\241\345\236\213-lora-\345\276\256\350\260\203\347\232\204\346\225\260\345\255\246\345\216\237\347\220\206/index.html" "b/public/articles/\345\244\247\346\250\241\345\236\213-lora-\345\276\256\350\260\203\347\232\204\346\225\260\345\255\246\345\216\237\347\220\206/index.html" index 78d8870..6eda79a 100644 --- "a/public/articles/\345\244\247\346\250\241\345\236\213-lora-\345\276\256\350\260\203\347\232\204\346\225\260\345\255\246\345\216\237\347\220\206/index.html" +++ "b/public/articles/\345\244\247\346\250\241\345\236\213-lora-\345\276\256\350\260\203\347\232\204\346\225\260\345\255\246\345\216\237\347\220\206/index.html" @@ -1964,8 +1964,8 @@

Related

diff --git "a/public/articles/\345\244\247\346\250\241\345\236\213\345\276\256\350\260\203\347\237\245\350\257\206\345\205\250\346\231\257/index.html" "b/public/articles/\345\244\247\346\250\241\345\236\213\345\276\256\350\260\203\347\237\245\350\257\206\345\205\250\346\231\257/index.html" index 9c29ea4..b3ce752 100644 --- "a/public/articles/\345\244\247\346\250\241\345\236\213\345\276\256\350\260\203\347\237\245\350\257\206\345\205\250\346\231\257/index.html" +++ "b/public/articles/\345\244\247\346\250\241\345\236\213\345\276\256\350\260\203\347\237\245\350\257\206\345\205\250\346\231\257/index.html" @@ -113,7 +113,7 @@ "keywords": ["AI","LLM","大模型微调","论文精读"], "mainEntityOfPage": "true", - "wordCount": "1146" + "wordCount": "1157" }] @@ -619,7 +619,7 @@

- ·1146 words· + ·1157 words· @@ -991,6 +991,8 @@

四、大模型微调的方法有哪些
  • 参数高效微调(Parameter-Efficient Fine-Tuning,PEFT):只对部分参数进行更新,训练速度快,消耗机器资源少。
  • 此外,还有一种不需要更新模型权重就可以完成微调的方法,叫做 In-Context Learning,通过在输入的 prompt 中提供与任务相关的上下文和例子,从而让模型能够更好地了理解我们的意图。

    +

    最新进展

    +

    在 OpenAI 最新的发布会中,还提出了一种叫做 RFT(Reinforcement Fine-Tuning) 的微调技术,能够以奖励驱动的方式不断完善大模型所掌握的知识,更多细节可以参考这篇文章:What Is OpenAI’s Reinforcement Fine-Tuning?

    4.1 FFT 的优缺点 @@ -1079,7 +1081,7 @@

    4.3 PEFT 的分类
  • Hybrid methods:根据实际情况,可以对上述方法进行组合,从而达到更好的效果。
  • 目前比较主流的几种参数高效微调方法包括:Prompt Tuning、Prefix Tuning、LoRA、QLoRA 等。

    -

    论文《Scaling Down to Scale Up: A Guide to Parameter-Efficient Fine-Tuning》中展示了各类参数高效微调方法及其所属的类别,如下所示:

    +

    论文《Scaling Down to Scale Up: A Guide to Parameter-Efficient Fine-Tuning》中展示了各类参数高效微调方法及其所属的类别,如下所示:

    1 @@ -2326,8 +2328,8 @@

    Related

    diff --git "a/public/articles/\345\257\214\347\210\270\347\210\270-\347\251\267\347\210\270\347\210\270\350\257\273\344\271\246\347\254\224\350\256\260-\350\264\242\345\225\206\346\225\231\350\202\262\345\220\257\350\222\231\344\271\213\344\275\234/index.html" "b/public/articles/\345\257\214\347\210\270\347\210\270-\347\251\267\347\210\270\347\210\270\350\257\273\344\271\246\347\254\224\350\256\260-\350\264\242\345\225\206\346\225\231\350\202\262\345\220\257\350\222\231\344\271\213\344\275\234/index.html" index 593c34d..dafb187 100644 --- "a/public/articles/\345\257\214\347\210\270\347\210\270-\347\251\267\347\210\270\347\210\270\350\257\273\344\271\246\347\254\224\350\256\260-\350\264\242\345\225\206\346\225\231\350\202\262\345\220\257\350\222\231\344\271\213\344\275\234/index.html" +++ "b/public/articles/\345\257\214\347\210\270\347\210\270-\347\251\267\347\210\270\347\210\270\350\257\273\344\271\246\347\254\224\350\256\260-\350\264\242\345\225\206\346\225\231\350\202\262\345\220\257\350\222\231\344\271\213\344\275\234/index.html" @@ -1181,8 +1181,8 @@

    三、心得体会 diff --git "a/public/articles/\345\260\217\345\262\233\347\273\217\346\265\216\345\255\246\350\257\273\344\271\246\347\254\224\350\256\260-\346\267\261\345\205\245\346\265\205\345\207\272\347\273\217\346\265\216\345\255\246\345\216\237\347\220\206/index.html" "b/public/articles/\345\260\217\345\262\233\347\273\217\346\265\216\345\255\246\350\257\273\344\271\246\347\254\224\350\256\260-\346\267\261\345\205\245\346\265\205\345\207\272\347\273\217\346\265\216\345\255\246\345\216\237\347\220\206/index.html" index 15b767a..ab05d79 100644 --- "a/public/articles/\345\260\217\345\262\233\347\273\217\346\265\216\345\255\246\350\257\273\344\271\246\347\254\224\350\256\260-\346\267\261\345\205\245\346\265\205\345\207\272\347\273\217\346\265\216\345\255\246\345\216\237\347\220\206/index.html" +++ "b/public/articles/\345\260\217\345\262\233\347\273\217\346\265\216\345\255\246\350\257\273\344\271\246\347\254\224\350\256\260-\346\267\261\345\205\245\346\265\205\345\207\272\347\273\217\346\265\216\345\255\246\345\216\237\347\220\206/index.html" @@ -1630,8 +1630,8 @@

    Related

    diff --git "a/public/articles/\346\210\221\346\234\211\350\207\252\345\267\261\347\232\204\345\256\207\345\256\231\350\257\273\344\271\246\347\254\224\350\256\260-\345\271\264\350\275\273\344\272\272\347\232\204\350\201\214\345\234\272\347\224\237\345\255\230\344\271\213\351\201\223/index.html" "b/public/articles/\346\210\221\346\234\211\350\207\252\345\267\261\347\232\204\345\256\207\345\256\231\350\257\273\344\271\246\347\254\224\350\256\260-\345\271\264\350\275\273\344\272\272\347\232\204\350\201\214\345\234\272\347\224\237\345\255\230\344\271\213\351\201\223/index.html" index 6ad24f6..e33c732 100644 --- "a/public/articles/\346\210\221\346\234\211\350\207\252\345\267\261\347\232\204\345\256\207\345\256\231\350\257\273\344\271\246\347\254\224\350\256\260-\345\271\264\350\275\273\344\272\272\347\232\204\350\201\214\345\234\272\347\224\237\345\255\230\344\271\213\351\201\223/index.html" +++ "b/public/articles/\346\210\221\346\234\211\350\207\252\345\267\261\347\232\204\345\256\207\345\256\231\350\257\273\344\271\246\347\254\224\350\256\260-\345\271\264\350\275\273\344\272\272\347\232\204\350\201\214\345\234\272\347\224\237\345\255\230\344\271\213\351\201\223/index.html" @@ -1683,8 +1683,8 @@

    Related

    diff --git "a/public/articles/\346\210\221\347\232\204\346\212\200\346\234\257\345\215\232\345\256\242-\346\200\273\350\247\210/index.html" "b/public/articles/\346\210\221\347\232\204\346\212\200\346\234\257\345\215\232\345\256\242-\346\200\273\350\247\210/index.html" index 092e5ec..85e05d1 100644 --- "a/public/articles/\346\210\221\347\232\204\346\212\200\346\234\257\345\215\232\345\256\242-\346\200\273\350\247\210/index.html" +++ "b/public/articles/\346\210\221\347\232\204\346\212\200\346\234\257\345\215\232\345\256\242-\346\200\273\350\247\210/index.html" @@ -1671,7 +1671,7 @@

    大模型微调
    - ·1146 words + ·1157 words @@ -2278,8 +2278,8 @@

    Git diff --git "a/public/articles/\346\210\221\347\232\204\350\257\273\344\271\246\347\254\224\350\256\260-\346\200\273\350\247\210/index.html" "b/public/articles/\346\210\221\347\232\204\350\257\273\344\271\246\347\254\224\350\256\260-\346\200\273\350\247\210/index.html" index e23416c..803f261 100644 --- "a/public/articles/\346\210\221\347\232\204\350\257\273\344\271\246\347\254\224\350\256\260-\346\200\273\350\247\210/index.html" +++ "b/public/articles/\346\210\221\347\232\204\350\257\273\344\271\246\347\254\224\350\256\260-\346\200\273\350\247\210/index.html" @@ -2006,8 +2006,8 @@

    个人健康 diff --git "a/public/articles/\346\227\251\350\265\267\347\232\204\345\245\207\350\277\271\350\257\273\344\271\246\347\254\224\350\256\260-\345\210\266\345\256\232\344\275\240\347\232\204\346\227\251\350\265\267\350\256\241\345\210\222/index.html" "b/public/articles/\346\227\251\350\265\267\347\232\204\345\245\207\350\277\271\350\257\273\344\271\246\347\254\224\350\256\260-\345\210\266\345\256\232\344\275\240\347\232\204\346\227\251\350\265\267\350\256\241\345\210\222/index.html" index 3256582..278edbf 100644 --- "a/public/articles/\346\227\251\350\265\267\347\232\204\345\245\207\350\277\271\350\257\273\344\271\246\347\254\224\350\256\260-\345\210\266\345\256\232\344\275\240\347\232\204\346\227\251\350\265\267\350\256\241\345\210\222/index.html" +++ "b/public/articles/\346\227\251\350\265\267\347\232\204\345\245\207\350\277\271\350\257\273\344\271\246\347\254\224\350\256\260-\345\210\266\345\256\232\344\275\240\347\232\204\346\227\251\350\265\267\350\256\241\345\210\222/index.html" @@ -1493,8 +1493,8 @@

    Related

    diff --git "a/public/articles/\347\235\241\347\234\240\351\235\251\345\221\275\350\257\273\344\271\246\347\254\224\350\256\260-\344\272\206\350\247\243\344\275\240\347\232\204\346\230\274\345\244\234\350\212\202\345\276\213/index.html" "b/public/articles/\347\235\241\347\234\240\351\235\251\345\221\275\350\257\273\344\271\246\347\254\224\350\256\260-\344\272\206\350\247\243\344\275\240\347\232\204\346\230\274\345\244\234\350\212\202\345\276\213/index.html" index 791a2e5..b65f0c8 100644 --- "a/public/articles/\347\235\241\347\234\240\351\235\251\345\221\275\350\257\273\344\271\246\347\254\224\350\256\260-\344\272\206\350\247\243\344\275\240\347\232\204\346\230\274\345\244\234\350\212\202\345\276\213/index.html" +++ "b/public/articles/\347\235\241\347\234\240\351\235\251\345\221\275\350\257\273\344\271\246\347\254\224\350\256\260-\344\272\206\350\247\243\344\275\240\347\232\204\346\230\274\345\244\234\350\212\202\345\276\213/index.html" @@ -1707,8 +1707,8 @@

    Related

    diff --git "a/public/articles/\350\242\253\350\256\250\345\216\214\347\232\204\345\213\207\346\260\224\350\257\273\344\271\246\347\254\224\350\256\260-\346\216\245\347\272\263\350\207\252\346\210\221\345\213\207\346\225\242\345\234\260\346\204\237\345\217\227\347\224\237\346\264\273/index.html" "b/public/articles/\350\242\253\350\256\250\345\216\214\347\232\204\345\213\207\346\260\224\350\257\273\344\271\246\347\254\224\350\256\260-\346\216\245\347\272\263\350\207\252\346\210\221\345\213\207\346\225\242\345\234\260\346\204\237\345\217\227\347\224\237\346\264\273/index.html" index 7e76862..9083221 100644 --- "a/public/articles/\350\242\253\350\256\250\345\216\214\347\232\204\345\213\207\346\260\224\350\257\273\344\271\246\347\254\224\350\256\260-\346\216\245\347\272\263\350\207\252\346\210\221\345\213\207\346\225\242\345\234\260\346\204\237\345\217\227\347\224\237\346\264\273/index.html" +++ "b/public/articles/\350\242\253\350\256\250\345\216\214\347\232\204\345\213\207\346\260\224\350\257\273\344\271\246\347\254\224\350\256\260-\346\216\245\347\272\263\350\207\252\346\210\221\345\213\207\346\225\242\345\234\260\346\204\237\345\217\227\347\224\237\346\264\273/index.html" @@ -1731,8 +1731,8 @@

    Related

    diff --git "a/public/articles/\350\256\244\347\237\245\350\247\211\351\206\222\350\257\273\344\271\246\347\254\224\350\256\260-\345\274\200\345\220\257\345\277\203\346\231\272\346\216\214\346\216\247\350\207\252\345\267\261\347\232\204\347\224\237\346\264\273/index.html" "b/public/articles/\350\256\244\347\237\245\350\247\211\351\206\222\350\257\273\344\271\246\347\254\224\350\256\260-\345\274\200\345\220\257\345\277\203\346\231\272\346\216\214\346\216\247\350\207\252\345\267\261\347\232\204\347\224\237\346\264\273/index.html" index b36b523..b9e084e 100644 --- "a/public/articles/\350\256\244\347\237\245\350\247\211\351\206\222\350\257\273\344\271\246\347\254\224\350\256\260-\345\274\200\345\220\257\345\277\203\346\231\272\346\216\214\346\216\247\350\207\252\345\267\261\347\232\204\347\224\237\346\264\273/index.html" +++ "b/public/articles/\350\256\244\347\237\245\350\247\211\351\206\222\350\257\273\344\271\246\347\254\224\350\256\260-\345\274\200\345\220\257\345\277\203\346\231\272\346\216\214\346\216\247\350\207\252\345\267\261\347\232\204\347\224\237\346\264\273/index.html" @@ -1403,8 +1403,8 @@

    Related

    diff --git "a/public/categories/\350\256\241\347\256\227\346\234\272/index.html" "b/public/categories/\350\256\241\347\256\227\346\234\272/index.html" index 12e65a4..bf896d8 100644 --- "a/public/categories/\350\256\241\347\256\227\346\234\272/index.html" +++ "b/public/categories/\350\256\241\347\256\227\346\234\272/index.html" @@ -628,7 +628,7 @@

    - ·1146 words + ·1157 words diff --git a/public/guide/index.html b/public/guide/index.html index e99e582..6094976 100644 --- a/public/guide/index.html +++ b/public/guide/index.html @@ -897,7 +897,7 @@

    Who am I ?
    - ·582 words + ·596 words @@ -1453,8 +1453,8 @@

    Roadmap diff --git a/public/index.html b/public/index.html index 3f33339..f3a57b4 100644 --- a/public/index.html +++ b/public/index.html @@ -988,7 +988,7 @@

    Recent

    - ·1146 words + ·1157 words diff --git a/public/index.json b/public/index.json index a2ca79a..8c709cd 100644 --- a/public/index.json +++ b/public/index.json @@ -1,2 +1,2 @@ -[{"content":" 一、关于本书 # 《小岛经济学》 经济学 作者:彼得·希夫 / 安德鲁·希夫 简介:本书通过将国家比作一个“小岛”,使用“鱼”、“渔网”以及“鱼邦储备券”等概念,形象地解释了经济学中的诸多概念,通俗易懂地揭示了经济学的运行原理。作者对“通货膨胀”、“信贷危机”以及“房地产泡沫”等经济现象进行了深入的分析和解释,清晰地点明了当今社会经济所存在的问题,丰富了我对于世界的认知,是一本非常适合非专业人士入门的经济学读物。 二、内容分享 # 2.1 小岛经济学 # 在一个小岛上,人们以捕鱼为生……\n一开始,人们徒手进行捕鱼,当天捕的鱼只够当天吃,为了生存,人们必需日日不断地坚持捕鱼。\n随着第一张渔网(资本)的发明,提高了人们的生产力,从而可以将更多的鱼存储起来以备不时之需(储蓄),此外,人们也有了更多地时间追求更加丰富生活,亦或是进行创造性工作(进一步提高生产力)。\n“资本指的是一种设备,这种设备的建设和使用本身没有什么意义,其意义在于利用设备建设和制造其它需要的东西。”\n“努力使有限的资源产生最大的效益以尽可能满足人类的需求,这就是经济这一概念最简单的定义。工具、资本以及创新是实现这一目标的关键。”\n“经济增长的原因:找到了生产人类所需物品的更好方式。”\n个人的一点思考:如果我们将“捕鱼”理解为“重复性的工作”、将“经济发展”理解为“个人能力的提高”,那么当我们将这一套理论运用到实际生活中,我是否可以这样认为——如果我们每天的时间除了休息之外都被重复性的工作占满,没有额外的时间去学习提升自己,那么我们个人的成长速度将会是缓慢、甚至是停滞的?\n后来,随着经济规模不断扩大,小岛上出现了“政府”和“社会分工”。\n“在一个经济体中,如果人们有所分工,从事不同的商业和服务活动,其结果一定会比所有人都做同一种工作要好。分工增加产量,高产量又能提高生活水平。”\n“社会分工”带来了各种各样的就业岗位,人们需要掌握特定的技能才能找到对应的工作,企业也需要雇佣合适的员工来创造利润。\n“一名员工的具体价值主要取决于三个方面:需求(雇主是否需要这名员工所掌握的技能)、供应(有多少人具备这些技能)以及生产力(这名员工对那些任务的完成程度如何)。”\n“员工只要工作就有报酬,而企业主想得到回报只能等到企业赢利,它们的收益是对承担风险的回报,也是对成功整合稀有资源的回报。对利润的不懈追求推动了产品创新、企业发展与经济增长。正是这样的推动力提高了每个人的生活水平。”\n当个人的财富积累到了一定规模,人们为了让自己的鱼(钱)更加安全,于是就出现了“银行”。人们将自己的“鱼”存入银行,银行再用这笔钱对外发放贷款,创造收益的一部分用于支付储户的利息,剩下的则作为银行的赢利。其中,银行的“利率”是调控国家经济的关键。\n“贷款利率决定了银行能支付给储户的利息。存款利率是随存款年限递增的。存款年限越长,造成银行存款短缺的风险越低。因此,如果储户愿意长期储蓄,获得的利率也就较高,进行短期储蓄的储户所获得的利率则较低。”\n“高利率会抑制借贷,延缓经济增长。但同时,高利率也能刺激储蓄。最终,银行资产会再次积累起来,到那时利率又会下降。而较低的存款率表明人们更愿意将储蓄用于近期消费,因而抑制了为满足未来消费需求而进行的投资。”\n“美联储理论上是一家私有银行,但实际上却是美国财政部的延伸,它制定了美国的基准利率,影响着整个市场。美联储可以把利率降到足够低,这样企业和个人就更愿意借贷,以此刺激不景气的经济。低利率会刺激借贷、抑制储蓄,难怪美国已经由一个储蓄者的国家转变成了借款人的国家。”\n2.2 量化宽松政策 # 一个国家的经济并非是一直处于增长中的,当市场的调节能力失控,且政府又恰好做出了错误的决策时,就有可能造成经济的衰退。\n而对于“如何解决经济衰退”这个问题,在经济学上主要有两派观点:\n“凯恩斯学派”:在经济不景气时,政府可以通过扩大货币供给和财政赤字缓和自由市场的波动; “奥地利学派”:经济衰退是经济繁荣期所做出的错误决定的必要补偿,经济迅猛发展过后必然会有一个相应的衰退期。 其中,“凯恩斯学派”的观点就是我们常说的“量化宽松”政策。\n什么是“量化宽松”?\n“尽管很多人知道美国依赖量化宽松,但很少有人真正看透其本质:向金融市场注入新的资金,以推动价格上涨。实际上,量化宽松不过是通货膨胀的一种委婉表达,它也成为美联储将政府债务货币化的隐秘手段。但想用量化宽松政策来修复萎靡的经济,就好比企图用汽油去救火一样,汽油越多,火势就越旺。量化宽松政策是延长经济衰退的办法,而不是促进经济复兴的良方。”\n“通货膨胀不过是把财富从以某种货币储蓄的人手中转移到以同种货币负债的人那里。如果遇到恶性通货膨胀,存款就会变得一文不值,负债却一笔勾销。引发恶性通货膨胀以及随后经济灾难的原因都惊人地相似。这些国家都是通过降低货币价值偿还巨额外债,结果,本国的人民陷入了赤贫之中。”\n由此可见,“量化宽松”只是一种治标不治本的办法,它只想通过刺激需求侧来促进经济的发展,却没考虑到供给侧的技术创新才是生产力不断进步、经济得以长期稳定发展的根本。如果真实的生产力没有提高,只是单纯地靠发放更多的钱,那么随之而来的是物价也会水涨船高。并且由于过去的资产贬值,人们会变得越来越不愿意储蓄,从短期来看可能可以促进消费,但站在长远的角度,国家会因为缺少足够的钱进行投资,从而延缓生产力的进步。\n“不断扩大货币供应量的做法以及政府看似无限的负债能力掩盖了一个事实——实际信贷是受有限储备制约的。”\n“我们的消费不能超过产能,我们的借款不能超出存款,至少不能长期这样。我们必须生产出什么东西,才会使消费有价值。”\n“大多数经济学家认为,给老百姓更多的钱花就可以增加需求,但是这种做法并不能改变真正的需求,只会使人们花更多的钱购买已经生产出来的商品。只有增加供给才能切实满足人们更多的需求。”\n与之相对的,是“奥地利学派”的观点——当经济衰退时,需要通过市场的力量去促进资源的再分配,淘汰落后的产能,使这个社会的人力物力重新汇聚到更有价值的行业中。\n“当经济不景气时,物价需要下跌才能平衡经济局势。然而,目前许多政府应对经济衰退的本能反应便是造更多的货币,因为他们认为物价下跌会导致经济陷入需求崩溃的万丈深渊。但是,它们忽略了一旦物价下跌到一定程度,人们就会开始消费。这个过程淘汰了不必要的产能,把物价调低到符合内在供求关系的水平。”\n“政府总会通过各种形式干预储蓄的配置,包括政府贷款担保、公司及个人税收减免以及税务罚款等。这些政策的关键推动因素就是认为政府规划者要比储蓄者更清楚什么有利于社会的发展。然而,实际上,历史上充斥着各种浮夸的政策与方案,这些方案都是政府智囊团策划的,最终全都没有兑现他们的承诺。更重要的是,政府介入储蓄者和借款人之间采取的强制手段将借款的原因与结果割裂开来,使得储蓄的分配效率极为低下。贷给个人或者企业的款项如果无法成功促成必要的创新或者提高产能,就会浪费储蓄的供给,削弱整体经济。”\n“美联储宽松的货币政策,阻止了这种有益的经济衰退发生,于是人们只能同时面对经济紧缩与通货膨胀,从而导致“经济停滞型通货膨胀”的出现。”\n2.3 房地产泡沫 # 在房地产刚开始兴起时,政府通过“提高贷款上限”、“降低首付款”以及“降低信用标准”等手段,让人们能够轻易地背上负债、买入房产。随着房产的不断增值,越来越多的人涌入这个市场,加入了“炒房”的行列。然而,房价远高于其本身的价值,这个泡沫总会有破裂的那一天,书中称当时这种情况为“虚假的繁荣”。\n随着越来越多的人买不起房、不敢买房,房地产进入了“供大于求”的转折点,房价开始持续下跌。对于不少背负贷款、高位买房的人来说,手上的房产想要脱手只能赔本卖掉,巨额的贷款给许多家庭带来了沉重的压力。许多人偿还不起银行的贷款,信贷市场逐渐崩溃;此外,由于大量新建的房屋无人购买,企业和政府自然也不会再继续大兴土木,随之而来的则是大批建筑工人以及相关行业从业人员的失业。\n“由于房屋价格不再上涨,房屋净值不再增加,短期炒房也就无利可图。没有了利益的诱惑,过高的贷款额度就成了无法承受的负担。越来越多的借款人拖欠还贷,信贷市场发生崩溃。因为消费者不再投资房屋,其相关产业也陷入了困境。大批建筑工人、设计顾问以及电器销售人员纷纷失业。”\n为了缓解房地产市场的崩溃,政府开始参与进来进行调控,比如:“注入货币以弥补损失,持续发放宽松贷款,提高市场对房屋的需求,从而防止房价继续下跌”。与“量化宽松”政策类似,这种行为只能缓解近况,却无法从根本上解决问题。\n“如果任由房屋价格下跌,同时停止建造新的房屋,反而对国家的经济有好处,至少在真正的需求出现之前是这样。这样一来,人们就不会花那么多钱购买房屋,而是把钱花在那些经济发展中真正缺乏的东西上。不幸的是,政府的干预阻碍了这种资源再分配。美国的领导人很不理智地鼓励购房,抑制储蓄,还很不理智地鼓励借贷。这些因素共同作用,破坏了市场。本应倒闭的公司又在政府的支持下站了起来,本应解放出来的资金和劳动力被困在了无效的经济活动中,无法发挥更高的经济效益。市场的力量正要戳破信贷和房产泡沫的时候,美国政府插手进来继续吹大这两个泡沫”\n目前,身边不少同学都已经在家里父母的支持下买了自己的房子,我虽然羡慕,却仍保持着理智。希望终有一天,房子能够回归它原本的价格,在此之前,我们只需坚持开源节流,不断积累自己的资产储备就好了。\n三、心得体会 # 初读本书时,正值 2024 年 10 月,当时美联储时隔多年再次宣布降息,全球的经济情况都或多或少地受到了影响,国内的投资市场也开始变得活跃。在此之前,我对于经融领域可谓是一无所知,于是乘此机会,我决定恶补一波“基础知识”。\n本书以“小岛”作为故事,生动形象地解释了经济学中的许多概念,更巧的是,“银行降准降息”以及“房地产泡沫破碎”等现象此时恰好就发身在我的身边,理论结合实际,让我对书中提到的经济学理论有了更加深刻的认识和理解。\n最后,作为一个普通人,我们可以去学习和了解经济运行的基本原理,却左右不了社会整体的发展趋势。在这个风云变幻的时代中,我们如果能够运用自己的一点知识,去规避一些错误的决策,从而让自己免遭巨大的损失,那也算是学有所用了。\n","date":"2024-12-15","externalUrl":null,"permalink":"/articles/%E5%B0%8F%E5%B2%9B%E7%BB%8F%E6%B5%8E%E5%AD%A6%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E6%B7%B1%E5%85%A5%E6%B5%85%E5%87%BA%E7%BB%8F%E6%B5%8E%E5%AD%A6%E5%8E%9F%E7%90%86/","section":"Articles","summary":"","title":"《小岛经济学》读书笔记 | 深入浅出经济学原理","type":"posts"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/articles/","section":"Articles","summary":"","title":"Articles","type":"posts"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/","section":"home","summary":"","title":"home","type":"page"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/tags/%E7%BB%8F%E6%B5%8E%E5%AD%A6/","section":"Tags","summary":"","title":"经济学","type":"tags"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/","section":"Tags","summary":"","title":"读书笔记","type":"tags"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/categories/%E9%98%85%E8%AF%BB/","section":"Categories","summary":"","title":"阅读","type":"categories"},{"content":" 一、关于本书 # 《被讨厌的勇气》 心理学 作者:岸见一郎 / 古贺史健 简介:本书通过浅显易懂的对话,对“自卑”、“自由”以及“幸福”等人生话题进行了深入的探讨,并借此引出了本书的核心思想——“阿德勒心理学”。“阿德勒心理学”是关于“勇气”的心理学,面对书中提到的诸多人生困境,归根结底,都是因为缺乏“勇气”造成的。拥有“被讨厌的勇气”,是我们成长路上的一堂必修课。 二、内容分享 # 2.1 改变自己的勇气 # “重要的不是被给予了什么,而是如何去利用被给予的东西。”\n不管自己当下的现状如何,都不要抱怨过去,而应该奋力成长,积极面对未来,这就是“改变自己的勇气”。\n在本书中,提到了“原因论”和“目的论”两个观点:\n“原因论”:一味地关注过去的原因,企图仅仅靠原因去解释事物; “目的论”:决定我们自身的不是过去的经历,而是我们自己赋予经历的意义。 其中,“原因论”是一种消极看待人生的观念,而“目的论”则更强调人的“主观能动性”,认为人是可以改变的。\n“如果过去决定一切而过去又无法改变的话,那么活在今天的我们对人生也将会束手无策。结果会如何呢?那就可能会陷入对世界绝望、对人生厌弃的虚无主义或悲观主义之中。”\n当面对自己过往不幸的经历或先天造成的不足,从而产生“自卑”或“逃避”等情绪时,我们要有积极面对未来的心态。换言之,我们不应被过去所束缚,亦或是沉溺于“舒适圈”之中,把过去的“不幸”当作自己“摆烂”和“不求改变”的借口,而应该承认自己目前的现状,接纳当下这个不完美的自己,并努力改变和提升自己,拥抱崭新的生活。\n人生最重要的是去思考如何打好自己手中现有的牌,而不是整天眼高手低、抱怨连天,幻想自己能有一手天胡的好牌。\n2.2 积极生活的勇气 # “人根本不可能一个人活着,只有在社会性的环境之下才能成为‘个人’。因此,阿德勒心理学把作为个人的‘自立’和在社会中的‘和谐’作为重大目标。那么,如何才能实现这些目标呢?——我们必须要克服‘工作’、‘交友’以及‘爱’这三大课题。”\n在工作中,当我们面对困难从而发现自己的不足时,难免会觉得气馁,甚至会产生“自卑感”,如何正确地看待这种“自卑感”是非常重要的。\n“人是作为一种无力的存在活在这个世界上。并且,人希望摆脱这种无力的状态,继而就有了普遍欲求。阿德勒称其为‘追求优越性’。”\n“人都处于追求优越性这一‘希望进步的状态’之中,树立某些理想或目标并努力为之奋斗。同时,对于无法达成理想的自己就会产生一种自卑感。”\n“无论是追求优越性还是自卑感,都不是病态,而是一种能够促进健康、正常的努力和成长的刺激。只要处理得当,自卑感也可以成为努力和成长的催化剂。”\n然而,如果我们把生活的不如意归结为自己天生的缺陷,认为自己无法改变,从而开始摆烂,这就会陷入“自卑情节”。\n“自卑情节是指把自己的自卑感当作某种借口使用的状态。比如:我因为 A,所以才做不到 B。如果抱着‘我因为学历低所以无法成功’之类的想法,那就不是‘不能成功’而是‘不想成功’。”\n“简单地说就是害怕向前迈进或者是不想真正地努力。不愿意为了改变自我而牺牲目前所享受的乐趣——比如玩乐或休闲时间。也就是拿不出改变生活方式的‘勇气’,即使有些不满或者不自由,也还是更愿意维持现状。”\n在交友中,“竞争”无处不在,但是我们不应把周围的人都看做“敌人”,而是应该把他们当作“伙伴”,要学会衷心地祝福他人的幸福,而不是嫉妒他人。\n“竞争的可怕之处就在于,即便不是败者、即便一直立于不败之地,处于竞争之中的人也会一刻不得安心、不想成为败者。而为了不成为败者就必须一直获胜、不能相信他人。之所以有很多人虽然取得了社会性的成功,但却感觉不到幸福,就是因为他们活在竞争之中。因为他们眼中的世界是敌人遍布的危险所在。”\n“‘无法真心祝福过得幸福的他人’,那就是因为站在竞争的角度来考虑人际关系,把他人的幸福看作‘我的失败’,所以才无法给予祝福。但是,一旦从竞争的怪圈中解放出来,就再也没有必要战胜任何人了,也就能够摆脱“或许会输”的恐惧心理了,变得能够真心祝福他人的幸福并能够为他人的幸福做出积极的贡献。”\n在爱情中,本书提到“爱”应该是相互平等、相处融洽的关系,而不是相互束缚的关系。\n“如果在一起感到苦闷或者紧张,那即使是恋爱关系也不能称之为爱。当人能够感觉到‘与这个人在一起可以无拘无束’的时候,才能够体会到爱。既没有自卑感也不必炫耀优越性,能够保持一种平静而自然的状态。”\n2.3 选择自由的勇气 # “自由就是被别人讨厌。”\n要想获得真正的自由,我们就无法“讨好所有人”,而应该“不在意他人的评价”,不随波逐流,为自己而活。\n“阿德勒心理学否定寻求他人的认可。如果做了恰当的事情就能够得到表扬,而如果做了不恰当的事情就会受到惩罚,阿德勒严厉批评这种赏罚式的教育。在赏罚式教育之下会产生这样一种错误的生活方式,那就是‘如果没人表扬,我就不去做好事’或者是‘如果没人惩罚,我也做坏事’。”\n“我们并不是为了满足别人的期待而活着。倘若自己都不为自己活出自己的人生,那还有谁会为自己而活呢?如果一味寻求别人的认可、在意别人的评价,那最终就会舍弃真正的自我,活在别人的人生之中。”\n2.4 主动付出的勇气 # “只关心自己的人往往认为自己位于世界的中心。对于这样的人来说,他人只是‘为我服务的人’;它们甚至会认为:‘大家都应该为我服务,应该优先考虑我的心情’。他们超越了‘人生的主人公’,进而越位到‘世界的主人公’。因此,在与他人接触的时候总是会想:‘这个人给了我什么?’。然而,这种期待并不会每次都能被满足,因为‘别人并不是为了满足你的期待而活’。当期待落空的时候,他们往往会大失所望并感觉受到了极大的侮辱,而且还会非常愤慨,产生诸如‘那个人什么也没有为我做’、‘那个人辜负了我的期望’或者‘那个人不再是朋友而是敌人’之类的想法。抱着自己位于世界中心这种信念的人很快就会失去‘朋友’。”\n自己以前在对待学校中的导师以及公司中的领导时,总是心怀不满,觉得“这人也没有帮助过我什么啊,我为什么还要这么尽心尽力地给他干活?”,而没想过“自己给别人提供了什么价值?”,我总是习惯于“先索取,再回报”,而不是自己先去积极主动地付出,因此也基本上对自己呆过的各个集体没有什么“归属感”,这是今后我需要改正的地方。\n“归属感不是仅仅靠‘在那里’就可以得到的,它必须靠积极地参与到共同体中去才能够得到。也就是不回避工作、交友以及爱之类的‘人生课题’,要积极主动地去面对。如果你认为自己就是世界的中心,那就丝毫不会主动融入共同体中,因为一切他人都是‘为我服务的人’,根本没必要由自己采取行动。”\n2.5 追求平等的勇气 # “阿德勒心理学反对一切‘纵向关系’,提倡把所有的人际关系都看作‘横向关系’。”\n对待比我们弱小的人,我们不应该表扬或批评他们,因为这种行为包含有“有能力者对没能力者所做的评价”的特点,是一种“操作别人”的方式。\n“评价性的语言是基于纵向关系的语言。如果能够建立起横向关系,那自然就会说出一些更加真诚地表达感谢、尊敬或者喜悦的话。”\n面对比我们强大的人,我们应该不卑不亢,既不能太过以自我为中心,也不能完全听从他人,完全不敢表达自己的想法。\n“尊敬长者非常重要。如果是公司组织,职责差异自然也会存在。并不是说将任何人都变成朋友或者像对待朋友一样去对待每一个人,重要的是意识上的平等以及坚持自己应有的主张。”\n2.6 拥抱幸福的勇气 # 要想获得幸福,我们就需要做到认可自己,同时处理好周围的人际关系,和他人建立起“共同体感觉”,这包括三个方面:“自我接纳”、“他者信赖”和“他者贡献”。\n“自我接纳”:\n“没必要特别积极地肯定自己,不是‘自我肯定’,而是‘自我接纳’。‘自我肯定’是明明做不到但还是暗示自己说‘我能行’或者‘我很强’,也可以说是一种容易导致‘优越情结’的想法,是对自己撒谎的生活方式;‘自我接纳’是指假如做不到就诚实地接受这个‘做不到的自己’,然后尽量朝着能够做到的方向去努力,不对自己撒谎。”\n“他者信赖”:\n“无条件的信赖是搞好人际关系和构建横向关系的一种手段。如果不敢去信赖别人,那最终就会与任何人都建立不了深厚的关系。”\n“如果关系浅,破裂时的痛苦就会小,但这种关系在生活中产生的喜悦也小。”\n“他者贡献”:\n“要想获得归属感就必须把他人看作伙伴,视他人为敌的人既做不到‘自我接纳’,也无法充分做到‘他者信赖’。对作为伙伴的他人给予影响、作出贡献,这就是‘他者贡献’。”\n“‘他者贡献’的意思并不是‘自我牺牲’。阿德勒把为他人牺牲自己人生的人称作‘过度适应社会的人’,并对此给予警示。”\n“‘他者贡献’并不是舍弃‘我’而为他人效劳,它反而是为了能够体会到‘我’的价值而采取的一种手段。我们应该思考的不是他人为我做了什么,而是我能为他人做什么,并积极地加以实践。”\n2.7 甘于平凡的勇气 # 本书的最后一部分是关于“甘于平凡的勇气”的内容,作者提到,我们没必要非要追求“优越性”,只有认识到自己的平凡和普通,才能真正做到“自我接纳”。\n“‘普通’并不等于‘无能’,我们根本没必要特意炫耀自己的优越性。”\n承认自己的不足,并不等于不努力去做改变,而是应该脚踏实地地去提高自己。然而,在这个过程中,我们却很容易陷入“只知道盯着最终的目标,却忽略了沿途的过程”的思维陷阱。\n“为遥远的将来设定一个目标,并认为现在是其准备阶段。一直想着‘真正想做的是这样的事情,等时机到了就去做’,这是一种拖延人生的生活方式。只要在拖延人生,我们就会无所进展,只能每天过着枯燥乏味的单调生活。因为在这种情况下,人就会认为‘此时此刻’只是准备阶段和忍耐阶段。”\n“人生中最大的谎言就是不活在‘此时此刻’。纠结过去、关注未来,把微弱而模糊的光打向人生整体,自认为看到了些什么。然而,起决定作用的既不是昨天也不是明天,而是‘此时此刻’。”\n“人生很简单,并不是什么深刻的事情。如果认真过好了每一个刹那,那就没什么必要令其过于深刻。站在现实性的角度,人生总是处于完结状态。”\n三、心得体会 # 本书全篇的核心其实就是一个词——“勇气”,生活中很多看似无解的问题,归根结底其实都是因为缺乏“勇气”造成的。其中,对我启发最大的,是关于“改变自己的勇气”、“积极生活的勇气”以及“主动付出的勇气”的部分,这也是在我过往的生活中,我认为自己做的还不太好的地方。\n曾经的我,面对自己孱弱的专业能力,我第一时间想到的不是如何脚踏实地地去补齐各方面的技能,而是只知道退而求其次,不敢去追求更好的机会,然后就会陷入焦虑和迷茫之中,从而形成恶性循环。现在的我则会理性分析自己欠缺的地方,会思考想要达成自己的目标还需要在哪些方面付出努力,然后会持之以恒地去付诸实践。\n曾经的我,会以“学业繁忙”和“工作繁忙”为借口,去逃避生活、逃避自己的“人生课题”。然而,“努力学习和工作”与“积极勇敢地去生活”其实是不冲突的,人不是机器,谁也不能保证自己能够除了吃饭睡觉之外能一直高效的学习和工作,人总是需要休息和调节的,而这就给了我们去感受生活的空间。现在的我,哪怕工作再忙、压力再大,我也会抽出时间去培养自己的兴趣爱好、去尝试更多的事情、去好好吃饭和睡觉。事实上,根据我观察其他人的经验,越是优秀的人就越能在忙碌的生活中管理好自己的时间,规划好自己的人生,“学习/工作/爱情/娱乐”样样不落。因此,我也应该更加积极地去拥抱生活,而不是找各种借口来逃避人际交往,放弃感受幸福的机会。\n曾经的我,以自我为中心,只有别人先为我付出了,我才能全心全意地去回报他,这些人包括但不限于我的师兄师姐、导师以及领导等。看完本书后,我知道了“他者贡献”的重要性,“我们应该思考的不是他人为我做了什么,而是我能为他人做什么,并积极地加以实践”,这是今后的我需要改进的地方。\n总而言之,这是一本非常不错的关于个人成长的心理学书籍,希望自己在今后的生活中,也能一直拥有书中所提到的“勇气”。\n","date":"2024-12-07","externalUrl":null,"permalink":"/articles/%E8%A2%AB%E8%AE%A8%E5%8E%8C%E7%9A%84%E5%8B%87%E6%B0%94%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E6%8E%A5%E7%BA%B3%E8%87%AA%E6%88%91%E5%8B%87%E6%95%A2%E5%9C%B0%E6%84%9F%E5%8F%97%E7%94%9F%E6%B4%BB/","section":"Articles","summary":"","title":"《被讨厌的勇气》读书笔记 | 接纳自我,勇敢地感受生活","type":"posts"},{"content":"","date":"2024-12-07","externalUrl":null,"permalink":"/tags/%E4%B8%AA%E4%BA%BA%E6%88%90%E9%95%BF/","section":"Tags","summary":"","title":"个人成长","type":"tags"},{"content":"","date":"2024-12-07","externalUrl":null,"permalink":"/tags/%E4%BA%BA%E9%99%85%E4%BA%A4%E5%BE%80/","section":"Tags","summary":"","title":"人际交往","type":"tags"},{"content":"","date":"2024-12-07","externalUrl":null,"permalink":"/tags/%E5%BF%83%E7%90%86%E5%AD%A6/","section":"Tags","summary":"","title":"心理学","type":"tags"},{"content":"","date":"2024-12-05","externalUrl":null,"permalink":"/tags/ai/","section":"Tags","summary":"","title":"AI","type":"tags"},{"content":"","date":"2024-12-05","externalUrl":null,"permalink":"/tags/llm/","section":"Tags","summary":"","title":"LLM","type":"tags"},{"content":"","date":"2024-12-05","externalUrl":null,"permalink":"/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%BE%AE%E8%B0%83/","section":"Tags","summary":"","title":"大模型微调","type":"tags"},{"content":" 一、大模型开发全流程 # 当我们训练大模型时,一般会经过 pre-training 和 post-training 两个阶段。其中,pre-training 阶段一般会先使用海量数据来训练 base 大模型,再通过增量预训练来为模型注入领域知识;而 post-training 阶段则主要包括监督微调和偏好对齐两个步骤,使我们训练的大模型能够更好地适应某些特定的任务,并符合人类的表达习惯和价值观。\npre-training:\n预训练(Pre-Training):利用海量数据、大量算力通过无监督训练得到基座模型。预训练后的模型具备强大的语言生成能力,但由于它主要是无监督训练的结果,可能不会直接适应具体的任务(如问答、对话),需要进一步的微调; 增量预训练(Continued Pre-Training): 一般垂直大模型是基于通用基座大模型进行二次的训练,为了给模型注入领域知识,就需要用领域内的语料进行继续预训练。 post-training:\n监督微调(Supervised Fine-Tuning, SFT):这一阶段是对基座模型进行微调,让模型能够适用于特定任务; 偏好对齐(Reinforcement Learning from Human Feedback, RLHF):这一阶段通过引入人类反馈进一步优化模型的生成质量,使其生成的回答更符合用户的期望和人类的价值观(对齐人类偏好)。由于直接从人类获取反馈的成本较高,通常会先训练一个奖励模型(Reward Model,RM)来代替人类打分,这样可以在 RL 的框架下实现大规模的自动优化。 了解了大模型开发的整体流程,下面将重点对大模型微调相关的知识进行介绍。\n二、什么是大模型微调 # 大模型微调一般指的是监督微调(SFT),即使用特定下游任务的数据继续训练“预训练模型(基座模型)”,使得模型能够满足特定下游任务的性能标准。\n示例一:将大模型微调为一个可以将德语翻译为英语的模型。\n我们需要使用大量输入为德语、输出为英语的带标签数据来训练 base 大模型,这样经过微调后的大模型就可以很好地用于将德语翻译为英语的任务。\n示例二:开源模型为了能够直接使用,一般会提供经过问答任务微调的版本,即 Chat 模型。\n三、为什么需要大模型微调 # 提升特定任务表现:预训练模型虽然具有广泛的语言理解能力,但在特定任务(如情感分析、问答系统、机器翻译等)上的表现可能不尽如人意。通过在特定任务的数据上进一步训练,使模型能够更好地理解和执行该任务; 领域适应性:预训练模型可能在一些通用领域表现良好,但在特定领域(如医学、法律、金融等)中可能难以准确理解专业术语和内容结构。通过微调,可以让模型更好地适应这些领域的语言特点,提高在这些领域中的应用效果; 数据稀缺性:对于一些数据稀缺的任务或领域,获取大量标签数据可能比较困难。微调允许在有限的数据集上进行有效训练,从而在数据稀缺的情况下也能取得较好的性能; 防止过拟合:预训练过程中模型可能会过度拟合于无监督学习的任务(如下一个词预测),而在特定任务中表现不佳。通过微调,可以让模型专注于特定任务的数据,这有助于减少过拟合的风险,提高模型在该任务上的泛化能力; 成本效益:与使用 prompt 来引导模型行为相比,微调通常可以更高效地优化模型的表现。微调后的模型通常可以更直接地执行任务,减少了对复杂提示的依赖。同时,微调可以在更小的模型上实现类似于大型模型的性能,从而降低推理的计算成本和延迟,比如与使用通用的 GPT-3.5 模型相比,经过微调的小型模型(如LLaMA-7B)在成本效益上可能更具优势,尤其是在特定任务的执行上。 四、大模型微调的方法有哪些 # 整体上,根据微调参数量的不同,大模型微调的方法可以分为以下两类:\n全量参数微调(Full Fine-tuning,FFT):对预训练模型的所有参数进行更新,训练速度较慢,消耗机器资源较多; 参数高效微调(Parameter-Efficient Fine-Tuning,PEFT):只对部分参数进行更新,训练速度快,消耗机器资源少。 此外,还有一种不需要更新模型权重就可以完成微调的方法,叫做 In-Context Learning,通过在输入的 prompt 中提供与任务相关的上下文和例子,从而让模型能够更好地了理解我们的意图。\n4.1 FFT 的优缺点 # 优点:\n提升特定任务性能:全参微调可以对所有模型参数进行优化,从而在某些任务上获得更好的性能。 缺点:\n训练成本高:全参微调所需要计算的参数量与预训练相同,随着模型规模变得越来越大,这使得在消费级硬件上进行全量微调变得不可行; 灾难性遗忘:用特定训练数据去微调可能会把这个领域的表现变好,但也可能会把原来表现好的别的领域的能力变差。 4.2 PEFT 的优缺点 # 优点:\n降低训练成本:减少计算消耗,缩短训练时间,降低对硬件性能的要求; 保证模型性能:针对特定下游任务,能够在一定程度上保证模型的表现和性能; 节省存储空间:降低存储占用,大部分的参数都可以在不同任务之间共享,只需额外保存经过不同任务微调后更新的参数。 缺点:\n特定任务性能有限:可能无法达到全参数微调在某些特定任务上的性能水平。 因此,在实际应用中,我们应该根据具体任务的要求和可用资源情况,在服务效率和模型质量之间做出权衡。对于资源有限或对训练时间有严格要求的场景,使用 PEFT 会是一个比较好的选择;而对于需要最佳性能的任务,使用 FFT 可能会更加合适。\n4.3 PEFT 的分类 # Addition-based methods:在预训练模型的基础上,新增参数或网络层,并只对这些新增的参数进行训练和更新; Adapters:在 transformer 中的 attention 和 ffn 层后增加 Adapter; Soft Prompts: Soft Prompt Tuning:为输入的 embedding 增加可训练的 soft prompt 参数; Prefix Tuning:为 transformer 的输入增加可训练的 prefix 参数; Selective methods:通过一定的算法和策略,选择预训练模型中的部分参数进行训练和更新; Reparametrization-based methods:利用低秩矩阵来近似地表达预训练模型需要更新的参数; LoRA; QLoRA; DLoRA; LongLoRA; GLoRA; AdaLoRA; LoRA-FA; VeRA; Delta-LoRA; LoRA+; LoRA-drop; …… Hybrid methods:根据实际情况,可以对上述方法进行组合,从而达到更好的效果。 目前比较主流的几种参数高效微调方法包括:Prompt Tuning、Prefix Tuning、LoRA、QLoRA 等。\n论文《Scaling Down to Scale Up: A Guide to Parameter-Efficient Fine-Tuning》中展示了各类参数高效微调方法及其所属的类别,如下所示:\n该论文中还对比了各类参数高效微调方法的表现和性能,如下所示:\n五、各类微调方法的原理是什么 # 5.1 In-Context Learning # 核心原理:\n当我们无法直接获取到模型并修改其权重(比如:直接通过 API 或用户接口访问模型)时,就可以使用 In-Context Learning 的方式来让模型更好地适应于特定的任务。\nIn-Context Learning 通过在输入的 prompt 中提供与任务相关的上下文和例子,从而让模型能够基于我们提供的上下文,更好地生成我们期望得到的结果。\n\u0026ldquo;Based on intuition from prompting, we believe that having a proper context can steer the LLM without changing its parameters.\u0026rdquo;\n示例:将大模型微调为一个可以将德语翻译为英语的模型。\n我们在输入的上下文中给出一些将德语翻译为英语的例子,然后再输入一句德语,这样模型就能更好地理解我们的意图,知道现在要做的是将输入的德语翻译为对应的英语。\n优点:\n当我们没有足够的带标签数据用于监督微调时,通过 In-Context Learning,只需少量的样例,就能提升模型对于特定任务的表现; 当我们无法直接获取到模型并修改其权重时,通过 In-Context Learning,无需额外对模型进行微调,就可以快速地进行尝试和验证。 缺点:\n相比于直接更新模型的权重进行微调,In-Context Learnin 的效果有限,模型在特定任务上的表现上不如 FFT 和 PEFT。 5.2 Soft Prompt Tuning # 核心原理:\nSoft Prompt Tuning 可以看作是 Prefix Tuning 的简化版本,它给每个任务定义了自己的 soft prompt,并将其拼接到数据上作为输入(在输入 embedding 层加入一段定长的可训练的向量,在微调的时候只更新 soft prompt 这部分的参数)。\n示例代码:\nx = EmbeddingLayer(input_ids) x = concatenate([soft_prompt_tensor, x], dim=seq_len) output = model(x) 其中,soft_prompt_tensor 具有与 embedded inputs 同样的特征维度,将两者拼接过后,就相当于是增加了输入的长度。\n5.3 Prefix Tuning # 核心原理:\nPrefix Tuning 通过对输入数据增加前缀(prefix)来做微调,即在输入 token 之前构造一段任务相关的 virtual tokens 作为 prefix,训练的时候只更新 prefix 这部分的参数,每个下游任务都可以单独训练一套 prefix token。\n示例代码:\ndef transformer_block_with_prefix(x): soft_prompt = FullyConnectedLayers(soft_prompt) # prefix x = concatenate([soft_prompt, x], dim=seq_len) x = SelfAttention(x) x = LayerNorm(x + residual) residual = x x = FullyConnectedLayers(x) x = LayerNorm(x + residual) return x 为什么增加 prefix 可以影响模型生成的结果?\n感性地理解一下,prefix 的作用是引导模型提取输入中的特定信息,进而更好地生成结果。\n另外,我们还可以针对不同的下游任务,训练不同的 prefix 并对其进行保存。这样当我们需要切换不同的下游任务时,只需要加载不同的 prefix 参数,就可以实现模型功能的快速切换。\n缺点:\n微调的效果存在上限,模型的表现并不一定会随着 prefix 长度的增加而提高; 由于模型的输入长度一般是固定的,而增加了 prefix 之后,留给原始文字数据的空间就少了,因此可能会降低原始文字中 prompt 的表达能力。 5.4 Adapter Tuning # 核心原理:\nAdapter Tuning 通过在 transformer 中的 multi-head self-attention 和 fully connected layers 后增加 Adapter 进行微调。其中,Adapter 中的第一个 fully connected layer 将高维的输入映射为了一个低维的表示,第二个 fully connected layer 再将其映射回高维的空间中,这样就能有效降低训练时需要更新的参数量。\n微调时,只更新 Adapter 部分的权重,原模型的参数是冻结的。\n注意:新增的 Adapter 与原模型中的层是顺序串行的关系。\n示例代码:\ndef transformer_block_with_adapter(x): residual = x x = SelfAttention(x) x = FullyConnectedLayers(x) # Adapter x = LayerNorm(x + residual) residual = x x = FullyConnectedLayers(x) x = FullyConnectedLayers(x) # Adapter x = LayerNorm(x + residual) return x 缺点:\n添加了 Adapter 后,模型整体的层数变深,会拖慢模型训练和推理的速度。 小结:\nFFT 成本太高; Prefix Tuning 难训且会减少原始训练数据中的有效文字长度; Adapter Tuning 存在训练和推理延迟。 为了解决以上问题,LoRA 系列微调方法便应运而生了。\n5.5 LoRA # 关于 LoRA(Low-Rank Adaptation,低秩适配器)的相关原理,请参考我之前写的这篇文章:\n大模型 LoRA 微调的数学原理 2024-11-13\u0026middot;529 words 计算机 AI LLM 大模型微调 论文精读 5.6 QLoRA # 核心原理:\nQLoRA(Quantized LoRA)的核心工作其实是模型量化,通过将预训练模型进行 NF4 量化,再结合 LoRA 的方式进行微调,可以大幅减少训练时显存的占用。\nQLoRA 有一个 NF4 的存储数据类型和 BF16 的计算数据类型。在进行前向和反向传播时,我们需要将存储数据类型反量化为计算数据类型,但是计算梯度时我们只计算添加的适配器的梯度,这一点和 LoRA 是一致的。\n预训练模型的参数:进行 NF4 量化; LoRA 的参数:保持 BF16 的精度。 核心工作:\n四位标准浮点数量化(4-bit Normal Float Quantization):结合了分位数量化和分块量化; 双重量化(Double Quantization):对模型进行了两次量化,其中第二次量化只作用在第一次量化产生的量化常数上,可以进一步节约显存占用; 分页优化(Paged Optimizer):使用 CPU 内存代替 GPU 显存保存部分梯度参数。 优缺点:\n优点:显存占用下降。由于原模型参数经过了量化,在计算时占用的内存减少了; 缺点:训练时间增加。由于引入了量化和反量化的计算过程,在训练时需要消耗更多的时间。 量化分位数的计算:\n根据每个块的特征的绝对值的最大值,我们为每个块保存一个量化常数(每个块中的特征取绝对值后的最大值); 计算每个张量的量化值(实际值/该块的量化常数); 在 Q(normal_map)中找到与每个张量最接近的值,并将其量化为该值对应的索引值。 normal_map 的计算:\nfrom scipy.stats import norm import torch def create_normal_map(offset=0.9677083, use_extra_value=True): if use_extra_value: # one more positive value, this is an asymmetric type v1 = norm.ppf(torch.linspace(offset, 0.5, 9)[:-1]).tolist() # 正数部分 v2 = [0]*(256-15) ## we have 15 non-zero values in this data type v3 = (-norm.ppf(torch.linspace(offset, 0.5, 8)[:-1])).tolist() #负数部分 v = v1 + v2 + v3 else: v1 = norm.ppf(torch.linspace(offset, 0.5, 8)[:-1]).tolist() v2 = [0]*(256-14) ## we have 14 non-zero values in this data type v3 = (-norm.ppf(torch.linspace(offset, 0.5, 8)[:-1])).tolist() v = v1 + v2 + v3 values = torch.Tensor(v) values = values.sort().values values /= values.max() assert values.numel() == 256 return values Q = create_normal_map() # Q = [-1.0, -0.6961928009986877, -0.5250730514526367, -0.39491748809814453, -0.28444138169288635, -0.18477343022823334, -0.09105003625154495, 0.0, 0.07958029955625534, 0.16093020141124725,0.24611230194568634, 0.33791524171829224, 0.44070982933044434, 0.5626170039176941, 0.7229568362236023, 1.0] 示例:\n假设一个张量有 16 个值,被分成了 4 块:\ninput_blocked_tensor = [[-1.28645003578589, -1.817660483275528, 9.889441349505042, 0.010208034676132627], [-15.009014631551885, 1.4136255086268115, -7.815595761491153, 10.766760590950263], [-0.731406153917959, 3.468224595908726, 2.445252541840315, -8.970824523299282], [-9.641638854625175, 7.696158363188889, -5.323939281255154, 5.97160401402024]] 根据每个块的特征的绝对值的最大值,我们为每个块保存一个量化常数,它的计算方式是每个块中特征的绝对值中最大的那个:\nc1 = max(|-1.28645003578589|, |-1.817660483275528|, |9.889441349505042|, |0.010208034676132627|) = 9.889441349505042 c2 = max(|-15.009014631551885|, |1.4136255086268115|, |-7.815595761491153|, |10.766760590950263|) = 15.009014631551885 c3 = max(|-0.731406153917959|, |3.468224595908726|, |2.445252541840315|, |-8.970824523299282|) = 8.970824523299282 c4 = max(|-9.641638854625175|, |7.696158363188889|, |-5.323939281255154|, |5.97160401402024|) = 9.641638854625175 计算张量的量化值:例如第一个值 -1.28645003578589,它除以这个块的量化常数 c1 后得到 -0.13008318572517502,我们可以在 Q 中找到与它最接近的值是 -0.09105003625154495,这个值在 Q 中对应的索引是 6,因此这个值被量化后的值是 6。\nQ = [-1.0, -0.6961928009986877, -0.5250730514526367, -0.39491748809814453, -0.28444138169288635, -0.18477343022823334, -0.09105003625154495, 0.0, 0.07958029955625534, 0.16093020141124725,0.24611230194568634, 0.33791524171829224, 0.44070982933044434, 0.5626170039176941, 0.7229568362236023, 1.0] 同理我们可以得到这个输入张量所有的值量化后的结果:\n[[6, 5, 15, 7], [0, 8, 2, 14], [6, 11, 10, 0], [0, 14, 2, 13]] 在模型保存时,除了要保存量化后的值,我们还要保存每个块对应的量化常数,因为这个值在我们进行反量化时需要用到。\n在反量化时,我们以量化结果作为索引,从 Q 中查找到它对应的分位数,再乘以为每个块保存的量化常数 ci,便可以得到最终结果。\n[[-0.9004339933799617, -1.8273060011889755, 9.889441349505042, 0.0], [-15.009014631551885, 1.1944218804231184, -7.880829111886221, 10.850869732860506], [-0.816793898052648, 3.0313783372030603, 2.2078302737800004, -8.970824523299282], [-9.641638854625175, 6.970488722350373, -5.062564734402345, 5.424549965245643]] 解决了什么问题?\n如果我们粗暴的使用 round 操作去映射到低精度的更近的值,我们可能造成大量的数据都被量化到同一个数上,这样特征之间的差异性在量化过程中就被丢失了。使用分位数将张量分成了大小相同的若干个块,这样我们得到更加均匀的量化特征,这也就是分位数量化。每两个分位数的中点便是模型量化到这个区间映射的值。\n双重量化:\nQLoRA 的双重量化是指对量化常数再做一次 8 bit 的量化,在进行量化常数的量化时,QLoRA 以每 256 个量化常数为一组再做一次量化。在进行反量化时我们也需要进行两次反量化才能把量化后的值还原。\n好处:减少了存储量化常数带来的额外显存占用。\n分页优化:\nQLoRA 的分页优化其实就是当显存不足时,将保存的部分梯度检查点转移到 CPU 内存上,和计算机的内存数据转移到硬盘上的常规内存分页一个道理。\n5.7 总结 # How to use and finetune pre-trained LLMs?\n总结一下,当我们经过预训练得到 base 大模型之后,还需要进行以下操作:\n增量预训练:注入领域知识; 监督微调:适配特定下游任务(各类微调方法百花齐放); 偏好对齐:使模型生成的结果符合人类偏好。 六、大模型微调的框架有哪些 # huggingface/transformers:提供了丰富的预训练模型和微调工具,支持大多数主流的 NLP 任务(如文本分类、序列标注、生成任务等),适合进行快速实验和生产部署; huggingface/peft:huggingface 开源的微调基础工具; modelscope/ms-swift:modelscope 开源的轻量级微调框架,以中文大模型为主,支持各类微调方法;可以通过执行脚本进行微调,也可以在代码环境中一键微调;自带微调数据集和验证数据集,可以一键完成微调和验证; hiyouga/LLaMA-Factory:全栈微调工具,支持海量模型和各种主流微调方法;支持通过脚本微调、基于 Web 端微调(使用简单);自带基础训练数据集;除微调外,支持增量预训练和全量微调; NVIDIA/Megatron-LM:NVIDIA 开发的大模型训练框架,支持大规模的预训练和微调,适用于需要极高性能和规模的大模型训练和微调。 七、如何在生产环境中进行微调 # 7.1 微调实践 # 整体架构和组件:\nHelm Char:管理集群和训练配置; PyTorchJob(with multiple workers):执行微调(分布式训练); PVC(Persistent Volume Claim):存储训练数据和模型数据; Secret:管理鉴权相关配置; Data Access Pod:……。 以上实践案例是基于 Kubeflow 提供的平台和组件进行实现的,下面将对 Kubeflow 进行介绍。\n7.2 Kubeflow # 整体介绍:\n\u0026ldquo;Kubeflow is a community and ecosystem of open-source projects to address each stage in the machine learning (ML) lifecycle with support for best-in-class open source tools and frameworks. Kubeflow makes AI/ML on Kubernetes simple, portable, and scalable.\u0026rdquo;\nKubeflow 包括:\nKubeflow Platform:AI 模型开发部署全流程工作平台; Standalone Kubeflow Components:可独立使用的各类组件。 Kubeflow Platform:\n\u0026ldquo;The Kubeflow Platform refers to the full suite of Kubeflow components bundled together with additional integration and management tools. Using Kubeflow as a platform means deploying a comprehensive ML toolkit for the entire ML lifecycle.\u0026rdquo;\nStandalone Kubeflow Components:\n\u0026ldquo;The Kubeflow ecosystem is composed of multiple open-source projects that address different aspects of the ML lifecycle. Many of these projects are designed to be usable both within the Kubeflow Platform and independently. These Kubeflow components can be installed standalone on a Kubernetes cluster. It provides flexibility to users who may not require the full Kubeflow Platform capabilities but wish to leverage specific ML functionalities such as model training or model serving.\u0026rdquo;\nKubeflow Overview Diagram:\nKubeflow Ecosystem:\n使用 Kubeflow 进行微调:\n\u0026ldquo;Once user executes train API, Training Operator creates PyTorchJob with appropriate resources to fine-tune LLM.\u0026rdquo;\n\u0026ldquo;Storage initializer InitContainer is added to the PyTorchJob worker 0 to download pre-trained model and dataset with provided parameters.\u0026rdquo;\n\u0026ldquo;PVC with ReadOnlyMany access mode attached to each PyTorchJob worker to distribute model and dataset across Pods.\u0026rdquo;\n\u0026ldquo;The PyTorchJob is a Kubernetes custom resource to run PyTorch training jobs on Kubernetes. The Kubeflow implementation of the PyTorchJob is in the training-operator.\u0026rdquo;\n八、参考资料 # Scaling Down to Scale Up: A Guide to Parameter-Efficient Fine-Tuning Prefix-Tuning: Optimizing Continuous Prompts for Generation LoRA: Low-Rank Adaptation of Large Language Models 李航《统计学习方法》 QLoRA: Efficient Finetuning of Quantized LLMs 浅谈 DeepLearning 的浮点数精度 FP32/FP16/TF32/BF16…… LongLoRA: Efficient Fine-tuning of Long-Context Large Language Models New LLM Pre-training and Post-training Paradigms Finetuning Large Language Models Using and Finetuning Pretrained Transformers Practical Tips for Finetuning LLMs Using LoRA LLM 微调理论 GPT 是如何炼成的:大模型微调基础概念指北 图解大模型微调系列之:大模型低秩适配器 LoRA(原理篇) 图解大模型微调系列之:大模型低秩适配器 LoRA(源码解读与实操篇) 图解 Fine-tuning:LoRA 系列微调技术概述 QLoRA(Quantized LoRA)详解 LongLoRA - 高效微调长上下文的 LLMs LLM 长 context 微调技巧 - LongLora LoRA、QLoRA、LoRA+、LongRA、DoRA、MaLoRA、GaLore方案都知道吗? LLM 微调实践 Fine Tuning a LLM Using Kubernetes with Intel® Xeon® Scalable Processors How to Fine-Tune LLMs with Kubeflow LLM Fine-Tuning with Training Operator - Architecture LLM Fine-Tuning with the Training Operator ","date":"2024-12-05","externalUrl":null,"permalink":"/articles/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%BE%AE%E8%B0%83%E7%9F%A5%E8%AF%86%E5%85%A8%E6%99%AF/","section":"Articles","summary":"","title":"大模型微调知识全景","type":"posts"},{"content":"","date":"2024-12-05","externalUrl":null,"permalink":"/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/","section":"Categories","summary":"","title":"计算机","type":"categories"},{"content":"","date":"2024-12-05","externalUrl":null,"permalink":"/tags/%E8%AE%BA%E6%96%87%E7%B2%BE%E8%AF%BB/","section":"Tags","summary":"","title":"论文精读","type":"tags"},{"content":"","date":"2024-11-23","externalUrl":null,"permalink":"/tags/%E5%8D%9A%E5%AE%A2%E6%B1%87%E6%80%BB/","section":"Tags","summary":"","title":"博客汇总","type":"tags"},{"content":" AI Infra # 环境搭建 # 基于 EulerOS \u0026 Ascend NPU 搭建 PyTorch 远程开发环境 2024-09-24\u0026middot;1163 words 计算机 AI Infra Ascend NPU CANN 硬件架构 # NVIDIA GPU 架构 \u0026 CUDA 平台入门学习 2024-08-26\u0026middot;570 words 计算机 AI Infra NVIDIA GPU CUDA Ascend NPU 架构 \u0026 CANN 平台入门学习 2024-08-31\u0026middot;462 words 计算机 AI Infra Ascend NPU CANN 算子开发 # Ascend aclnn 算子开发入门 2024-10-23\u0026middot;1663 words 计算机 AI Infra 算子开发 Ascend NPU CANN LLM # 大模型微调 # 大模型微调知识全景 2024-12-05\u0026middot;1146 words 计算机 AI LLM 大模型微调 论文精读 大模型 LoRA 微调的数学原理 2024-11-13\u0026middot;529 words 计算机 AI LLM 大模型微调 论文精读 Java # Maven 项目编译报错解决方法 2024-09-04\u0026middot;126 words 计算机 Java Maven IDEA Git # Git 实践案例 | 合并多个分散的 commit 节点 2024-10-18\u0026middot;236 words 计算机 Git ","date":"2024-11-23","externalUrl":null,"permalink":"/articles/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%8D%9A%E5%AE%A2-%E6%80%BB%E8%A7%88/","section":"Articles","summary":"","title":"我的技术博客 | 总览","type":"posts"},{"content":" Introduction # Welcome to my personal website!\nThis website is build to show my articles of cs-learning, reading notes, life experiences and maybe some photographs in the future.\nWho am I ? # You can read the page shown below to get more information about me.\nAbout me 2024-11-02\u0026middot;582 words Articles # Most of the articles I have written and put on this site are about computer science (especially AI Infra and LLM) and reading notes.\nTechnical articles # You can read the article shown below to get a whole picture of all the technical articles I have written.\n我的技术博客 | 总览 2024-11-23\u0026middot;109 words 计算机 博客汇总 Reading notes # You can read the article shown below to get a whole picture of all the books I have read, and the reading notes I have written are also shown here.\n我的读书笔记 | 总览 2024-11-23\u0026middot;72 words 阅读 书单汇总 Others # There are some other kinds of articles such as some reflections in my life, you can find them in the Articles page.\nPhotographs # Coming soon.\nRoadmap # You can read the page shown below to get more information about the roadmap of this site.\nRoadmap 2024-11-17\u0026middot;98 words ","date":"2024-11-23","externalUrl":null,"permalink":"/guide/","section":"home","summary":"","title":"Guide","type":"page"},{"content":"","date":"2024-11-23","externalUrl":null,"permalink":"/tags/%E4%B9%A6%E5%8D%95%E6%B1%87%E6%80%BB/","section":"Tags","summary":"","title":"书单汇总","type":"tags"},{"content":" 心理学 # 《被讨厌的勇气》读书笔记 | 接纳自我,勇敢地感受生活 2024-12-07\u0026middot;90 words 阅读 读书笔记 心理学 个人成长 人际交往 《我有自己的宇宙》读书笔记 | 年轻人的职场生存之道 2024-08-10\u0026middot;94 words 阅读 读书笔记 心理学 个人成长 职场之道 经济学 # 《小岛经济学》读书笔记 | 深入浅出经济学原理 2024-12-15\u0026middot;66 words 阅读 读书笔记 经济学 《富爸爸 穷爸爸》读书笔记 | 财商教育启蒙之作 2024-05-07\u0026middot;35 words 阅读 读书笔记 经济学 投资理财 个人成长 # 《早起的奇迹》读书笔记 | 制定你的早起计划 2024-07-09\u0026middot;89 words 阅读 读书笔记 个人成长 个人健康 《认知觉醒》读书笔记 | 开启心智,掌控自己的生活 2024-06-05\u0026middot;115 words 阅读 读书笔记 个人成长 学习方法 个人健康 # 《睡眠革命》读书笔记 | 了解你的昼夜节律 2024-07-19\u0026middot;146 words 阅读 读书笔记 个人健康 ","date":"2024-11-23","externalUrl":null,"permalink":"/articles/%E6%88%91%E7%9A%84%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E6%80%BB%E8%A7%88/","section":"Articles","summary":"","title":"我的读书笔记 | 总览","type":"posts"},{"content":" Functions and Styles # 增加链接文字高亮显示(鼠标悬于其上时) 增加网站评论功能 增加文章阅读数和点赞数统计功能 分开展示文章卡片中的 category 和 tag Contents # 迁移之前写的英语学习笔记到本站(增加英语分类) 梳理个人技能树(增加个人随笔分类) 梳理大模型推理知识全景 梳理 Python 基本语法(装饰器 / typing) 梳理 vLLM 学习笔记(自定义算子 / Multi-LoRA 推理加速) 增加相册展示页 写 2024 年年度读书报告 写《非暴力沟通》读书笔记 写《娱乐至死》读书笔记 写《费曼学习法》读书笔记 写《海绵阅读法》读书笔记 2024-12-18: 写《小岛经济学》读书笔记 2024-12-08: 写《被讨厌的勇气》读书笔记 2024-12-05: 梳理大模型微调知识全景 2024-11-23: 增加网站导航页 2024-11-23: 增加技术文章总览 2024-11-23: 增加我的书单总览 2024-11-19: 个人介绍加入技术栈和开源贡献 2024-11-19: 迁移之前写的技术博客到本站 2024-11-18: 完善个人介绍页 2024-11-13: 梳理大模型 LoRA 微调的数学原理 2024-10-23: 梳理 Ascend aclnn 算子开发入门学习笔记 2024-10-18: 梳理 Git 实践案例——合并多个分散的 commit 节点 2024-09-24: 梳理 Ascend NPU 远程开发环境搭建教程 2024-09-04: 梳理 IDEA Maven 项目编译或启动报错解决方法 2024-08-31: 梳理 Ascend NPU 架构学习笔记 2024-08-26: 梳理 NVIDIA GPU 架构学习笔记 2024-08-10: 写《我有自己的宇宙》读书笔记 2024-07-19: 写《睡眠革命》读书笔记 2024-07-09: 写《早起的奇迹》读书笔记 2024-06-05: 写《认知觉醒》读书笔记 2024-05-07: 写《富爸爸穷爸爸》读书笔记 ","date":"2024-11-17","externalUrl":null,"permalink":"/roadmap/","section":"home","summary":"","title":"Roadmap","type":"page"},{"content":" 一、概述 # LoRA(Low-Rank Adaptation,低秩适配器)是目前非常热门的大模型微调技术之一,网上已经有许多关于其原理的分析和讲解,本文将着重从 LoRA 背后的数学原理进行解读。\n二、背景介绍 # 2.1 基本概念 # 大模型微调(Fine-tuning):基于已经训练好的预训练模型,针对特定的下游任务,在特定领域的数据集上进行二次训练,以提升模型在特定任务上的表现。\n全量微调:在下游任务的训练中,对预训练模型的每一个参数都做更新(训练代价昂贵); 局部微调:冻结(不更新)预训练模型的权重,只对部分增量权重进行训练,从而有效降低训练的代价(实用性更高)。 2.2 研究现状 # 在 LoRA 微调技术出现之前,现有的大模型微调技术存在以下缺点:\nAdapter Tuning:在模型中添加额外的 Adapter 层,并只针对这些 Adapter 的权重进行训练。这将导致模型整体的层数变深,从而使模型的训练/推理耗时增加(因为新增的 Adapter 层与模型中的其它层是串行的,无法充分利用硬件能力进行并行计算); Prefix Tuning:对输入数据增加前缀(prefix),并只针对这些 prefix token 进行训练(prefix 的作用是引导模型提取输入中的特定信息,进而更好地生成结果)。这将导致输入数据中有效信息的减少,因为一般输入数据的长度是固定的,而增加的 prefix 将会占用原本输入数据的空间,从而使输入文字表达力下降。 三、LoRA 基本原理 # 3.1 LoRA 模型结构 # LoRA 使用 \\(\\mathbf{A}\\) 和 \\(\\mathbf{B}\\) 两个与原模型并行的低秩矩阵来代替原本的增量权重矩阵 \\(\\mathbf{\\Delta W}\\),从而可以在保证模型性能的同时,有效降低需要训练的参数量。\n对于输入 \\(\\mathbf{x}\\),模型的输出 \\(\\mathbf{h}\\) 为:\n$$ \\mathbf{h} = \\mathbf{W}\\mathbf{x} + \\mathbf{\\Delta W}\\mathbf{x} \\approx \\mathbf{W}\\mathbf{x} + \\mathbf{B}\\mathbf{A}\\mathbf{x} $$\n其中 \\(\\mathbf{W}、\\mathbf{\\Delta W} \\in \\mathbb{R}^{d \\times d}\\),\\(\\mathbf{A} \\in \\mathbb{R}^{r \\times d}\\)(初始化为正态分布),\\(\\mathbf{B} \\in \\mathbb{R}^{d \\times r}\\)(初始化为零),\\(r\\) 为矩阵 \\(\\mathbf{\\Delta W}\\) 的秩。\n3.2 LoRA 的优点 # 节约内存:在训练时,需要更新的权重数量从 \\(d \\times d\\) 下降到了 \\(2 \\times d \\times r\\),显著降低了模型微调对硬件性能的要求; 保证性能:在推理时,可以将 LoRA 的权重直接合并到预训练权重中,从而可以保证推理的速度不受影响; 灵活易用:针对不同的下游任务,用户可以训练不同的 LoRA,并且在微调完成后,只需要保存新增的这一部分权重(不同任务间共享预训练模型的权重),相比于存储整个模型的权重,只需要极少的内存。 四、LoRA 的数学原理与论文实验分析 # 简单介绍完了 LoRA 的基本原理,下面将针对以下几个问题进行分析和说明,这些问题也是我在刚开始学习 LoRA 时产生的疑惑。\n为什么可以将 \\(\\mathbf{\\Delta W}\\) 拆分为 \\(\\mathbf{A}\\) 和 \\(\\mathbf{B}\\)?这样做为什么是有效的? \\(r\\) 作为一个超参数,它的取值是如何影响 LoRA 的表现的? \\(\\mathbf{A}\\) 和 \\(\\mathbf{B}\\) 为什么要这样初始化? 4.1 SVD 定理 # 为什么可以将 \\(\\mathbf{\\Delta W}\\) 拆分为 \\(\\mathbf{A}\\) 和 \\(\\mathbf{B}\\)?这样做为什么是有效的?\n在回答这个问题之前,我们需要先了解一个基本概念——SVD(Singular Value Decomposition,奇异值分解)。\n对于一个非零的 \\(m \\times n\\) 实矩阵 \\(\\mathbf{M} \\in \\mathbb{R}^{m \\times n}\\),我们可以将其表示为以下三个实矩阵乘积形式的运算:\n$$ \\mathbf{M} = \\mathbf{U}\\mathbf{Σ}\\mathbf{V}^{T} $$\n其中 \\(\\mathbf{U}\\) 是 \\(m\\) 阶正交矩阵,\\(\\mathbf{V}\\) 是 \\(n\\) 阶正交矩阵,\\(\\mathbf{Σ}\\) 是由降序排列的对角线元素组成的 \\(m \\times n\\) 矩形对角矩阵。\n$$ \\mathbf{Σ} = diag(\\sigma_{1}, \\sigma_{2}, \u0026hellip;, \\sigma_{p}) \\\\ \\sigma_{1} \\geq \\sigma_{2} \\geq \u0026hellip; \\geq \\sigma_{p} \\geq 0 $$\n\\(\\mathbf{U}\\mathbf{Σ}\\mathbf{V}^{T}\\) 称为矩阵 \\(\\mathbf{M}\\) 的奇异值分解,\\(\\sigma_{i}\\) 称为矩阵 \\(\\mathbf{M}\\) 的奇异值,\\(\\mathbf{U}\\) 的列向量称为左奇异向量,\\(\\mathbf{V}\\) 的列向量称为右奇异向量。\n“正交矩阵”:\n每两行/列之间互相正交(线性无关),且都是单位向量; 是方阵; 元素都是实数; 其转置矩阵同时也是其逆矩阵。 矩阵 \\(\\mathbf{M}\\) 的奇异值分解一定存在,但不一定唯一。\n上面的矩阵分解方式又叫做完全奇异值分解,而实际中更加常用的则是其紧凑形式和截断形式。\n紧奇异值分解:\n设有 \\(m \\times n\\) 实矩阵 \\(\\mathbf{M}\\),其秩为 \\(r\\),则有紧奇异值分解为:\n$$ \\mathbf{M} = \\mathbf{U}_{r}\\mathbf{Σ}_{r}\\mathbf{V}^{T}_{r} $$\n其中 \\(\\mathbf{U}_{r}\\) 是 \\(m \\times r\\) 矩阵,\\(\\mathbf{V}_{r}\\) 是 \\(n \\times r\\) 矩阵,\\(\\mathbf{Σ}_{r}\\) 是 \\(r\\) 阶对角矩阵。矩阵 \\(\\mathbf{U}_{r}\\) 由完全奇异值分解中 \\(\\mathbf{U}\\) 的前 \\(r\\) 列构成,矩阵 \\(\\mathbf{V}_{r}\\) 由完全奇异值分解中 \\(\\mathbf{V}\\) 的前 \\(r\\) 列构成,矩阵 \\(\\mathbf{Σ}_{r}\\) 由完全奇异值分解中 \\(\\mathbf{Σ}\\) 的前 \\(r\\) 个对角线元素构成。\n截断奇异值分解:\n与“紧奇异值分解”类似,只不过这里只保留最大的 \\(k\\) 个奇异值(\\(k \u0026lt; r\\))及其对应的奇异向量,有:\n$$ \\mathbf{M} \\approx \\mathbf{U}_{k}\\mathbf{Σ}_{k}\\mathbf{V}^{T}_{k} $$\n在实际应用中,常常需要对矩阵的数据进行压缩,将其近似表示,奇异值分解提供了一种方法。奇异值分解是在平方损失(弗罗贝尼乌斯范数)意义下对矩阵的最优近似。紧奇异值分解对应着无损压缩,截断奇异值分解对应着有损压缩。\n因此,SVD 的原理告诉我们,可以用低秩矩阵来近似地表达原矩阵。\n“弗罗贝尼乌斯范数”: $$ \\lVert A \\rVert_{F} = \\bigg( \\sum_{i = 1}^{m}\\sum_{j = 1}^{n} (a_{ij})^{2} \\bigg)^{\\frac{1}{2}}, \\mathbf{A} \\in \\mathbb{R}^{m \\times n} $$ “奇异值分解在统计中的主要应用为主成分分析(PCA)。数据集的特征值(在 SVD 中用奇异值表征)按照重要性排列,降维的过程就是舍弃不重要的特征向量的过程,而剩下的特征向量张成空间为降维后的空间。”\n具体地,在 LoRA 中,将矩阵 \\(\\mathbf{U}\\) 和 \\(\\mathbf{Σ}\\) 合并为了一个矩阵 \\(\\mathbf{B} \\in \\mathbb{R}^{m \\times k}\\),将矩阵 \\(\\mathbf{V}^{T}\\) 表示为了矩阵 \\(\\mathbf{A} \\in \\mathbb{R}^{k \\times n}\\),从而可以用更少的数据量来表示矩阵 \\(\\mathbf{\\Delta W}\\)。\n在实际微调中,由于事先并不知道矩阵 \\(\\mathbf{\\Delta W}\\) 中具体的值(除非我们先全参微调一遍,但是这样的话就没必要用 LoRA 了),我们无法直接计算出 \\(\\mathbf{\\Delta W}\\) 的 SVD 分解结果,因此论文作者将秩 \\(r\\) 作为一个超参数,并让模型在训练中自己去学习矩阵 \\(\\mathbf{A}\\) 和 \\(\\mathbf{B}\\) 的值。\n4.2 论文实验分析 # 4.2.1 实验一:不同微调方法的效果对比 # 在论文中,作者将 LoRA 与其它微调方法在多种场景下的表现进行了对比,如下图所示:\n实验结论:LoRA 在显著降低了微调参数量的同时,还能在大部分场景下保证最优的性能。\n4.2.2 实验二:不同 \\(r\\) 对微调效果的影响 # \\(r\\) 作为一个超参数,它的取值是如何影响 LoRA 的表现的?\n作者对比了当 \\(r\\) 取不同值时,对模型不同的层应用 LoRA 的效果,如下图所示:\n可以看出:\n当我们同时对 \\(\\mathbf{W}_{q}\\) 和 \\(\\mathbf{W}_{v}\\) 应用 LoRA 时,哪怕取了一个很小的 \\(r=1\\),其效果也超过了单独对 \\(\\mathbf{W}_{q}\\) 应用 LoRA 且 \\(r=64\\) 时的表现; 在同一行(同样的 Weight Type)内,取 \\(r=1\\) 与 \\(r=64\\) 的结果差异不大。 实验结论:增量权重矩阵 \\(\\mathbf{\\Delta W}\\) 的秩可能很小,因此我们能够用秩很小的两个矩阵来近似表达该矩阵。\n到此为止还没完,作者还做了更进一步的实验,来证明 LoRA 这种分解方式的有效性。 (´・_・`)\n4.2.3 实验三:\\(\\mathbf{\\Delta W}\\) 的左奇异矩阵 \\(\\mathbf{U}\\) 不同子空间的相似度对比 # 作者对比了 \\(\\mathbf{U}_{A_{r=8}}\\) 和 \\(\\mathbf{U}_{A_{r=64}}\\) 不同维度子空间的相似度,其中 \\(\\mathbf{U}_{A_{r=8}}\\) 为 \\(r=8\\) 时矩阵 \\(\\mathbf{A}\\) 的左奇异矩阵,\\(\\mathbf{U}_{A_{r=64}}\\) 同理。\n子空间相似度的计算方式:\n这里的 \\(\\lVert \u0026hellip; \\rVert_{F}\\) 为上面提到的“弗罗贝尼乌斯范数”。\n实验结果如下图所示(图 3 和图 4 分别为图 1 和图 2 的左下角部分):\n这个图可能比较难理解,下面举例进行说明:\n当 \\(i=1\\) 时,\\(j\\) 从 \\(1\\) 取到 \\(64\\),发现颜色都比较浅(相似度高),说明当 \\(r=8\\) 时,\\(\\mathbf{\\Delta W}\\) 分解出的矩阵 \\(\\mathbf{A}_{r=8}\\) 的左奇异矩阵 \\(\\mathbf{U}_{A_{r=8}}\\) 的第一个左奇异向量表示的特征(或者说信息)与 \\(\\mathbf{A}_{r=64}\\) 的前 64 个左奇异向量组成的子空间表示的特征重合度很高,即高秩矩阵(\\(r=64\\))的大部分信息都已经包含在了低秩矩阵(\\(r=8\\))的前几维子空间中。\n实验结论:越靠前的奇异向量(奇异值按降序排序)包含的信息越多(或者说越重要),不管 \\(r\\) 取多大,前几维(如 \\(r\u0026lt;8\\))子空间表示的信息都是差不多的,越后面(如 \\(8\u0026lt;r\u0026lt;64\\))的子空间包含的有效信息越少,噪声越多,再次证明了用低秩矩阵近似表达高秩矩阵的有效性。\n4.2.4 实验四:\\(\\mathbf{\\Delta W}\\) 与 \\(\\mathbf{W}\\) 不同子空间的相似度对比 # 与实验三类似,作者还比较了 \\(\\mathbf{\\Delta W}\\) 与 \\(\\mathbf{W}\\) 不同维度子空间的相似度,如下图所示: 其中,\\(i\\) 表示使用预训练权重矩阵 \\(\\mathbf{W}\\) 的前 \\(i\\) 个左奇异向量组成的子空间,\\(j\\) 表示使用 \\(\\mathbf{\\Delta W}\\) 的前 \\(j\\) 个左奇异向量组成的子空间(\\(j \\leq r\\))。\n可以看出,\\(\\mathbf{W}\\) 中靠前(靠上)的奇异向量组成的子空间与 \\(\\mathbf{\\Delta W}\\) 的子空间相似度并不高,反而是 \\(\\mathbf{W}\\) 中最后的一些奇异向量与 \\(\\mathbf{\\Delta W}\\) 中的奇异向量有较高的相似度。\n实验结论:\\(\\mathbf{\\Delta W}\\) 中学习到的特征都是原来预训练权重矩阵中没有被强调的部分,说明 \\(\\mathbf{\\Delta W}\\) 能够有效学习到针对特定下游任务、区别于原特征的新特征,证明了 LoRA 在微调方面的有效性。\n说明:这里只挑了论文中我比较感兴趣的几个实验来进行分析,对于论文中的其它实验,感兴趣的读者可以自行阅读论文进行了解。\n4.3 缩放因子 \\(\\alpha\\) # LoRA 引入了一个超参数 \\(\\alpha\\),可以看作是学习率,同时也代表了 LoRA 对从特定下游任务学习而来的特征的放大程度,有:\n$$ \\mathbf{h} = \\mathbf{W}\\mathbf{x} + \\mathbf{\\Delta W}\\mathbf{x} \\approx \\mathbf{W}\\mathbf{x} + \\frac{\\alpha}{r}\\mathbf{B}\\mathbf{A}\\mathbf{x} $$\n在实际微调中,一般先随机设置一个 \\(r\\),并让 \\(\\alpha = r\\),然后保持 \\(\\alpha\\) 不变,通过不断调整 \\(r\\) 的值来调整微调的效果。\n五、参考资料 # LoRA: Low-Rank Adaptation of Large Language Models 《统计学习方法》 图解大模型微调系列之:大模型低秩适配器 LoRA(原理篇) ","date":"2024-11-13","externalUrl":null,"permalink":"/articles/%E5%A4%A7%E6%A8%A1%E5%9E%8B-lora-%E5%BE%AE%E8%B0%83%E7%9A%84%E6%95%B0%E5%AD%A6%E5%8E%9F%E7%90%86/","section":"Articles","summary":"","title":"大模型 LoRA 微调的数学原理","type":"posts"},{"content":" Overview # Shanshan Shen | 申杉杉\nSeptember 18th, 1998\nHello, I am currently a software engineer working at Huawei Ascend, engaging in AI Infra and LLM. I have contributed to some open-source projects such as vLLM, llama.cpp and ChatTTS, to build a easy-to-use software ecosystem for Ascend. Before this, I was a student at Beijing Jiao Tong University (BSc/MSc), majoring in communication engineering. You can see more projects at my Github, or see my posts at CSDN and Zhihu.\nExperience # Huawei Software Engineer July \u0026#39;23 - Present Working at Huawei from MetaCRM to Ascend, engaging in AI Infra and software development. Beijing Jiao Tong University Postgraduate Student September \u0026#39;21 - June \u0026#39;23 Studying at School of Electronic and Information Engineering, engaging in security research of wireless communication. Beijing Jiao Tong University Undergraduate Student September \u0026#39;16 - June \u0026#39;20 Studying at School of Electronic and Information Engineering, majoring in communication engineering. Programming Skills # Open Source Contribution # I am a contributor to many open source projects on GitHub, which are shown below.\nggerganov/llama.cpp LLM inference in C/C++ C\u0026#43;\u0026#43; 68755 9872 2noise/ChatTTS A generative speech model for daily dialogue. Python 32444 3523 Hobbies # In my spare time, I have many hobbies such as reading books, playing badminton and doing exercise. I am not only a programmer, but also a guitarist loving play both acoustic and electric guitars.\n📚 Books 🎸 Guitars 🎵 Albums 🏸 Badminton 📚 Books # I love reading books over last ten years, and here are books I highly recommended.\n🎸 Guitars # I love listening to guitar music especially fingerstyle songs and may do some recordings myself when I come across some beautiful songs. My guitars and gears used for recording are shown below, and there are also my albums that have been published so far. You can find my music at bilibili or NetEase Cloud Music.\nFender Telecaster Player Electric Guitar May \u0026#39;20 - Present This is my electric guitar which is made of Alderwood (body) and Maplewood (fingerboard), and is equipped with pickup system of Alnico 5 single coil. Morris s101 Ⅲ Acoustic Guitar November \u0026#39;18 - Present This is my favorite guitar which is made of Sitka Spruce (panel) and Indian Rosewood (backboard), and is equipped with pickup system of Lim magnet (passive) and Lim piezo. 🎵 Albums # Acoustic Guitar Cover Collection Guitar Recordings March \u0026#39;18 - January \u0026#39;23 This album is a collection of multi-track acoustic guitar cover which contains 11 songs that I like, such as In The Rain and Crack. You can listen to these songs at here. Fingerstyle Guitar Cover Collection Guitar Recordings March \u0026#39;18 - January \u0026#39;23 This album is a collection of fingerstyle guitar and ukulele cover which contains 11 songs that I like, such as Tickets to Starry Sky and Rain stops, Good-bye. You can listen to these songs at here. 🏸 Badminton # I love playing badminton and will participate in this activity 1~4 times a week. Awards I have received are shown below. Welcome to battle with me!\nFourth place in the Inter-Department Team Competition of Xian Huawei at 2024 Second place in the Quality and Process IT Department Team Competition of Xian Huawei at 2024 Third place in the Inter-School Cup of Beijing Jiao Tong University at 2023 Contact me # Feel free to drop me an email, gmail and qq-mail are both available.\nYou can also have my WeChat through the picture below:\nRemember to add a explaination before sending a request!\n","date":"2024-11-02","externalUrl":null,"permalink":"/about/","section":"home","summary":"","title":"About me","type":"page"},{"content":"","date":"2024-10-23","externalUrl":null,"permalink":"/tags/ai-infra/","section":"Tags","summary":"","title":"AI Infra","type":"tags"},{"content":"","date":"2024-10-23","externalUrl":null,"permalink":"/tags/ascend/","section":"Tags","summary":"","title":"Ascend","type":"tags"},{"content":" 一、概述 # 什么是算子?\n在 AI 框架中,算子一般指一些最基本的代数运算(如:矩阵加法、矩阵乘法等),多个算子之间也可以根据需要组合成更加复杂的融合算子(如:flash-attention 算子等)。算子的输入和输出都是 Tensor(张量)。\n融合算子:将多个独立的“小算子”融合成一个“大算子”,多个小算子的功能和大算子的功能等价,但融合算子在性能或者内存等方面优于独立的小算子。\n另外,算子更多地是 AI 框架中的一个概念,在硬件底层算子具体的执行部分,一般叫做 Kernel(核函数)。\n下面将首先对算子开发中涉及的一些基本概念进行介绍(可以用 CUDA 作为参考,大部分概念都是相似的),然后会以具体的矩阵加法和乘法算子的代码实现为例进行讲解。\n二、基本概念 # 2.1 Device # Host:一般指 CPU(负责调度); Device:一般指 GPU、NPU(负责计算)。 2.2 Context # Context 主要负责管理线程中各项资源的生命周期。\n一般来说,Context 与其它概念之间具有以下关系:\n一个进程可以创建多个 Context; 一个线程只能同时使用一个 Context,该 Context 对应一个唯一的 Device,线程可以通过切换 Context 来切换要使用的 Device; 一个 Device 可以拥有多个 Context,但同时只能使用一个 Context。 每一个线程都具有一个默认的 Context,无需手动创建,也无法被删除。我们也可以手动创建更多的 Context,使用后需要及时释放。另外,在线程中,默认使用最后一次创建的 Context。\n2.3 Stream # Stream 主要负责维护一些异步操作的执行顺序,这些操作包括:\nHost 到 Device 的数据传输; 调用 Kernel; 其它由 Host 发起并由 Device 执行的动作。 说明:在 GPU/NPU 上调用的函数,被称为核函数(Kernel function)。核函数使用 __global__ 关键字进行定义,会被 GPU/NPU 上的多个线程执行。\n同一个 Stream 里的操作是严格串行的(顺序执行),而不同 Stream 之间则可以并行执行。来自不同 Stream 的 Kernel 可以共享 GPU/NPU 的内核并发执行。\n一般来说,Context 与其它概念之间具有以下关系:\n一个线程或 Context 中可以创建多个 Stream; 不同线程或 Context 间的 Stream 在 Device 上相互隔离。 每一个 Context 都具有一个默认的 Stream,无需手动创建,也无法被删除。我们也可以手动创建更多的 Stream,并将多个操作分配到不同的 Stream 上,这样就可以实现多个操作的并行,Stream 使用后需要及时释放。\n2.4 Task # Task 或 Kernel,是 Device 上真正的任务执行体。\n一般来说,Task 与其它概念之间具有以下关系:\n一个 Stream 中可以下发多个 Task; 多个 Task 之间可以插入 Event,用于同步不同 Stream 之间的 Task。 参考资料:\nAscend 算子开发基本概念 CUDA 基础 CUDA 介绍 三、单算子开发 # 官方介绍:\nAscendCL(Ascend Computing Language)是一套用于在昇腾平台上开发深度神经网络应用的 C 语言 API 库,提供运行资源管理、内存管理、模型加载与执行、算子加载与执行、媒体数据处理等 API,能够实现利用昇腾硬件计算资源、在昇腾 CANN 平台上进行深度学习推理计算、图形图像预处理、单算子加速计算等能力。简单来说,就是统一的 API 框架,实现对所有资源的调用。\n面向算子开发场景的编程语言 Ascend C,原生支持 C/C++ 标准规范,最大化匹配用户开发习惯;通过多层接口抽象、自动并行计算、孪生调试等关键技术,极大提高算子开发效率,助力 AI 开发者低成本完成算子开发和模型调优部署。\n3.1 单算子调用方式 # 单算子 API 执行: 直接调用 CANN 已经提供的算子 API; 使用 Ascend C 开发并调用自定义算子。 单算子模型执行。 3.2 单算子 API 执行 # NN 算子; DVPP 算子; 融合算子; …… 更详细的算子 API 文档可以参考:算子加速库接口。\n两段式接口:单算子 API 执行时,针对每个算子,都需要依次先调用 aclxxXxxGetWorkspaceSize() 接口获取算子执行需要的 workspace 内存大小、再调用 aclxxXxx() 接口执行算子。\n参考资料:\n单算子调用基础知识 Ascend 开源融合算子 四、代码实现 # 本小节将以 aclnnAdd 和 aclnnMatmul 算子为例,实现具体的代码。\n更详细的 API 文档可以参考:\n加法算子:aclnnAdd 乘法算子:aclnnMatmul 4.1 环境搭建 # 快速安装昇腾环境 基于 EulerOS \u0026amp; Ascend NPU 搭建 PyTorch 远程开发环境 4.2 单算子开发流程 # 4.3 常见参数说明 # strides:描述 Tensor 维度上相邻两个元素的间隔,详见非连续的 Tensor; workspace:在 device 侧申请的 workspace 内存地址; workspaceSize:在 device 侧申请的 workspace 大小; executor:算子执行器,实现了算子的计算流程; aclnnStatus:详见aclnn 返回码。 注意:\n多个输入数据之间,数据类型需要满足互推导关系:当一个 API(如 aclnnAdd()、aclnnMul() 等)输入的 Tensor 数据类型不一致时,API 内部会推导出一个数据类型,将输入数据转换成该数据类型进行计算; 多个输入数据之间,shape 需要满足广播关系:在某些情况下,较小的数组可以“广播至”较大的数组,使两者shape互相兼容; 更多算子 API 信息详见:CANN 社区版开发文档,位置:【CANN 社区版 -\u0026gt; 8.0.RC3.alpha003 -\u0026gt; API 参考 -\u0026gt; 算子加速库接口 -\u0026gt; NN 算子接口】。 4.4 矩阵加法算子 # 目录结构:\nsss@xxx:~/xxx/add$ tree . |-- CMakeLists.txt |-- build `-- test_add.cpp CMakeLists:\n# Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. # CMake lowest version requirement cmake_minimum_required(VERSION 3.14) # 设置工程名 project(ACLNN_EXAMPLE) # Compile options add_compile_options(-std=c++11) # 设置编译选项 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \u0026#34;./bin\u0026#34;) set(CMAKE_CXX_FLAGS_DEBUG \u0026#34;-fPIC -O0 -g -Wall\u0026#34;) set(CMAKE_CXX_FLAGS_RELEASE \u0026#34;-fPIC -O2 -Wall\u0026#34;) # 设置可执行文件名(如opapi_test),并指定待运行算子文件*.cpp所在目录 add_executable(opapi_add_test ../test_add.cpp) # 设置ASCEND_PATH(CANN软件包目录,请根据实际路径修改)和INCLUDE_BASE_DIR(头文件目录) if(NOT \u0026#34;$ENV{ASCEND_CUSTOM_PATH}\u0026#34; STREQUAL \u0026#34;\u0026#34;) set(ASCEND_PATH $ENV{ASCEND_CUSTOM_PATH}) else() set(ASCEND_PATH \u0026#34;/home/sss/Ascend/ascend-toolkit/latest\u0026#34;) # 示例:/usr/local/Ascend/ascend-toolkit/latest endif() set(INCLUDE_BASE_DIR \u0026#34;${ASCEND_PATH}/include\u0026#34;) include_directories( ${INCLUDE_BASE_DIR} ${INCLUDE_BASE_DIR}/aclnn ) # 设置链接的动态库文件路径 # arch表示操作系统架构,os表示操作系统 target_link_libraries(opapi_test PRIVATE ${ASCEND_PATH}/lib64/libascendcl.so ${ASCEND_PATH}/lib64/libnnopbase.so ${ASCEND_PATH}/lib64/libopapi.so) # 可执行文件在CMakeLists文件所在目录的bin目录下 install(TARGETS opapi_test DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 编译构建:\nmkdir build cd build cmake .. -DCMAKE_CXX_COMPILER=g++ -DCMAKE_SKIP_RPATH=TRUE make test_add 代码:\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026#34;acl/acl.h\u0026#34; #include \u0026#34;aclnnop/aclnn_add.h\u0026#34; #define CHECK_RET(cond, return_expr) \\ do { \\ if (!(cond)) { \\ return_expr; \\ } \\ } while (0) #define LOG_PRINT(message, ...) \\ do { \\ printf(message, ##__VA_ARGS__); \\ } while (0) int64_t GetShapeSize(const std::vector\u0026lt;int64_t\u0026gt;\u0026amp; shape) { int64_t shape_size = 1; for (auto i : shape) { shape_size *= i; } return shape_size; } int Init(int32_t deviceId, aclrtStream* stream) { // 固定写法,AscendCL初始化 auto ret = aclInit(nullptr); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclInit failed. ERROR: %d\\n\u0026#34;, ret); return ret); ret = aclrtSetDevice(deviceId); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtSetDevice failed. ERROR: %d\\n\u0026#34;, ret); return ret); ret = aclrtCreateStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtCreateStream failed. ERROR: %d\\n\u0026#34;, ret); return ret); return 0; } template \u0026lt;typename T\u0026gt; int CreateAclTensor(const std::vector\u0026lt;T\u0026gt;\u0026amp; hostData, const std::vector\u0026lt;int64_t\u0026gt;\u0026amp; shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size = GetShapeSize(shape) * sizeof(T); // 调用aclrtMalloc申请device侧内存 auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtMalloc failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtMemcpy failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 计算连续tensor的strides std::vector\u0026lt;int64_t\u0026gt; strides(shape.size(), 1); for (int64_t i = shape.size() - 2; i \u0026gt;= 0; i--) { strides[i] = shape[i + 1] * strides[i + 1]; } // 调用aclCreateTensor接口创建aclTensor *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } int main() { // 1. (固定写法)device/stream初始化, 参考AscendCL对外接口列表 // 根据自己的实际device填写deviceId int32_t deviceId = 0; aclrtStream stream; auto ret = Init(deviceId, \u0026amp;stream); // check根据自己的需要处理 CHECK_RET(ret == 0, LOG_PRINT(\u0026#34;Init acl failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 2. 构造输入与输出,需要根据API的接口自定义构造 std::vector\u0026lt;int64_t\u0026gt; selfShape = {4, 2}; std::vector\u0026lt;int64_t\u0026gt; otherShape = {4, 2}; std::vector\u0026lt;int64_t\u0026gt; outShape = {4, 2}; void* selfDeviceAddr = nullptr; void* otherDeviceAddr = nullptr; void* outDeviceAddr = nullptr; aclTensor* self = nullptr; aclTensor* other = nullptr; aclScalar* alpha = nullptr; aclTensor* out = nullptr; std::vector\u0026lt;float\u0026gt; selfHostData = {0, 1, 2, 3, 4, 5, 6, 7}; std::vector\u0026lt;float\u0026gt; otherHostData = {1, 1, 1, 2, 2, 2, 3, 3}; std::vector\u0026lt;float\u0026gt; outHostData = {0, 0, 0, 0, 0, 0, 0, 0}; float alphaValue = 1.2f; // 创建self aclTensor ret = CreateAclTensor(selfHostData, selfShape, \u0026amp;selfDeviceAddr, aclDataType::ACL_FLOAT, \u0026amp;self); CHECK_RET(ret == ACL_SUCCESS, return ret); // 创建other aclTensor ret = CreateAclTensor(otherHostData, otherShape, \u0026amp;otherDeviceAddr, aclDataType::ACL_FLOAT, \u0026amp;other); CHECK_RET(ret == ACL_SUCCESS, return ret); // 创建alpha aclScalar alpha = aclCreateScalar(\u0026amp;alphaValue, aclDataType::ACL_FLOAT); CHECK_RET(alpha != nullptr, return ret); // 创建out aclTensor ret = CreateAclTensor(outHostData, outShape, \u0026amp;outDeviceAddr, aclDataType::ACL_FLOAT, \u0026amp;out); CHECK_RET(ret == ACL_SUCCESS, return ret); // 3. 调用CANN算子库API,需要修改为具体的算子接口 uint64_t workspaceSize = 0; aclOpExecutor* executor; // 调用aclnnAdd第一段接口 ret = aclnnAddGetWorkspaceSize(self, other, alpha, out, \u0026amp;workspaceSize, \u0026amp;executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclnnAddGetWorkspaceSize failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void* workspaceAddr = nullptr; if (workspaceSize \u0026gt; 0) { ret = aclrtMalloc(\u0026amp;workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;allocate workspace failed. ERROR: %d\\n\u0026#34;, ret); return ret;); } // 调用aclnnAdd第二段接口 ret = aclnnAdd(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclnnAdd failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 4.( 固定写法)同步等待任务执行结束 ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtSynchronizeStream failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改 auto size = GetShapeSize(outShape); std::vector\u0026lt;float\u0026gt; resultData(size, 0); ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;copy result from device to host failed. ERROR: %d\\n\u0026#34;, ret); return ret); for (int64_t i = 0; i \u0026lt; size; i++) { LOG_PRINT(\u0026#34;result[%ld] is: %f\\n\u0026#34;, i, resultData[i]); } // 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改 aclDestroyTensor(self); aclDestroyTensor(other); aclDestroyScalar(alpha); aclDestroyTensor(out); // 7. 释放device资源,需要根据具体API的接口定义修改 aclrtFree(selfDeviceAddr); aclrtFree(otherDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize \u0026gt; 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; } 运行程序:\n./opapi_add_test 运行结果:\nsss@xxx:~/xxx/add/build/bin$ ./opapi_test result[0] is: 1.200000 result[1] is: 2.200000 result[2] is: 3.200000 result[3] is: 5.400000 result[4] is: 6.400000 result[5] is: 7.400000 result[6] is: 9.600000 result[7] is: 10.600000 4.5 矩阵乘法算子 # 目录结构:\nsss@xxx:~/xxx/mul$ tree . |-- CMakeLists.txt |-- build `-- test_mul.cpp CMakeLists:\n# Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. # CMake lowest version requirement cmake_minimum_required(VERSION 3.14) # 设置工程名 project(ACLNN_EXAMPLE) # Compile options add_compile_options(-std=c++11) # 设置编译选项 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \u0026#34;./bin\u0026#34;) set(CMAKE_CXX_FLAGS_DEBUG \u0026#34;-fPIC -O0 -g -Wall\u0026#34;) set(CMAKE_CXX_FLAGS_RELEASE \u0026#34;-fPIC -O2 -Wall\u0026#34;) # 设置可执行文件名(如opapi_test),并指定待运行算子文件*.cpp所在目录 add_executable(opapi_mul_test ../test_mul.cpp) # 设置ASCEND_PATH(CANN软件包目录,请根据实际路径修改)和INCLUDE_BASE_DIR(头文件目录) if(NOT \u0026#34;$ENV{ASCEND_CUSTOM_PATH}\u0026#34; STREQUAL \u0026#34;\u0026#34;) set(ASCEND_PATH $ENV{ASCEND_CUSTOM_PATH}) else() set(ASCEND_PATH \u0026#34;/home/sss/Ascend/ascend-toolkit/latest\u0026#34;) # 示例:/usr/local/Ascend/ascend-toolkit/latest endif() set(INCLUDE_BASE_DIR \u0026#34;${ASCEND_PATH}/include\u0026#34;) include_directories( ${INCLUDE_BASE_DIR} ${INCLUDE_BASE_DIR}/aclnn ) # 设置链接的动态库文件路径 # arch表示操作系统架构,os表示操作系统 target_link_libraries(opapi_test PRIVATE ${ASCEND_PATH}/lib64/libascendcl.so ${ASCEND_PATH}/lib64/libnnopbase.so ${ASCEND_PATH}/lib64/libopapi.so) # 可执行文件在CMakeLists文件所在目录的bin目录下 install(TARGETS opapi_test DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 编译构建:\nmkdir build cd build cmake .. -DCMAKE_CXX_COMPILER=g++ -DCMAKE_SKIP_RPATH=TRUE make test_mul 代码:\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026#34;acl/acl.h\u0026#34; #include \u0026#34;aclnnop/aclnn_matmul.h\u0026#34; #define CHECK_RET(cond, return_expr) \\ do { \\ if (!(cond)) { \\ return_expr; \\ } \\ } while (0) #define LOG_PRINT(message, ...) \\ do { \\ printf(message, ##__VA_ARGS__); \\ } while (0) int64_t GetShapeSize(const std::vector\u0026lt;int64_t\u0026gt;\u0026amp; shape) { int64_t shape_size = 1; for (auto i : shape) { shape_size *= i; } return shape_size; } int Init(int32_t deviceId, aclrtStream* stream) { // 固定写法,AscendCL初始化 auto ret = aclInit(nullptr); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclInit failed. ERROR: %d\\n\u0026#34;, ret); return ret); ret = aclrtSetDevice(deviceId); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtSetDevice failed. ERROR: %d\\n\u0026#34;, ret); return ret); ret = aclrtCreateStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtCreateStream failed. ERROR: %d\\n\u0026#34;, ret); return ret); return 0; } template \u0026lt;typename T\u0026gt; int CreateAclTensor(const std::vector\u0026lt;T\u0026gt;\u0026amp; hostData, const std::vector\u0026lt;int64_t\u0026gt;\u0026amp; shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size = GetShapeSize(shape) * sizeof(T); // 调用aclrtMalloc申请device侧内存 auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtMalloc failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtMemcpy failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 计算连续tensor的strides std::vector\u0026lt;int64_t\u0026gt; strides(shape.size(), 1); for (int64_t i = shape.size() - 2; i \u0026gt;= 0; i--) { strides[i] = shape[i + 1] * strides[i + 1]; } // 调用aclCreateTensor接口创建aclTensor *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } int main() { // 1.初始化 int32_t deviceId = 0; aclrtStream stream; auto ret = Init(deviceId, \u0026amp;stream); CHECK_RET(ret == 0, LOG_PRINT(\u0026#34;Init acl failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 2.准备数据 // 矩阵 1 std::vector\u0026lt;float\u0026gt; mat1HostData = {1, 2, 3, 4, 5, 6}; std::vector\u0026lt;int64_t\u0026gt; mat1Shape = {3, 2}; void* mat1DeviceAddr = nullptr; aclTensor* mat1 = nullptr; ret = CreateAclTensor(mat1HostData, mat1Shape, \u0026amp;mat1DeviceAddr, aclDataType::ACL_FLOAT, \u0026amp;mat1); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;CreateAclTensor for mat1 failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 矩阵 2 std::vector\u0026lt;float\u0026gt; mat2HostData = {1, 2, 3, 4, 5, 6}; std::vector\u0026lt;int64_t\u0026gt; mat2Shape = {2, 3}; void* mat2DeviceAddr = nullptr; aclTensor* mat2 = nullptr; ret = CreateAclTensor(mat2HostData, mat2Shape, \u0026amp;mat2DeviceAddr, aclDataType::ACL_FLOAT, \u0026amp;mat2); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;CreateAclTensor for mat2 failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 结果矩阵 std::vector\u0026lt;float\u0026gt; outHostData = {0, 0, 0, 0, 0, 0, 0, 0, 0}; std::vector\u0026lt;int64_t\u0026gt; outShape = {3, 3}; void* outDeviceAddr = nullptr; aclTensor* out = nullptr; ret = CreateAclTensor(outHostData, outShape, \u0026amp;outDeviceAddr, aclDataType::ACL_FLOAT, \u0026amp;out); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;CreateAclTensor for out failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 3.调用 CANN 算子库 API uint64_t workspaceSize = 0; aclOpExecutor* executor; int8_t cubeMathType = 1; // 计算 device 内存 ret = aclnnMatmulGetWorkspaceSize(mat1, mat2, out, cubeMathType, \u0026amp;workspaceSize, \u0026amp;executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclnnMatmulGetWorkspaceSize failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 申请 device 内存 void* workspaceAddr = nullptr; if (workspaceSize \u0026gt; 0) { ret = aclrtMalloc(\u0026amp;workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;allocate workspace failed. ERROR: %d\\n\u0026#34;, ret); return ret;); } // 执行计算过程 ret = aclnnMatmul(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclnnMatmul failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 4.等待计算结果 ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtSynchronizeStream failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 5.将 device 侧内存上的结果拷贝至 host 侧 auto size = GetShapeSize(outShape); std::vector\u0026lt;float\u0026gt; resultData(size, 0); ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;copy result from device to host failed. ERROR: %d\\n\u0026#34;, ret); return ret); for (int64_t i = 0; i \u0026lt; size; i++) { LOG_PRINT(\u0026#34;result[%ld] is: %f\\n\u0026#34;, i, resultData[i]); } // 6.释放 aclTensor aclDestroyTensor(mat1); aclDestroyTensor(mat1); aclDestroyTensor(out); // 7.释放 device 资源 aclrtFree(mat1DeviceAddr); aclrtFree(mat2DeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize \u0026gt; 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; } 运行程序:\n./opapi_mul_test 运行结果:\nsss@xxx:~/xxx/mul/build/bin$ ./opapi_test result[0] is: 9.000000 result[1] is: 12.000000 result[2] is: 15.000000 result[3] is: 19.000000 result[4] is: 26.000000 result[5] is: 33.000000 result[6] is: 29.000000 result[7] is: 40.000000 result[8] is: 51.000000 参考资料:\nAscend 算子开发指南 CANN 社区版开发文档 调用 NN 算子接口示例代码 算子加速库接口 ","date":"2024-10-23","externalUrl":null,"permalink":"/articles/ascend-aclnn-%E7%AE%97%E5%AD%90%E5%BC%80%E5%8F%91%E5%85%A5%E9%97%A8/","section":"Articles","summary":"","title":"Ascend aclnn 算子开发入门","type":"posts"},{"content":"","date":"2024-10-23","externalUrl":null,"permalink":"/tags/cann/","section":"Tags","summary":"","title":"CANN","type":"tags"},{"content":"","date":"2024-10-23","externalUrl":null,"permalink":"/tags/npu/","section":"Tags","summary":"","title":"NPU","type":"tags"},{"content":"","date":"2024-10-23","externalUrl":null,"permalink":"/tags/%E7%AE%97%E5%AD%90%E5%BC%80%E5%8F%91/","section":"Tags","summary":"","title":"算子开发","type":"tags"},{"content":"","date":"2024-10-18","externalUrl":null,"permalink":"/tags/git/","section":"Tags","summary":"","title":"Git","type":"tags"},{"content":" 一、概述 # 本文记录了我在开源贡献的过程中遇到的一个小问题(使用 git 调整 commit 的顺序,并整合多个 commit 节点)以及最后是怎么解决的。\n二、背景介绍 # 一般在进行开源贡献提交 PR 之前,我们需要先 fork 想要贡献的仓库到我们自己的 GitHub 仓库中。\n首先,将上游仓库关联到我们的 remote 中,可以使用如下命令:\ngit remote add upstream \u0026lt;xxx.git\u0026gt; # 新增想要 fork 的仓库的 url git remote -v # 查看 remote 中所有的 url,可以看到 origin 和 upstream(共 4 个 url) git branch -vva # 查看本地和远程的所有分支 然后,将上游仓库中最新的改动同步到自己的远程仓库,可以使用如下命令:\ngit fetch upstream # 将上游所有最新的改动下载到本地的一个新分支中,但不更新本地分支 git checkout \u0026lt;branch\u0026gt; # 签出本地分支 git merge upstream/\u0026lt;branch\u0026gt; # 合并上游分支 git push origin \u0026lt;branch\u0026gt; # 推送本地分支 关于 git fetch 的详细原理,可以参考:Git fetch 原理解析。 总结:git pull = git fetch + git merge。\n三、我的问题 # 当我基于上游分支进行了一段时间的开发,并且已经多次 commit,还 push 到了我的 origin 中时,我 merge 了上游分支最新的修改,然后发现在我的多次 commit 中,穿插有其他人的 commit。\n当前本地分支的 git log 情况如下:\n注意:上图中,B 和 D 是已经合入上游仓库中的 commit;A 和 C 是只 push 到了我个人的远程仓库,还未合入上游的 commit。\n我的诉求:将 A 和 C 这两个我的 commit 节点合并,并放到 B 之后,然后更新到我的远程仓库。\n期望的 git log 是这样:\n四、解决方法 # 先将本地分支回退到 D 节点,然后暂存当前 A + C 的修改,重新 merge 上游分支,然后恢复暂存的修改,最后提交并推送到远程仓库。\n使用的命令如下:\ngit reset --soft \u0026lt;commit_id\u0026gt; # 当前所有修改内容前最近的一个提交节点(D) git fetch upstream # 再同步一下上游仓库 git stash # 暂存 A 和 C 修改的内容 git merge upstream/\u0026lt;branch\u0026gt; # 合并上游分支最新的修改(B),此时 git log 变为:【B-\u0026gt;D-\u0026gt;...】 git stash pop # 恢复 A 和 C 修改的内容 git add . git commit -am \u0026#34;A + C 的提交信息\u0026#34; # 此时 git log 变为:【E-\u0026gt;B-\u0026gt;D-\u0026gt;...】 git push origin \u0026lt;branch\u0026gt; --force # 推送到自己的远程仓库 注意:最后 push origin 时必须加上 \u0026ndash;force,因为此时 origin 中的 commit 顺序为:【A-\u0026gt;B-\u0026gt;C-\u0026gt;D-\u0026gt;\u0026hellip;】(因为之前已经 push 过一次 origin 了),而本地分支现在是:【E-\u0026gt;B-\u0026gt;D-\u0026gt;\u0026hellip;】,会发生冲突。此时,Git 会提示你需要先合并 origin 中的内容才能 push,这样的话 commit 的顺序就又乱了!因此,我们直接加上 \u0026ndash;force 选项,用本地分支直接覆盖 origin 中的分支。\n最后,我们成功将 A + C 的 commit 节点合并,并放到了 B 节点的后面。\n为什么要这样做呢?\n因为我的 A 和 C 提交属于同一个内容,如果中间又穿插了一个别人提交的 B,那么同一个改动的相关内容就被分隔开了,这样不利于内容管理以及问题回溯。\n","date":"2024-10-18","externalUrl":null,"permalink":"/articles/git-%E5%AE%9E%E8%B7%B5%E6%A1%88%E4%BE%8B-%E5%90%88%E5%B9%B6%E5%A4%9A%E4%B8%AA%E5%88%86%E6%95%A3%E7%9A%84-commit-%E8%8A%82%E7%82%B9/","section":"Articles","summary":"","title":"Git 实践案例 | 合并多个分散的 commit 节点","type":"posts"},{"content":" 一、概述 # 本文记录了自己在基于 EulerOS \u0026amp; Ascend NPU 的华为云远程服务器上,使用 docker 容器搭建 PyTorch 开发环境的主要过程以及遇到的问题。\n硬件规格如下:\nKunpeng + Ascend: 192 CPU,1.5T MEM,21T DISK,8*Ascend XXX 型号 EulerOS V2 R10 二、创建 docker 镜像并运行容器 # 2.1 编写 Dockerfile # 创建 Dockerfile 如下:\nFROM ubuntu:20.04 # define your var ARG YOUR_USER_NAME=\u0026#34;sss\u0026#34; ARG YOUR_GROUP_ID=\u0026#34;9005\u0026#34; ARG YOUR_USER_ID=\u0026#34;9005\u0026#34; ARG DEBIAN_FRONTEND=noninteractive ENV TZ=\u0026#34;Asia/shanghai\u0026#34; RUN sed -i \u0026#39;s/ports.ubuntu.com/mirrors.aliyun.com/g\u0026#39; /etc/apt/sources.list \u0026amp;\u0026amp; \\ apt-get update \u0026amp;\u0026amp; \\ yes | unminimize \u0026amp;\u0026amp; \\ apt-get install -y adduser sudo vim gcc g++ cmake make gdb git tmux openssh-server \\ net-tools iputils-ping python3-distutils python3-setuptools \\ python3-wheel python3-yaml python3-dbg python3-pip libmpich-dev # Config user. User must join group HwHiAiUser(1000) to use npu. # Identify user id and group id to match user out of docker. (optional) RUN groupadd -g $YOUR_GROUP_ID $YOUR_USER_NAME \u0026amp;\u0026amp; \\ useradd -u $YOUR_USER_ID -g $YOUR_USER_NAME -ms /bin/bash $YOUR_USER_NAME \u0026amp;\u0026amp; \\ sed -i \u0026#34;/root\\tALL=(ALL:ALL) ALL/a\u0026#34;${YOUR_USER_NAME}\u0026#34;\\tALL=(ALL:ALL) ALL\u0026#34; /etc/sudoers \u0026amp;\u0026amp; \\ echo \u0026#34;source /home/${YOUR_USER_NAME}/Ascend/ascend-toolkit/set_env.sh\u0026#34; \u0026gt;\u0026gt; /home/\u0026#34;$YOUR_USER_NAME\u0026#34;/.bashrc \u0026amp;\u0026amp; \\ echo \u0026#34;export LD_LIBRARY_PATH=/usr/local/Ascend/driver/lib64/common/:/usr/local/Ascend/driver/lib64/driver/:${LD_LIBRARY_PATH}\u0026#34; \u0026gt;\u0026gt; /home/\u0026#34;$YOUR_USER_NAME\u0026#34;/.bashrc \u0026amp;\u0026amp; \\ ssh-keygen -A CMD [\u0026#34;/bin/bash\u0026#34;, \u0026#34;/home/sss/bin/entrypoint.sh\u0026#34;] 注意:\nDockerfile 中的 YOUR_USER_NAME、YOUR_GROUP_ID 以及 YOUR_USER_ID 为将要在容器中创建的用户和用户组,请自行进行设置; CMD 中的 entrypoint.sh 脚本文件为容器每次启动时都会去执行的命令集合,这里文件前面的路径需要替换为自己容器中的路径(Dockerfile 里面写的路径都是指容器里面的); 我们需要将自己宿主机中存放个人数据的目录(我是 /data/disk/sss)挂载到容器中自己的用户目录(我是 /home/sss)下,并将编写好的 entrypoint.sh 脚本存放到 /data/disk/sss/bin 目录下,这样我们进入容器后,就能在容器的 /home/sss/bin 目录下找到 entrypoint.sh 脚本并执行它(如果目录映射不对,在容器中找不到该脚本文件,那么容器启动时就会报错)。 2.2 构建 base 镜像 # 在 Dockerfile 所在目录执行下面的命令:\n# docker build -t \u0026lt;镜像名称\u0026gt;:\u0026lt;镜像tag\u0026gt; \u0026lt;Dockerfile所在目录\u0026gt; docker build -t sss_base_image:1.0 . 其它镜像常用命令:\n# 查看镜像 docker images # 删除镜像 docker rmi image:tag # 创建新的标签 docker tag \u0026lt;old_image_name\u0026gt;:\u0026lt;old_tag\u0026gt; \u0026lt;new_image_name\u0026gt;:\u0026lt;new_tag\u0026gt; 2.3 编写容器启动脚本 # 创建 entrypoint.sh 脚本文件如下:\n# /bin/bash # define your var your_user_name=\u0026#34;sss\u0026#34; your_password=\u0026#34;xxx\u0026#34; # Create passwd echo \u0026#34;${your_user_name}:${your_password}\u0026#34; | chpasswd # Add to group 1000(HwHiAiUser) to use npu cat /etc/passwd | awk -F \u0026#34;:\u0026#34; \u0026#39;{print $4}\u0026#39; | grep 1000 if [ $? -ne 0 ] then groupadd -g 1000 HwHiAiUser useradd -u 1000 -g HwHiAiUser -ms /bin/bash HwHiAiUser fi usermod -a -G 1000 ${your_user_name} # For jumper if [ $(grep -c \u0026#34;HostkeyAlgorithms +ssh-rsa\u0026#34; /etc/ssh/sshd_config) -eq 0 ] then echo \u0026#34;HostkeyAlgorithms +ssh-rsa\u0026#34; \u0026gt;\u0026gt; /etc/ssh/sshd_config fi if [ ! -d /run/sshd ] then mkdir /run/sshd fi /usr/sbin/sshd -D chown -R sss:sss /home/sss 注意:脚本中的 your_user_name 和 your_password 为容器中的用户,请自行进行设置(该用户会被加入到 HwHiAiUser 用户组中,这样该用户才能使用 NPU 进行计算)。\n2.4 编写容器配置文件 # 创建 docker-compose.yaml 配置文件如下:\nservices: chattts: image: sss_base_image:1.0 container_name: sss volumes: # 保证 ~/bin/entrypoint.sh 文件的映射路径正确 - /data/disk3/sss:/home/sss # ----- 此处保持不变 ----- # - /usr/local/dcmi:/usr/local/dcmi - /usr/local/bin/npu-smi:/usr/local/bin/npu-smi - /usr/local/Ascend/driver/lib64:/usr/local/Ascend/driver/lib64 - /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info - /etc/ascend_install.info:/etc/ascend_install.info # ---------------------- # ports: # 映射22端口,方便 ssh 远程连接容器 - 8333:22 # 可添加更多端口映射 - 8008:8008 restart: unless-stopped hostname: ascend910b-02 tty: true devices: # 此处更改为可用的 NPU 卡号,可通过 npu-list 查询卡的占用状态 - /dev/davinci3 - /dev/davinci_manager - /dev/devmm_svm - /dev/hisi_hdc cap_add: - SYS_PTRACE shm_size: 20gb # command: /bin/bash -c \u0026#34;chown -R sss:sss /home/sss \u0026amp;\u0026amp; /bin/bash\u0026#34; 将配置文件中的以下变量替换为自己的:\nchattts:服务名称; image: sss_base_image:1.0:镜像名称:tag(注意:如果你的镜像 tag 不是 latest 的话,不能省略版本信息); container_name: sss:容器名称; - /data/disk3/sss:/home/sss:将自己的用户目录(/data/disk3/sss)挂载到容器中的用户目录(/data/disk3/sss)下; - 8333:22:将宿主机端口(自己设置,这里我随便设置的 8333)映射到容器中的端口(22,这是 ssh 服务的默认端口,方便后续使用 ssh 直接连接到自己的容器中); hostname: ascend910b-02:宿主机名称。 注意:这里的 docker-compose.yaml 中不能加 command 选项,因为该选项中的命令会覆盖 Dockerfile 中的 CMD 选项,导致 entrypoint.sh 脚本不会被执行(后果很严重!)。这里如果还想加一些在容器启动时需要执行的命令,可以直接加到 entrypoint.sh 脚本中,这样每次容器启动时都会执行这些命令。\n2.5 启动并进入容器 # 启动容器:\n# 临时启动(运行一次):docker-compose -p \u0026lt;project-name\u0026gt; up # 后台启动(一直运行):docker-compose -p \u0026lt;project-name\u0026gt; up -d docker-compose -p chattts up -d 进入容器:\n# docker exec -it \u0026lt;容器名或ID\u0026gt; /bin/bash docker exec -it sss /bin/bash # 退出容器:exit 其它容器常用命令:\n# 停止容器 docker stop \u0026lt;容器名或ID\u0026gt; # 重启停止的容器 docker restart \u0026lt;容器名或ID\u0026gt; # 保存容器为新的镜像 docker commit \u0026lt;容器名或ID\u0026gt; \u0026lt;镜像名\u0026gt; # 删除容器 docker rm \u0026lt;容器名或ID\u0026gt; 参考资料:使用 docker-compose 搭建 npu 环境的容器。\n三、安装 CANN 软件 # 3.1 确认环境 # 进入容器,检查当前环境是否满足以下要求:\n软件 要求版本 操作系统 openEuler20.03/22.03, Ubuntu 20.04/22.04 python 3.8, 3.9, 3.10 确认昇腾 AI 处理器已安装:\n# 安装 lspci sudo apt-get install pciutils # 查看是否有 Processing accelerators lspci | grep \u0026#39;Processing accelerators\u0026#39; 确认操作系统架构及版本:\nuname -m \u0026amp;\u0026amp; cat /etc/*release 确认 Python 版本:\npython --version 3.2 安装 miniconda # # 安装 miniconda wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh bash Miniconda3-latest-Linux-aarch64.sh # 启用 conda 环境(这里替换为自己的安装路径) eval \u0026#34;$(/home/sss/bin/miniconda/miniconda3/bin/conda shell.bash hook)\u0026#34; # 创建 conda 虚拟环境并激活 conda create -n cann python=3.10 conda env list conda activate cann # 查看 python 版本和已安装的 python 包 python --version conda list 参考资料:\n安装 miniconda aarch64 版本 conda 环境启用 \u0026amp; 基本使用 设置 miniconda 的 channel:\n# 设置为清华镜像源 conda config --add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ conda config --add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/ 参考资料:miniconda 设置 channel。\n安装 python 依赖:\nconda install -i https://pypi.tuna.tsinghua.edu.cn/simple attrs numpy decorator sympy cffi pyyaml pathlib2 psutil protobuf scipy requests absl-py wheel typing_extensions 3.3 安装 CANN-toolkit # wget \u0026#34;https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/Milan-ASL/Milan-ASL V100R001C19SPC802/Ascend-cann-toolkit_8.0.RC3.alpha001_linux-aarch64.run\u0026#34; sh Ascend-cann-toolkit_8.0.RC3.alpha001_linux-aarch64.run --install # 使用 sh 安装可能会报错,换 bash 试试 # bash Ascend-cann-toolkit_8.0.RC3.alpha001_linux-aarch64.run --install 注意:在容器中安装 CANN 软件时,为保证安装路径的正确,需要切换到自己的用户进行安装(该软件会安装到 ~/Ascend 目录下)。\n# 切换用户:su \u0026lt;用户名\u0026gt; su sss # 退出当前用户:exit 3.4 安装算子包 # wget \u0026#34;https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/Milan-ASL/Milan-ASL V100R001C19SPC802/Ascend-cann-kernels-910b_8.0.RC3.alpha001_linux.run\u0026#34; sh Ascend-cann-kernels-910b_8.0.RC3.alpha001_linux.run --install # 使用 sh 安装可能会报错,换 bash 试试 # bash Ascend-cann-kernels-910b_8.0.RC3.alpha001_linux.run --install 注意:这里同样也需要切换到自己的用户进行安装。\n3.5 设置环境变量 # echo \u0026#34;source ~/Ascend/ascend-toolkit/set_env.sh\u0026#34; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc 参考资料:快速安装昇腾环境。\n3.6 其它问题 # 个人用户缺少权限:\n问题现象:在容器中,从 root 用户切换为个人用户后,发现访问不了某些目录,显示 permission denied。\n执行(该命令已添加到容器启动脚本 entrypoint.sh 中):\nchown -R sss:sss /home/sss 个人用户缺少命令:\n问题现象:在容器中,从 root 用户切换为个人用户后,执行 ll,显示用户没有该命令。\n检查个人用户目录下是否缺少 .bash_lougout、.bashrc、.profile 文件,若没有,则将容器中 /etc/skel 目录下的这三个文件拷贝一份到自己的用户目录下并修改其权限。\ncp /etc/skel/.bashrc /home/sss/ cp /etc/skel/.bash_logout /home/sss/ cp /etc/skel/.profile /home/sss/ # 644 means writable, readable and executable for the user and readable for groups and others. chmod 644 .bashrc chmod 644 .bash_logout chmod 644 .profile chown sss:sss .bashrc chown sss:sss .bash_logout chown sss:sss .profile 参考资料:Bash on Ubuntu on Windows gives error \u0026ldquo;-bash: /home/user/.bashrc: Permission denied\u0026rdquo; on startup。\n安装 CANN 软件报错:\n问题现象:安装 CANN 软件报错,显示当前用户没有被添加到 HwHiAiUser 用户组中。\nUser is not belong to the dirver or firmware\u0026#39;s installed usergroup! Please add the user (sss) to the group (HwHiAiUser). 检查 entrypoint.sh 脚本是否有被成功执行,然后检查用户组 HwHiAiUser 有没有成功被创建。\n我自己在安装时报了这个错是因为 Dockerfile 中的 CMD 被后来在 docker-compose.yaml 中添加的 command 选项覆盖了,导致 entrypoint.sh 脚本未成功执行,用户 sss 未被添加到用户组 HwHiAiUser 中,因此无法使用 NPU。\n四、安装 PyTorch # 4.1 安装 torch # pip install torch==2.1.0 -i https://pypi.tuna.tsinghua.edu.cn/simple pip install pyyaml -i https://pypi.tuna.tsinghua.edu.cn/simple pip install setuptools -i https://pypi.tuna.tsinghua.edu.cn/simple 4.2 安装 torch-npu # 关于 torch-npu:\nThis repository develops the Ascend Extension for PyTorch named torch_npu to adapt Ascend NPU to PyTorch so that developers who use the PyTorch can obtain powerful compute capabilities of Ascend AI Processors. Ascend is a full-stack AI computing infrastructure for industry applications and services based on Huawei Ascend processors and software. For more information about Ascend, see Ascend Community.\nGitHub 仓库地址:Ascend Extension for PyTorch。\n安装 torch-npu:\npip install torch-npu==2.1.0.post6 -i https://pypi.tuna.tsinghua.edu.cn/simple 注意:torch-npu 的大版本(如:2.1.0)需要和 torch 匹配,具体的版本匹配信息请参考 Ascend Extension for PyTorch 中的 Ascend Auxiliary Software 部分。\n4.3 验证安装是否成功 # 创建 test.py 程序如下:\nimport torch import torch_npu x = torch.randn(2, 2).npu() y = torch.randn(2, 2).npu() z = x.mm(y) print(z) 运行程序 python test.py,若出现以下信息则说明安装成功:\ntensor([...], device=\u0026#39;npu:0\u0026#39;) 参考资料:Ascend Extension for PyTorch 配置与安装。\n五、开启容器 SSH 服务 # 5.1 安装并配置 openssh # # 安装 openssh sudo apt-get update sudo apt-get install openssh-server # 查看 SSH 是否启动(打印 sshd 则说明已成功启动) ps -e | grep ssh 修改 ssh 配置:\nPubkeyAuthentication yes #启用公钥私钥配对认证方式 AuthorizedKeysFile .ssh/authorized_keys #公钥文件路径(和上面生成的文件同) PermitRootLogin yes #root能使用ssh登录 ClientAliveInterval 60 #参数数值是秒 , 是指超时时间 ClientAliveCountMax 3 #设置允许超时的次数 UsePAM yes # 更改为 UsePAM no Port 80 #指定好端口号,默认是22 后面这个数字要在你run容器的时候用到 然后重启 SSH 服务:\nsystemctl restart sshd.service # 或: sudo /etc/init.d/ssh restart 参考资料:\nUbuntu 安装 SSH SERVER 使用 Docker 容器配置 ssh 服务,远程直接进入容器 5.2 配置 VSCode 客户端 # 在 VSCode 的远程资源管理器中点击设置,找到 xxx/.ssh/config 文件,添加以下配置:\n# 远程服务器名称,这里可以随意设置 Host sss-docker # 替换为自己的宿主机 IP HostName xxx.xxx.xxx.xxx # 替换为自己容器的 22 端口映射的宿主机端口,在 docker-compose.yaml 中设置的,我设置的是 8333:22 Port 8333 # 容器中的个人用户,使用个人用户密码登录容器,在 entrypoint.sh 脚本中设置的 User sss ForwardAgent yes # 每300秒向服务端主动发个包,防止一会儿不操作就和服务器断开连接 ServerAliveInterval 300 # 3次发包均无响应会断开连接 ServerAliveCountMax 3 六、配置 Git 并拉取代码 # 6.1 在容器中配置 Git # # 配置用户名和邮箱 git config --global user.name \u0026#34;shanshan shen\u0026#34; git config --global user.email xxx@gmail.com # 查看配置 git config --list 6.2 拉取代码 # git clone xxx.git 七、总结 # 到此为止,我们就可以在基于 EulerOS \u0026amp; Ascend NPU 的华为云远程服务器上,在自己搭建的 docker 容器中使用 PyTorch 框架并进行 AI 模型的训练与推理。\n","date":"2024-09-24","externalUrl":null,"permalink":"/articles/%E5%9F%BA%E4%BA%8E-euleros-ascend-npu-%E6%90%AD%E5%BB%BA-pytorch-%E8%BF%9C%E7%A8%8B%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83/","section":"Articles","summary":"","title":"基于 EulerOS \u0026 Ascend NPU 搭建 PyTorch 远程开发环境","type":"posts"},{"content":"","date":"2024-09-04","externalUrl":null,"permalink":"/tags/idea/","section":"Tags","summary":"","title":"IDEA","type":"tags"},{"content":"","date":"2024-09-04","externalUrl":null,"permalink":"/tags/java/","section":"Tags","summary":"","title":"Java","type":"tags"},{"content":"","date":"2024-09-04","externalUrl":null,"permalink":"/tags/maven/","section":"Tags","summary":"","title":"Maven","type":"tags"},{"content":" 一、拉取最新依赖 # 拉取最新的 dev 分支代码,然后点击下图中的圆圈,重新加载依赖项。\n二、强制更新依赖 # 运行以下命令,强制更新依赖项。\nmvn clean install -s settings.xml -U 三、清除 IDEA 缓存 # 文件 -\u0026gt; 清除缓存 -\u0026gt; 勾选所有项,点击【清除并重启】。\n四、检查 settings 文件 # 在 settings.xml 文件的 \u0026lt;localRepository\u0026gt; 标签中定义的路径为本地仓库路径。\n检查 IDEA 中设置的本地仓库路径是否与 settings.xml 文件中设置的一致。\n五、删除 repository 中的某些文件 # 若 Maven 报错如下(这是 Maven 的一个 bug):\n[ERROR] Malformed \\uxxxx encoding. [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. 此时我们可以去项目的 repository 目录中搜索并删除所有的 resolver-status.properties 文件,然后再重新下载依赖。\n六、取消勾选脱机模式 # 当 Maven 报错信息如下:\nCannot access mcr-huawei-product-maven xxx in offline mode and the artifact xxx has not been downloaded from it before. 此时可以取消勾选 Maven 中的【脱机工作】选项,这个选项的意思是不读取远程仓库,只读取本地已有的仓库。\n上面报错的原因就是因为本地仓库缺少相应的依赖,还选择了脱机工作,导致下载不了相应的依赖。\n七、配置启动 VM 选项 # 报错信息如下,原因是缺少微服务配置中心相关的配置项:\njava.lang.IllegalStateException: Required key \u0026#39;configcenter_url\u0026#39; not found 此时可以检查项目启动的 VM 选项是否缺少相应的配置,补充相应的配置即可。\n","date":"2024-09-04","externalUrl":null,"permalink":"/articles/maven-%E9%A1%B9%E7%9B%AE%E7%BC%96%E8%AF%91%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/","section":"Articles","summary":"","title":"Maven 项目编译报错解决方法","type":"posts"},{"content":" 一、概述 # 昇腾 NPU 是专门用于 AI 训练/推理计算的 AI 专用处理器,其中的 AI Core 能够在很大程度上提高 AI 计算的效率。\n本文将主要介绍 ASCEND NPU 的硬件架构 \u0026amp; 工作原理、AI Core 的计算模式以及异构计算平台 CANN 等内容。\n二、NPU 硬件架构 # 2.1 NPU SOC 架构 # 2.1.1 Ascend 310 架构 # AI Core:计算核心,负责执行矩阵、向量、标量计算密集的算子任务,采用达芬奇架构; AI CPU:承担非矩阵类复杂计算,即负责执行不适合跑在 AI Core 上的算子; TS Core:作为任务调度器(Task Scheduler,TS),以实现计算任务在 AI Core 上的高效分配和调度(专门服务于 AI Core 和 AI CPU,不承担任何其它的工作); ARM CPU:控制芯片整体运行; DVPP:数字视觉预处理子系统,完成图像视频编解码; Cache \u0026amp; Buffer。 2.1.2 Ascend 910 架构 # AI Core:32 个,上下各 16 个,中间放 buffer,方便更快地取数据; Taishan Core:一部分为 AI CPU,承担部分 AI 计算,一部分为 Ctrl CPU,负责 SoC 控制功能,两类 CPU 占用的核数由软件进行分配; TS CPU:负责任务调度,把算子任务切分之后,通过硬件调度器(HWTS)分发给 AI Core 或 AI CPU 执行计算; Nimbus:提供 PCIe 接口和 Host CPU 对接;提供 NIC 用于跨服务器传递数据;集成一个 ARM CPU 核,执行启动、功耗控制等硬件管理任务; Cache \u0026amp; Buffer。 2.2 NPU 达芬奇架构 # NPU 的达芬奇架构中共包含 3 种类型的单元:\n计算单元:包含矩阵计算单元(DaVinci Core)、向量计算单元(Vector)、标量计算单元(Scalar); 存储系统:AI Core 片上存储单元和相应数据通路构成存储系统; 控制单元:计算过程提供指令控制,负责 AI Core 的运行。 2.2.1 计算单元 # Cube Core:每次执行可以完成 fp16 的矩阵乘,如 C = A(1616) * B(1616),更大的矩阵运算需要先对矩阵进行分块(在 L1 Buffer 中进行缓存); Vector Unit:算力低于 Cube,灵活度高(如数学中的求倒数、平方根等),Vector 所有计算的源数据和目标数据都会存储在 Unified Buffer 中(Unified Buffer 再与 L1 Buffer 进行交互),并按 32 Byte 对齐; Scalar Unit:负责各类型标量数据运算和程序流程控制,算力最低,功能上类比小核 CPU,完成整个程序循环控制、分支判断、Cube/Vector 等指令地址和参数计算以及基本算术运算等; Accumulator(累加器):把当前矩阵乘的结果与上一次计算的结果相加,可以用于完成卷积中增加 bias 等操作。 注意:AI Core 就是指 Cube Core,即矩阵计算单元。\n2.2.2 存储单元 # 存储控制单元(MTE):作为 AI Core 内部数据通路传输控制器,负责 AI Core 内部数据在不同缓冲区间的读写管理,以及完成一系列的格式转换操作; 缓冲区: 输入缓冲区(L1 Buffer):AI Core 采用了大容量片上缓冲区设计,通过增大片上缓存的数据量来减少数据从片外搬运到 AI Core 中的频次,从而降低数据搬运过程中所产生的功耗和时延,有效控制整体计算耗能和提升性能; 输出缓冲区(Unified Buffer):用来存放神经网络中每层计算的中间结果,从而在进入下一层时方便获取数据。 寄存器(SPR/GPR):寄存器资源主要是标量计算单元在使用。 上图中,HBM/DDR 和 L2 缓冲器都属于 AI Core 核外的数据存储系统。\n数据通路是数据在 AI Core 中的流通路径,它有以下特点:\n多进单出,可通过并行输入来提高数据流入的效率; 将多种输入数据处理完成后只生成输出特征矩阵,数据种类相对单一,单输出数据通路,可以节约芯片硬件资源。 2.2.3 控制单元 # 系统控制模块:控制“任务块”(AI Core 中最小的任务计算粒度)的执行进程,在任务块执行完成后,系统控制模块会进行中断处理和状态申报。 指令缓存; 标量指令处理队列; 指令发射模块:根据指令类型分别发送指令到对应的执行队列中; 事件同步模块。 2.3 AI Core 电路结构 # 对于运算 C = A(16*16) * B(16*16),矩阵 C 中的每一个元素都需要进行 16 次乘法与 15 次加法计算得到(一个矩阵计算子电路)。\n在 AI Core 中,共有 256 个矩阵计算子电路,每一条指令都可以并行完成 256 个矩阵 C 中的元素的计算。\n三、NPU 工作原理 # 3.1 NPU 并行计算架构 # 异步指令流:Scalar 计算单元读取指令序列,并把向量计算、矩阵计算、数据搬运指令发送给对应的指令队列,Vector 计算单元、Cube 计算单元、DMA 搬运单元异步地并行执行接收到的指令(“异步并行”:将串行的指令流分解); 同步信号流:指令间可能会存在依赖关系,为了保证不同指令队列间的指令按照正确的逻辑关系执行,Scalar 计算单元也会给对应单元下发同步指令; 计算数据流:DMA 搬入单元把数据搬运到 Local Memory,Vector/Cube 计算单元完成数据计算,并把计算结果回写到 Local Memory,DMA 搬出单元把处理好的数据搬运回 Global Memory。 3.2 AI Core 计算模式 # Cube 单元能够高效地执行 MAC(矩阵乘加)操作,目前支持的矩阵大小为 16*16*16。\n注意:通常矩阵乘中两矩阵很大,因此数据是分块(Tiling)后送入 Cube 单元的,每送完一块,结果存放到累加器,最后得到结果。\n在 CPU 的计算过程中,矩阵 A 按行扫描,矩阵 B 按列扫描。典型的存储方式是 A 和 B 都按照行方式进行存储(Row-Major),而内存读取按行读更方便,因此对 A 矩阵高效,对 B 矩阵低效。\n为了提高内存读取的效率,NPU 将矩阵 B 的存储方式转成按列存储(Column-Major),通过改变矩阵存储的方式来提升矩阵计算的效率。\nCube Core 一条指令完成两个 16*16 矩阵的 MAC,相当于一个时钟周期进行 163 = 4096 个 MAC 运算。执行前将 A 按行、将 B 按列存放在 Input Buffer,并通过 Cube Core 计算得到 C,按行存放在 Output Buffer。\n注意:矩阵 A 不需要转换,可以直接从 L1 Buffer 读取到 AI Core 里面;矩阵 B 需要预先读到 MTE 中进行转换),然后再读取到 AI Core 里进行计算。\n矩阵的预处理:\n分块(Tiling):因为片上缓存容量有限,因此将整个矩阵 B 划分为多个子矩阵,并依次搬运到缓存中,最后得到结果矩阵 C; 填充(Padding):A 和 B 都等分成同样大小的块(16*16),排不满的地方可以通过补 0 实现。 四、CANN 平台 # 4.1 整体架构 # 异构计算架构 CANN(Compute Architecture for Neural Networks)是华为针对 AI 场景推出的异构计算架构,向上支持多种 AI 框架,包括 MindSpore、PyTorch、TensorFlow 等,向下服务 AI 处理器与编程,发挥承上启下的关键作用,是提升昇腾 AI 处理器计算效率的关键平台。同时针对多样化应用场景,提供多层次编程接口,支持用户快速构建基于昇腾平台的AI应用和业务。\n计算语言开发接口: 应用开发接口:提供深度学习推理计算、图像预处理、单算子加速计算能力; 图开发接口:提供统一网络构图接口; 算子开发接口:Ascend C 是 CANN 在算子开发场景的编程语言,原生支持 C\u0026amp;C++ 标准规范,最大化匹配用户开发习惯。 计算服务层: 昇腾算子库 AOL; 昇腾调优引擎 AOE:用于在推理、训练等场景对模型、算子、子图等进行调优,充分利用硬件资源,提升网络性能。 计算编译层: 图转换:各种模型编译器; 图编译:图准备、图优化、图拆分、图编译; Tensor Boost Engine(TBE):算子融合、算子编译。 计算执行层:类似于 CUDA 的 runtime,提供 stream 管理、context 管理等功能; 基础服务层:资源管理、通信管理、设备管理、芯片 IP 驱动、公共服务。 CANN 官方社区:CANN 社区版 8.0.RC3.alpha002 开发文档。\n4.2 Ascend C 算子编程 # Ascend C 算子编程使用 C++ 语言和 Ascend C 类库 API 进行开发。\nAscend C 类库 API:\n基本 API:计算 API、搬运 API、同步 API、……; 高阶 API:Matmul API、Conv API、Softmax API、……。 Ascend C 算子编程采用 SPMD(单程序多数据编程)模式,即一个核的算子程序被调用时,会启动 N 个实例,每个运行实例称为一个 block,它们都执行相同的代码,有相同的参数,但参与计算的数据不同。\n注意:block 类似于线程(进程),而 block_idx 类似于 thread_id,算子程序需要使用 block_idx 来进行数据并行计算切分。\n4.3 推理应用开发 # AMCT 模型压缩:支持量化、通道稀疏、张量分解,降低模型的数据量和计算量,提升计算性能; 量化:将模型的权重和数据从浮点型转换为整型(比如:低精度 int8),从而生成更加轻量化的网络模型,减少数据和计算量; 张量分解:分解卷积 Tensor,将一个大卷积转化成两个级联的小卷积,从而降低存储空间和计算量(张量分解处理后,需要重训练以保证模型精度); 稀疏:通过结构剪枝,对模型中的部分算子裁剪部分权重和参数,从而得到参数量和计算量更小的网络模型(稀疏处理后,需要重训练以保证模型精度)。 ATC 模型转换工具:将开源框架的网络模型转换为适配昇腾 AI NPU 的 om 模型; AOE 智能调优工具:支持算子计算过程的自动寻优,提升整体网络的性能。 ","date":"2024-08-31","externalUrl":null,"permalink":"/articles/ascend-npu-%E6%9E%B6%E6%9E%84-cann-%E5%B9%B3%E5%8F%B0%E5%85%A5%E9%97%A8%E5%AD%A6%E4%B9%A0/","section":"Articles","summary":"","title":"Ascend NPU 架构 \u0026 CANN 平台入门学习","type":"posts"},{"content":"","date":"2024-08-26","externalUrl":null,"permalink":"/tags/cuda/","section":"Tags","summary":"","title":"CUDA","type":"tags"},{"content":"","date":"2024-08-26","externalUrl":null,"permalink":"/tags/gpu/","section":"Tags","summary":"","title":"GPU","type":"tags"},{"content":"","date":"2024-08-26","externalUrl":null,"permalink":"/tags/nvidia/","section":"Tags","summary":"","title":"NVIDIA","type":"tags"},{"content":" 一、概述 # 随着大模型产业的发展,AI 训练 \u0026amp; 推理对算力的需求越来越大,AI 的计算也越来越离不开 GPU 的支持。\n目前,用于 AI 计算的芯片可以分为:\nCPU(通用处理器); GPU(通用图形处理器); NPU / TPU(AI 专用处理器)。 那么 CPU 和 GPU 有什么区别呢?\n从硬件设计上来看,GPU 的 DRAM 时延(数据搬运、指令执行的延迟)远高于 CPU,但 GPU 的线程数远高于 CPU(有非常多的线程,为大量大规模任务并行而去设计的)。\n关注重点:\nCPU:降低延迟、并发(Concurrency,能够处理多个任务的功能,但不一定是同时); GPU:最大化吞吐量、并行度(Parallelism,同时可以执行多少任务)。 总结:\nCPU:希望在一个线程里完成所有的工作(串行,优化线程的执行速率和效率); GPU:利用多线程对循环进行展开,来提高硬件整体的利用率(并行,用足够多的线程去解决延迟的问题)。 参考资料:AI System (chenzomi12.github.io)。\n二、GPU 硬件架构 # 2.1 发展历史 # Fermi 架构:提出了首个完整的 GPU 计算架构; Kepler 架构; Maxwell 架构; Pascal 架构:提出了 NVLink; Volta 架构:将 CUDA Core 进行了拆分,分离了 FPU 和 ALU;独立线程调度:每个线程都有独立的 PC(Program Counter)和 Stack;提出了 Tensor Core:针对深度学习提供张量计算核心,专门针对卷积运算进行加速; Turing 架构:提出了 RT Core(Ray Tracing Core),用于三角形与光线的求交; Ampere 架构:提出 NVSwitch,单卡之间通过 NVLink 互联,多卡之间通过 NVSwitch 互联; Hopper 架构。 Hopper 架构的 GPU 如下:\n2.2 硬件单元 # GPU 几乎主要由计算单元 ALU 组成,仅有少量的控制单元和存储单元,因此具有强大的计算能力。\n上图中,左边为 CPU,右边为 GPU。\n在 GPU 的硬件架构中,包含以下单元:\nGPC:Graph Possessed Cluster,图像处理簇; TPC:Texture Possessed Cluster,纹理处理簇; SM:Stream Multiprocessors,流式多处理器; HBM:High Band Memory,高带宽处理器。 一个 GPC 包含多个 TPC,一个 TPC 包含多个 SM,一个 SM 包含多个 Block、Thread 以及各种 CUDA Tensor Core。\n2.3 SM # SM(流式多处理器)的核心组件包括:CUDA 核心、共享内存、寄存器等,它包含许多为线程执行数学运算的 Core,是 NVIDIA 的核心,每一个 SM 都可以并发地执行数百个线程。\n具体地,SM 包括以下单元:\nCUDA Core:向量运行单元(FP32-FPU、FP64-DPU、INT32-ALU); Tensor Core:张量运算单元(FP16、BF16、INT8、INT4,专门针对 AI 的矩阵计算); Special Function Units:特殊函数单元,SFU,超越函数和数学函数; warp Scheduler:线程束调度器; Dispatch Unit:指令分发单元; Multi Level Cache:多级缓存(L0/L1 Instruction Cache、L1 Data Cache \u0026amp; Shared Memory); Register File:寄存器堆; Load/Store:访问存储单元(LD/ST,负责处理数据)。 后来,CUDA Core 演变为了单独的 FP32、FPU、INT32-ALU。\n2.4 Tensor Core # Tensor Core 可以支持混合精度运算。混合精度是指在底层硬件算子(Tensor Core)层面,使用半精度(FP16)作为输入和输出,使用全精度(FP32)进行中间结果计算从而不损失过多精度的技术。\n每个 Tensor Core 每周期能执行 4*4*4 GEMM,即 64 个 FMA。\nTensor Core 可以通过 Warp 把多个线程聚合起来一起进行计算和控制。最终对外提供一个 16*16*16 的 API 给到 CUDA。\n2.5 NVLink # NVLink 让单台服务器里面的 GPU 可以进行数据的互联,是用于 CPU 和 GPU 之间进行通信的 PCIe 的带宽的 3 倍,避免了数据通过 PCIe 回传到 CPU 的内存里面,从而减少了数据传输的延迟,实现了整个网络的拓扑互联。\n2.6 NVSwitch # 单卡之间通过 NVLink 互联,多卡之间通过 NVSwitch 互联。\n三、GPU 工作原理 # 3.1 缓存机制 # GPU 的多级缓存\nHBM 的作用是什么?\nHBM(High Bandwidth Memory,高带宽内存):显存,GPU 里的主内存。\n越靠近 SM 的缓存,数据搬运的延迟越低,因此把数据存在离 SM 越近的地方,将更有利于提高计算的效率。在实际计算中,我们总是希望尽快将当前缓存里的数据计算完并将结果返回,然后去换下一批数据上来。如果 GPU 里没有自己的 HBM,那么每一次计算都需要去 CPU 里读取数据(通过 PCIe),延迟太高。\n因此,并不是算力越高,计算的效率就越高,还需考虑数据传输的效率(带宽)等因素,这里引入“计算强度”的概念。\n什么是“计算强度”?\n假设你往 L1 cache 里面传了一个字节,这个字节参与了 8 个时钟周期的运算,则计算强度就是 8。每种算法都有一个对应的计算强度,这个值越低,就越受制于内存带宽(需要更频繁地搬运、刷新数据)。\n因此,只有线程数足够多,才能让整个系统的内存处于忙碌的状态。\n下面对各级内存的性能进行了对比:\nData Location Bandwitch (GB/sec) Compute Intensity Latency (ns) Threads Required L1 Cache 19400 8 27 32738 L2 Cache 4000 39 150 37500 HBM 1555 100 404 39264 NVLink 300 520 700 13125 PCIe 25 6240 1470 2297 从上图中可以看到,计算强度越低的地方,需要的线程数和带宽就越高。\n计算强度与矩阵大小的关系如下:\n随着参与计算的矩阵不断变大,GPU 里的内存就会逐渐空闲下来(不是指内存的容量降低,而是指内存的搬运变慢),因为此时 GPU 的计算单元需要花费更多的时间去对矩阵进行运算。\n因此,AI 的计算需要找到一个平衡点(计算与带宽),以匹配矩阵计算的强度。不管算得有多快,如果内存来不及搬运,那么超额的算力也是没用的。\nGPU 的解决方法:\n通过超配的线程来掩盖时延; 通过多级缓存来平衡计算和带宽的 Gap; 通过 Tensor Core 来增加峰值算力(Tensor Core:专门用于矩阵计算,以提高计算的强度,可以提升单芯片浮点运算的能力)。 3.2 线程机制 # GPU 里提供了大量的线程,超配的线程数可以支持对不同层级的数据进行搬运和计算。\n在一个 SM 中包含了大量的 warp(线程束),每 4 个 warp 可以并发地执行。\n什么是 warp?\n从逻辑上看,所有的线程是并行的;但从硬件的角度看,并不是所有的线程都能在同一时刻去执行。因此,GPU 使用 warp 来对线程进行批量管理。\nwarp 是 SM 的基本执行单元,一个 warp 包含 32 个并行的 Thread,这 32 个 Thread 执行于 SIMT 模式(所有 Thread 以锁同步的方式执行同一条指令,但每个 Thread 会使用各自的数据执行指令分支)。\nGPU 的线程分级\n在 AI 计算中,不是所有的计算都可以支持线程独立运算,有些计算模式的数据之间互相依赖,线程之间需要进行配合。——线程分层执行。\nGPU 中的线程包含以下 3 个层次:\nGrid(网格)表示所有要执行的任务; 一个 Grid 中包含了很多具有相同 Thread(线程)数量的 Block(块); 一个 Block 中的多个 Thread 之间独立执行,可以通过本地数据共享 Local Data Share 同步交换数据。 这样,在同一个 Block 中,可以同时执行大量相关的操作,并且超配的 Block 数也可以弥补数据处理的延迟。\n四、CUDA 平台 # 4.1 基本概念 # CUDA(Compute Unified Device Architecture)具有以下两层意义:\n是一种并行计算架构(Parallel Computing Architecture):用于控制 GPU 里各种并行的硬件; 是一种编程模型(Programming Model):基于 LLVM 构建了 CUDA 编译器,方便开发者使用 C/C++ 和 Python 进行开发。 CUDA 实现了软硬件的解耦。\n4.2 程序架构 # 主设概念:主机程序(Host)和设备程序(Device)之间可以进行通信(数据拷贝)。Device 通过 GPU 进行并行操作,计算完成后将结果传递给 CPU 进行处理。\nHost 部分的代码在 CPU 上执行(.c/cpp 文件); Device 部分的代码在 GPU 上执行(.cu 文件),当遇到需要并行处理的部分,CUDA 就会将程序编译成 GPU 能执行的程序,并传送到 GPU,这个部分叫做 kernel。 示例代码(cuda_device.cu):\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;math.h\u0026gt; // __global__是变量声明符,作用是将add()函数变成可以在GPU上运行的函数(kernel) __global__ void add(int n, float *x, float *y) { for(int i = 0; i \u0026lt; n; i++) { y[i] = x[i] + y[i]; } } int main(void) { int N = 1\u0026lt;\u0026lt;25; float *x, *y; // 在cuda里开辟内存 cudaMallocManaged(\u0026amp;x, N*sizeof(float)); cudaMallocManaged(\u0026amp;y, N*sizeof(float)); // initialize x and y arrays on the host for (int i = 0; i \u0026lt; N; i++) { x[i] = 1.0f; y[i] = 2.0f; } // 在GPU上执行kernel函数 add\u0026lt;\u0026lt;\u0026lt;1, 1\u0026gt;\u0026gt;\u0026gt;(N, x, y); // CPU需要等待cuda上的代码运行完毕,才能对数据进行读取 cudaDeviceSynchronize(); // 释放cuda内存 cudaFree(x); cudaFree(y); return 0; } 线程分级:\n执行一个 kernel 时,所有的线程都会封装在一个 Grid 里面。同一个 Grid 里面的线程可以共享全局内存(Global Memory),即 __global__ 里的数据都是共享的。\nBlock 间并行执行,并且无法通信,也没有执行顺序。每个 Block 中有一个共享内存(Shared Memory),同一个 Block 里的 Thread 是可以同步的,通过共享内存进行通讯和数据之间的传输。\nCUDA 并行程序,会被多个 Thread 同时执行。\nCUDA 与 GPU 硬件的对应关系:\n一个 Block 线程块只在一个 SM 上通过 warp 进行调度; 一旦在 SM 上调起了 Block 线程块,就会一直保留到执行完 kernel; 一个 SM 可以同时保存多个 Block 线程块,块间并行地执行。 五、算力计算 # ","date":"2024-08-26","externalUrl":null,"permalink":"/articles/nvidia-gpu-%E6%9E%B6%E6%9E%84-cuda-%E5%B9%B3%E5%8F%B0%E5%85%A5%E9%97%A8%E5%AD%A6%E4%B9%A0/","section":"Articles","summary":"","title":"NVIDIA GPU 架构 \u0026 CUDA 平台入门学习","type":"posts"},{"content":" 一、关于本书 # 《我有自己的宇宙》 心理学 作者:钱婧 简介:本书的作者钱婧老师是一位北师大的职场心理学教授,通过与广大年轻人进行交流沟通,她用自己多年的知识和经验为读者解答了许多关于职场、学业、恋爱、赚钱、社交以及家庭等诸多方面的疑惑。本书提出了一种叫做“中庸我”的思维模式,能够让我们在职场的风风雨雨中,甚至在更漫长的人生路上,坚守自己的一点本心,能够沉下心来朝着自己的目标踏实地做点实事。本书让我获益匪浅,也坚定了我在职场中的行事原则,让我摆脱了焦虑和内耗,变得更加自信和从容。 二、内容分享 # 2.1 与“梦幻我”进行思维谈判 # 在本书中,与“中庸我”相反的思维模式叫做“梦幻我”,那什么是“中庸我”?什么是“梦幻我”?\n“中庸我”:相信在以自我为中心和顺从他人或环境这两极之间有一个平衡态的存在,而一切平衡的基础在于认识、认领和践行“核心我”; “梦幻我”:认为顺从他人或环境会导致失去自我,拥有自我意味着不能顺从。 在职场上,我们常常会被领导安排去做一些自己不想做的“杂活”或“脏活”,面对这种情况:\n“中庸我”思维会认为:如果一时的忍受和顺从是为了一个更内核的自我诉求和目标,比如升职加薪,那么耐着性子去把这些小事做好,这也是符合自己的价值实现的; “梦幻我”思维会认为:干这些杂活,自己有点儿委屈,想敷衍了事或者干脆推掉,但是心里又害怕得罪领导和同事,在行为和态度上扭扭捏捏,心不甘情不愿。这会让其它和自己交互的人觉得我们这个人配合度很低,还没有什么想法。在这种恶性循环中,自己的精神和能量也逐渐被侵蚀,导致每天上班都愤愤不平、郁郁寡欢。 以“中庸我”思维来看待职场中的糟心事,能够让我们少抱怨、多做事,以更加平和的心态面对自己当下的处境。当然,迎合领导的期待去做事,是在不撼动我们的底层内核为前提的,毕竟如果工作内容本身与我们的核心自我背道而驰,人就会变得撕裂扭曲,人根本快乐不起来,对工作没有激情,又何谈成长与发展呢?\n以我自己为例,在我刚参加工作的第一年间,便是常常被领导安排去做一些所谓的“杂活”,自己也常常陷入到内耗和自我怀疑之中。领导常说诸如“做这些活是有利于你的能力提升的”、“之后本来还打算给你们升职加薪的”。然而,面对投入了大量的时间和精力,自己的能力却没有什么大的变化,幻想中的回报也并没有兑现的现实,“自我感动”的谎言便被打碎了。\n这份经历也让我开始意识到,在职场上一味地听从领导的安排、没有自己的主见是不行的。我们应该对自己的目标有清晰的认知,对自己今后的发展有合理的规划,然后只需要围绕这一条主线去努力奋斗就好了,构建自己的核心竞争力才是第一目标。面对任何偏离自己主航道的安排和任务,有时候该佛系的就佛系,该摸鱼的就摸鱼得了,不过分纠结于别人对自己的评价,做好自己分内的事,拥有“被讨厌的勇气”也不失为一种职场中的生存智慧。\n“在职场这种混沌的环境里,冗思是最没用的行为,对他人的判断,也是“论迹不论心”比较好。别人的心思是猜不得也猜不全的。既然上班是为了获得发展,那么我们大可以放弃在职场中得到的情绪价值这种内分泌混乱的消费品。”\n总结一下,就是我们的行为逻辑要“自洽”,不要活得“拧巴”,不要“既要又要还要”。想清楚自己到底想要什么?(是舔好领导然后升职加薪?还是追求 work life balance?)哪些能力是值得花时间提升的?哪些事情是需要做好的?其它的事情就“let it go”吧。\n这也正如书中所说:\n“抓大放小。在大事上卷,在小事上躺。”\n而要做到这一点,首先就需要我们能够清楚地辨识并认领我们的“核心我”。\n2.2 辨识并认领自己的“核心我” # “中庸我”的主体叫做“核心我”,它是我们自我的起点,是人生的“基本盘”。\n“核心我”包括以下两个方面:\n“基底内核”:即我们当前暂时无法改变的客观条件,如:年龄、身体素质、性格、学历以及专业等; “精神内核”:即我们的思维模式,如:人生观、金钱观以及工作观等。 面对这些“既成事实”,我们应该要不卑不亢地认清并认领自己的“核心我”,比如当下我在自己的专业领域内到底是什么水平?我的核心诉求、我追求的生活方式是什么?\n“中庸我”思维能够接受自己的“核心我”,并在当下的现状上清醒地寻找所有可能的机会,努力地改变自己; “梦幻我”思维则很容易轻视自己,认为自己被过往所拖累,拧巴纠结,放不下过去,大把的时间和精力消耗在自怜自艾、怨天尤人上了,那成长的空间自然就小了。 当然,认清自己的“核心我”是需要时间的,是需要一定的经历过后才能发现的。\n“有一些人,在经历过一番折腾之后,会发现自己当时想要的,现在已经不想要了。不是吃不了苦意义上的不想要,而是发现真的不适合自己,并且自己也确实没那么喜欢。”\n看清了自己的“核心我”之后,则是需要积极地去践行它。在努力的路上,我们很多时候是孤独的、被否定的,这时候就需要耐得住性子,放弃短暂的享乐,聚焦长线的筹谋,持续提升自己的弹性能力。\n“在一无所有的时候,有一份不怠惰的耐心,这种能力将成为一件威力很大的武器。”\n2.3 围绕“核心我”提升弹性能力 # 围绕“核心我”提升弹性能力,就是指提升我们的“弹性我”,即构建我们自己的个人价值。\n“工作多年的职场人,在别人眼中所看中的,是掌握了什么跑赢工龄的专业技能,是能带来什么样的资源和人际关系,是综合实力和氛围感,代表的是一个立体的维度。”\n在“弹性我”中,需要我们不断去提升的能力包括:\n时间管理能力:聚焦主线,舍弃不重要的工作,用“核心我”去度量,做好平衡,杂中有序,忙而不乱; 积极注意力偏好:成长型思维,关注成长和发展,而不是负面和缺陷; 信息接收和处理能力:对外界信息保持敏感,主动寻求反馈,学会对信息进行提炼和取舍; 表达能力:主动提高沟通的频率可以帮助我们在职场中拿到属于自己的主动权,获得领导的信任,交流和汇报不要无脑抛问题,要带上自己的思考和方案; 领导能力:分好“蛋糕”,不空讲愿景和未来,敢于担责,建立信任,抓大放小,眼里容得下沙子,高效开会,保持坦诚; 展示能力:做好“印象管理”,经营好自己的人设,向上管理。 “当我们感觉目标离自己还比较遥远时,弹性就是解药——它可以让我们看得见自己的变化,了解自己的增量,体会到所谓的日子有功。”\n2.4 修补“隐性我”的创伤,或带伤前行 # “隐性我”指的是我们在成长的过程中,所遭遇过的一些伤痛(比如原生家庭造成的影响),从而让我们在今后的生活中变得更加敏感、自卑;以及自己过往的一些(可能不太正确的)价值和思维模式,从而在某些方面限制我们的发展。\n“能超脱介质成长的人是极少的,小时候依托于原生家庭,长大了依托工作。”\n“中庸我”思维会选择和“隐性我”共存,或者通过行为去影响这些底层认知。“中庸我”不会过度地去让“核心我”和“隐性我”纠缠,产生不必要的内耗; “梦幻我”思维则会把“核心我”和“隐性我”搅和到一起。当遇到困境的时候,“梦幻我”会让你怨念自己的过去和“核心我”,觉得自己就是这样,让自己憋得无处可发、无能为力。 我们的过去并不能决定我们的未来,人生是一个不断向前、无法后退的过程,只有一直朝前看,去积极探索人生的各种可能性,我们的生活才可能变得更加精彩。\n另外,本书中关于职场“PUA”的部分,让我对人们口中常说的“PUA”一词有了更加清晰的认识,比如并不是所有的批评都是 PUA。有的领导可能比较心直口快,但就事论事,目的是能让下属直观地认识到自己的问题并改进,这不是 PUA。那么什么是 PUA 呢?不同于一般的批评和指责,PUA 中的否定和贬低是不分缘由的,或者不成比例的,也就是俗话说的“对人不对事”。其目的是让下属产生“资格感缺失”,感觉到自己不配,最后形成隐性的自卑。\n“心理学中的“煤气灯效应”就是指通过施加情感虐待和控制,让受害者逐渐丧失自尊,产生自我怀疑,人格瓦解,无处可逃,最后人生崩塌。这其实就是我们现在讨论的比较多的“PUA”,即“Pick-up Artist”的缩写。”\n那么我们应该如何避免被 PUA 呢?书中给出了一些建议:\n明辨真相,客观看待事实; 建立并认领自己的“核心我”,不盲目地屈服; 不靠近、甚至远离令自己感到不适的职场环境。 2.5 应对“混沌我”的挑战,抬头向前走 # “我们人类首先存在于环境之中,我们不能脱离环境,环境塑造了我们,决定了我们的可能性。”\n“混沌我”指的就是面对外部大环境的变换,我们要如何凝聚自我,并坚定地走好自己的路。\n作为一名身处这个局势风云变幻的大环境中的从业者,我更加清晰地认识到,自己能够有当下的境遇首先是“时代+平台+运气”,最后才是个人能力所堆彻而成的结果,毕竟,这个时代从来不缺“卷王”。认识到这一点后,我明白我要更加努力地工作,珍惜自己现在的岗位,但同时我也知道,个人的努力在这个社会的大势面前是微不足道的。因此,我们要努力,但无需过度努力(这里的“过度努力”指的是过度加班从而伤及自己的身体健康、甚至心灵能量,个人认为有点类似于修仙小说中的“为了快速突破境界,然后以自己的寿元和根基为代价服用各种药物,或修炼邪功,从而获得不属于自己的实力”),踏踏实实地过好每一天,做好长远规划,有风险意识,持续学习和提升自己即可,每一片落叶自会有它的去处。\n另外,关于如何避免“内卷”?如何正确地去“卷”?本书中的一些建议给了我很大的启发:\n“少数事卷,多数事躺”:抓大放小,要在大事上卷,做好真正重要的事,至于其它的一些小事,有时候就躺一躺也无妨,不要把自己逼得太紧; “长期的事卷,短期的事躺”:聚焦长线目标,比如专业能力的提升、职场技能的锻炼,越是需要我们长期去做的事就越重要,越值得我们坚持,而至于一些临时的“支线任务”,摸摸鱼也是可以的; “行动上卷,情绪上躺”:不要“过度反思”,从而陷入情绪内耗的漩涡,可以让自己先行动起来,有些时候事情做着做着可能就“柳暗花明又一村”了。 “正确的卷法是去好好发展,快乐地卷,而不是当一个偏执的神经病。”\n三、心得体会 # 作为一名从校园进入职场刚满一年的小白,面对无尽的加班、干不完的杂活以及领导的否定,自己也常常陷入到迷茫和痛苦之中。如何在职场中活出自我,同时更好地平衡工作与生活?如何认清自己的目标,以更加恰当的方式去付出和努力?这些问题一直困扰着我。\n最近,我看了钱婧老师写的这本书,看完过后,我的不少疑惑得到了解答,内心的焦虑也在很大程度上得到了缓解。本书中提到的职场生存策略让我获益匪浅,在职场中我开始变得不再内耗和拧巴,对任何人和事都有了自己清晰的判断,也开始能够静下心来做好自己的事情,持续提升自己的技能,构建自己的竞争力。“在一无所有的时候,有一份不怠惰的耐心,这种能力将成为一件威力很大的武器”,这句话给了我很大的鼓舞,也是我在职场中砥砺前行、坚守自我的一份信仰。\n","date":"2024-08-10","externalUrl":null,"permalink":"/articles/%E6%88%91%E6%9C%89%E8%87%AA%E5%B7%B1%E7%9A%84%E5%AE%87%E5%AE%99%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E5%B9%B4%E8%BD%BB%E4%BA%BA%E7%9A%84%E8%81%8C%E5%9C%BA%E7%94%9F%E5%AD%98%E4%B9%8B%E9%81%93/","section":"Articles","summary":"","title":"《我有自己的宇宙》读书笔记 | 年轻人的职场生存之道","type":"posts"},{"content":"","date":"2024-08-10","externalUrl":null,"permalink":"/tags/%E8%81%8C%E5%9C%BA%E4%B9%8B%E9%81%93/","section":"Tags","summary":"","title":"职场之道","type":"tags"},{"content":" 一、关于本书 # 《睡眠革命》 个人健康 作者:尼克·利特尔黑尔斯 简介:本书的作者是英超曼联的运动睡眠教练,专门负责帮助运动员解决睡眠难题、改善睡眠质量。他将自己多年总结的方法论在书中进行了讲解,让人们能够正确认识人体的昼夜戒律并合理安排自己的睡眠周期。通过书中的方法,我们就能够彻底改善自己的睡眠质量,实现最大限度的身心修复,提高生活效率,让你自信且快乐地度过每一天。 二、内容分享 # 2.1 什么是昼夜节律? # 在现代社会,由于学业、工作以及生存的压力,许多人长期压榨自己的睡眠时间,熬夜、作息不规律,这种违背人类昼夜节律的生活方式,正在逐渐摧毁现代人的健康。\n什么是昼夜节律?\n“如果作息时间非常“规律”,能在早晨按时起床,那么到了晚上,睡眠需求就会达到峰值。这完全符合昼夜节律,为我们提供了最佳入睡时机。我们会在凌晨 2~3 点进入一个高效的睡眠阶段(与此相对应,12 小时后又会出现一个睡意蒙眬的时段,它会以午后倦怠的形式出现),并且不久之后,在太阳升起、新的一天开始之前,我们的体温也会降到最低点。这时,就像按下了一个开关一样,人体会停止分泌褪黑素,因为我们将从黑暗慢慢进入光明。随着日光渐强,我们体内开始分泌血清素,一种刺激情绪的神经递质,它将和褪黑素此消彼长。”\n昼夜节律是大自然的法则,如果强行挑战规律,就会为自己的身体健康埋下诸多隐患。\n那么,我们应该如何掌控自己的睡眠,从而更好地适应昼夜节律呢?——了解自己的睡眠周期。\n2.2 什么是睡眠周期? # 在本书中,作者提出了一种 R90 方案,用于管理自己的睡眠周期:\n“R90 方案:R90 指的是以 90 分钟为一个周期,获得身体修复。从临床上说,90 分钟是一个人经历各个睡眠阶段所需的时间,这些睡眠阶段组成了一个睡眠周期。”\n在我们的睡眠周期中,根据睡眠质量可以分为:\n深睡眠 浅睡眠 快速眼动睡眠 “睡眠的生理修复功效大多产生于深睡眠阶段,比如生长激素分泌量的增加。人体生长激素是一种能促进新细胞生长和组织的修复、让人体能在日常劳作后获得休整、让人恢复生机与活力的关键成分,我们都离不开它。”\n因此,如果一直被困在浅睡眠中,那么我们将无法充分享受睡眠的种种益处。\n另外,每晚的各个睡眠周期是互不相同的:\n在较早的睡眠周期中,深睡眠所占的比重更大,因为此时身体希望我们尽快进入深睡眠状态; 而在较迟的睡眠周期中,快速眼动睡眠占有更大比重。 在理想状态下,我们应该以“睡眠—\u0026gt;醒来—\u0026gt;睡眠—\u0026gt;醒来”的模式,顺利地从一个睡眠周期过渡到下一个睡眠周期,并随着时间的流逝,逐渐减少深睡眠、增加快速眼动睡眠,直到清晨醒来。\n那么,我们应该如何管理自己的睡眠周期,并提高睡眠修复的质量呢?\n2.3 如何管理睡眠周期? # 2.3.1 设定固定的起床时间 # 在本书中,作者分享了一些制定最佳起床时间的建议:\n这个起床时间应该是每天都能实现的,并且除了某些特殊情况(比如要赶早晨的航班)之外,在你的日常生活中,并没有任何事情需要你起得比这个时间更早; 周末也要遵循这一起床时间,因此不要选择不太现实的时间,然后幻想你可以在周末大睡懒觉; 在理想状态下,你的固定起床时间,应该比你必须上班、上学或做其它事的时间早至少 90 分钟,这样你才有充分的准备时间。 下面分享一下我个人实践后的一些心得:\n要想养成一个固定的睡眠习惯,并在每天起床时都精神满满、自觉醒来,那么就需要计算自己的睡眠周期。一般我们需要睡 5 个睡眠周期(7.5 小时),但是之前自己并没有考虑到入睡也需要一个过程,不应该直接将上床关灯的时间作为计算睡眠周期的开始。我曾尝试过在 12 点准时关灯睡觉,早上 7 点半(我 9 点上班,起床时间应比上班时间提前 90 分钟)闹钟响起时,大脑仍然很困。因此,若想要每天在 7 点半自然醒来,并在早晨拥有更多的锻炼和学习时间,则需要提前 7.5 + 0.5 (缓冲时间) = 8 小时(一般是晚上 11 点半到 12 点之间)就关灯睡觉,因为你并不能马上入睡。另外,要想形成习惯,就应该每天坚持这个作息规律,包括周末。任何打破该作息的睡眠行为都会破坏早起习惯的养成,因此我不会在周末睡懒觉。\n2.3.2 从整体把控睡眠情况 # 许多人都会面临的一个问题就是,担心自己睡不好。不困就上床或是还没有准备好就上床,只会引发更多问题。而在夜间睡到一半时,因为睡不着了而压力重重、忧心忡忡,这也并不能让自己快速睡着。一旦开始担心这个问题,身体就会释放出肾上腺素和皮质醇等压力激素,反而更加清醒。\n面对这个问题,我们不妨以“周”为维度来统计我们的睡眠周期,而不是每晚睡了几小时。一个晚上并不会决定一切。所以,如果我们每晚需要 5 个睡眠周期,那完全可以将每周获得 35 个睡眠周期,设为自己的目标。\n“特殊情况(比如备战考试、熬夜加班)时可以不必强求非要睡满 5 个周期,但每周要争取获得至少 4 个睡眠充足的晚上。”\n2.3.3 注意睡眠前后的行为 # “睡觉前的行为会直接影响睡眠质量和持续时间,而醒来后的行为会对新的一天产生重大影响。在理想状态下,你需要 90 分钟的睡眠前适应时间和同等时长的睡眠后适应时间。”\n在本书中,作者给出了一些关于睡眠前后行为的建议,下面分别按“睡前行为”和“睡后行为”进行了归纳。\n睡前行为:\n如果你吃得很晚,就不应该马上上床睡觉,胃里堆满食物、忙着消化将会影响你的昼夜节律,从而影响睡眠质量; 尽管酒精会带来昏昏欲睡的感觉,但是不应摄入过量的酒精; 在睡觉前提前关闭电脑、平板电脑、智能手机和电视机,能减少你暴露在这些设备发出的蓝光下的时间(同时也有利于避免产生新的焦虑,各种杂乱思绪是破坏睡眠周期的一大干扰); 让卧室保持凉爽,并在睡前冲一个温水澡,从而能更好地适应从白天到夜间的温度变化; 让一切暗淡下来(在昏暗环境中,体内会分泌褪黑素,然后我们就会睡意朦胧); 准备一下明天的生活必需品,能放空你的大脑,为入睡做好准备; 避免剧烈运动。 睡后行为:\n起床后不要马上看手机,可以远离电子设备一会儿; 以一杯茶或一杯咖啡开启新的一天是一个不错的选择; 轻微的锻炼可以让你的身体慢慢适应新的一天; 到户外散散步,沐浴阳光,并呼吸一下新鲜的空气; 循序渐进地发动你的大脑,可以看看书、听听播客、做做家务,这些都是开始新的一天、再度融入这个世界的好办法。 恰当的睡后行为能让我们在新的一天更有效率,尽管完成这一系列程序需要花一点儿时间,但随后投入工作或社交时,我们能更加地清醒和从容。\n2.3.4 抓住日间小睡的机会 # “如果能在充分利用好夜间睡眠的同时,也利用好白天的时间,就能给身体和心灵带来一个不断重新启动的机会,帮助你满足现代社会的各种需求。我们并不是不加选择地随意打瞌睡,而是主动掌控并利用白天的各个机会,力争从中获取最大收益。”\n在午后小睡:\n午后时光是一天中次优的身心修复时段。\n如果夜间缺失了一个睡眠周期,午后就是最佳的弥补时机。这一时机不仅时间长,而且效率高。如果当天晚上你有可能会晚睡,那不妨利用好第二天的午睡时光。我们既可以利用这个时机,插入一个 90 分钟的睡眠周期(不推荐,容易出现睡眠惰性,睡醒后会表现出精神恍惚),也可以补充一个 30 分钟的可控修复期(推荐)。日间小睡有助于维持或改善随后的表现,提高生理和心理的灵敏度,并能有效改善情绪。\n即使你并没有真正进入睡眠状态也没有关系。重要的是,你能利用这段时间闭上眼睛、脱离这个世界片刻。能够睡着固然很棒,但徘徊在似睡非睡、似醒非醒的蒙眬、迷糊的状态中,同样也能达到休息的效果。\n在傍晚小睡:\n另外,我们可以利用傍晚的时间(下午 5~7 点之间)小睡一会儿,从而能够更好地利用晚上的时间。\n如果错过了午后的小睡,可以利用这一时机,小憩 30 分钟左右(插入一个可控修复期)。但此时不适合睡上 90 分钟(一个完整的睡眠周期),否则会影响夜间的睡眠。\n利用好日间的这两个休憩时机,将给你带来无穷信心,减轻睡眠压力,让你晚上可以晚点儿睡觉,并且不必为睡眠不足而过度担忧。\n从长远来看,这些日间小睡并不能代替夜间的睡眠。但这些日间小睡能和你的生理节律保持协调,补充夜间睡眠周期,促进身心的全面修复,并让你保持良好的精神状态和较高的工作效率。\n2.3.5 合理安排饮食和锻炼 # 如果我们在白天进行了体育锻炼,那么晚上躺在床上时,身体比平时更疲乏,会更容易睡着。合理的饮食和锻炼,有利于睡眠质量的提高。三者齐头并进,将给你的生活质量带来飞跃式的提高。\n三、心得体会 # 这本书是在我看完《早起的奇迹》之后紧接着阅读的一本书,因为当时自己想尝试早起,却因为总是犯困而很难坚持下去。本书的作者从人体的昼夜戒律开始讲解,让我对睡眠的生理学原理有了更具体的认识,作者也针对个人的睡眠管理给出了许多有用的意见,让我获益匪浅。通过合理地把控自己的睡眠周期,我们就能轻松实现早起,让每一天都变得更加高效。\n","date":"2024-07-19","externalUrl":null,"permalink":"/articles/%E7%9D%A1%E7%9C%A0%E9%9D%A9%E5%91%BD%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E4%BA%86%E8%A7%A3%E4%BD%A0%E7%9A%84%E6%98%BC%E5%A4%9C%E8%8A%82%E5%BE%8B/","section":"Articles","summary":"","title":"《睡眠革命》读书笔记 | 了解你的昼夜节律","type":"posts"},{"content":"","date":"2024-07-19","externalUrl":null,"permalink":"/tags/%E4%B8%AA%E4%BA%BA%E5%81%A5%E5%BA%B7/","section":"Tags","summary":"","title":"个人健康","type":"tags"},{"content":" 一、关于本书 # 《早起的奇迹》 个人成长 作者:哈尔·埃尔罗德 简介:这本书讲述了作者在遭遇了人生的重大挫折之后,通过每天坚持早起并执行“SAVERS”人生拯救计划,从而实现了人生的奇迹。本书对“SAVERS”人生拯救计划的内容进行了详细的介绍,通过长期实践该计划,就能彻底改变你的人生、健康、财富和人际关系。 二、内容分享 # 2.1 为什么要早起?(Why) # 我自己曾经也经历过长达几年的晚睡晚起(或是晚睡但被迫早起)的时光,有时是因为学习和工作,但更多的时候却并没有特别重要的事,只是习惯性地熬到那个点才会睡,最终收获的只能是第二天一上午的混沌,甚至是一整天的低效。\n另外,有时我会想在睡前安排做一些事,比如:阅读、学英语,但往往事与愿违,我总是因为疲惫或别的借口而放弃。\n如果,我能将每天一些例行的安排放到早上上班之前进行,我便不能再有借口去逃避,并且还能以一个更加饱满的精神状态投入到这些任务上,于是我决定尝试早起,并通过阅读学习相关的一些方法论。\n“缺乏自我提升的意识和紧迫感是导致 95% 的人平庸、无能,无法过上理想生活最主要的原因之一。”\n将早起后的时光用于个人发展项目(比如:冥想、阅读、正念、学习、晨练),那么我们的内心将会变得更加充盈与自信,并开启充满激情的人生。\n2.2 早起后做什么?(What) # 在本书中,作者提出了一个叫做 **SAVERS(拯救者)**的早起计划,它包括以下内容:\nS: Silence (心静) A: Affirmations (自我肯定) V: Visualization (内心演练) E: Exercise (锻炼) R: Reading (阅读) S: Scribing (写作) S:Silence (心静):\n“心静,灵魂才能更清楚地看清前路,分辨善恶、认清真假,并抵达澄明之境。”\n“心静”可以提高自我觉醒度,并让我们认领自己的“核心我”(即什么事对我而言是最重要的,除此之外的事我都可以忍受或做出让步)。“心静”可以使我们的头脑变得明晰,每天都能专注自己的目标和优先事项,并缓解我们当下的焦虑。\n练习方式:冥想、祈祷、沉思、深呼吸、感恩。\nA:Affirmations (自我肯定):\n“自我肯定会影响你的思维方式和自我认知,帮你突破自己信念与行为上的局限,为成功打下坚实的基础。”\n我们应该停止为过去内疚,放下自卑,勇敢地朝着自己想要获得的成功前进。\n我会将自己平时看到的一些能够激励我的句子写下来,贴到自己的房间里,每天看一遍。\nV:Visualization (内心演练):\n内心演练是指将自己的主要目标、最深的欲望和最激动人心的梦想具象化,你的想象越是详细和具体,就越有可能采取必要的行动将理想变为现实。\nE:Exercise (锻炼):\n每天早晨,哪怕只锻炼几分钟,也能大大提高你的精气神,让你保持健康,使自己变得更加自信,情绪得到改善,思维变得清晰。\n晨练可以唤醒你的身体,激活你的大脑,帮助你在白天保持精力充沛。\nR:Reading (阅读):\n“读书给人以乐趣,给人以光彩,给人以才干。”\n坚持读书和思考,是一辈子的事。\n在开始读一本书之前,先问自己几个问题:\n我为什么想读这本书? 通过读这本书,我希望获得什么? 我打算将学到的知识运用到实际生活中吗? 另外,有时与其读一本完全陌生的新书,不如重读一本你认为对自己有用的书。只要一本书能够改变自己生活的某个方面,我就会再读一遍,或者至少重读那些标记了重点的地方,并输出读书笔记。\nS:Scribing (写作):\n写日记是练习写作的好方法,将自己的一些想法记录下来会有以下好处:\n使思维更加清晰:写作会迫使我们更深入地思考,可以让你的思维变得更清晰,激起头脑风暴,帮助你解决难题; 捕获灵感:写日记不仅能帮你拓展思维,还可以防止你遗忘重要的灵感; 回顾经验教训:写日记可以帮你回顾自己所学的知识; 检阅自己的进步:年终时重读自己一年的经历,感觉非常奇妙,你可以看到自己的进步,这是人生中最为励志、自豪、享受的体验之一。 2.3 如何坚持早起?(How) # 在本书中,作者给出了一套流程,从而能够帮助我们更好地实现早起,包括以下内容:\n睡前进行积极的心理暗示; 将闹钟放到离床较远的地方,并在闹钟响起后及时起床; 起床后立即刷牙洗脸(不要马上看手机),并以此作为缓冲,让大脑逐渐清醒; 喝水,及时补充水分能帮助我们恢复身体的活力; 晨练,让自己出一点汗,大脑将迅速变得清醒。 三、心得体会 # 在阅读本书之前,我是一个长期习惯于晚睡晚起的人。后来,有一次偶然间在微信读书的首页上刷到了这本书,在好奇心的驱使下我读了几页,然后就被其中的内容彻底吸引了。本书从为什么要早起、早起后应该做什么以及怎么实现早起等多个方面论述了与“早起”这一话题相关的方法论,并辅以作者的个人经历,让我下定决心要尝试一下书中的生活方式,改掉之前的恶习。\n然而,道理是简单的,但实践起来却总是充满了困难。书中的方法虽然确实有用,但在我刚开始实践时,却总是会感觉很困、起不来,这也引发了我对人类睡眠机制的好奇。因此,我看了《睡眠革命》这本书,它让我对人类的昼夜节律有了深刻的认识,并了解了掌控自己睡眠的方法。\n经过几周的尝试过后,我终于找到了自己的作息节奏。自从开始早起以来,我明显感觉自己的身体状况和情绪状况产生了积极的改变,对工作更加专注,心情更加愉悦,对生活更加充满信心。希望自己在今后的生活中,能够将“早睡早起”这一习惯长期坚持下去,像书中的作者一样,始终保持对生活乐观的态度。\n想要制定更加科学的早起计划,就需要对人体的昼夜节律有一定的了解,感兴趣的朋友可以阅读我写的另一篇文章:《睡眠革命》读书笔记。\n","date":"2024-07-09","externalUrl":null,"permalink":"/articles/%E6%97%A9%E8%B5%B7%E7%9A%84%E5%A5%87%E8%BF%B9%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E5%88%B6%E5%AE%9A%E4%BD%A0%E7%9A%84%E6%97%A9%E8%B5%B7%E8%AE%A1%E5%88%92/","section":"Articles","summary":"","title":"《早起的奇迹》读书笔记 | 制定你的早起计划","type":"posts"},{"content":" 一、关于本书 # 《认知觉醒》 个人成长 作者:周岭 简介:这是一本非常全面的个人成长指南,它打开了我的视野,提出了关于生活各个方面的认知方法,同时也解释了许多自己之前遇到过,却并不知道其底层原理的现象,让我从一个更高的维度认识到了许多事情的本质。当然,就像书中提到的,了解到这些知识只是开始,如何将学到的知识落实到行动,进而改变自己的生活才是最重要的。通过阅读本书,我们将从根本的认知上完成觉醒,进而开启不一样的人生。 二、内容分享 # 2.1 学会应对焦虑 # “一切焦虑的根源都是因为自己的欲望大于能力,又极度缺乏耐心。”\n在生活中,我们总是会遇到一些各方面都比自己厉害很多的人,也会看到别人在互联网上晒出自己精致的生活,这时我们会幻想自己什么时候也能变成别人那样,但往往由于一些客观条件的限制,亦或者自身与别人的能力差距过大,从而让自己陷入到焦虑和内耗之中。\n“痛苦来源于与他人的比较。”\n本书中提到了几个可以缓解焦虑的办法,我认为很有帮助:\n克制欲望,不要让自己同时做很多事; 面对现实,看清自己真实的能力水平; 要事优先,想办法只做最重要的事情; 接受环境,在局限中做力所能及的事; 直面核心,狠狠逼自己一把去突破它。 另外,前不久从网上一位博主口中了解到了“持续努力”的概念,这位博主一开始只是一位普通学校的学生,却通过自己合理的规划以及长期的坚持,最终拿到了亚洲顶尖大学之一的全奖 cs phd offer。通过这个例子,我个人总结了一下,“成功 = 规划(信息/决策)+ 行动力 + 耐力(长期坚持/终身学习)+ 运气”,这也正符合本书中关于”舒适区理论“以及“复利效应”的内容。培养自己“延迟满足”的能力,将有利于在漫长的人生中收获更大的成功。\n2.2 了解自己的潜意识 # 什么是潜意识?\n“潜意识没有思维,只关心眼前的事物,喜欢即刻、确定、简单、舒适,这是属于天性的部分,同时,它处理信息的速度又极快,至少可达 11000000 次/秒,能极其敏锐地感知很多不易察觉的信息,这是属于感性的部分。”\n关于潜意识,书中让我印象最深刻的点是,潜意识处理信息的速度远远超过我们的理性思考,因此当我们第一次见到一个人、第一次来到一个新的环境以及第一次尝试一个新的事情时,潜意识往往会给出一些或正向或负向的反馈,这是因为潜意识往往已经在瞬间从周围的外部环境接收了大量的信息,从而产生了反馈,这应该就是人们常说的“第六感”或者说“直觉”。有些时候,选择相信自己的直觉(follow your heart),往往可以选择出最适合自己的路,并规避掉一些不必要的折磨。\n2.3 培养深度沉浸的能力 # 本书提到了一个概念叫做 “身心分离模式”,即我们常说的走神(身体在做 A 事情,脑子里却在想 B 事情)。据我观察,一个人所谓的学习能力,在很大程度上取决于其深度沉浸(专注)的能力。如果我们在做一件事情时,总是无法全身心地投入其中,那么我们做事的效率以及最后的结果恐怕都不会太好,人与人之间能力的差异也由此被拉开。\n如何提高自己的专注力?\n“慢慢练习收回感受,让注意力回到当下,进而改变自己的底层行为模式。”\n从现在开始,尝试在专心做事之前,将手机扔到一旁,抛掉脑中的杂念,专注于当下吧!\n2.4 形式主义的陷阱 # 如今,许多人都热衷于通过打卡的方式来督促自己去做某件事,比如学英语、健身等等,但是往往在初期做得热火朝天,却在后续的执行中陷入为了打卡而打卡的陷阱。当我们在完成一项任务时,如果心里只想着还差多少就能完成今天的任务,比如今天还要背几个单词、今天还要看几页书……,而不专注于事情本身,那就只是为了完成而完成,我们从这个过程中并没有真正收获什么。\n本书提到了一个概念叫做 “认知闭合需求”。所谓“认知闭合需求”,就是指当人们面对一个模糊的问题时,就有给问题找出一个明确的答案的欲望。\n将这一概念扩展到行为上也是一样的:\n“一件事若迟迟没有完成,心里就总是记挂,期盼着早点结束;此事一旦完成,做这件事的动机就会立即趋向于零。任务一旦闭合,大脑就会清理原先被占用的记忆空间,那件事很快就会退出脑海,行动的动机也就消失了。”\n如何避免这一现象?\n我们可以用“记录”代替“打卡”。目前,我个人采用的时间管理方法是:首先,列出今天想要完成的事件清单,并根据优先级分为(高、中、低);然后,我会优先集中精力,用相对比较大块的时间去完成高优先级的任务,同时用一些相对零散的时间去完成一些优先级为中或低的任务。这样便能够在一定程度上规避“认知闭合需求”,因为列出的任务清单只是为今天要做的事指明一些方向,并不需要全部完成,只要保证完成了优先级最高的项即可,这样我便不用想着今天还有多少卡没有打,从而能够更加专注地投入于手中的任务;最后,我会使用 App 记录每天用于各项任务的时长,这样我便能对自己的学习情况有一定的掌控,并在之后进行调整和改进。\n2.5 高效学习的秘诀 # “是否有及时、持续的正向反馈,正是产生学习效果差异的关键。”\n今天,在传统的学校教育中,在初期沉浸于大量的理论学习是大多数人的学习方式,然而只有输入没有输出,进而没有反馈的学习往往只是停留于表面,是低效的。如何最大程度地挖掘自己的主观能动性?——持续获取正反馈。我们要有 “作品意识”,即不管学到了什么东西,都可以尝试把知识整理为能够输出的东西(一篇文章、一个视频、……),或发表到网上,或讲给别人听,从而获取反馈,并促使自己在之后不断做得更好。\n另外,我从本书中学习到了 “极度专注 + 主动休息” 的学习模式。一个人每天的精力和专注力是有限的,正确的学习策略应该是:在学习和工作时全情投入,并在感到疲惫时及时休息、放松,以便在下一段时间中更好地投入到工作之中。\n“有效学习的关键是保持极度专注,而非一味比拼毅力和耐心。”\n目前,我除了每晚的例行睡眠之外,还会在每天中午 1-2 点和傍晚 6-7 点之间,插入一个时长在 30 分钟左右的日间修复性睡眠,这能让我短暂地放空因工作而变得迟钝的大脑,回复精力,并在之后以更好的精神状态投入到学习和工作之中。\n2.6 无痛自律的秘诀 # 如何变得自律?\n“真正的行动力并不完全来源于自制力。”\n本书提到了一个概念叫做 “注意力的增强回路”。它指的是每天早上起床后,我们都可以刷新前一天的精神状态,并获得一份纯净的注意力,这时如果你选择在起床后立马就去刷手机、沉迷于新闻和短视频,那么你的注意力就会持续地往负向的方向增强,你沉浸得越久,想要“刹车”就越难;反之,如果你选择在起床后立马投入到学习中,那么你将会更容易持续地专注下去,我把这种现象叫做大脑的“惯性”。因此,是否能够度过自律的一天,往往在一天的开始时就决定了。\n另外,书中还提到,实现自律的另一个关键还在于个人的 “认知层次”,我称之为“目标感”。即如果一个人心中有远大的目标,并愿意为之持久的奋斗,那么在追寻梦想的这条道路上,则大概率会比那些浑然度日的人拥有更强的自律性。\n2.7 坚持早起 # 本书中提到,个人成长最高效的方法便是 “早冥读写跑” 五件套。其中,关于“早起”的部分对我的影响最大,也最深刻地改变了我的生活。\n在此前的生活中,我总是习惯于晚睡晚起,上午的时光对我来说似乎总是迷糊和短暂的。通过本书,我认识到了早起的好处。早起能让自己在头脑最清醒的时候,拥有充足的时间进行学习和思考;早起后适当的运动,也有利于提高上午的工作效率和专注度;更重要的是,早起能够让自己以一个更加积极的心态来面对新的一天,减缓焦虑等负面情绪。\n为了实现我的“早起计划”,我还特意去阅读了相关的书籍,这里推荐两本书:《早起的奇迹》和《睡眠革命》。这两本书让我对人体的昼夜节律有了更加深刻的认识,让我学会了如何更好地掌控自己的睡眠习惯,从而实现更加美好的人生。\n从我坚持早起以来,在每天早晨正式开始工作之前,我有了更多的时间去学习英语、健身和阅读,我的心态也发生了积极的改变,能够以更加饱满的精神状态来面对我的工作。总之,希望自己能在今后的生活中,将早起这一习惯继续坚持下去!\n三、心得体会 # 初读这本书时,正值我刚毕业进入职场块一年的时候,也是我人生中最迷茫的几个时刻之一。刚入职那会儿,觉得自己毕业就进入大厂,自以为正是可以为一份事业而激情奋斗之时,却发现理想是好的,但现实却是残酷的。我进入了菊厂中最没有“技术”的几个部门之一,人称“华为公务员”,每天都干着没什么技术含量,也没什么成就感的工作,可能只有刚入职的头几个月里是自己成长最快的时候。在这样日复一日的“牛马”生活中,我曾尝试奋力挣扎过,企图有朝一日能摆脱这样的生活,进入更好的平台去追求自己心中所谓的事业,但更多的时候却是在现实的摧残下,开始对工作和生活变得越来越麻木。\n“救赎之道,就在其中”,这句话出自电影《肖申克的救赎》,也是支撑我度过这段灰暗时光的精神力量之一,它让我相信在人生的逆境中,只要坚持不懈地努力,总会有逃脱当下这个“牢笼”的一天(强烈推荐没有看过这部电影的可以去看看,经典老电影能流传下来果然是有其独到的魅力的)。而至于本书,则教会了我具体的做法,让我懂得了怎么去利用好每一天的时间,化焦虑为具体,默默努力,静待翻身的那一天。\n最后,写下这篇读书笔记时(2024 年 11 月),我已经通过自己的“洞察 + 规划 + 努力”摆脱了之前的处境,通过公司内部转岗来到了目前这个更好的平台,也希望自己在今后的生活中,能够铭记这一份经历,坚持“长期规划 + 持续努力”,并最终实现成长和蜕变。\n","date":"2024-06-05","externalUrl":null,"permalink":"/articles/%E8%AE%A4%E7%9F%A5%E8%A7%89%E9%86%92%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E5%BC%80%E5%90%AF%E5%BF%83%E6%99%BA%E6%8E%8C%E6%8E%A7%E8%87%AA%E5%B7%B1%E7%9A%84%E7%94%9F%E6%B4%BB/","section":"Articles","summary":"","title":"《认知觉醒》读书笔记 | 开启心智,掌控自己的生活","type":"posts"},{"content":"","date":"2024-06-05","externalUrl":null,"permalink":"/tags/%E5%AD%A6%E4%B9%A0%E6%96%B9%E6%B3%95/","section":"Tags","summary":"","title":"学习方法","type":"tags"},{"content":" 一、关于本书 # 《富爸爸 穷爸爸》 投资理财 作者:罗伯特·清崎 简介:本书是我读过的第一本理财相关的书籍,它不仅让我第一次认识到了财商教育的重要性,也让我明白了什么是好的消费观和理财观。书中关于“应该以什么样的态度来对待工作和生活”的部分,让我获益匪浅。通过阅读本书,我们可以对自己当下的消费方式有一个更加清醒的了解和认识。 二、内容分享 # 2.1 财商教育的重要性 # “富人之所以越来越富,穷人之所以越来越穷,中产阶级之所以总是在债务的泥潭中挣扎,其中一个主要原因就是,他们对金钱的认识不是来自学校,而是来自家庭。遗憾的是,学校并没有开设有关“金钱”的课程。学校教育只专注于学术知识的传授和专业技能的培养,却忽视了理财技能的培训。所以众多精明的银行家、医生和会计师在学校时成绩优异,可还是要一辈子在财务问题上挣扎。如果我们继续把教孩子理财的重任交给那些濒于贫困边缘或已陷入贫困境地的父母,我们的国家又该怎么发展下去?”\n今天,我们大多数人的财商教育都来自于自己的父母,我们的消费观也大概率是由自己所处的原生家庭所塑造的。学校并没有将理财这项技能纳入到义务教育的范畴之中,以至于我自己长久以来并没有特意关注过自己的财务状况,有钱就存着,需要用钱就花。\n得益于我的母亲曾经是一名银行的会计,自己对于理财这件事的态度似乎也受到了家庭的影响。我并不排斥去理财,甚至有点喜欢和这些数字打交道。特别是现在自己已经工作,有了一定的收入和储蓄,我更加意识到了财务知识的重要性。如果我们只知道埋头挣钱,而不懂得如何管理好自己的财富,那么就极有可能使自己陷入到朝不保夕的财务风险之中。\n2.2 什么是资产?什么是负债? # “资产”:不管我是否工作,只需要我付出最低限度的劳动,就可以将钱放进我口袋里的东西; “负债”:把钱从我口袋里拿出去的东西。 今天,大家好像都特别热衷于买房买车,哪怕自己没有钱,也要贷款去买。通过本书,我了解到房子和车子都属于负债,而不是资产。在年轻的时候,我们应该做的,是尽力去积累自己的资产项(而不是去买入负债),从而可以在将来收获更多、更长久的财富。\n而大多数人都是随波逐流的,或是迫于社会压力,亦或是因为需要结婚等理由,在自己没有能力负担的情况下买了房和车,以至于早早地就背上巨额的负债,抗风险能力急剧降低,不敢轻易失业,自然也就没有了追寻自己真正所向往的生活的底气与条件。\n2.3 什么是好的消费观? # 通过本书,我知道了什么是好的消费观,这一点也是我认为最贴近自己当下的生活,最具有现实指导意义的收获。\n对于像我一样刚毕业的年轻人而言,我们首先应该尽量减少开支和借款,并勤劳地工作,从而打下一个坚实的资产基础。然后,我们要将自己挣的一部分钱用于构筑自己的资产,从而产生更多的现金流。当自己能够掌控的现金流足够多时,就可以去买房买车了。最后,不要妄图一夜暴富,平时多存钱,提高自己的抗风险能力,并且坚持学习财务知识,提高自己的理财能力。否则,即便自己拥有大量的财富,如果没有支配管理对应数额的金钱的能力,那么这些财富也终将会离你而去。\n2.4 构建多样化的收入来源 # “当你在自己的专业领域内变得越来越精通时,那么同时也意味着你被社会所异化了,你的选择变少了。”\n因此,作者鼓励我们除了做好自己的本职工作之外,一定要再去发展一项额外的技能,这样当你失业时,你还能有其他的经济来源维持自己的生活。目前,我也在积极探索工作以外的收入来源(在不影响本职工作的前提下),不让工资成为自己唯一的经济来源,并提高自己在财务上的抗风险能力。\n三、心得体会 # 这本书算是我的理财启蒙之作,也激起了我对投资理财相关知识的兴趣。在今后,希望我能继续阅读更多经济学相关的书籍,从而不断提高自己的风险意识和理财能力。\n","date":"2024-05-07","externalUrl":null,"permalink":"/articles/%E5%AF%8C%E7%88%B8%E7%88%B8-%E7%A9%B7%E7%88%B8%E7%88%B8%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%B4%A2%E5%95%86%E6%95%99%E8%82%B2%E5%90%AF%E8%92%99%E4%B9%8B%E4%BD%9C/","section":"Articles","summary":"","title":"《富爸爸 穷爸爸》读书笔记 | 财商教育启蒙之作","type":"posts"},{"content":"","date":"2024-05-07","externalUrl":null,"permalink":"/tags/%E6%8A%95%E8%B5%84%E7%90%86%E8%B4%A2/","section":"Tags","summary":"","title":"投资理财","type":"tags"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}] \ No newline at end of file +[{"content":" 一、关于本书 # 《小岛经济学》 经济学 作者:彼得·希夫 / 安德鲁·希夫 简介:本书通过将国家比作一个“小岛”,使用“鱼”、“渔网”以及“鱼邦储备券”等概念,形象地解释了经济学中的诸多概念,通俗易懂地揭示了经济学的运行原理。作者对“通货膨胀”、“信贷危机”以及“房地产泡沫”等经济现象进行了深入的分析和解释,清晰地点明了当今社会经济所存在的问题,丰富了我对于世界的认知,是一本非常适合非专业人士入门的经济学读物。\r二、内容分享 # 2.1 小岛经济学 # 在一个小岛上,人们以捕鱼为生……\n一开始,人们徒手进行捕鱼,当天捕的鱼只够当天吃,为了生存,人们必需日日不断地坚持捕鱼。\n随着第一张渔网(资本)的发明,提高了人们的生产力,从而可以将更多的鱼存储起来以备不时之需(储蓄),此外,人们也有了更多地时间追求更加丰富生活,亦或是进行创造性工作(进一步提高生产力)。\n“资本指的是一种设备,这种设备的建设和使用本身没有什么意义,其意义在于利用设备建设和制造其它需要的东西。”\n“努力使有限的资源产生最大的效益以尽可能满足人类的需求,这就是经济这一概念最简单的定义。工具、资本以及创新是实现这一目标的关键。”\n“经济增长的原因:找到了生产人类所需物品的更好方式。”\n个人的一点思考:如果我们将“捕鱼”理解为“重复性的工作”、将“经济发展”理解为“个人能力的提高”,那么当我们将这一套理论运用到实际生活中,我是否可以这样认为——如果我们每天的时间除了休息之外都被重复性的工作占满,没有额外的时间去学习提升自己,那么我们个人的成长速度将会是缓慢、甚至是停滞的?\n后来,随着经济规模不断扩大,小岛上出现了“政府”和“社会分工”。\n“在一个经济体中,如果人们有所分工,从事不同的商业和服务活动,其结果一定会比所有人都做同一种工作要好。分工增加产量,高产量又能提高生活水平。”\n“社会分工”带来了各种各样的就业岗位,人们需要掌握特定的技能才能找到对应的工作,企业也需要雇佣合适的员工来创造利润。\n“一名员工的具体价值主要取决于三个方面:需求(雇主是否需要这名员工所掌握的技能)、供应(有多少人具备这些技能)以及生产力(这名员工对那些任务的完成程度如何)。”\n“员工只要工作就有报酬,而企业主想得到回报只能等到企业赢利,它们的收益是对承担风险的回报,也是对成功整合稀有资源的回报。对利润的不懈追求推动了产品创新、企业发展与经济增长。正是这样的推动力提高了每个人的生活水平。”\n当个人的财富积累到了一定规模,人们为了让自己的鱼(钱)更加安全,于是就出现了“银行”。人们将自己的“鱼”存入银行,银行再用这笔钱对外发放贷款,创造收益的一部分用于支付储户的利息,剩下的则作为银行的赢利。其中,银行的“利率”是调控国家经济的关键。\n“贷款利率决定了银行能支付给储户的利息。存款利率是随存款年限递增的。存款年限越长,造成银行存款短缺的风险越低。因此,如果储户愿意长期储蓄,获得的利率也就较高,进行短期储蓄的储户所获得的利率则较低。”\n“高利率会抑制借贷,延缓经济增长。但同时,高利率也能刺激储蓄。最终,银行资产会再次积累起来,到那时利率又会下降。而较低的存款率表明人们更愿意将储蓄用于近期消费,因而抑制了为满足未来消费需求而进行的投资。”\n“美联储理论上是一家私有银行,但实际上却是美国财政部的延伸,它制定了美国的基准利率,影响着整个市场。美联储可以把利率降到足够低,这样企业和个人就更愿意借贷,以此刺激不景气的经济。低利率会刺激借贷、抑制储蓄,难怪美国已经由一个储蓄者的国家转变成了借款人的国家。”\n2.2 量化宽松政策 # 一个国家的经济并非是一直处于增长中的,当市场的调节能力失控,且政府又恰好做出了错误的决策时,就有可能造成经济的衰退。\n而对于“如何解决经济衰退”这个问题,在经济学上主要有两派观点:\n“凯恩斯学派”:在经济不景气时,政府可以通过扩大货币供给和财政赤字缓和自由市场的波动; “奥地利学派”:经济衰退是经济繁荣期所做出的错误决定的必要补偿,经济迅猛发展过后必然会有一个相应的衰退期。 其中,“凯恩斯学派”的观点就是我们常说的“量化宽松”政策。\n什么是“量化宽松”?\n“尽管很多人知道美国依赖量化宽松,但很少有人真正看透其本质:向金融市场注入新的资金,以推动价格上涨。实际上,量化宽松不过是通货膨胀的一种委婉表达,它也成为美联储将政府债务货币化的隐秘手段。但想用量化宽松政策来修复萎靡的经济,就好比企图用汽油去救火一样,汽油越多,火势就越旺。量化宽松政策是延长经济衰退的办法,而不是促进经济复兴的良方。”\n“通货膨胀不过是把财富从以某种货币储蓄的人手中转移到以同种货币负债的人那里。如果遇到恶性通货膨胀,存款就会变得一文不值,负债却一笔勾销。引发恶性通货膨胀以及随后经济灾难的原因都惊人地相似。这些国家都是通过降低货币价值偿还巨额外债,结果,本国的人民陷入了赤贫之中。”\n由此可见,“量化宽松”只是一种治标不治本的办法,它只想通过刺激需求侧来促进经济的发展,却没考虑到供给侧的技术创新才是生产力不断进步、经济得以长期稳定发展的根本。如果真实的生产力没有提高,只是单纯地靠发放更多的钱,那么随之而来的是物价也会水涨船高。并且由于过去的资产贬值,人们会变得越来越不愿意储蓄,从短期来看可能可以促进消费,但站在长远的角度,国家会因为缺少足够的钱进行投资,从而延缓生产力的进步。\n“不断扩大货币供应量的做法以及政府看似无限的负债能力掩盖了一个事实——实际信贷是受有限储备制约的。”\n“我们的消费不能超过产能,我们的借款不能超出存款,至少不能长期这样。我们必须生产出什么东西,才会使消费有价值。”\n“大多数经济学家认为,给老百姓更多的钱花就可以增加需求,但是这种做法并不能改变真正的需求,只会使人们花更多的钱购买已经生产出来的商品。只有增加供给才能切实满足人们更多的需求。”\n与之相对的,是“奥地利学派”的观点——当经济衰退时,需要通过市场的力量去促进资源的再分配,淘汰落后的产能,使这个社会的人力物力重新汇聚到更有价值的行业中。\n“当经济不景气时,物价需要下跌才能平衡经济局势。然而,目前许多政府应对经济衰退的本能反应便是造更多的货币,因为他们认为物价下跌会导致经济陷入需求崩溃的万丈深渊。但是,它们忽略了一旦物价下跌到一定程度,人们就会开始消费。这个过程淘汰了不必要的产能,把物价调低到符合内在供求关系的水平。”\n“政府总会通过各种形式干预储蓄的配置,包括政府贷款担保、公司及个人税收减免以及税务罚款等。这些政策的关键推动因素就是认为政府规划者要比储蓄者更清楚什么有利于社会的发展。然而,实际上,历史上充斥着各种浮夸的政策与方案,这些方案都是政府智囊团策划的,最终全都没有兑现他们的承诺。更重要的是,政府介入储蓄者和借款人之间采取的强制手段将借款的原因与结果割裂开来,使得储蓄的分配效率极为低下。贷给个人或者企业的款项如果无法成功促成必要的创新或者提高产能,就会浪费储蓄的供给,削弱整体经济。”\n“美联储宽松的货币政策,阻止了这种有益的经济衰退发生,于是人们只能同时面对经济紧缩与通货膨胀,从而导致“经济停滞型通货膨胀”的出现。”\n2.3 房地产泡沫 # 在房地产刚开始兴起时,政府通过“提高贷款上限”、“降低首付款”以及“降低信用标准”等手段,让人们能够轻易地背上负债、买入房产。随着房产的不断增值,越来越多的人涌入这个市场,加入了“炒房”的行列。然而,房价远高于其本身的价值,这个泡沫总会有破裂的那一天,书中称当时这种情况为“虚假的繁荣”。\n随着越来越多的人买不起房、不敢买房,房地产进入了“供大于求”的转折点,房价开始持续下跌。对于不少背负贷款、高位买房的人来说,手上的房产想要脱手只能赔本卖掉,巨额的贷款给许多家庭带来了沉重的压力。许多人偿还不起银行的贷款,信贷市场逐渐崩溃;此外,由于大量新建的房屋无人购买,企业和政府自然也不会再继续大兴土木,随之而来的则是大批建筑工人以及相关行业从业人员的失业。\n“由于房屋价格不再上涨,房屋净值不再增加,短期炒房也就无利可图。没有了利益的诱惑,过高的贷款额度就成了无法承受的负担。越来越多的借款人拖欠还贷,信贷市场发生崩溃。因为消费者不再投资房屋,其相关产业也陷入了困境。大批建筑工人、设计顾问以及电器销售人员纷纷失业。”\n为了缓解房地产市场的崩溃,政府开始参与进来进行调控,比如:“注入货币以弥补损失,持续发放宽松贷款,提高市场对房屋的需求,从而防止房价继续下跌”。与“量化宽松”政策类似,这种行为只能缓解近况,却无法从根本上解决问题。\n“如果任由房屋价格下跌,同时停止建造新的房屋,反而对国家的经济有好处,至少在真正的需求出现之前是这样。这样一来,人们就不会花那么多钱购买房屋,而是把钱花在那些经济发展中真正缺乏的东西上。不幸的是,政府的干预阻碍了这种资源再分配。美国的领导人很不理智地鼓励购房,抑制储蓄,还很不理智地鼓励借贷。这些因素共同作用,破坏了市场。本应倒闭的公司又在政府的支持下站了起来,本应解放出来的资金和劳动力被困在了无效的经济活动中,无法发挥更高的经济效益。市场的力量正要戳破信贷和房产泡沫的时候,美国政府插手进来继续吹大这两个泡沫”\n目前,身边不少同学都已经在家里父母的支持下买了自己的房子,我虽然羡慕,却仍保持着理智。希望终有一天,房子能够回归它原本的价格,在此之前,我们只需坚持开源节流,不断积累自己的资产储备就好了。\n三、心得体会 # 初读本书时,正值 2024 年 10 月,当时美联储时隔多年再次宣布降息,全球的经济情况都或多或少地受到了影响,国内的投资市场也开始变得活跃。在此之前,我对于经融领域可谓是一无所知,于是乘此机会,我决定恶补一波“基础知识”。\n本书以“小岛”作为故事,生动形象地解释了经济学中的许多概念,更巧的是,“银行降准降息”以及“房地产泡沫破碎”等现象此时恰好就发身在我的身边,理论结合实际,让我对书中提到的经济学理论有了更加深刻的认识和理解。\n最后,作为一个普通人,我们可以去学习和了解经济运行的基本原理,却左右不了社会整体的发展趋势。在这个风云变幻的时代中,我们如果能够运用自己的一点知识,去规避一些错误的决策,从而让自己免遭巨大的损失,那也算是学有所用了。\n","date":"2024-12-15","externalUrl":null,"permalink":"/articles/%E5%B0%8F%E5%B2%9B%E7%BB%8F%E6%B5%8E%E5%AD%A6%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E6%B7%B1%E5%85%A5%E6%B5%85%E5%87%BA%E7%BB%8F%E6%B5%8E%E5%AD%A6%E5%8E%9F%E7%90%86/","section":"Articles","summary":"","title":"《小岛经济学》读书笔记 | 深入浅出经济学原理","type":"posts"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/articles/","section":"Articles","summary":"","title":"Articles","type":"posts"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/","section":"home","summary":"","title":"home","type":"page"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/tags/%E7%BB%8F%E6%B5%8E%E5%AD%A6/","section":"Tags","summary":"","title":"经济学","type":"tags"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/","section":"Tags","summary":"","title":"读书笔记","type":"tags"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/categories/%E9%98%85%E8%AF%BB/","section":"Categories","summary":"","title":"阅读","type":"categories"},{"content":" 一、关于本书 # 《被讨厌的勇气》 心理学 作者:岸见一郎 / 古贺史健 简介:本书通过浅显易懂的对话,对“自卑”、“自由”以及“幸福”等人生话题进行了深入的探讨,并借此引出了本书的核心思想——“阿德勒心理学”。“阿德勒心理学”是关于“勇气”的心理学,面对书中提到的诸多人生困境,归根结底,都是因为缺乏“勇气”造成的。拥有“被讨厌的勇气”,是我们成长路上的一堂必修课。\r二、内容分享 # 2.1 改变自己的勇气 # “重要的不是被给予了什么,而是如何去利用被给予的东西。”\n不管自己当下的现状如何,都不要抱怨过去,而应该奋力成长,积极面对未来,这就是“改变自己的勇气”。\n在本书中,提到了“原因论”和“目的论”两个观点:\n“原因论”:一味地关注过去的原因,企图仅仅靠原因去解释事物; “目的论”:决定我们自身的不是过去的经历,而是我们自己赋予经历的意义。 其中,“原因论”是一种消极看待人生的观念,而“目的论”则更强调人的“主观能动性”,认为人是可以改变的。\n“如果过去决定一切而过去又无法改变的话,那么活在今天的我们对人生也将会束手无策。结果会如何呢?那就可能会陷入对世界绝望、对人生厌弃的虚无主义或悲观主义之中。”\n当面对自己过往不幸的经历或先天造成的不足,从而产生“自卑”或“逃避”等情绪时,我们要有积极面对未来的心态。换言之,我们不应被过去所束缚,亦或是沉溺于“舒适圈”之中,把过去的“不幸”当作自己“摆烂”和“不求改变”的借口,而应该承认自己目前的现状,接纳当下这个不完美的自己,并努力改变和提升自己,拥抱崭新的生活。\n人生最重要的是去思考如何打好自己手中现有的牌,而不是整天眼高手低、抱怨连天,幻想自己能有一手天胡的好牌。\n2.2 积极生活的勇气 # “人根本不可能一个人活着,只有在社会性的环境之下才能成为‘个人’。因此,阿德勒心理学把作为个人的‘自立’和在社会中的‘和谐’作为重大目标。那么,如何才能实现这些目标呢?——我们必须要克服‘工作’、‘交友’以及‘爱’这三大课题。”\n在工作中,当我们面对困难从而发现自己的不足时,难免会觉得气馁,甚至会产生“自卑感”,如何正确地看待这种“自卑感”是非常重要的。\n“人是作为一种无力的存在活在这个世界上。并且,人希望摆脱这种无力的状态,继而就有了普遍欲求。阿德勒称其为‘追求优越性’。”\n“人都处于追求优越性这一‘希望进步的状态’之中,树立某些理想或目标并努力为之奋斗。同时,对于无法达成理想的自己就会产生一种自卑感。”\n“无论是追求优越性还是自卑感,都不是病态,而是一种能够促进健康、正常的努力和成长的刺激。只要处理得当,自卑感也可以成为努力和成长的催化剂。”\n然而,如果我们把生活的不如意归结为自己天生的缺陷,认为自己无法改变,从而开始摆烂,这就会陷入“自卑情节”。\n“自卑情节是指把自己的自卑感当作某种借口使用的状态。比如:我因为 A,所以才做不到 B。如果抱着‘我因为学历低所以无法成功’之类的想法,那就不是‘不能成功’而是‘不想成功’。”\n“简单地说就是害怕向前迈进或者是不想真正地努力。不愿意为了改变自我而牺牲目前所享受的乐趣——比如玩乐或休闲时间。也就是拿不出改变生活方式的‘勇气’,即使有些不满或者不自由,也还是更愿意维持现状。”\n在交友中,“竞争”无处不在,但是我们不应把周围的人都看做“敌人”,而是应该把他们当作“伙伴”,要学会衷心地祝福他人的幸福,而不是嫉妒他人。\n“竞争的可怕之处就在于,即便不是败者、即便一直立于不败之地,处于竞争之中的人也会一刻不得安心、不想成为败者。而为了不成为败者就必须一直获胜、不能相信他人。之所以有很多人虽然取得了社会性的成功,但却感觉不到幸福,就是因为他们活在竞争之中。因为他们眼中的世界是敌人遍布的危险所在。”\n“‘无法真心祝福过得幸福的他人’,那就是因为站在竞争的角度来考虑人际关系,把他人的幸福看作‘我的失败’,所以才无法给予祝福。但是,一旦从竞争的怪圈中解放出来,就再也没有必要战胜任何人了,也就能够摆脱“或许会输”的恐惧心理了,变得能够真心祝福他人的幸福并能够为他人的幸福做出积极的贡献。”\n在爱情中,本书提到“爱”应该是相互平等、相处融洽的关系,而不是相互束缚的关系。\n“如果在一起感到苦闷或者紧张,那即使是恋爱关系也不能称之为爱。当人能够感觉到‘与这个人在一起可以无拘无束’的时候,才能够体会到爱。既没有自卑感也不必炫耀优越性,能够保持一种平静而自然的状态。”\n2.3 选择自由的勇气 # “自由就是被别人讨厌。”\n要想获得真正的自由,我们就无法“讨好所有人”,而应该“不在意他人的评价”,不随波逐流,为自己而活。\n“阿德勒心理学否定寻求他人的认可。如果做了恰当的事情就能够得到表扬,而如果做了不恰当的事情就会受到惩罚,阿德勒严厉批评这种赏罚式的教育。在赏罚式教育之下会产生这样一种错误的生活方式,那就是‘如果没人表扬,我就不去做好事’或者是‘如果没人惩罚,我也做坏事’。”\n“我们并不是为了满足别人的期待而活着。倘若自己都不为自己活出自己的人生,那还有谁会为自己而活呢?如果一味寻求别人的认可、在意别人的评价,那最终就会舍弃真正的自我,活在别人的人生之中。”\n2.4 主动付出的勇气 # “只关心自己的人往往认为自己位于世界的中心。对于这样的人来说,他人只是‘为我服务的人’;它们甚至会认为:‘大家都应该为我服务,应该优先考虑我的心情’。他们超越了‘人生的主人公’,进而越位到‘世界的主人公’。因此,在与他人接触的时候总是会想:‘这个人给了我什么?’。然而,这种期待并不会每次都能被满足,因为‘别人并不是为了满足你的期待而活’。当期待落空的时候,他们往往会大失所望并感觉受到了极大的侮辱,而且还会非常愤慨,产生诸如‘那个人什么也没有为我做’、‘那个人辜负了我的期望’或者‘那个人不再是朋友而是敌人’之类的想法。抱着自己位于世界中心这种信念的人很快就会失去‘朋友’。”\n自己以前在对待学校中的导师以及公司中的领导时,总是心怀不满,觉得“这人也没有帮助过我什么啊,我为什么还要这么尽心尽力地给他干活?”,而没想过“自己给别人提供了什么价值?”,我总是习惯于“先索取,再回报”,而不是自己先去积极主动地付出,因此也基本上对自己呆过的各个集体没有什么“归属感”,这是今后我需要改正的地方。\n“归属感不是仅仅靠‘在那里’就可以得到的,它必须靠积极地参与到共同体中去才能够得到。也就是不回避工作、交友以及爱之类的‘人生课题’,要积极主动地去面对。如果你认为自己就是世界的中心,那就丝毫不会主动融入共同体中,因为一切他人都是‘为我服务的人’,根本没必要由自己采取行动。”\n2.5 追求平等的勇气 # “阿德勒心理学反对一切‘纵向关系’,提倡把所有的人际关系都看作‘横向关系’。”\n对待比我们弱小的人,我们不应该表扬或批评他们,因为这种行为包含有“有能力者对没能力者所做的评价”的特点,是一种“操作别人”的方式。\n“评价性的语言是基于纵向关系的语言。如果能够建立起横向关系,那自然就会说出一些更加真诚地表达感谢、尊敬或者喜悦的话。”\n面对比我们强大的人,我们应该不卑不亢,既不能太过以自我为中心,也不能完全听从他人,完全不敢表达自己的想法。\n“尊敬长者非常重要。如果是公司组织,职责差异自然也会存在。并不是说将任何人都变成朋友或者像对待朋友一样去对待每一个人,重要的是意识上的平等以及坚持自己应有的主张。”\n2.6 拥抱幸福的勇气 # 要想获得幸福,我们就需要做到认可自己,同时处理好周围的人际关系,和他人建立起“共同体感觉”,这包括三个方面:“自我接纳”、“他者信赖”和“他者贡献”。\n“自我接纳”:\n“没必要特别积极地肯定自己,不是‘自我肯定’,而是‘自我接纳’。‘自我肯定’是明明做不到但还是暗示自己说‘我能行’或者‘我很强’,也可以说是一种容易导致‘优越情结’的想法,是对自己撒谎的生活方式;‘自我接纳’是指假如做不到就诚实地接受这个‘做不到的自己’,然后尽量朝着能够做到的方向去努力,不对自己撒谎。”\n“他者信赖”:\n“无条件的信赖是搞好人际关系和构建横向关系的一种手段。如果不敢去信赖别人,那最终就会与任何人都建立不了深厚的关系。”\n“如果关系浅,破裂时的痛苦就会小,但这种关系在生活中产生的喜悦也小。”\n“他者贡献”:\n“要想获得归属感就必须把他人看作伙伴,视他人为敌的人既做不到‘自我接纳’,也无法充分做到‘他者信赖’。对作为伙伴的他人给予影响、作出贡献,这就是‘他者贡献’。”\n“‘他者贡献’的意思并不是‘自我牺牲’。阿德勒把为他人牺牲自己人生的人称作‘过度适应社会的人’,并对此给予警示。”\n“‘他者贡献’并不是舍弃‘我’而为他人效劳,它反而是为了能够体会到‘我’的价值而采取的一种手段。我们应该思考的不是他人为我做了什么,而是我能为他人做什么,并积极地加以实践。”\n2.7 甘于平凡的勇气 # 本书的最后一部分是关于“甘于平凡的勇气”的内容,作者提到,我们没必要非要追求“优越性”,只有认识到自己的平凡和普通,才能真正做到“自我接纳”。\n“‘普通’并不等于‘无能’,我们根本没必要特意炫耀自己的优越性。”\n承认自己的不足,并不等于不努力去做改变,而是应该脚踏实地地去提高自己。然而,在这个过程中,我们却很容易陷入“只知道盯着最终的目标,却忽略了沿途的过程”的思维陷阱。\n“为遥远的将来设定一个目标,并认为现在是其准备阶段。一直想着‘真正想做的是这样的事情,等时机到了就去做’,这是一种拖延人生的生活方式。只要在拖延人生,我们就会无所进展,只能每天过着枯燥乏味的单调生活。因为在这种情况下,人就会认为‘此时此刻’只是准备阶段和忍耐阶段。”\n“人生中最大的谎言就是不活在‘此时此刻’。纠结过去、关注未来,把微弱而模糊的光打向人生整体,自认为看到了些什么。然而,起决定作用的既不是昨天也不是明天,而是‘此时此刻’。”\n“人生很简单,并不是什么深刻的事情。如果认真过好了每一个刹那,那就没什么必要令其过于深刻。站在现实性的角度,人生总是处于完结状态。”\n三、心得体会 # 本书全篇的核心其实就是一个词——“勇气”,生活中很多看似无解的问题,归根结底其实都是因为缺乏“勇气”造成的。其中,对我启发最大的,是关于“改变自己的勇气”、“积极生活的勇气”以及“主动付出的勇气”的部分,这也是在我过往的生活中,我认为自己做的还不太好的地方。\n曾经的我,面对自己孱弱的专业能力,我第一时间想到的不是如何脚踏实地地去补齐各方面的技能,而是只知道退而求其次,不敢去追求更好的机会,然后就会陷入焦虑和迷茫之中,从而形成恶性循环。现在的我则会理性分析自己欠缺的地方,会思考想要达成自己的目标还需要在哪些方面付出努力,然后会持之以恒地去付诸实践。\n曾经的我,会以“学业繁忙”和“工作繁忙”为借口,去逃避生活、逃避自己的“人生课题”。然而,“努力学习和工作”与“积极勇敢地去生活”其实是不冲突的,人不是机器,谁也不能保证自己能够除了吃饭睡觉之外能一直高效的学习和工作,人总是需要休息和调节的,而这就给了我们去感受生活的空间。现在的我,哪怕工作再忙、压力再大,我也会抽出时间去培养自己的兴趣爱好、去尝试更多的事情、去好好吃饭和睡觉。事实上,根据我观察其他人的经验,越是优秀的人就越能在忙碌的生活中管理好自己的时间,规划好自己的人生,“学习/工作/爱情/娱乐”样样不落。因此,我也应该更加积极地去拥抱生活,而不是找各种借口来逃避人际交往,放弃感受幸福的机会。\n曾经的我,以自我为中心,只有别人先为我付出了,我才能全心全意地去回报他,这些人包括但不限于我的师兄师姐、导师以及领导等。看完本书后,我知道了“他者贡献”的重要性,“我们应该思考的不是他人为我做了什么,而是我能为他人做什么,并积极地加以实践”,这是今后的我需要改进的地方。\n总而言之,这是一本非常不错的关于个人成长的心理学书籍,希望自己在今后的生活中,也能一直拥有书中所提到的“勇气”。\n","date":"2024-12-07","externalUrl":null,"permalink":"/articles/%E8%A2%AB%E8%AE%A8%E5%8E%8C%E7%9A%84%E5%8B%87%E6%B0%94%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E6%8E%A5%E7%BA%B3%E8%87%AA%E6%88%91%E5%8B%87%E6%95%A2%E5%9C%B0%E6%84%9F%E5%8F%97%E7%94%9F%E6%B4%BB/","section":"Articles","summary":"","title":"《被讨厌的勇气》读书笔记 | 接纳自我,勇敢地感受生活","type":"posts"},{"content":"","date":"2024-12-07","externalUrl":null,"permalink":"/tags/%E4%B8%AA%E4%BA%BA%E6%88%90%E9%95%BF/","section":"Tags","summary":"","title":"个人成长","type":"tags"},{"content":"","date":"2024-12-07","externalUrl":null,"permalink":"/tags/%E4%BA%BA%E9%99%85%E4%BA%A4%E5%BE%80/","section":"Tags","summary":"","title":"人际交往","type":"tags"},{"content":"","date":"2024-12-07","externalUrl":null,"permalink":"/tags/%E5%BF%83%E7%90%86%E5%AD%A6/","section":"Tags","summary":"","title":"心理学","type":"tags"},{"content":"","date":"2024-12-05","externalUrl":null,"permalink":"/tags/ai/","section":"Tags","summary":"","title":"AI","type":"tags"},{"content":"","date":"2024-12-05","externalUrl":null,"permalink":"/tags/llm/","section":"Tags","summary":"","title":"LLM","type":"tags"},{"content":"","date":"2024-12-05","externalUrl":null,"permalink":"/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%BE%AE%E8%B0%83/","section":"Tags","summary":"","title":"大模型微调","type":"tags"},{"content":" 一、大模型开发全流程 # 当我们训练大模型时,一般会经过 pre-training 和 post-training 两个阶段。其中,pre-training 阶段一般会先使用海量数据来训练 base 大模型,再通过增量预训练来为模型注入领域知识;而 post-training 阶段则主要包括监督微调和偏好对齐两个步骤,使我们训练的大模型能够更好地适应某些特定的任务,并符合人类的表达习惯和价值观。\npre-training:\n预训练(Pre-Training):利用海量数据、大量算力通过无监督训练得到基座模型。预训练后的模型具备强大的语言生成能力,但由于它主要是无监督训练的结果,可能不会直接适应具体的任务(如问答、对话),需要进一步的微调; 增量预训练(Continued Pre-Training): 一般垂直大模型是基于通用基座大模型进行二次的训练,为了给模型注入领域知识,就需要用领域内的语料进行继续预训练。 post-training:\n监督微调(Supervised Fine-Tuning, SFT):这一阶段是对基座模型进行微调,让模型能够适用于特定任务; 偏好对齐(Reinforcement Learning from Human Feedback, RLHF):这一阶段通过引入人类反馈进一步优化模型的生成质量,使其生成的回答更符合用户的期望和人类的价值观(对齐人类偏好)。由于直接从人类获取反馈的成本较高,通常会先训练一个奖励模型(Reward Model,RM)来代替人类打分,这样可以在 RL 的框架下实现大规模的自动优化。 了解了大模型开发的整体流程,下面将重点对大模型微调相关的知识进行介绍。\n二、什么是大模型微调 # 大模型微调一般指的是监督微调(SFT),即使用特定下游任务的数据继续训练“预训练模型(基座模型)”,使得模型能够满足特定下游任务的性能标准。\n示例一:将大模型微调为一个可以将德语翻译为英语的模型。\n我们需要使用大量输入为德语、输出为英语的带标签数据来训练 base 大模型,这样经过微调后的大模型就可以很好地用于将德语翻译为英语的任务。\n示例二:开源模型为了能够直接使用,一般会提供经过问答任务微调的版本,即 Chat 模型。\n三、为什么需要大模型微调 # 提升特定任务表现:预训练模型虽然具有广泛的语言理解能力,但在特定任务(如情感分析、问答系统、机器翻译等)上的表现可能不尽如人意。通过在特定任务的数据上进一步训练,使模型能够更好地理解和执行该任务; 领域适应性:预训练模型可能在一些通用领域表现良好,但在特定领域(如医学、法律、金融等)中可能难以准确理解专业术语和内容结构。通过微调,可以让模型更好地适应这些领域的语言特点,提高在这些领域中的应用效果; 数据稀缺性:对于一些数据稀缺的任务或领域,获取大量标签数据可能比较困难。微调允许在有限的数据集上进行有效训练,从而在数据稀缺的情况下也能取得较好的性能; 防止过拟合:预训练过程中模型可能会过度拟合于无监督学习的任务(如下一个词预测),而在特定任务中表现不佳。通过微调,可以让模型专注于特定任务的数据,这有助于减少过拟合的风险,提高模型在该任务上的泛化能力; 成本效益:与使用 prompt 来引导模型行为相比,微调通常可以更高效地优化模型的表现。微调后的模型通常可以更直接地执行任务,减少了对复杂提示的依赖。同时,微调可以在更小的模型上实现类似于大型模型的性能,从而降低推理的计算成本和延迟,比如与使用通用的 GPT-3.5 模型相比,经过微调的小型模型(如LLaMA-7B)在成本效益上可能更具优势,尤其是在特定任务的执行上。 四、大模型微调的方法有哪些 # 整体上,根据微调参数量的不同,大模型微调的方法可以分为以下两类:\n全量参数微调(Full Fine-tuning,FFT):对预训练模型的所有参数进行更新,训练速度较慢,消耗机器资源较多; 参数高效微调(Parameter-Efficient Fine-Tuning,PEFT):只对部分参数进行更新,训练速度快,消耗机器资源少。 此外,还有一种不需要更新模型权重就可以完成微调的方法,叫做 In-Context Learning,通过在输入的 prompt 中提供与任务相关的上下文和例子,从而让模型能够更好地了理解我们的意图。\n最新进展:\n在 OpenAI 最新的发布会中,还提出了一种叫做 RFT(Reinforcement Fine-Tuning) 的微调技术,能够以奖励驱动的方式不断完善大模型所掌握的知识,更多细节可以参考这篇文章:What Is OpenAI\u0026rsquo;s Reinforcement Fine-Tuning?。\n4.1 FFT 的优缺点 # 优点:\n提升特定任务性能:全参微调可以对所有模型参数进行优化,从而在某些任务上获得更好的性能。 缺点:\n训练成本高:全参微调所需要计算的参数量与预训练相同,随着模型规模变得越来越大,这使得在消费级硬件上进行全量微调变得不可行; 灾难性遗忘:用特定训练数据去微调可能会把这个领域的表现变好,但也可能会把原来表现好的别的领域的能力变差。 4.2 PEFT 的优缺点 # 优点:\n降低训练成本:减少计算消耗,缩短训练时间,降低对硬件性能的要求; 保证模型性能:针对特定下游任务,能够在一定程度上保证模型的表现和性能; 节省存储空间:降低存储占用,大部分的参数都可以在不同任务之间共享,只需额外保存经过不同任务微调后更新的参数。 缺点:\n特定任务性能有限:可能无法达到全参数微调在某些特定任务上的性能水平。 因此,在实际应用中,我们应该根据具体任务的要求和可用资源情况,在服务效率和模型质量之间做出权衡。对于资源有限或对训练时间有严格要求的场景,使用 PEFT 会是一个比较好的选择;而对于需要最佳性能的任务,使用 FFT 可能会更加合适。\n4.3 PEFT 的分类 # Addition-based methods:在预训练模型的基础上,新增参数或网络层,并只对这些新增的参数进行训练和更新; Adapters:在 transformer 中的 attention 和 ffn 层后增加 Adapter; Soft Prompts: Soft Prompt Tuning:为输入的 embedding 增加可训练的 soft prompt 参数; Prefix Tuning:为 transformer 的输入增加可训练的 prefix 参数; Selective methods:通过一定的算法和策略,选择预训练模型中的部分参数进行训练和更新; Reparametrization-based methods:利用低秩矩阵来近似地表达预训练模型需要更新的参数; LoRA; QLoRA; DLoRA; LongLoRA; GLoRA; AdaLoRA; LoRA-FA; VeRA; Delta-LoRA; LoRA+; LoRA-drop; …… Hybrid methods:根据实际情况,可以对上述方法进行组合,从而达到更好的效果。 目前比较主流的几种参数高效微调方法包括:Prompt Tuning、Prefix Tuning、LoRA、QLoRA 等。\n论文《Scaling Down to Scale Up: A Guide to Parameter-Efficient Fine-Tuning》中展示了各类参数高效微调方法及其所属的类别,如下所示:\n该论文中还对比了各类参数高效微调方法的表现和性能,如下所示:\n五、各类微调方法的原理是什么 # 5.1 In-Context Learning # 核心原理:\n当我们无法直接获取到模型并修改其权重(比如:直接通过 API 或用户接口访问模型)时,就可以使用 In-Context Learning 的方式来让模型更好地适应于特定的任务。\nIn-Context Learning 通过在输入的 prompt 中提供与任务相关的上下文和例子,从而让模型能够基于我们提供的上下文,更好地生成我们期望得到的结果。\n\u0026ldquo;Based on intuition from prompting, we believe that having a proper context can steer the LLM without changing its parameters.\u0026rdquo;\n示例:将大模型微调为一个可以将德语翻译为英语的模型。\n我们在输入的上下文中给出一些将德语翻译为英语的例子,然后再输入一句德语,这样模型就能更好地理解我们的意图,知道现在要做的是将输入的德语翻译为对应的英语。\n优点:\n当我们没有足够的带标签数据用于监督微调时,通过 In-Context Learning,只需少量的样例,就能提升模型对于特定任务的表现; 当我们无法直接获取到模型并修改其权重时,通过 In-Context Learning,无需额外对模型进行微调,就可以快速地进行尝试和验证。 缺点:\n相比于直接更新模型的权重进行微调,In-Context Learnin 的效果有限,模型在特定任务上的表现上不如 FFT 和 PEFT。 5.2 Soft Prompt Tuning # 核心原理:\nSoft Prompt Tuning 可以看作是 Prefix Tuning 的简化版本,它给每个任务定义了自己的 soft prompt,并将其拼接到数据上作为输入(在输入 embedding 层加入一段定长的可训练的向量,在微调的时候只更新 soft prompt 这部分的参数)。\n示例代码:\nx = EmbeddingLayer(input_ids) x = concatenate([soft_prompt_tensor, x], dim=seq_len) output = model(x) 其中,soft_prompt_tensor 具有与 embedded inputs 同样的特征维度,将两者拼接过后,就相当于是增加了输入的长度。\n5.3 Prefix Tuning # 核心原理:\nPrefix Tuning 通过对输入数据增加前缀(prefix)来做微调,即在输入 token 之前构造一段任务相关的 virtual tokens 作为 prefix,训练的时候只更新 prefix 这部分的参数,每个下游任务都可以单独训练一套 prefix token。\n示例代码:\ndef transformer_block_with_prefix(x): soft_prompt = FullyConnectedLayers(soft_prompt) # prefix x = concatenate([soft_prompt, x], dim=seq_len) x = SelfAttention(x) x = LayerNorm(x + residual) residual = x x = FullyConnectedLayers(x) x = LayerNorm(x + residual) return x 为什么增加 prefix 可以影响模型生成的结果?\n感性地理解一下,prefix 的作用是引导模型提取输入中的特定信息,进而更好地生成结果。\n另外,我们还可以针对不同的下游任务,训练不同的 prefix 并对其进行保存。这样当我们需要切换不同的下游任务时,只需要加载不同的 prefix 参数,就可以实现模型功能的快速切换。\n缺点:\n微调的效果存在上限,模型的表现并不一定会随着 prefix 长度的增加而提高; 由于模型的输入长度一般是固定的,而增加了 prefix 之后,留给原始文字数据的空间就少了,因此可能会降低原始文字中 prompt 的表达能力。 5.4 Adapter Tuning # 核心原理:\nAdapter Tuning 通过在 transformer 中的 multi-head self-attention 和 fully connected layers 后增加 Adapter 进行微调。其中,Adapter 中的第一个 fully connected layer 将高维的输入映射为了一个低维的表示,第二个 fully connected layer 再将其映射回高维的空间中,这样就能有效降低训练时需要更新的参数量。\n微调时,只更新 Adapter 部分的权重,原模型的参数是冻结的。\n注意:新增的 Adapter 与原模型中的层是顺序串行的关系。\n示例代码:\ndef transformer_block_with_adapter(x): residual = x x = SelfAttention(x) x = FullyConnectedLayers(x) # Adapter x = LayerNorm(x + residual) residual = x x = FullyConnectedLayers(x) x = FullyConnectedLayers(x) # Adapter x = LayerNorm(x + residual) return x 缺点:\n添加了 Adapter 后,模型整体的层数变深,会拖慢模型训练和推理的速度。 小结:\nFFT 成本太高; Prefix Tuning 难训且会减少原始训练数据中的有效文字长度; Adapter Tuning 存在训练和推理延迟。 为了解决以上问题,LoRA 系列微调方法便应运而生了。\n5.5 LoRA # 关于 LoRA(Low-Rank Adaptation,低秩适配器)的相关原理,请参考我之前写的这篇文章:\n大模型 LoRA 微调的数学原理 2024-11-13\u0026middot;529 words 计算机 AI LLM 大模型微调 论文精读 5.6 QLoRA # 核心原理:\nQLoRA(Quantized LoRA)的核心工作其实是模型量化,通过将预训练模型进行 NF4 量化,再结合 LoRA 的方式进行微调,可以大幅减少训练时显存的占用。\nQLoRA 有一个 NF4 的存储数据类型和 BF16 的计算数据类型。在进行前向和反向传播时,我们需要将存储数据类型反量化为计算数据类型,但是计算梯度时我们只计算添加的适配器的梯度,这一点和 LoRA 是一致的。\n预训练模型的参数:进行 NF4 量化; LoRA 的参数:保持 BF16 的精度。 核心工作:\n四位标准浮点数量化(4-bit Normal Float Quantization):结合了分位数量化和分块量化; 双重量化(Double Quantization):对模型进行了两次量化,其中第二次量化只作用在第一次量化产生的量化常数上,可以进一步节约显存占用; 分页优化(Paged Optimizer):使用 CPU 内存代替 GPU 显存保存部分梯度参数。 优缺点:\n优点:显存占用下降。由于原模型参数经过了量化,在计算时占用的内存减少了; 缺点:训练时间增加。由于引入了量化和反量化的计算过程,在训练时需要消耗更多的时间。 量化分位数的计算:\n根据每个块的特征的绝对值的最大值,我们为每个块保存一个量化常数(每个块中的特征取绝对值后的最大值); 计算每个张量的量化值(实际值/该块的量化常数); 在 Q(normal_map)中找到与每个张量最接近的值,并将其量化为该值对应的索引值。 normal_map 的计算:\nfrom scipy.stats import norm import torch def create_normal_map(offset=0.9677083, use_extra_value=True): if use_extra_value: # one more positive value, this is an asymmetric type v1 = norm.ppf(torch.linspace(offset, 0.5, 9)[:-1]).tolist() # 正数部分 v2 = [0]*(256-15) ## we have 15 non-zero values in this data type v3 = (-norm.ppf(torch.linspace(offset, 0.5, 8)[:-1])).tolist() #负数部分 v = v1 + v2 + v3 else: v1 = norm.ppf(torch.linspace(offset, 0.5, 8)[:-1]).tolist() v2 = [0]*(256-14) ## we have 14 non-zero values in this data type v3 = (-norm.ppf(torch.linspace(offset, 0.5, 8)[:-1])).tolist() v = v1 + v2 + v3 values = torch.Tensor(v) values = values.sort().values values /= values.max() assert values.numel() == 256 return values Q = create_normal_map() # Q = [-1.0, -0.6961928009986877, -0.5250730514526367, -0.39491748809814453, -0.28444138169288635, -0.18477343022823334, -0.09105003625154495, 0.0, 0.07958029955625534, 0.16093020141124725,0.24611230194568634, 0.33791524171829224, 0.44070982933044434, 0.5626170039176941, 0.7229568362236023, 1.0] 示例:\n假设一个张量有 16 个值,被分成了 4 块:\ninput_blocked_tensor = [[-1.28645003578589, -1.817660483275528, 9.889441349505042, 0.010208034676132627], [-15.009014631551885, 1.4136255086268115, -7.815595761491153, 10.766760590950263], [-0.731406153917959, 3.468224595908726, 2.445252541840315, -8.970824523299282], [-9.641638854625175, 7.696158363188889, -5.323939281255154, 5.97160401402024]] 根据每个块的特征的绝对值的最大值,我们为每个块保存一个量化常数,它的计算方式是每个块中特征的绝对值中最大的那个:\nc1 = max(|-1.28645003578589|, |-1.817660483275528|, |9.889441349505042|, |0.010208034676132627|) = 9.889441349505042 c2 = max(|-15.009014631551885|, |1.4136255086268115|, |-7.815595761491153|, |10.766760590950263|) = 15.009014631551885 c3 = max(|-0.731406153917959|, |3.468224595908726|, |2.445252541840315|, |-8.970824523299282|) = 8.970824523299282 c4 = max(|-9.641638854625175|, |7.696158363188889|, |-5.323939281255154|, |5.97160401402024|) = 9.641638854625175 计算张量的量化值:例如第一个值 -1.28645003578589,它除以这个块的量化常数 c1 后得到 -0.13008318572517502,我们可以在 Q 中找到与它最接近的值是 -0.09105003625154495,这个值在 Q 中对应的索引是 6,因此这个值被量化后的值是 6。\nQ = [-1.0, -0.6961928009986877, -0.5250730514526367, -0.39491748809814453, -0.28444138169288635, -0.18477343022823334, -0.09105003625154495, 0.0, 0.07958029955625534, 0.16093020141124725,0.24611230194568634, 0.33791524171829224, 0.44070982933044434, 0.5626170039176941, 0.7229568362236023, 1.0] 同理我们可以得到这个输入张量所有的值量化后的结果:\n[[6, 5, 15, 7], [0, 8, 2, 14], [6, 11, 10, 0], [0, 14, 2, 13]] 在模型保存时,除了要保存量化后的值,我们还要保存每个块对应的量化常数,因为这个值在我们进行反量化时需要用到。\n在反量化时,我们以量化结果作为索引,从 Q 中查找到它对应的分位数,再乘以为每个块保存的量化常数 ci,便可以得到最终结果。\n[[-0.9004339933799617, -1.8273060011889755, 9.889441349505042, 0.0], [-15.009014631551885, 1.1944218804231184, -7.880829111886221, 10.850869732860506], [-0.816793898052648, 3.0313783372030603, 2.2078302737800004, -8.970824523299282], [-9.641638854625175, 6.970488722350373, -5.062564734402345, 5.424549965245643]] 解决了什么问题?\n如果我们粗暴的使用 round 操作去映射到低精度的更近的值,我们可能造成大量的数据都被量化到同一个数上,这样特征之间的差异性在量化过程中就被丢失了。使用分位数将张量分成了大小相同的若干个块,这样我们得到更加均匀的量化特征,这也就是分位数量化。每两个分位数的中点便是模型量化到这个区间映射的值。\n双重量化:\nQLoRA 的双重量化是指对量化常数再做一次 8 bit 的量化,在进行量化常数的量化时,QLoRA 以每 256 个量化常数为一组再做一次量化。在进行反量化时我们也需要进行两次反量化才能把量化后的值还原。\n好处:减少了存储量化常数带来的额外显存占用。\n分页优化:\nQLoRA 的分页优化其实就是当显存不足时,将保存的部分梯度检查点转移到 CPU 内存上,和计算机的内存数据转移到硬盘上的常规内存分页一个道理。\n5.7 总结 # How to use and finetune pre-trained LLMs?\n总结一下,当我们经过预训练得到 base 大模型之后,还需要进行以下操作:\n增量预训练:注入领域知识; 监督微调:适配特定下游任务(各类微调方法百花齐放); 偏好对齐:使模型生成的结果符合人类偏好。 六、大模型微调的框架有哪些 # huggingface/transformers:提供了丰富的预训练模型和微调工具,支持大多数主流的 NLP 任务(如文本分类、序列标注、生成任务等),适合进行快速实验和生产部署; huggingface/peft:huggingface 开源的微调基础工具; modelscope/ms-swift:modelscope 开源的轻量级微调框架,以中文大模型为主,支持各类微调方法;可以通过执行脚本进行微调,也可以在代码环境中一键微调;自带微调数据集和验证数据集,可以一键完成微调和验证; hiyouga/LLaMA-Factory:全栈微调工具,支持海量模型和各种主流微调方法;支持通过脚本微调、基于 Web 端微调(使用简单);自带基础训练数据集;除微调外,支持增量预训练和全量微调; NVIDIA/Megatron-LM:NVIDIA 开发的大模型训练框架,支持大规模的预训练和微调,适用于需要极高性能和规模的大模型训练和微调。 七、如何在生产环境中进行微调 # 7.1 微调实践 # 整体架构和组件:\nHelm Char:管理集群和训练配置; PyTorchJob(with multiple workers):执行微调(分布式训练); PVC(Persistent Volume Claim):存储训练数据和模型数据; Secret:管理鉴权相关配置; Data Access Pod:……。 以上实践案例是基于 Kubeflow 提供的平台和组件进行实现的,下面将对 Kubeflow 进行介绍。\n7.2 Kubeflow # 整体介绍:\n\u0026ldquo;Kubeflow is a community and ecosystem of open-source projects to address each stage in the machine learning (ML) lifecycle with support for best-in-class open source tools and frameworks. Kubeflow makes AI/ML on Kubernetes simple, portable, and scalable.\u0026rdquo;\nKubeflow 包括:\nKubeflow Platform:AI 模型开发部署全流程工作平台; Standalone Kubeflow Components:可独立使用的各类组件。 Kubeflow Platform:\n\u0026ldquo;The Kubeflow Platform refers to the full suite of Kubeflow components bundled together with additional integration and management tools. Using Kubeflow as a platform means deploying a comprehensive ML toolkit for the entire ML lifecycle.\u0026rdquo;\nStandalone Kubeflow Components:\n\u0026ldquo;The Kubeflow ecosystem is composed of multiple open-source projects that address different aspects of the ML lifecycle. Many of these projects are designed to be usable both within the Kubeflow Platform and independently. These Kubeflow components can be installed standalone on a Kubernetes cluster. It provides flexibility to users who may not require the full Kubeflow Platform capabilities but wish to leverage specific ML functionalities such as model training or model serving.\u0026rdquo;\nKubeflow Overview Diagram:\nKubeflow Ecosystem:\n使用 Kubeflow 进行微调:\n\u0026ldquo;Once user executes train API, Training Operator creates PyTorchJob with appropriate resources to fine-tune LLM.\u0026rdquo;\n\u0026ldquo;Storage initializer InitContainer is added to the PyTorchJob worker 0 to download pre-trained model and dataset with provided parameters.\u0026rdquo;\n\u0026ldquo;PVC with ReadOnlyMany access mode attached to each PyTorchJob worker to distribute model and dataset across Pods.\u0026rdquo;\n\u0026ldquo;The PyTorchJob is a Kubernetes custom resource to run PyTorch training jobs on Kubernetes. The Kubeflow implementation of the PyTorchJob is in the training-operator.\u0026rdquo;\n八、参考资料 # Scaling Down to Scale Up: A Guide to Parameter-Efficient Fine-Tuning Prefix-Tuning: Optimizing Continuous Prompts for Generation LoRA: Low-Rank Adaptation of Large Language Models 李航《统计学习方法》 QLoRA: Efficient Finetuning of Quantized LLMs 浅谈 DeepLearning 的浮点数精度 FP32/FP16/TF32/BF16…… LongLoRA: Efficient Fine-tuning of Long-Context Large Language Models New LLM Pre-training and Post-training Paradigms Finetuning Large Language Models Using and Finetuning Pretrained Transformers Practical Tips for Finetuning LLMs Using LoRA LLM 微调理论 GPT 是如何炼成的:大模型微调基础概念指北 图解大模型微调系列之:大模型低秩适配器 LoRA(原理篇) 图解大模型微调系列之:大模型低秩适配器 LoRA(源码解读与实操篇) 图解 Fine-tuning:LoRA 系列微调技术概述 QLoRA(Quantized LoRA)详解 LongLoRA - 高效微调长上下文的 LLMs LLM 长 context 微调技巧 - LongLora LoRA、QLoRA、LoRA+、LongRA、DoRA、MaLoRA、GaLore方案都知道吗? LLM 微调实践 Fine Tuning a LLM Using Kubernetes with Intel® Xeon® Scalable Processors How to Fine-Tune LLMs with Kubeflow LLM Fine-Tuning with Training Operator - Architecture LLM Fine-Tuning with the Training Operator ","date":"2024-12-05","externalUrl":null,"permalink":"/articles/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%BE%AE%E8%B0%83%E7%9F%A5%E8%AF%86%E5%85%A8%E6%99%AF/","section":"Articles","summary":"","title":"大模型微调知识全景","type":"posts"},{"content":"","date":"2024-12-05","externalUrl":null,"permalink":"/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/","section":"Categories","summary":"","title":"计算机","type":"categories"},{"content":"","date":"2024-12-05","externalUrl":null,"permalink":"/tags/%E8%AE%BA%E6%96%87%E7%B2%BE%E8%AF%BB/","section":"Tags","summary":"","title":"论文精读","type":"tags"},{"content":"","date":"2024-11-23","externalUrl":null,"permalink":"/tags/%E5%8D%9A%E5%AE%A2%E6%B1%87%E6%80%BB/","section":"Tags","summary":"","title":"博客汇总","type":"tags"},{"content":"\rAI Infra # 环境搭建 # 基于 EulerOS \u0026 Ascend NPU 搭建 PyTorch 远程开发环境 2024-09-24\u0026middot;1163 words 计算机 AI Infra Ascend NPU CANN 硬件架构 # NVIDIA GPU 架构 \u0026 CUDA 平台入门学习 2024-08-26\u0026middot;570 words 计算机 AI Infra NVIDIA GPU CUDA Ascend NPU 架构 \u0026 CANN 平台入门学习 2024-08-31\u0026middot;462 words 计算机 AI Infra Ascend NPU CANN 算子开发 # Ascend aclnn 算子开发入门 2024-10-23\u0026middot;1663 words 计算机 AI Infra 算子开发 Ascend NPU CANN LLM # 大模型微调 # 大模型微调知识全景 2024-12-05\u0026middot;1157 words 计算机 AI LLM 大模型微调 论文精读 大模型 LoRA 微调的数学原理 2024-11-13\u0026middot;529 words 计算机 AI LLM 大模型微调 论文精读 Java # Maven 项目编译报错解决方法 2024-09-04\u0026middot;126 words 计算机 Java Maven IDEA Git # Git 实践案例 | 合并多个分散的 commit 节点 2024-10-18\u0026middot;236 words 计算机 Git ","date":"2024-11-23","externalUrl":null,"permalink":"/articles/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%8D%9A%E5%AE%A2-%E6%80%BB%E8%A7%88/","section":"Articles","summary":"","title":"我的技术博客 | 总览","type":"posts"},{"content":" Introduction # Welcome to my personal website!\nThis website is build to show my articles of cs-learning, reading notes, life experiences and maybe some photographs in the future.\nWho am I ? # You can read the page shown below to get more information about me.\nAbout me 2024-11-02\u0026middot;596 words Articles # Most of the articles I have written and put on this site are about computer science (especially AI Infra and LLM) and reading notes.\nTechnical articles # You can read the article shown below to get a whole picture of all the technical articles I have written.\n我的技术博客 | 总览 2024-11-23\u0026middot;109 words 计算机 博客汇总 Reading notes # You can read the article shown below to get a whole picture of all the books I have read, and the reading notes I have written are also shown here.\n我的读书笔记 | 总览 2024-11-23\u0026middot;72 words 阅读 书单汇总 Others # There are some other kinds of articles such as some reflections in my life, you can find them in the Articles page.\nPhotographs # Coming soon.\nRoadmap # You can read the page shown below to get more information about the roadmap of this site.\nRoadmap 2024-11-17\u0026middot;98 words ","date":"2024-11-23","externalUrl":null,"permalink":"/guide/","section":"home","summary":"","title":"Guide","type":"page"},{"content":"","date":"2024-11-23","externalUrl":null,"permalink":"/tags/%E4%B9%A6%E5%8D%95%E6%B1%87%E6%80%BB/","section":"Tags","summary":"","title":"书单汇总","type":"tags"},{"content":" 心理学 # 《被讨厌的勇气》读书笔记 | 接纳自我,勇敢地感受生活 2024-12-07\u0026middot;90 words 阅读 读书笔记 心理学 个人成长 人际交往 《我有自己的宇宙》读书笔记 | 年轻人的职场生存之道 2024-08-10\u0026middot;94 words 阅读 读书笔记 心理学 个人成长 职场之道 经济学 # 《小岛经济学》读书笔记 | 深入浅出经济学原理 2024-12-15\u0026middot;66 words 阅读 读书笔记 经济学 《富爸爸 穷爸爸》读书笔记 | 财商教育启蒙之作 2024-05-07\u0026middot;35 words 阅读 读书笔记 经济学 投资理财 个人成长 # 《早起的奇迹》读书笔记 | 制定你的早起计划 2024-07-09\u0026middot;89 words 阅读 读书笔记 个人成长 个人健康 《认知觉醒》读书笔记 | 开启心智,掌控自己的生活 2024-06-05\u0026middot;115 words 阅读 读书笔记 个人成长 学习方法 个人健康 # 《睡眠革命》读书笔记 | 了解你的昼夜节律 2024-07-19\u0026middot;146 words 阅读 读书笔记 个人健康 ","date":"2024-11-23","externalUrl":null,"permalink":"/articles/%E6%88%91%E7%9A%84%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E6%80%BB%E8%A7%88/","section":"Articles","summary":"","title":"我的读书笔记 | 总览","type":"posts"},{"content":" Functions and Styles # 增加链接文字高亮显示(鼠标悬于其上时) 增加网站评论功能 增加文章阅读数和点赞数统计功能 分开展示文章卡片中的 category 和 tag Contents # 迁移之前写的英语学习笔记到本站(增加英语分类) 梳理个人技能树(增加个人随笔分类) 梳理大模型推理知识全景 梳理 Python 基本语法(装饰器 / typing) 梳理 vLLM 学习笔记(自定义算子 / Multi-LoRA 推理加速) 增加相册展示页 写 2024 年年度读书报告 写《非暴力沟通》读书笔记 写《娱乐至死》读书笔记 写《费曼学习法》读书笔记 写《海绵阅读法》读书笔记 2024-12-18: 写《小岛经济学》读书笔记 2024-12-08: 写《被讨厌的勇气》读书笔记 2024-12-05: 梳理大模型微调知识全景 2024-11-23: 增加网站导航页 2024-11-23: 增加技术文章总览 2024-11-23: 增加我的书单总览 2024-11-19: 个人介绍加入技术栈和开源贡献 2024-11-19: 迁移之前写的技术博客到本站 2024-11-18: 完善个人介绍页 2024-11-13: 梳理大模型 LoRA 微调的数学原理 2024-10-23: 梳理 Ascend aclnn 算子开发入门学习笔记 2024-10-18: 梳理 Git 实践案例——合并多个分散的 commit 节点 2024-09-24: 梳理 Ascend NPU 远程开发环境搭建教程 2024-09-04: 梳理 IDEA Maven 项目编译或启动报错解决方法 2024-08-31: 梳理 Ascend NPU 架构学习笔记 2024-08-26: 梳理 NVIDIA GPU 架构学习笔记 2024-08-10: 写《我有自己的宇宙》读书笔记 2024-07-19: 写《睡眠革命》读书笔记 2024-07-09: 写《早起的奇迹》读书笔记 2024-06-05: 写《认知觉醒》读书笔记 2024-05-07: 写《富爸爸穷爸爸》读书笔记 ","date":"2024-11-17","externalUrl":null,"permalink":"/roadmap/","section":"home","summary":"","title":"Roadmap","type":"page"},{"content":" 一、概述 # LoRA(Low-Rank Adaptation,低秩适配器)是目前非常热门的大模型微调技术之一,网上已经有许多关于其原理的分析和讲解,本文将着重从 LoRA 背后的数学原理进行解读。\n二、背景介绍 # 2.1 基本概念 # 大模型微调(Fine-tuning):基于已经训练好的预训练模型,针对特定的下游任务,在特定领域的数据集上进行二次训练,以提升模型在特定任务上的表现。\n全量微调:在下游任务的训练中,对预训练模型的每一个参数都做更新(训练代价昂贵); 局部微调:冻结(不更新)预训练模型的权重,只对部分增量权重进行训练,从而有效降低训练的代价(实用性更高)。 2.2 研究现状 # 在 LoRA 微调技术出现之前,现有的大模型微调技术存在以下缺点:\nAdapter Tuning:在模型中添加额外的 Adapter 层,并只针对这些 Adapter 的权重进行训练。这将导致模型整体的层数变深,从而使模型的训练/推理耗时增加(因为新增的 Adapter 层与模型中的其它层是串行的,无法充分利用硬件能力进行并行计算); Prefix Tuning:对输入数据增加前缀(prefix),并只针对这些 prefix token 进行训练(prefix 的作用是引导模型提取输入中的特定信息,进而更好地生成结果)。这将导致输入数据中有效信息的减少,因为一般输入数据的长度是固定的,而增加的 prefix 将会占用原本输入数据的空间,从而使输入文字表达力下降。 三、LoRA 基本原理 # 3.1 LoRA 模型结构 # LoRA 使用 \\(\\mathbf{A}\\) 和 \\(\\mathbf{B}\\) 两个与原模型并行的低秩矩阵来代替原本的增量权重矩阵 \\(\\mathbf{\\Delta W}\\),从而可以在保证模型性能的同时,有效降低需要训练的参数量。\n对于输入 \\(\\mathbf{x}\\),模型的输出 \\(\\mathbf{h}\\) 为:\n$$ \\mathbf{h} = \\mathbf{W}\\mathbf{x} + \\mathbf{\\Delta W}\\mathbf{x} \\approx \\mathbf{W}\\mathbf{x} + \\mathbf{B}\\mathbf{A}\\mathbf{x} $$\n其中 \\(\\mathbf{W}、\\mathbf{\\Delta W} \\in \\mathbb{R}^{d \\times d}\\),\\(\\mathbf{A} \\in \\mathbb{R}^{r \\times d}\\)(初始化为正态分布),\\(\\mathbf{B} \\in \\mathbb{R}^{d \\times r}\\)(初始化为零),\\(r\\) 为矩阵 \\(\\mathbf{\\Delta W}\\) 的秩。\n3.2 LoRA 的优点 # 节约内存:在训练时,需要更新的权重数量从 \\(d \\times d\\) 下降到了 \\(2 \\times d \\times r\\),显著降低了模型微调对硬件性能的要求; 保证性能:在推理时,可以将 LoRA 的权重直接合并到预训练权重中,从而可以保证推理的速度不受影响; 灵活易用:针对不同的下游任务,用户可以训练不同的 LoRA,并且在微调完成后,只需要保存新增的这一部分权重(不同任务间共享预训练模型的权重),相比于存储整个模型的权重,只需要极少的内存。 四、LoRA 的数学原理与论文实验分析 # 简单介绍完了 LoRA 的基本原理,下面将针对以下几个问题进行分析和说明,这些问题也是我在刚开始学习 LoRA 时产生的疑惑。\n为什么可以将 \\(\\mathbf{\\Delta W}\\) 拆分为 \\(\\mathbf{A}\\) 和 \\(\\mathbf{B}\\)?这样做为什么是有效的? \\(r\\) 作为一个超参数,它的取值是如何影响 LoRA 的表现的? \\(\\mathbf{A}\\) 和 \\(\\mathbf{B}\\) 为什么要这样初始化? 4.1 SVD 定理 # 为什么可以将 \\(\\mathbf{\\Delta W}\\) 拆分为 \\(\\mathbf{A}\\) 和 \\(\\mathbf{B}\\)?这样做为什么是有效的?\n在回答这个问题之前,我们需要先了解一个基本概念——SVD(Singular Value Decomposition,奇异值分解)。\n对于一个非零的 \\(m \\times n\\) 实矩阵 \\(\\mathbf{M} \\in \\mathbb{R}^{m \\times n}\\),我们可以将其表示为以下三个实矩阵乘积形式的运算:\n$$ \\mathbf{M} = \\mathbf{U}\\mathbf{Σ}\\mathbf{V}^{T} $$\n其中 \\(\\mathbf{U}\\) 是 \\(m\\) 阶正交矩阵,\\(\\mathbf{V}\\) 是 \\(n\\) 阶正交矩阵,\\(\\mathbf{Σ}\\) 是由降序排列的对角线元素组成的 \\(m \\times n\\) 矩形对角矩阵。\n$$ \\mathbf{Σ} = diag(\\sigma_{1}, \\sigma_{2}, \u0026hellip;, \\sigma_{p}) \\\\ \\sigma_{1} \\geq \\sigma_{2} \\geq \u0026hellip; \\geq \\sigma_{p} \\geq 0 $$\n\\(\\mathbf{U}\\mathbf{Σ}\\mathbf{V}^{T}\\) 称为矩阵 \\(\\mathbf{M}\\) 的奇异值分解,\\(\\sigma_{i}\\) 称为矩阵 \\(\\mathbf{M}\\) 的奇异值,\\(\\mathbf{U}\\) 的列向量称为左奇异向量,\\(\\mathbf{V}\\) 的列向量称为右奇异向量。\n“正交矩阵”:\n每两行/列之间互相正交(线性无关),且都是单位向量; 是方阵; 元素都是实数; 其转置矩阵同时也是其逆矩阵。 矩阵 \\(\\mathbf{M}\\) 的奇异值分解一定存在,但不一定唯一。\n上面的矩阵分解方式又叫做完全奇异值分解,而实际中更加常用的则是其紧凑形式和截断形式。\n紧奇异值分解:\n设有 \\(m \\times n\\) 实矩阵 \\(\\mathbf{M}\\),其秩为 \\(r\\),则有紧奇异值分解为:\n$$ \\mathbf{M} = \\mathbf{U}_{r}\\mathbf{Σ}_{r}\\mathbf{V}^{T}_{r} $$\n其中 \\(\\mathbf{U}_{r}\\) 是 \\(m \\times r\\) 矩阵,\\(\\mathbf{V}_{r}\\) 是 \\(n \\times r\\) 矩阵,\\(\\mathbf{Σ}_{r}\\) 是 \\(r\\) 阶对角矩阵。矩阵 \\(\\mathbf{U}_{r}\\) 由完全奇异值分解中 \\(\\mathbf{U}\\) 的前 \\(r\\) 列构成,矩阵 \\(\\mathbf{V}_{r}\\) 由完全奇异值分解中 \\(\\mathbf{V}\\) 的前 \\(r\\) 列构成,矩阵 \\(\\mathbf{Σ}_{r}\\) 由完全奇异值分解中 \\(\\mathbf{Σ}\\) 的前 \\(r\\) 个对角线元素构成。\n截断奇异值分解:\n与“紧奇异值分解”类似,只不过这里只保留最大的 \\(k\\) 个奇异值(\\(k \u0026lt; r\\))及其对应的奇异向量,有:\n$$ \\mathbf{M} \\approx \\mathbf{U}_{k}\\mathbf{Σ}_{k}\\mathbf{V}^{T}_{k} $$\n在实际应用中,常常需要对矩阵的数据进行压缩,将其近似表示,奇异值分解提供了一种方法。奇异值分解是在平方损失(弗罗贝尼乌斯范数)意义下对矩阵的最优近似。紧奇异值分解对应着无损压缩,截断奇异值分解对应着有损压缩。\n因此,SVD 的原理告诉我们,可以用低秩矩阵来近似地表达原矩阵。\n“弗罗贝尼乌斯范数”: $$ \\lVert A \\rVert_{F} = \\bigg( \\sum_{i = 1}^{m}\\sum_{j = 1}^{n} (a_{ij})^{2} \\bigg)^{\\frac{1}{2}}, \\mathbf{A} \\in \\mathbb{R}^{m \\times n} $$ “奇异值分解在统计中的主要应用为主成分分析(PCA)。数据集的特征值(在 SVD 中用奇异值表征)按照重要性排列,降维的过程就是舍弃不重要的特征向量的过程,而剩下的特征向量张成空间为降维后的空间。”\n具体地,在 LoRA 中,将矩阵 \\(\\mathbf{U}\\) 和 \\(\\mathbf{Σ}\\) 合并为了一个矩阵 \\(\\mathbf{B} \\in \\mathbb{R}^{m \\times k}\\),将矩阵 \\(\\mathbf{V}^{T}\\) 表示为了矩阵 \\(\\mathbf{A} \\in \\mathbb{R}^{k \\times n}\\),从而可以用更少的数据量来表示矩阵 \\(\\mathbf{\\Delta W}\\)。\n在实际微调中,由于事先并不知道矩阵 \\(\\mathbf{\\Delta W}\\) 中具体的值(除非我们先全参微调一遍,但是这样的话就没必要用 LoRA 了),我们无法直接计算出 \\(\\mathbf{\\Delta W}\\) 的 SVD 分解结果,因此论文作者将秩 \\(r\\) 作为一个超参数,并让模型在训练中自己去学习矩阵 \\(\\mathbf{A}\\) 和 \\(\\mathbf{B}\\) 的值。\n4.2 论文实验分析 # 4.2.1 实验一:不同微调方法的效果对比 # 在论文中,作者将 LoRA 与其它微调方法在多种场景下的表现进行了对比,如下图所示:\n实验结论:LoRA 在显著降低了微调参数量的同时,还能在大部分场景下保证最优的性能。\n4.2.2 实验二:不同 \\(r\\) 对微调效果的影响 # \\(r\\) 作为一个超参数,它的取值是如何影响 LoRA 的表现的?\n作者对比了当 \\(r\\) 取不同值时,对模型不同的层应用 LoRA 的效果,如下图所示:\n可以看出:\n当我们同时对 \\(\\mathbf{W}_{q}\\) 和 \\(\\mathbf{W}_{v}\\) 应用 LoRA 时,哪怕取了一个很小的 \\(r=1\\),其效果也超过了单独对 \\(\\mathbf{W}_{q}\\) 应用 LoRA 且 \\(r=64\\) 时的表现; 在同一行(同样的 Weight Type)内,取 \\(r=1\\) 与 \\(r=64\\) 的结果差异不大。 实验结论:增量权重矩阵 \\(\\mathbf{\\Delta W}\\) 的秩可能很小,因此我们能够用秩很小的两个矩阵来近似表达该矩阵。\n到此为止还没完,作者还做了更进一步的实验,来证明 LoRA 这种分解方式的有效性。 (´・_・`)\n4.2.3 实验三:\\(\\mathbf{\\Delta W}\\) 的左奇异矩阵 \\(\\mathbf{U}\\) 不同子空间的相似度对比 # 作者对比了 \\(\\mathbf{U}_{A_{r=8}}\\) 和 \\(\\mathbf{U}_{A_{r=64}}\\) 不同维度子空间的相似度,其中 \\(\\mathbf{U}_{A_{r=8}}\\) 为 \\(r=8\\) 时矩阵 \\(\\mathbf{A}\\) 的左奇异矩阵,\\(\\mathbf{U}_{A_{r=64}}\\) 同理。\n子空间相似度的计算方式:\n这里的 \\(\\lVert \u0026hellip; \\rVert_{F}\\) 为上面提到的“弗罗贝尼乌斯范数”。\n实验结果如下图所示(图 3 和图 4 分别为图 1 和图 2 的左下角部分):\n这个图可能比较难理解,下面举例进行说明:\n当 \\(i=1\\) 时,\\(j\\) 从 \\(1\\) 取到 \\(64\\),发现颜色都比较浅(相似度高),说明当 \\(r=8\\) 时,\\(\\mathbf{\\Delta W}\\) 分解出的矩阵 \\(\\mathbf{A}_{r=8}\\) 的左奇异矩阵 \\(\\mathbf{U}_{A_{r=8}}\\) 的第一个左奇异向量表示的特征(或者说信息)与 \\(\\mathbf{A}_{r=64}\\) 的前 64 个左奇异向量组成的子空间表示的特征重合度很高,即高秩矩阵(\\(r=64\\))的大部分信息都已经包含在了低秩矩阵(\\(r=8\\))的前几维子空间中。\n实验结论:越靠前的奇异向量(奇异值按降序排序)包含的信息越多(或者说越重要),不管 \\(r\\) 取多大,前几维(如 \\(r\u0026lt;8\\))子空间表示的信息都是差不多的,越后面(如 \\(8\u0026lt;r\u0026lt;64\\))的子空间包含的有效信息越少,噪声越多,再次证明了用低秩矩阵近似表达高秩矩阵的有效性。\n4.2.4 实验四:\\(\\mathbf{\\Delta W}\\) 与 \\(\\mathbf{W}\\) 不同子空间的相似度对比 # 与实验三类似,作者还比较了 \\(\\mathbf{\\Delta W}\\) 与 \\(\\mathbf{W}\\) 不同维度子空间的相似度,如下图所示: 其中,\\(i\\) 表示使用预训练权重矩阵 \\(\\mathbf{W}\\) 的前 \\(i\\) 个左奇异向量组成的子空间,\\(j\\) 表示使用 \\(\\mathbf{\\Delta W}\\) 的前 \\(j\\) 个左奇异向量组成的子空间(\\(j \\leq r\\))。\n可以看出,\\(\\mathbf{W}\\) 中靠前(靠上)的奇异向量组成的子空间与 \\(\\mathbf{\\Delta W}\\) 的子空间相似度并不高,反而是 \\(\\mathbf{W}\\) 中最后的一些奇异向量与 \\(\\mathbf{\\Delta W}\\) 中的奇异向量有较高的相似度。\n实验结论:\\(\\mathbf{\\Delta W}\\) 中学习到的特征都是原来预训练权重矩阵中没有被强调的部分,说明 \\(\\mathbf{\\Delta W}\\) 能够有效学习到针对特定下游任务、区别于原特征的新特征,证明了 LoRA 在微调方面的有效性。\n说明:这里只挑了论文中我比较感兴趣的几个实验来进行分析,对于论文中的其它实验,感兴趣的读者可以自行阅读论文进行了解。\n4.3 缩放因子 \\(\\alpha\\) # LoRA 引入了一个超参数 \\(\\alpha\\),可以看作是学习率,同时也代表了 LoRA 对从特定下游任务学习而来的特征的放大程度,有:\n$$ \\mathbf{h} = \\mathbf{W}\\mathbf{x} + \\mathbf{\\Delta W}\\mathbf{x} \\approx \\mathbf{W}\\mathbf{x} + \\frac{\\alpha}{r}\\mathbf{B}\\mathbf{A}\\mathbf{x} $$\n在实际微调中,一般先随机设置一个 \\(r\\),并让 \\(\\alpha = r\\),然后保持 \\(\\alpha\\) 不变,通过不断调整 \\(r\\) 的值来调整微调的效果。\n五、参考资料 # LoRA: Low-Rank Adaptation of Large Language Models 《统计学习方法》 图解大模型微调系列之:大模型低秩适配器 LoRA(原理篇) ","date":"2024-11-13","externalUrl":null,"permalink":"/articles/%E5%A4%A7%E6%A8%A1%E5%9E%8B-lora-%E5%BE%AE%E8%B0%83%E7%9A%84%E6%95%B0%E5%AD%A6%E5%8E%9F%E7%90%86/","section":"Articles","summary":"","title":"大模型 LoRA 微调的数学原理","type":"posts"},{"content":" Overview # Shanshan Shen | 申杉杉\nSeptember 18th, 1998\nHello, I am currently a software engineer working at Huawei Ascend, engaging in AI Infra and LLM. I have contributed to some open-source projects such as vLLM, llama.cpp and ChatTTS, to build a easy-to-use software ecosystem for Ascend. Before this, I was a student at Beijing Jiao Tong University (BSc/MSc), majoring in communication engineering. You can see more projects at my Github, or see my posts at CSDN and Zhihu.\nExperience # Huawei Software Engineer July \u0026#39;23 - Present Working at Huawei from MetaCRM to Ascend, engaging in AI Infra and software development.\rBeijing Jiao Tong University Postgraduate Student September \u0026#39;21 - June \u0026#39;23 Studying at School of Electronic and Information Engineering, engaging in security research of wireless communication.\rBeijing Jiao Tong University Undergraduate Student September \u0026#39;16 - June \u0026#39;20 Studying at School of Electronic and Information Engineering, majoring in communication engineering.\rProgramming Skills # Open Source Contribution # I am a contributor to many open source projects on GitHub, which are shown below.\nvllm-project/vllm A high-throughput and memory-efficient inference and serving engine for LLMs Python 32277 4917 ggerganov/llama.cpp LLM inference in C/C++ C\u0026#43;\u0026#43; 68201 9780 2noise/ChatTTS A generative speech model for daily dialogue. Python 32525 3530 Hobbies # In my spare time, I have many hobbies such as reading books, playing badminton and doing exercise. I am not only a programmer, but also a guitarist loving play both acoustic and electric guitars.\n📚 Books 🎸 Guitars 🎵 Albums 🏸 Badminton 📚 Books # I love reading books over last ten years, and here are books I highly recommended.\n🎸 Guitars # I love listening to guitar music especially fingerstyle songs and may do some recordings myself when I come across some beautiful songs. My guitars and gears used for recording are shown below, and there are also my albums that have been published so far. You can find my music at bilibili or NetEase Cloud Music.\nFender Telecaster Player Electric Guitar May \u0026#39;20 - Present This is my electric guitar which is made of Alderwood (body) and Maplewood (fingerboard), and is equipped with pickup system of Alnico 5 single coil.\rMorris s101 Ⅲ Acoustic Guitar November \u0026#39;18 - Present This is my favorite guitar which is made of Sitka Spruce (panel) and Indian Rosewood (backboard), and is equipped with pickup system of Lim magnet (passive) and Lim piezo.\r🎵 Albums # Acoustic Guitar Cover Collection Guitar Recordings March \u0026#39;18 - January \u0026#39;23 This album is a collection of multi-track acoustic guitar cover which contains 11 songs that I like, such as In The Rain and Crack. You can listen to these songs at here.\rFingerstyle Guitar Cover Collection Guitar Recordings March \u0026#39;18 - January \u0026#39;23 This album is a collection of fingerstyle guitar and ukulele cover which contains 11 songs that I like, such as Tickets to Starry Sky and Rain stops, Good-bye. You can listen to these songs at here.\r🏸 Badminton # I love playing badminton and will participate in this activity 1~4 times a week. Awards I have received are shown below. Welcome to battle with me!\nFourth place in the Inter-Department Team Competition of Xian Huawei at 2024 Second place in the Quality and Process IT Department Team Competition of Xian Huawei at 2024 Third place in the Inter-School Cup of Beijing Jiao Tong University at 2023 Contact me # Feel free to drop me an email, gmail and qq-mail are both available.\nYou can also have my WeChat through the picture below:\nRemember to add a explaination before sending a request!\n","date":"2024-11-02","externalUrl":null,"permalink":"/about/","section":"home","summary":"","title":"About me","type":"page"},{"content":"","date":"2024-10-23","externalUrl":null,"permalink":"/tags/ai-infra/","section":"Tags","summary":"","title":"AI Infra","type":"tags"},{"content":"","date":"2024-10-23","externalUrl":null,"permalink":"/tags/ascend/","section":"Tags","summary":"","title":"Ascend","type":"tags"},{"content":" 一、概述 # 什么是算子?\n在 AI 框架中,算子一般指一些最基本的代数运算(如:矩阵加法、矩阵乘法等),多个算子之间也可以根据需要组合成更加复杂的融合算子(如:flash-attention 算子等)。算子的输入和输出都是 Tensor(张量)。\n融合算子:将多个独立的“小算子”融合成一个“大算子”,多个小算子的功能和大算子的功能等价,但融合算子在性能或者内存等方面优于独立的小算子。\n另外,算子更多地是 AI 框架中的一个概念,在硬件底层算子具体的执行部分,一般叫做 Kernel(核函数)。\n下面将首先对算子开发中涉及的一些基本概念进行介绍(可以用 CUDA 作为参考,大部分概念都是相似的),然后会以具体的矩阵加法和乘法算子的代码实现为例进行讲解。\n二、基本概念 # 2.1 Device # Host:一般指 CPU(负责调度); Device:一般指 GPU、NPU(负责计算)。 2.2 Context # Context 主要负责管理线程中各项资源的生命周期。\n一般来说,Context 与其它概念之间具有以下关系:\n一个进程可以创建多个 Context; 一个线程只能同时使用一个 Context,该 Context 对应一个唯一的 Device,线程可以通过切换 Context 来切换要使用的 Device; 一个 Device 可以拥有多个 Context,但同时只能使用一个 Context。 每一个线程都具有一个默认的 Context,无需手动创建,也无法被删除。我们也可以手动创建更多的 Context,使用后需要及时释放。另外,在线程中,默认使用最后一次创建的 Context。\n2.3 Stream # Stream 主要负责维护一些异步操作的执行顺序,这些操作包括:\nHost 到 Device 的数据传输; 调用 Kernel; 其它由 Host 发起并由 Device 执行的动作。 说明:在 GPU/NPU 上调用的函数,被称为核函数(Kernel function)。核函数使用 __global__ 关键字进行定义,会被 GPU/NPU 上的多个线程执行。\n同一个 Stream 里的操作是严格串行的(顺序执行),而不同 Stream 之间则可以并行执行。来自不同 Stream 的 Kernel 可以共享 GPU/NPU 的内核并发执行。\n一般来说,Context 与其它概念之间具有以下关系:\n一个线程或 Context 中可以创建多个 Stream; 不同线程或 Context 间的 Stream 在 Device 上相互隔离。 每一个 Context 都具有一个默认的 Stream,无需手动创建,也无法被删除。我们也可以手动创建更多的 Stream,并将多个操作分配到不同的 Stream 上,这样就可以实现多个操作的并行,Stream 使用后需要及时释放。\n2.4 Task # Task 或 Kernel,是 Device 上真正的任务执行体。\n一般来说,Task 与其它概念之间具有以下关系:\n一个 Stream 中可以下发多个 Task; 多个 Task 之间可以插入 Event,用于同步不同 Stream 之间的 Task。 参考资料:\nAscend 算子开发基本概念 CUDA 基础 CUDA 介绍 三、单算子开发 # 官方介绍:\nAscendCL(Ascend Computing Language)是一套用于在昇腾平台上开发深度神经网络应用的 C 语言 API 库,提供运行资源管理、内存管理、模型加载与执行、算子加载与执行、媒体数据处理等 API,能够实现利用昇腾硬件计算资源、在昇腾 CANN 平台上进行深度学习推理计算、图形图像预处理、单算子加速计算等能力。简单来说,就是统一的 API 框架,实现对所有资源的调用。\n面向算子开发场景的编程语言 Ascend C,原生支持 C/C++ 标准规范,最大化匹配用户开发习惯;通过多层接口抽象、自动并行计算、孪生调试等关键技术,极大提高算子开发效率,助力 AI 开发者低成本完成算子开发和模型调优部署。\n3.1 单算子调用方式 # 单算子 API 执行: 直接调用 CANN 已经提供的算子 API; 使用 Ascend C 开发并调用自定义算子。 单算子模型执行。 3.2 单算子 API 执行 # NN 算子; DVPP 算子; 融合算子; …… 更详细的算子 API 文档可以参考:算子加速库接口。\n两段式接口:单算子 API 执行时,针对每个算子,都需要依次先调用 aclxxXxxGetWorkspaceSize() 接口获取算子执行需要的 workspace 内存大小、再调用 aclxxXxx() 接口执行算子。\n参考资料:\n单算子调用基础知识 Ascend 开源融合算子 四、代码实现 # 本小节将以 aclnnAdd 和 aclnnMatmul 算子为例,实现具体的代码。\n更详细的 API 文档可以参考:\n加法算子:aclnnAdd 乘法算子:aclnnMatmul 4.1 环境搭建 # 快速安装昇腾环境 基于 EulerOS \u0026amp; Ascend NPU 搭建 PyTorch 远程开发环境 4.2 单算子开发流程 # 4.3 常见参数说明 # strides:描述 Tensor 维度上相邻两个元素的间隔,详见非连续的 Tensor; workspace:在 device 侧申请的 workspace 内存地址; workspaceSize:在 device 侧申请的 workspace 大小; executor:算子执行器,实现了算子的计算流程; aclnnStatus:详见aclnn 返回码。 注意:\n多个输入数据之间,数据类型需要满足互推导关系:当一个 API(如 aclnnAdd()、aclnnMul() 等)输入的 Tensor 数据类型不一致时,API 内部会推导出一个数据类型,将输入数据转换成该数据类型进行计算; 多个输入数据之间,shape 需要满足广播关系:在某些情况下,较小的数组可以“广播至”较大的数组,使两者shape互相兼容; 更多算子 API 信息详见:CANN 社区版开发文档,位置:【CANN 社区版 -\u0026gt; 8.0.RC3.alpha003 -\u0026gt; API 参考 -\u0026gt; 算子加速库接口 -\u0026gt; NN 算子接口】。 4.4 矩阵加法算子 # 目录结构:\nsss@xxx:~/xxx/add$ tree . |-- CMakeLists.txt |-- build `-- test_add.cpp CMakeLists:\n# Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. # CMake lowest version requirement cmake_minimum_required(VERSION 3.14) # 设置工程名 project(ACLNN_EXAMPLE) # Compile options add_compile_options(-std=c++11) # 设置编译选项 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \u0026#34;./bin\u0026#34;) set(CMAKE_CXX_FLAGS_DEBUG \u0026#34;-fPIC -O0 -g -Wall\u0026#34;) set(CMAKE_CXX_FLAGS_RELEASE \u0026#34;-fPIC -O2 -Wall\u0026#34;) # 设置可执行文件名(如opapi_test),并指定待运行算子文件*.cpp所在目录 add_executable(opapi_add_test ../test_add.cpp) # 设置ASCEND_PATH(CANN软件包目录,请根据实际路径修改)和INCLUDE_BASE_DIR(头文件目录) if(NOT \u0026#34;$ENV{ASCEND_CUSTOM_PATH}\u0026#34; STREQUAL \u0026#34;\u0026#34;) set(ASCEND_PATH $ENV{ASCEND_CUSTOM_PATH}) else() set(ASCEND_PATH \u0026#34;/home/sss/Ascend/ascend-toolkit/latest\u0026#34;) # 示例:/usr/local/Ascend/ascend-toolkit/latest endif() set(INCLUDE_BASE_DIR \u0026#34;${ASCEND_PATH}/include\u0026#34;) include_directories( ${INCLUDE_BASE_DIR} ${INCLUDE_BASE_DIR}/aclnn ) # 设置链接的动态库文件路径 # arch表示操作系统架构,os表示操作系统 target_link_libraries(opapi_test PRIVATE ${ASCEND_PATH}/lib64/libascendcl.so ${ASCEND_PATH}/lib64/libnnopbase.so ${ASCEND_PATH}/lib64/libopapi.so) # 可执行文件在CMakeLists文件所在目录的bin目录下 install(TARGETS opapi_test DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 编译构建:\nmkdir build cd build cmake .. -DCMAKE_CXX_COMPILER=g++ -DCMAKE_SKIP_RPATH=TRUE make test_add 代码:\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026#34;acl/acl.h\u0026#34; #include \u0026#34;aclnnop/aclnn_add.h\u0026#34; #define CHECK_RET(cond, return_expr) \\ do { \\ if (!(cond)) { \\ return_expr; \\ } \\ } while (0) #define LOG_PRINT(message, ...) \\ do { \\ printf(message, ##__VA_ARGS__); \\ } while (0) int64_t GetShapeSize(const std::vector\u0026lt;int64_t\u0026gt;\u0026amp; shape) { int64_t shape_size = 1; for (auto i : shape) { shape_size *= i; } return shape_size; } int Init(int32_t deviceId, aclrtStream* stream) { // 固定写法,AscendCL初始化 auto ret = aclInit(nullptr); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclInit failed. ERROR: %d\\n\u0026#34;, ret); return ret); ret = aclrtSetDevice(deviceId); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtSetDevice failed. ERROR: %d\\n\u0026#34;, ret); return ret); ret = aclrtCreateStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtCreateStream failed. ERROR: %d\\n\u0026#34;, ret); return ret); return 0; } template \u0026lt;typename T\u0026gt; int CreateAclTensor(const std::vector\u0026lt;T\u0026gt;\u0026amp; hostData, const std::vector\u0026lt;int64_t\u0026gt;\u0026amp; shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size = GetShapeSize(shape) * sizeof(T); // 调用aclrtMalloc申请device侧内存 auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtMalloc failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtMemcpy failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 计算连续tensor的strides std::vector\u0026lt;int64_t\u0026gt; strides(shape.size(), 1); for (int64_t i = shape.size() - 2; i \u0026gt;= 0; i--) { strides[i] = shape[i + 1] * strides[i + 1]; } // 调用aclCreateTensor接口创建aclTensor *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } int main() { // 1. (固定写法)device/stream初始化, 参考AscendCL对外接口列表 // 根据自己的实际device填写deviceId int32_t deviceId = 0; aclrtStream stream; auto ret = Init(deviceId, \u0026amp;stream); // check根据自己的需要处理 CHECK_RET(ret == 0, LOG_PRINT(\u0026#34;Init acl failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 2. 构造输入与输出,需要根据API的接口自定义构造 std::vector\u0026lt;int64_t\u0026gt; selfShape = {4, 2}; std::vector\u0026lt;int64_t\u0026gt; otherShape = {4, 2}; std::vector\u0026lt;int64_t\u0026gt; outShape = {4, 2}; void* selfDeviceAddr = nullptr; void* otherDeviceAddr = nullptr; void* outDeviceAddr = nullptr; aclTensor* self = nullptr; aclTensor* other = nullptr; aclScalar* alpha = nullptr; aclTensor* out = nullptr; std::vector\u0026lt;float\u0026gt; selfHostData = {0, 1, 2, 3, 4, 5, 6, 7}; std::vector\u0026lt;float\u0026gt; otherHostData = {1, 1, 1, 2, 2, 2, 3, 3}; std::vector\u0026lt;float\u0026gt; outHostData = {0, 0, 0, 0, 0, 0, 0, 0}; float alphaValue = 1.2f; // 创建self aclTensor ret = CreateAclTensor(selfHostData, selfShape, \u0026amp;selfDeviceAddr, aclDataType::ACL_FLOAT, \u0026amp;self); CHECK_RET(ret == ACL_SUCCESS, return ret); // 创建other aclTensor ret = CreateAclTensor(otherHostData, otherShape, \u0026amp;otherDeviceAddr, aclDataType::ACL_FLOAT, \u0026amp;other); CHECK_RET(ret == ACL_SUCCESS, return ret); // 创建alpha aclScalar alpha = aclCreateScalar(\u0026amp;alphaValue, aclDataType::ACL_FLOAT); CHECK_RET(alpha != nullptr, return ret); // 创建out aclTensor ret = CreateAclTensor(outHostData, outShape, \u0026amp;outDeviceAddr, aclDataType::ACL_FLOAT, \u0026amp;out); CHECK_RET(ret == ACL_SUCCESS, return ret); // 3. 调用CANN算子库API,需要修改为具体的算子接口 uint64_t workspaceSize = 0; aclOpExecutor* executor; // 调用aclnnAdd第一段接口 ret = aclnnAddGetWorkspaceSize(self, other, alpha, out, \u0026amp;workspaceSize, \u0026amp;executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclnnAddGetWorkspaceSize failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void* workspaceAddr = nullptr; if (workspaceSize \u0026gt; 0) { ret = aclrtMalloc(\u0026amp;workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;allocate workspace failed. ERROR: %d\\n\u0026#34;, ret); return ret;); } // 调用aclnnAdd第二段接口 ret = aclnnAdd(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclnnAdd failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 4.( 固定写法)同步等待任务执行结束 ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtSynchronizeStream failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改 auto size = GetShapeSize(outShape); std::vector\u0026lt;float\u0026gt; resultData(size, 0); ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;copy result from device to host failed. ERROR: %d\\n\u0026#34;, ret); return ret); for (int64_t i = 0; i \u0026lt; size; i++) { LOG_PRINT(\u0026#34;result[%ld] is: %f\\n\u0026#34;, i, resultData[i]); } // 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改 aclDestroyTensor(self); aclDestroyTensor(other); aclDestroyScalar(alpha); aclDestroyTensor(out); // 7. 释放device资源,需要根据具体API的接口定义修改 aclrtFree(selfDeviceAddr); aclrtFree(otherDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize \u0026gt; 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; } 运行程序:\n./opapi_add_test 运行结果:\nsss@xxx:~/xxx/add/build/bin$ ./opapi_test result[0] is: 1.200000 result[1] is: 2.200000 result[2] is: 3.200000 result[3] is: 5.400000 result[4] is: 6.400000 result[5] is: 7.400000 result[6] is: 9.600000 result[7] is: 10.600000 4.5 矩阵乘法算子 # 目录结构:\nsss@xxx:~/xxx/mul$ tree . |-- CMakeLists.txt |-- build `-- test_mul.cpp CMakeLists:\n# Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. # CMake lowest version requirement cmake_minimum_required(VERSION 3.14) # 设置工程名 project(ACLNN_EXAMPLE) # Compile options add_compile_options(-std=c++11) # 设置编译选项 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \u0026#34;./bin\u0026#34;) set(CMAKE_CXX_FLAGS_DEBUG \u0026#34;-fPIC -O0 -g -Wall\u0026#34;) set(CMAKE_CXX_FLAGS_RELEASE \u0026#34;-fPIC -O2 -Wall\u0026#34;) # 设置可执行文件名(如opapi_test),并指定待运行算子文件*.cpp所在目录 add_executable(opapi_mul_test ../test_mul.cpp) # 设置ASCEND_PATH(CANN软件包目录,请根据实际路径修改)和INCLUDE_BASE_DIR(头文件目录) if(NOT \u0026#34;$ENV{ASCEND_CUSTOM_PATH}\u0026#34; STREQUAL \u0026#34;\u0026#34;) set(ASCEND_PATH $ENV{ASCEND_CUSTOM_PATH}) else() set(ASCEND_PATH \u0026#34;/home/sss/Ascend/ascend-toolkit/latest\u0026#34;) # 示例:/usr/local/Ascend/ascend-toolkit/latest endif() set(INCLUDE_BASE_DIR \u0026#34;${ASCEND_PATH}/include\u0026#34;) include_directories( ${INCLUDE_BASE_DIR} ${INCLUDE_BASE_DIR}/aclnn ) # 设置链接的动态库文件路径 # arch表示操作系统架构,os表示操作系统 target_link_libraries(opapi_test PRIVATE ${ASCEND_PATH}/lib64/libascendcl.so ${ASCEND_PATH}/lib64/libnnopbase.so ${ASCEND_PATH}/lib64/libopapi.so) # 可执行文件在CMakeLists文件所在目录的bin目录下 install(TARGETS opapi_test DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 编译构建:\nmkdir build cd build cmake .. -DCMAKE_CXX_COMPILER=g++ -DCMAKE_SKIP_RPATH=TRUE make test_mul 代码:\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026#34;acl/acl.h\u0026#34; #include \u0026#34;aclnnop/aclnn_matmul.h\u0026#34; #define CHECK_RET(cond, return_expr) \\ do { \\ if (!(cond)) { \\ return_expr; \\ } \\ } while (0) #define LOG_PRINT(message, ...) \\ do { \\ printf(message, ##__VA_ARGS__); \\ } while (0) int64_t GetShapeSize(const std::vector\u0026lt;int64_t\u0026gt;\u0026amp; shape) { int64_t shape_size = 1; for (auto i : shape) { shape_size *= i; } return shape_size; } int Init(int32_t deviceId, aclrtStream* stream) { // 固定写法,AscendCL初始化 auto ret = aclInit(nullptr); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclInit failed. ERROR: %d\\n\u0026#34;, ret); return ret); ret = aclrtSetDevice(deviceId); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtSetDevice failed. ERROR: %d\\n\u0026#34;, ret); return ret); ret = aclrtCreateStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtCreateStream failed. ERROR: %d\\n\u0026#34;, ret); return ret); return 0; } template \u0026lt;typename T\u0026gt; int CreateAclTensor(const std::vector\u0026lt;T\u0026gt;\u0026amp; hostData, const std::vector\u0026lt;int64_t\u0026gt;\u0026amp; shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size = GetShapeSize(shape) * sizeof(T); // 调用aclrtMalloc申请device侧内存 auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtMalloc failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtMemcpy failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 计算连续tensor的strides std::vector\u0026lt;int64_t\u0026gt; strides(shape.size(), 1); for (int64_t i = shape.size() - 2; i \u0026gt;= 0; i--) { strides[i] = shape[i + 1] * strides[i + 1]; } // 调用aclCreateTensor接口创建aclTensor *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } int main() { // 1.初始化 int32_t deviceId = 0; aclrtStream stream; auto ret = Init(deviceId, \u0026amp;stream); CHECK_RET(ret == 0, LOG_PRINT(\u0026#34;Init acl failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 2.准备数据 // 矩阵 1 std::vector\u0026lt;float\u0026gt; mat1HostData = {1, 2, 3, 4, 5, 6}; std::vector\u0026lt;int64_t\u0026gt; mat1Shape = {3, 2}; void* mat1DeviceAddr = nullptr; aclTensor* mat1 = nullptr; ret = CreateAclTensor(mat1HostData, mat1Shape, \u0026amp;mat1DeviceAddr, aclDataType::ACL_FLOAT, \u0026amp;mat1); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;CreateAclTensor for mat1 failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 矩阵 2 std::vector\u0026lt;float\u0026gt; mat2HostData = {1, 2, 3, 4, 5, 6}; std::vector\u0026lt;int64_t\u0026gt; mat2Shape = {2, 3}; void* mat2DeviceAddr = nullptr; aclTensor* mat2 = nullptr; ret = CreateAclTensor(mat2HostData, mat2Shape, \u0026amp;mat2DeviceAddr, aclDataType::ACL_FLOAT, \u0026amp;mat2); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;CreateAclTensor for mat2 failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 结果矩阵 std::vector\u0026lt;float\u0026gt; outHostData = {0, 0, 0, 0, 0, 0, 0, 0, 0}; std::vector\u0026lt;int64_t\u0026gt; outShape = {3, 3}; void* outDeviceAddr = nullptr; aclTensor* out = nullptr; ret = CreateAclTensor(outHostData, outShape, \u0026amp;outDeviceAddr, aclDataType::ACL_FLOAT, \u0026amp;out); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;CreateAclTensor for out failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 3.调用 CANN 算子库 API uint64_t workspaceSize = 0; aclOpExecutor* executor; int8_t cubeMathType = 1; // 计算 device 内存 ret = aclnnMatmulGetWorkspaceSize(mat1, mat2, out, cubeMathType, \u0026amp;workspaceSize, \u0026amp;executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclnnMatmulGetWorkspaceSize failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 申请 device 内存 void* workspaceAddr = nullptr; if (workspaceSize \u0026gt; 0) { ret = aclrtMalloc(\u0026amp;workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;allocate workspace failed. ERROR: %d\\n\u0026#34;, ret); return ret;); } // 执行计算过程 ret = aclnnMatmul(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclnnMatmul failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 4.等待计算结果 ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;aclrtSynchronizeStream failed. ERROR: %d\\n\u0026#34;, ret); return ret); // 5.将 device 侧内存上的结果拷贝至 host 侧 auto size = GetShapeSize(outShape); std::vector\u0026lt;float\u0026gt; resultData(size, 0); ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT(\u0026#34;copy result from device to host failed. ERROR: %d\\n\u0026#34;, ret); return ret); for (int64_t i = 0; i \u0026lt; size; i++) { LOG_PRINT(\u0026#34;result[%ld] is: %f\\n\u0026#34;, i, resultData[i]); } // 6.释放 aclTensor aclDestroyTensor(mat1); aclDestroyTensor(mat1); aclDestroyTensor(out); // 7.释放 device 资源 aclrtFree(mat1DeviceAddr); aclrtFree(mat2DeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize \u0026gt; 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; } 运行程序:\n./opapi_mul_test 运行结果:\nsss@xxx:~/xxx/mul/build/bin$ ./opapi_test result[0] is: 9.000000 result[1] is: 12.000000 result[2] is: 15.000000 result[3] is: 19.000000 result[4] is: 26.000000 result[5] is: 33.000000 result[6] is: 29.000000 result[7] is: 40.000000 result[8] is: 51.000000 参考资料:\nAscend 算子开发指南 CANN 社区版开发文档 调用 NN 算子接口示例代码 算子加速库接口 ","date":"2024-10-23","externalUrl":null,"permalink":"/articles/ascend-aclnn-%E7%AE%97%E5%AD%90%E5%BC%80%E5%8F%91%E5%85%A5%E9%97%A8/","section":"Articles","summary":"","title":"Ascend aclnn 算子开发入门","type":"posts"},{"content":"","date":"2024-10-23","externalUrl":null,"permalink":"/tags/cann/","section":"Tags","summary":"","title":"CANN","type":"tags"},{"content":"","date":"2024-10-23","externalUrl":null,"permalink":"/tags/npu/","section":"Tags","summary":"","title":"NPU","type":"tags"},{"content":"","date":"2024-10-23","externalUrl":null,"permalink":"/tags/%E7%AE%97%E5%AD%90%E5%BC%80%E5%8F%91/","section":"Tags","summary":"","title":"算子开发","type":"tags"},{"content":"","date":"2024-10-18","externalUrl":null,"permalink":"/tags/git/","section":"Tags","summary":"","title":"Git","type":"tags"},{"content":" 一、概述 # 本文记录了我在开源贡献的过程中遇到的一个小问题(使用 git 调整 commit 的顺序,并整合多个 commit 节点)以及最后是怎么解决的。\n二、背景介绍 # 一般在进行开源贡献提交 PR 之前,我们需要先 fork 想要贡献的仓库到我们自己的 GitHub 仓库中。\n首先,将上游仓库关联到我们的 remote 中,可以使用如下命令:\ngit remote add upstream \u0026lt;xxx.git\u0026gt; # 新增想要 fork 的仓库的 url git remote -v # 查看 remote 中所有的 url,可以看到 origin 和 upstream(共 4 个 url) git branch -vva # 查看本地和远程的所有分支 然后,将上游仓库中最新的改动同步到自己的远程仓库,可以使用如下命令:\ngit fetch upstream # 将上游所有最新的改动下载到本地的一个新分支中,但不更新本地分支 git checkout \u0026lt;branch\u0026gt; # 签出本地分支 git merge upstream/\u0026lt;branch\u0026gt; # 合并上游分支 git push origin \u0026lt;branch\u0026gt; # 推送本地分支 关于 git fetch 的详细原理,可以参考:Git fetch 原理解析。 总结:git pull = git fetch + git merge。\n三、我的问题 # 当我基于上游分支进行了一段时间的开发,并且已经多次 commit,还 push 到了我的 origin 中时,我 merge 了上游分支最新的修改,然后发现在我的多次 commit 中,穿插有其他人的 commit。\n当前本地分支的 git log 情况如下:\n注意:上图中,B 和 D 是已经合入上游仓库中的 commit;A 和 C 是只 push 到了我个人的远程仓库,还未合入上游的 commit。\n我的诉求:将 A 和 C 这两个我的 commit 节点合并,并放到 B 之后,然后更新到我的远程仓库。\n期望的 git log 是这样:\n四、解决方法 # 先将本地分支回退到 D 节点,然后暂存当前 A + C 的修改,重新 merge 上游分支,然后恢复暂存的修改,最后提交并推送到远程仓库。\n使用的命令如下:\ngit reset --soft \u0026lt;commit_id\u0026gt; # 当前所有修改内容前最近的一个提交节点(D) git fetch upstream # 再同步一下上游仓库 git stash # 暂存 A 和 C 修改的内容 git merge upstream/\u0026lt;branch\u0026gt; # 合并上游分支最新的修改(B),此时 git log 变为:【B-\u0026gt;D-\u0026gt;...】 git stash pop # 恢复 A 和 C 修改的内容 git add . git commit -am \u0026#34;A + C 的提交信息\u0026#34; # 此时 git log 变为:【E-\u0026gt;B-\u0026gt;D-\u0026gt;...】 git push origin \u0026lt;branch\u0026gt; --force # 推送到自己的远程仓库 注意:最后 push origin 时必须加上 \u0026ndash;force,因为此时 origin 中的 commit 顺序为:【A-\u0026gt;B-\u0026gt;C-\u0026gt;D-\u0026gt;\u0026hellip;】(因为之前已经 push 过一次 origin 了),而本地分支现在是:【E-\u0026gt;B-\u0026gt;D-\u0026gt;\u0026hellip;】,会发生冲突。此时,Git 会提示你需要先合并 origin 中的内容才能 push,这样的话 commit 的顺序就又乱了!因此,我们直接加上 \u0026ndash;force 选项,用本地分支直接覆盖 origin 中的分支。\n最后,我们成功将 A + C 的 commit 节点合并,并放到了 B 节点的后面。\n为什么要这样做呢?\n因为我的 A 和 C 提交属于同一个内容,如果中间又穿插了一个别人提交的 B,那么同一个改动的相关内容就被分隔开了,这样不利于内容管理以及问题回溯。\n","date":"2024-10-18","externalUrl":null,"permalink":"/articles/git-%E5%AE%9E%E8%B7%B5%E6%A1%88%E4%BE%8B-%E5%90%88%E5%B9%B6%E5%A4%9A%E4%B8%AA%E5%88%86%E6%95%A3%E7%9A%84-commit-%E8%8A%82%E7%82%B9/","section":"Articles","summary":"","title":"Git 实践案例 | 合并多个分散的 commit 节点","type":"posts"},{"content":" 一、概述 # 本文记录了自己在基于 EulerOS \u0026amp; Ascend NPU 的华为云远程服务器上,使用 docker 容器搭建 PyTorch 开发环境的主要过程以及遇到的问题。\n硬件规格如下:\nKunpeng + Ascend: 192 CPU,1.5T MEM,21T DISK,8*Ascend XXX 型号\rEulerOS V2 R10 二、创建 docker 镜像并运行容器 # 2.1 编写 Dockerfile # 创建 Dockerfile 如下:\nFROM ubuntu:20.04 # define your var ARG YOUR_USER_NAME=\u0026#34;sss\u0026#34; ARG YOUR_GROUP_ID=\u0026#34;9005\u0026#34; ARG YOUR_USER_ID=\u0026#34;9005\u0026#34; ARG DEBIAN_FRONTEND=noninteractive ENV TZ=\u0026#34;Asia/shanghai\u0026#34; RUN sed -i \u0026#39;s/ports.ubuntu.com/mirrors.aliyun.com/g\u0026#39; /etc/apt/sources.list \u0026amp;\u0026amp; \\ apt-get update \u0026amp;\u0026amp; \\ yes | unminimize \u0026amp;\u0026amp; \\ apt-get install -y adduser sudo vim gcc g++ cmake make gdb git tmux openssh-server \\ net-tools iputils-ping python3-distutils python3-setuptools \\ python3-wheel python3-yaml python3-dbg python3-pip libmpich-dev # Config user. User must join group HwHiAiUser(1000) to use npu. # Identify user id and group id to match user out of docker. (optional) RUN groupadd -g $YOUR_GROUP_ID $YOUR_USER_NAME \u0026amp;\u0026amp; \\ useradd -u $YOUR_USER_ID -g $YOUR_USER_NAME -ms /bin/bash $YOUR_USER_NAME \u0026amp;\u0026amp; \\ sed -i \u0026#34;/root\\tALL=(ALL:ALL) ALL/a\u0026#34;${YOUR_USER_NAME}\u0026#34;\\tALL=(ALL:ALL) ALL\u0026#34; /etc/sudoers \u0026amp;\u0026amp; \\ echo \u0026#34;source /home/${YOUR_USER_NAME}/Ascend/ascend-toolkit/set_env.sh\u0026#34; \u0026gt;\u0026gt; /home/\u0026#34;$YOUR_USER_NAME\u0026#34;/.bashrc \u0026amp;\u0026amp; \\ echo \u0026#34;export LD_LIBRARY_PATH=/usr/local/Ascend/driver/lib64/common/:/usr/local/Ascend/driver/lib64/driver/:${LD_LIBRARY_PATH}\u0026#34; \u0026gt;\u0026gt; /home/\u0026#34;$YOUR_USER_NAME\u0026#34;/.bashrc \u0026amp;\u0026amp; \\ ssh-keygen -A CMD [\u0026#34;/bin/bash\u0026#34;, \u0026#34;/home/sss/bin/entrypoint.sh\u0026#34;] 注意:\nDockerfile 中的 YOUR_USER_NAME、YOUR_GROUP_ID 以及 YOUR_USER_ID 为将要在容器中创建的用户和用户组,请自行进行设置; CMD 中的 entrypoint.sh 脚本文件为容器每次启动时都会去执行的命令集合,这里文件前面的路径需要替换为自己容器中的路径(Dockerfile 里面写的路径都是指容器里面的); 我们需要将自己宿主机中存放个人数据的目录(我是 /data/disk/sss)挂载到容器中自己的用户目录(我是 /home/sss)下,并将编写好的 entrypoint.sh 脚本存放到 /data/disk/sss/bin 目录下,这样我们进入容器后,就能在容器的 /home/sss/bin 目录下找到 entrypoint.sh 脚本并执行它(如果目录映射不对,在容器中找不到该脚本文件,那么容器启动时就会报错)。 2.2 构建 base 镜像 # 在 Dockerfile 所在目录执行下面的命令:\n# docker build -t \u0026lt;镜像名称\u0026gt;:\u0026lt;镜像tag\u0026gt; \u0026lt;Dockerfile所在目录\u0026gt; docker build -t sss_base_image:1.0 . 其它镜像常用命令:\n# 查看镜像 docker images # 删除镜像 docker rmi image:tag # 创建新的标签 docker tag \u0026lt;old_image_name\u0026gt;:\u0026lt;old_tag\u0026gt; \u0026lt;new_image_name\u0026gt;:\u0026lt;new_tag\u0026gt; 2.3 编写容器启动脚本 # 创建 entrypoint.sh 脚本文件如下:\n# /bin/bash # define your var your_user_name=\u0026#34;sss\u0026#34; your_password=\u0026#34;xxx\u0026#34; # Create passwd echo \u0026#34;${your_user_name}:${your_password}\u0026#34; | chpasswd # Add to group 1000(HwHiAiUser) to use npu cat /etc/passwd | awk -F \u0026#34;:\u0026#34; \u0026#39;{print $4}\u0026#39; | grep 1000 if [ $? -ne 0 ] then groupadd -g 1000 HwHiAiUser useradd -u 1000 -g HwHiAiUser -ms /bin/bash HwHiAiUser fi usermod -a -G 1000 ${your_user_name} # For jumper if [ $(grep -c \u0026#34;HostkeyAlgorithms +ssh-rsa\u0026#34; /etc/ssh/sshd_config) -eq 0 ] then echo \u0026#34;HostkeyAlgorithms +ssh-rsa\u0026#34; \u0026gt;\u0026gt; /etc/ssh/sshd_config fi if [ ! -d /run/sshd ] then mkdir /run/sshd fi /usr/sbin/sshd -D chown -R sss:sss /home/sss 注意:脚本中的 your_user_name 和 your_password 为容器中的用户,请自行进行设置(该用户会被加入到 HwHiAiUser 用户组中,这样该用户才能使用 NPU 进行计算)。\n2.4 编写容器配置文件 # 创建 docker-compose.yaml 配置文件如下:\nservices: chattts: image: sss_base_image:1.0 container_name: sss volumes: # 保证 ~/bin/entrypoint.sh 文件的映射路径正确 - /data/disk3/sss:/home/sss # ----- 此处保持不变 ----- # - /usr/local/dcmi:/usr/local/dcmi - /usr/local/bin/npu-smi:/usr/local/bin/npu-smi - /usr/local/Ascend/driver/lib64:/usr/local/Ascend/driver/lib64 - /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info - /etc/ascend_install.info:/etc/ascend_install.info # ---------------------- # ports: # 映射22端口,方便 ssh 远程连接容器 - 8333:22 # 可添加更多端口映射 - 8008:8008 restart: unless-stopped hostname: ascend910b-02 tty: true devices: # 此处更改为可用的 NPU 卡号,可通过 npu-list 查询卡的占用状态 - /dev/davinci3 - /dev/davinci_manager - /dev/devmm_svm - /dev/hisi_hdc cap_add: - SYS_PTRACE shm_size: 20gb # command: /bin/bash -c \u0026#34;chown -R sss:sss /home/sss \u0026amp;\u0026amp; /bin/bash\u0026#34; 将配置文件中的以下变量替换为自己的:\nchattts:服务名称; image: sss_base_image:1.0:镜像名称:tag(注意:如果你的镜像 tag 不是 latest 的话,不能省略版本信息); container_name: sss:容器名称; - /data/disk3/sss:/home/sss:将自己的用户目录(/data/disk3/sss)挂载到容器中的用户目录(/data/disk3/sss)下; - 8333:22:将宿主机端口(自己设置,这里我随便设置的 8333)映射到容器中的端口(22,这是 ssh 服务的默认端口,方便后续使用 ssh 直接连接到自己的容器中); hostname: ascend910b-02:宿主机名称。 注意:这里的 docker-compose.yaml 中不能加 command 选项,因为该选项中的命令会覆盖 Dockerfile 中的 CMD 选项,导致 entrypoint.sh 脚本不会被执行(后果很严重!)。这里如果还想加一些在容器启动时需要执行的命令,可以直接加到 entrypoint.sh 脚本中,这样每次容器启动时都会执行这些命令。\n2.5 启动并进入容器 # 启动容器:\n# 临时启动(运行一次):docker-compose -p \u0026lt;project-name\u0026gt; up # 后台启动(一直运行):docker-compose -p \u0026lt;project-name\u0026gt; up -d docker-compose -p chattts up -d 进入容器:\n# docker exec -it \u0026lt;容器名或ID\u0026gt; /bin/bash docker exec -it sss /bin/bash # 退出容器:exit 其它容器常用命令:\n# 停止容器 docker stop \u0026lt;容器名或ID\u0026gt; # 重启停止的容器 docker restart \u0026lt;容器名或ID\u0026gt; # 保存容器为新的镜像 docker commit \u0026lt;容器名或ID\u0026gt; \u0026lt;镜像名\u0026gt; # 删除容器 docker rm \u0026lt;容器名或ID\u0026gt; 参考资料:使用 docker-compose 搭建 npu 环境的容器。\n三、安装 CANN 软件 # 3.1 确认环境 # 进入容器,检查当前环境是否满足以下要求:\n软件 要求版本 操作系统 openEuler20.03/22.03, Ubuntu 20.04/22.04 python 3.8, 3.9, 3.10 确认昇腾 AI 处理器已安装:\n# 安装 lspci sudo apt-get install pciutils # 查看是否有 Processing accelerators lspci | grep \u0026#39;Processing accelerators\u0026#39; 确认操作系统架构及版本:\nuname -m \u0026amp;\u0026amp; cat /etc/*release 确认 Python 版本:\npython --version 3.2 安装 miniconda # # 安装 miniconda wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh bash Miniconda3-latest-Linux-aarch64.sh # 启用 conda 环境(这里替换为自己的安装路径) eval \u0026#34;$(/home/sss/bin/miniconda/miniconda3/bin/conda shell.bash hook)\u0026#34; # 创建 conda 虚拟环境并激活 conda create -n cann python=3.10 conda env list conda activate cann # 查看 python 版本和已安装的 python 包 python --version conda list 参考资料:\n安装 miniconda aarch64 版本 conda 环境启用 \u0026amp; 基本使用 设置 miniconda 的 channel:\n# 设置为清华镜像源 conda config --add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ conda config --add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/ 参考资料:miniconda 设置 channel。\n安装 python 依赖:\nconda install -i https://pypi.tuna.tsinghua.edu.cn/simple attrs numpy decorator sympy cffi pyyaml pathlib2 psutil protobuf scipy requests absl-py wheel typing_extensions 3.3 安装 CANN-toolkit # wget \u0026#34;https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/Milan-ASL/Milan-ASL V100R001C19SPC802/Ascend-cann-toolkit_8.0.RC3.alpha001_linux-aarch64.run\u0026#34; sh Ascend-cann-toolkit_8.0.RC3.alpha001_linux-aarch64.run --install # 使用 sh 安装可能会报错,换 bash 试试 # bash Ascend-cann-toolkit_8.0.RC3.alpha001_linux-aarch64.run --install 注意:在容器中安装 CANN 软件时,为保证安装路径的正确,需要切换到自己的用户进行安装(该软件会安装到 ~/Ascend 目录下)。\n# 切换用户:su \u0026lt;用户名\u0026gt; su sss # 退出当前用户:exit 3.4 安装算子包 # wget \u0026#34;https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/Milan-ASL/Milan-ASL V100R001C19SPC802/Ascend-cann-kernels-910b_8.0.RC3.alpha001_linux.run\u0026#34; sh Ascend-cann-kernels-910b_8.0.RC3.alpha001_linux.run --install # 使用 sh 安装可能会报错,换 bash 试试 # bash Ascend-cann-kernels-910b_8.0.RC3.alpha001_linux.run --install 注意:这里同样也需要切换到自己的用户进行安装。\n3.5 设置环境变量 # echo \u0026#34;source ~/Ascend/ascend-toolkit/set_env.sh\u0026#34; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc 参考资料:快速安装昇腾环境。\n3.6 其它问题 # 个人用户缺少权限:\n问题现象:在容器中,从 root 用户切换为个人用户后,发现访问不了某些目录,显示 permission denied。\n执行(该命令已添加到容器启动脚本 entrypoint.sh 中):\nchown -R sss:sss /home/sss 个人用户缺少命令:\n问题现象:在容器中,从 root 用户切换为个人用户后,执行 ll,显示用户没有该命令。\n检查个人用户目录下是否缺少 .bash_lougout、.bashrc、.profile 文件,若没有,则将容器中 /etc/skel 目录下的这三个文件拷贝一份到自己的用户目录下并修改其权限。\ncp /etc/skel/.bashrc /home/sss/ cp /etc/skel/.bash_logout /home/sss/ cp /etc/skel/.profile /home/sss/ # 644 means writable, readable and executable for the user and readable for groups and others. chmod 644 .bashrc chmod 644 .bash_logout chmod 644 .profile chown sss:sss .bashrc chown sss:sss .bash_logout chown sss:sss .profile 参考资料:Bash on Ubuntu on Windows gives error \u0026ldquo;-bash: /home/user/.bashrc: Permission denied\u0026rdquo; on startup。\n安装 CANN 软件报错:\n问题现象:安装 CANN 软件报错,显示当前用户没有被添加到 HwHiAiUser 用户组中。\nUser is not belong to the dirver or firmware\u0026#39;s installed usergroup! Please add the user (sss) to the group (HwHiAiUser). 检查 entrypoint.sh 脚本是否有被成功执行,然后检查用户组 HwHiAiUser 有没有成功被创建。\n我自己在安装时报了这个错是因为 Dockerfile 中的 CMD 被后来在 docker-compose.yaml 中添加的 command 选项覆盖了,导致 entrypoint.sh 脚本未成功执行,用户 sss 未被添加到用户组 HwHiAiUser 中,因此无法使用 NPU。\n四、安装 PyTorch # 4.1 安装 torch # pip install torch==2.1.0 -i https://pypi.tuna.tsinghua.edu.cn/simple pip install pyyaml -i https://pypi.tuna.tsinghua.edu.cn/simple pip install setuptools -i https://pypi.tuna.tsinghua.edu.cn/simple 4.2 安装 torch-npu # 关于 torch-npu:\nThis repository develops the Ascend Extension for PyTorch named torch_npu to adapt Ascend NPU to PyTorch so that developers who use the PyTorch can obtain powerful compute capabilities of Ascend AI Processors. Ascend is a full-stack AI computing infrastructure for industry applications and services based on Huawei Ascend processors and software. For more information about Ascend, see Ascend Community.\nGitHub 仓库地址:Ascend Extension for PyTorch。\n安装 torch-npu:\npip install torch-npu==2.1.0.post6 -i https://pypi.tuna.tsinghua.edu.cn/simple 注意:torch-npu 的大版本(如:2.1.0)需要和 torch 匹配,具体的版本匹配信息请参考 Ascend Extension for PyTorch 中的 Ascend Auxiliary Software 部分。\n4.3 验证安装是否成功 # 创建 test.py 程序如下:\nimport torch import torch_npu x = torch.randn(2, 2).npu() y = torch.randn(2, 2).npu() z = x.mm(y) print(z) 运行程序 python test.py,若出现以下信息则说明安装成功:\ntensor([...], device=\u0026#39;npu:0\u0026#39;) 参考资料:Ascend Extension for PyTorch 配置与安装。\n五、开启容器 SSH 服务 # 5.1 安装并配置 openssh # # 安装 openssh sudo apt-get update sudo apt-get install openssh-server # 查看 SSH 是否启动(打印 sshd 则说明已成功启动) ps -e | grep ssh 修改 ssh 配置:\nPubkeyAuthentication yes #启用公钥私钥配对认证方式 AuthorizedKeysFile .ssh/authorized_keys #公钥文件路径(和上面生成的文件同) PermitRootLogin yes #root能使用ssh登录 ClientAliveInterval 60 #参数数值是秒 , 是指超时时间 ClientAliveCountMax 3 #设置允许超时的次数 UsePAM yes # 更改为 UsePAM no Port 80 #指定好端口号,默认是22 后面这个数字要在你run容器的时候用到 然后重启 SSH 服务:\nsystemctl restart sshd.service # 或: sudo /etc/init.d/ssh restart 参考资料:\nUbuntu 安装 SSH SERVER 使用 Docker 容器配置 ssh 服务,远程直接进入容器 5.2 配置 VSCode 客户端 # 在 VSCode 的远程资源管理器中点击设置,找到 xxx/.ssh/config 文件,添加以下配置:\n# 远程服务器名称,这里可以随意设置 Host sss-docker # 替换为自己的宿主机 IP HostName xxx.xxx.xxx.xxx # 替换为自己容器的 22 端口映射的宿主机端口,在 docker-compose.yaml 中设置的,我设置的是 8333:22 Port 8333 # 容器中的个人用户,使用个人用户密码登录容器,在 entrypoint.sh 脚本中设置的 User sss ForwardAgent yes # 每300秒向服务端主动发个包,防止一会儿不操作就和服务器断开连接 ServerAliveInterval 300 # 3次发包均无响应会断开连接 ServerAliveCountMax 3 六、配置 Git 并拉取代码 # 6.1 在容器中配置 Git # # 配置用户名和邮箱 git config --global user.name \u0026#34;shanshan shen\u0026#34; git config --global user.email xxx@gmail.com # 查看配置 git config --list 6.2 拉取代码 # git clone xxx.git 七、总结 # 到此为止,我们就可以在基于 EulerOS \u0026amp; Ascend NPU 的华为云远程服务器上,在自己搭建的 docker 容器中使用 PyTorch 框架并进行 AI 模型的训练与推理。\n","date":"2024-09-24","externalUrl":null,"permalink":"/articles/%E5%9F%BA%E4%BA%8E-euleros-ascend-npu-%E6%90%AD%E5%BB%BA-pytorch-%E8%BF%9C%E7%A8%8B%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83/","section":"Articles","summary":"","title":"基于 EulerOS \u0026 Ascend NPU 搭建 PyTorch 远程开发环境","type":"posts"},{"content":"","date":"2024-09-04","externalUrl":null,"permalink":"/tags/idea/","section":"Tags","summary":"","title":"IDEA","type":"tags"},{"content":"","date":"2024-09-04","externalUrl":null,"permalink":"/tags/java/","section":"Tags","summary":"","title":"Java","type":"tags"},{"content":"","date":"2024-09-04","externalUrl":null,"permalink":"/tags/maven/","section":"Tags","summary":"","title":"Maven","type":"tags"},{"content":" 一、拉取最新依赖 # 拉取最新的 dev 分支代码,然后点击下图中的圆圈,重新加载依赖项。\n二、强制更新依赖 # 运行以下命令,强制更新依赖项。\nmvn clean install -s settings.xml -U 三、清除 IDEA 缓存 # 文件 -\u0026gt; 清除缓存 -\u0026gt; 勾选所有项,点击【清除并重启】。\n四、检查 settings 文件 # 在 settings.xml 文件的 \u0026lt;localRepository\u0026gt; 标签中定义的路径为本地仓库路径。\n检查 IDEA 中设置的本地仓库路径是否与 settings.xml 文件中设置的一致。\n五、删除 repository 中的某些文件 # 若 Maven 报错如下(这是 Maven 的一个 bug):\n[ERROR] Malformed \\uxxxx encoding.\r[ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.\r[ERROR] Re-run Maven using the -X switch to enable full debug logging. 此时我们可以去项目的 repository 目录中搜索并删除所有的 resolver-status.properties 文件,然后再重新下载依赖。\n六、取消勾选脱机模式 # 当 Maven 报错信息如下:\nCannot access mcr-huawei-product-maven xxx in offline mode and the artifact xxx has not been downloaded from it before. 此时可以取消勾选 Maven 中的【脱机工作】选项,这个选项的意思是不读取远程仓库,只读取本地已有的仓库。\n上面报错的原因就是因为本地仓库缺少相应的依赖,还选择了脱机工作,导致下载不了相应的依赖。\n七、配置启动 VM 选项 # 报错信息如下,原因是缺少微服务配置中心相关的配置项:\njava.lang.IllegalStateException: Required key \u0026#39;configcenter_url\u0026#39; not found 此时可以检查项目启动的 VM 选项是否缺少相应的配置,补充相应的配置即可。\n","date":"2024-09-04","externalUrl":null,"permalink":"/articles/maven-%E9%A1%B9%E7%9B%AE%E7%BC%96%E8%AF%91%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/","section":"Articles","summary":"","title":"Maven 项目编译报错解决方法","type":"posts"},{"content":" 一、概述 # 昇腾 NPU 是专门用于 AI 训练/推理计算的 AI 专用处理器,其中的 AI Core 能够在很大程度上提高 AI 计算的效率。\n本文将主要介绍 ASCEND NPU 的硬件架构 \u0026amp; 工作原理、AI Core 的计算模式以及异构计算平台 CANN 等内容。\n二、NPU 硬件架构 # 2.1 NPU SOC 架构 # 2.1.1 Ascend 310 架构 # AI Core:计算核心,负责执行矩阵、向量、标量计算密集的算子任务,采用达芬奇架构; AI CPU:承担非矩阵类复杂计算,即负责执行不适合跑在 AI Core 上的算子; TS Core:作为任务调度器(Task Scheduler,TS),以实现计算任务在 AI Core 上的高效分配和调度(专门服务于 AI Core 和 AI CPU,不承担任何其它的工作); ARM CPU:控制芯片整体运行; DVPP:数字视觉预处理子系统,完成图像视频编解码; Cache \u0026amp; Buffer。 2.1.2 Ascend 910 架构 # AI Core:32 个,上下各 16 个,中间放 buffer,方便更快地取数据; Taishan Core:一部分为 AI CPU,承担部分 AI 计算,一部分为 Ctrl CPU,负责 SoC 控制功能,两类 CPU 占用的核数由软件进行分配; TS CPU:负责任务调度,把算子任务切分之后,通过硬件调度器(HWTS)分发给 AI Core 或 AI CPU 执行计算; Nimbus:提供 PCIe 接口和 Host CPU 对接;提供 NIC 用于跨服务器传递数据;集成一个 ARM CPU 核,执行启动、功耗控制等硬件管理任务; Cache \u0026amp; Buffer。 2.2 NPU 达芬奇架构 # NPU 的达芬奇架构中共包含 3 种类型的单元:\n计算单元:包含矩阵计算单元(DaVinci Core)、向量计算单元(Vector)、标量计算单元(Scalar); 存储系统:AI Core 片上存储单元和相应数据通路构成存储系统; 控制单元:计算过程提供指令控制,负责 AI Core 的运行。 2.2.1 计算单元 # Cube Core:每次执行可以完成 fp16 的矩阵乘,如 C = A(1616) * B(1616),更大的矩阵运算需要先对矩阵进行分块(在 L1 Buffer 中进行缓存); Vector Unit:算力低于 Cube,灵活度高(如数学中的求倒数、平方根等),Vector 所有计算的源数据和目标数据都会存储在 Unified Buffer 中(Unified Buffer 再与 L1 Buffer 进行交互),并按 32 Byte 对齐; Scalar Unit:负责各类型标量数据运算和程序流程控制,算力最低,功能上类比小核 CPU,完成整个程序循环控制、分支判断、Cube/Vector 等指令地址和参数计算以及基本算术运算等; Accumulator(累加器):把当前矩阵乘的结果与上一次计算的结果相加,可以用于完成卷积中增加 bias 等操作。 注意:AI Core 就是指 Cube Core,即矩阵计算单元。\n2.2.2 存储单元 # 存储控制单元(MTE):作为 AI Core 内部数据通路传输控制器,负责 AI Core 内部数据在不同缓冲区间的读写管理,以及完成一系列的格式转换操作; 缓冲区: 输入缓冲区(L1 Buffer):AI Core 采用了大容量片上缓冲区设计,通过增大片上缓存的数据量来减少数据从片外搬运到 AI Core 中的频次,从而降低数据搬运过程中所产生的功耗和时延,有效控制整体计算耗能和提升性能; 输出缓冲区(Unified Buffer):用来存放神经网络中每层计算的中间结果,从而在进入下一层时方便获取数据。 寄存器(SPR/GPR):寄存器资源主要是标量计算单元在使用。 上图中,HBM/DDR 和 L2 缓冲器都属于 AI Core 核外的数据存储系统。\n数据通路是数据在 AI Core 中的流通路径,它有以下特点:\n多进单出,可通过并行输入来提高数据流入的效率; 将多种输入数据处理完成后只生成输出特征矩阵,数据种类相对单一,单输出数据通路,可以节约芯片硬件资源。 2.2.3 控制单元 # 系统控制模块:控制“任务块”(AI Core 中最小的任务计算粒度)的执行进程,在任务块执行完成后,系统控制模块会进行中断处理和状态申报。 指令缓存; 标量指令处理队列; 指令发射模块:根据指令类型分别发送指令到对应的执行队列中; 事件同步模块。 2.3 AI Core 电路结构 # 对于运算 C = A(16*16) * B(16*16),矩阵 C 中的每一个元素都需要进行 16 次乘法与 15 次加法计算得到(一个矩阵计算子电路)。\n在 AI Core 中,共有 256 个矩阵计算子电路,每一条指令都可以并行完成 256 个矩阵 C 中的元素的计算。\n三、NPU 工作原理 # 3.1 NPU 并行计算架构 # 异步指令流:Scalar 计算单元读取指令序列,并把向量计算、矩阵计算、数据搬运指令发送给对应的指令队列,Vector 计算单元、Cube 计算单元、DMA 搬运单元异步地并行执行接收到的指令(“异步并行”:将串行的指令流分解); 同步信号流:指令间可能会存在依赖关系,为了保证不同指令队列间的指令按照正确的逻辑关系执行,Scalar 计算单元也会给对应单元下发同步指令; 计算数据流:DMA 搬入单元把数据搬运到 Local Memory,Vector/Cube 计算单元完成数据计算,并把计算结果回写到 Local Memory,DMA 搬出单元把处理好的数据搬运回 Global Memory。 3.2 AI Core 计算模式 # Cube 单元能够高效地执行 MAC(矩阵乘加)操作,目前支持的矩阵大小为 16*16*16。\n注意:通常矩阵乘中两矩阵很大,因此数据是分块(Tiling)后送入 Cube 单元的,每送完一块,结果存放到累加器,最后得到结果。\n在 CPU 的计算过程中,矩阵 A 按行扫描,矩阵 B 按列扫描。典型的存储方式是 A 和 B 都按照行方式进行存储(Row-Major),而内存读取按行读更方便,因此对 A 矩阵高效,对 B 矩阵低效。\n为了提高内存读取的效率,NPU 将矩阵 B 的存储方式转成按列存储(Column-Major),通过改变矩阵存储的方式来提升矩阵计算的效率。\nCube Core 一条指令完成两个 16*16 矩阵的 MAC,相当于一个时钟周期进行 163 = 4096 个 MAC 运算。执行前将 A 按行、将 B 按列存放在 Input Buffer,并通过 Cube Core 计算得到 C,按行存放在 Output Buffer。\n注意:矩阵 A 不需要转换,可以直接从 L1 Buffer 读取到 AI Core 里面;矩阵 B 需要预先读到 MTE 中进行转换),然后再读取到 AI Core 里进行计算。\n矩阵的预处理:\n分块(Tiling):因为片上缓存容量有限,因此将整个矩阵 B 划分为多个子矩阵,并依次搬运到缓存中,最后得到结果矩阵 C; 填充(Padding):A 和 B 都等分成同样大小的块(16*16),排不满的地方可以通过补 0 实现。 四、CANN 平台 # 4.1 整体架构 # 异构计算架构 CANN(Compute Architecture for Neural Networks)是华为针对 AI 场景推出的异构计算架构,向上支持多种 AI 框架,包括 MindSpore、PyTorch、TensorFlow 等,向下服务 AI 处理器与编程,发挥承上启下的关键作用,是提升昇腾 AI 处理器计算效率的关键平台。同时针对多样化应用场景,提供多层次编程接口,支持用户快速构建基于昇腾平台的AI应用和业务。\n计算语言开发接口: 应用开发接口:提供深度学习推理计算、图像预处理、单算子加速计算能力; 图开发接口:提供统一网络构图接口; 算子开发接口:Ascend C 是 CANN 在算子开发场景的编程语言,原生支持 C\u0026amp;C++ 标准规范,最大化匹配用户开发习惯。 计算服务层: 昇腾算子库 AOL; 昇腾调优引擎 AOE:用于在推理、训练等场景对模型、算子、子图等进行调优,充分利用硬件资源,提升网络性能。 计算编译层: 图转换:各种模型编译器; 图编译:图准备、图优化、图拆分、图编译; Tensor Boost Engine(TBE):算子融合、算子编译。 计算执行层:类似于 CUDA 的 runtime,提供 stream 管理、context 管理等功能; 基础服务层:资源管理、通信管理、设备管理、芯片 IP 驱动、公共服务。 CANN 官方社区:CANN 社区版 8.0.RC3.alpha002 开发文档。\n4.2 Ascend C 算子编程 # Ascend C 算子编程使用 C++ 语言和 Ascend C 类库 API 进行开发。\nAscend C 类库 API:\n基本 API:计算 API、搬运 API、同步 API、……; 高阶 API:Matmul API、Conv API、Softmax API、……。 Ascend C 算子编程采用 SPMD(单程序多数据编程)模式,即一个核的算子程序被调用时,会启动 N 个实例,每个运行实例称为一个 block,它们都执行相同的代码,有相同的参数,但参与计算的数据不同。\n注意:block 类似于线程(进程),而 block_idx 类似于 thread_id,算子程序需要使用 block_idx 来进行数据并行计算切分。\n4.3 推理应用开发 # AMCT 模型压缩:支持量化、通道稀疏、张量分解,降低模型的数据量和计算量,提升计算性能; 量化:将模型的权重和数据从浮点型转换为整型(比如:低精度 int8),从而生成更加轻量化的网络模型,减少数据和计算量; 张量分解:分解卷积 Tensor,将一个大卷积转化成两个级联的小卷积,从而降低存储空间和计算量(张量分解处理后,需要重训练以保证模型精度); 稀疏:通过结构剪枝,对模型中的部分算子裁剪部分权重和参数,从而得到参数量和计算量更小的网络模型(稀疏处理后,需要重训练以保证模型精度)。 ATC 模型转换工具:将开源框架的网络模型转换为适配昇腾 AI NPU 的 om 模型; AOE 智能调优工具:支持算子计算过程的自动寻优,提升整体网络的性能。 ","date":"2024-08-31","externalUrl":null,"permalink":"/articles/ascend-npu-%E6%9E%B6%E6%9E%84-cann-%E5%B9%B3%E5%8F%B0%E5%85%A5%E9%97%A8%E5%AD%A6%E4%B9%A0/","section":"Articles","summary":"","title":"Ascend NPU 架构 \u0026 CANN 平台入门学习","type":"posts"},{"content":"","date":"2024-08-26","externalUrl":null,"permalink":"/tags/cuda/","section":"Tags","summary":"","title":"CUDA","type":"tags"},{"content":"","date":"2024-08-26","externalUrl":null,"permalink":"/tags/gpu/","section":"Tags","summary":"","title":"GPU","type":"tags"},{"content":"","date":"2024-08-26","externalUrl":null,"permalink":"/tags/nvidia/","section":"Tags","summary":"","title":"NVIDIA","type":"tags"},{"content":" 一、概述 # 随着大模型产业的发展,AI 训练 \u0026amp; 推理对算力的需求越来越大,AI 的计算也越来越离不开 GPU 的支持。\n目前,用于 AI 计算的芯片可以分为:\nCPU(通用处理器); GPU(通用图形处理器); NPU / TPU(AI 专用处理器)。 那么 CPU 和 GPU 有什么区别呢?\n从硬件设计上来看,GPU 的 DRAM 时延(数据搬运、指令执行的延迟)远高于 CPU,但 GPU 的线程数远高于 CPU(有非常多的线程,为大量大规模任务并行而去设计的)。\n关注重点:\nCPU:降低延迟、并发(Concurrency,能够处理多个任务的功能,但不一定是同时); GPU:最大化吞吐量、并行度(Parallelism,同时可以执行多少任务)。 总结:\nCPU:希望在一个线程里完成所有的工作(串行,优化线程的执行速率和效率); GPU:利用多线程对循环进行展开,来提高硬件整体的利用率(并行,用足够多的线程去解决延迟的问题)。 参考资料:AI System (chenzomi12.github.io)。\n二、GPU 硬件架构 # 2.1 发展历史 # Fermi 架构:提出了首个完整的 GPU 计算架构; Kepler 架构; Maxwell 架构; Pascal 架构:提出了 NVLink; Volta 架构:将 CUDA Core 进行了拆分,分离了 FPU 和 ALU;独立线程调度:每个线程都有独立的 PC(Program Counter)和 Stack;提出了 Tensor Core:针对深度学习提供张量计算核心,专门针对卷积运算进行加速; Turing 架构:提出了 RT Core(Ray Tracing Core),用于三角形与光线的求交; Ampere 架构:提出 NVSwitch,单卡之间通过 NVLink 互联,多卡之间通过 NVSwitch 互联; Hopper 架构。 Hopper 架构的 GPU 如下:\n2.2 硬件单元 # GPU 几乎主要由计算单元 ALU 组成,仅有少量的控制单元和存储单元,因此具有强大的计算能力。\n上图中,左边为 CPU,右边为 GPU。\n在 GPU 的硬件架构中,包含以下单元:\nGPC:Graph Possessed Cluster,图像处理簇; TPC:Texture Possessed Cluster,纹理处理簇; SM:Stream Multiprocessors,流式多处理器; HBM:High Band Memory,高带宽处理器。 一个 GPC 包含多个 TPC,一个 TPC 包含多个 SM,一个 SM 包含多个 Block、Thread 以及各种 CUDA Tensor Core。\n2.3 SM # SM(流式多处理器)的核心组件包括:CUDA 核心、共享内存、寄存器等,它包含许多为线程执行数学运算的 Core,是 NVIDIA 的核心,每一个 SM 都可以并发地执行数百个线程。\n具体地,SM 包括以下单元:\nCUDA Core:向量运行单元(FP32-FPU、FP64-DPU、INT32-ALU); Tensor Core:张量运算单元(FP16、BF16、INT8、INT4,专门针对 AI 的矩阵计算); Special Function Units:特殊函数单元,SFU,超越函数和数学函数; warp Scheduler:线程束调度器; Dispatch Unit:指令分发单元; Multi Level Cache:多级缓存(L0/L1 Instruction Cache、L1 Data Cache \u0026amp; Shared Memory); Register File:寄存器堆; Load/Store:访问存储单元(LD/ST,负责处理数据)。 后来,CUDA Core 演变为了单独的 FP32、FPU、INT32-ALU。\n2.4 Tensor Core # Tensor Core 可以支持混合精度运算。混合精度是指在底层硬件算子(Tensor Core)层面,使用半精度(FP16)作为输入和输出,使用全精度(FP32)进行中间结果计算从而不损失过多精度的技术。\n每个 Tensor Core 每周期能执行 4*4*4 GEMM,即 64 个 FMA。\nTensor Core 可以通过 Warp 把多个线程聚合起来一起进行计算和控制。最终对外提供一个 16*16*16 的 API 给到 CUDA。\n2.5 NVLink # NVLink 让单台服务器里面的 GPU 可以进行数据的互联,是用于 CPU 和 GPU 之间进行通信的 PCIe 的带宽的 3 倍,避免了数据通过 PCIe 回传到 CPU 的内存里面,从而减少了数据传输的延迟,实现了整个网络的拓扑互联。\n2.6 NVSwitch # 单卡之间通过 NVLink 互联,多卡之间通过 NVSwitch 互联。\n三、GPU 工作原理 # 3.1 缓存机制 # GPU 的多级缓存\nHBM 的作用是什么?\nHBM(High Bandwidth Memory,高带宽内存):显存,GPU 里的主内存。\n越靠近 SM 的缓存,数据搬运的延迟越低,因此把数据存在离 SM 越近的地方,将更有利于提高计算的效率。在实际计算中,我们总是希望尽快将当前缓存里的数据计算完并将结果返回,然后去换下一批数据上来。如果 GPU 里没有自己的 HBM,那么每一次计算都需要去 CPU 里读取数据(通过 PCIe),延迟太高。\n因此,并不是算力越高,计算的效率就越高,还需考虑数据传输的效率(带宽)等因素,这里引入“计算强度”的概念。\n什么是“计算强度”?\n假设你往 L1 cache 里面传了一个字节,这个字节参与了 8 个时钟周期的运算,则计算强度就是 8。每种算法都有一个对应的计算强度,这个值越低,就越受制于内存带宽(需要更频繁地搬运、刷新数据)。\n因此,只有线程数足够多,才能让整个系统的内存处于忙碌的状态。\n下面对各级内存的性能进行了对比:\nData Location Bandwitch (GB/sec) Compute Intensity Latency (ns) Threads Required L1 Cache 19400 8 27 32738 L2 Cache 4000 39 150 37500 HBM 1555 100 404 39264 NVLink 300 520 700 13125 PCIe 25 6240 1470 2297 从上图中可以看到,计算强度越低的地方,需要的线程数和带宽就越高。\n计算强度与矩阵大小的关系如下:\n随着参与计算的矩阵不断变大,GPU 里的内存就会逐渐空闲下来(不是指内存的容量降低,而是指内存的搬运变慢),因为此时 GPU 的计算单元需要花费更多的时间去对矩阵进行运算。\n因此,AI 的计算需要找到一个平衡点(计算与带宽),以匹配矩阵计算的强度。不管算得有多快,如果内存来不及搬运,那么超额的算力也是没用的。\nGPU 的解决方法:\n通过超配的线程来掩盖时延; 通过多级缓存来平衡计算和带宽的 Gap; 通过 Tensor Core 来增加峰值算力(Tensor Core:专门用于矩阵计算,以提高计算的强度,可以提升单芯片浮点运算的能力)。 3.2 线程机制 # GPU 里提供了大量的线程,超配的线程数可以支持对不同层级的数据进行搬运和计算。\n在一个 SM 中包含了大量的 warp(线程束),每 4 个 warp 可以并发地执行。\n什么是 warp?\n从逻辑上看,所有的线程是并行的;但从硬件的角度看,并不是所有的线程都能在同一时刻去执行。因此,GPU 使用 warp 来对线程进行批量管理。\nwarp 是 SM 的基本执行单元,一个 warp 包含 32 个并行的 Thread,这 32 个 Thread 执行于 SIMT 模式(所有 Thread 以锁同步的方式执行同一条指令,但每个 Thread 会使用各自的数据执行指令分支)。\nGPU 的线程分级\n在 AI 计算中,不是所有的计算都可以支持线程独立运算,有些计算模式的数据之间互相依赖,线程之间需要进行配合。——线程分层执行。\nGPU 中的线程包含以下 3 个层次:\nGrid(网格)表示所有要执行的任务; 一个 Grid 中包含了很多具有相同 Thread(线程)数量的 Block(块); 一个 Block 中的多个 Thread 之间独立执行,可以通过本地数据共享 Local Data Share 同步交换数据。 这样,在同一个 Block 中,可以同时执行大量相关的操作,并且超配的 Block 数也可以弥补数据处理的延迟。\n四、CUDA 平台 # 4.1 基本概念 # CUDA(Compute Unified Device Architecture)具有以下两层意义:\n是一种并行计算架构(Parallel Computing Architecture):用于控制 GPU 里各种并行的硬件; 是一种编程模型(Programming Model):基于 LLVM 构建了 CUDA 编译器,方便开发者使用 C/C++ 和 Python 进行开发。 CUDA 实现了软硬件的解耦。\n4.2 程序架构 # 主设概念:主机程序(Host)和设备程序(Device)之间可以进行通信(数据拷贝)。Device 通过 GPU 进行并行操作,计算完成后将结果传递给 CPU 进行处理。\nHost 部分的代码在 CPU 上执行(.c/cpp 文件); Device 部分的代码在 GPU 上执行(.cu 文件),当遇到需要并行处理的部分,CUDA 就会将程序编译成 GPU 能执行的程序,并传送到 GPU,这个部分叫做 kernel。 示例代码(cuda_device.cu):\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;math.h\u0026gt; // __global__是变量声明符,作用是将add()函数变成可以在GPU上运行的函数(kernel) __global__ void add(int n, float *x, float *y) { for(int i = 0; i \u0026lt; n; i++) { y[i] = x[i] + y[i]; } } int main(void) { int N = 1\u0026lt;\u0026lt;25; float *x, *y; // 在cuda里开辟内存 cudaMallocManaged(\u0026amp;x, N*sizeof(float)); cudaMallocManaged(\u0026amp;y, N*sizeof(float)); // initialize x and y arrays on the host for (int i = 0; i \u0026lt; N; i++) { x[i] = 1.0f; y[i] = 2.0f; } // 在GPU上执行kernel函数 add\u0026lt;\u0026lt;\u0026lt;1, 1\u0026gt;\u0026gt;\u0026gt;(N, x, y); // CPU需要等待cuda上的代码运行完毕,才能对数据进行读取 cudaDeviceSynchronize(); // 释放cuda内存 cudaFree(x); cudaFree(y); return 0; } 线程分级:\n执行一个 kernel 时,所有的线程都会封装在一个 Grid 里面。同一个 Grid 里面的线程可以共享全局内存(Global Memory),即 __global__ 里的数据都是共享的。\nBlock 间并行执行,并且无法通信,也没有执行顺序。每个 Block 中有一个共享内存(Shared Memory),同一个 Block 里的 Thread 是可以同步的,通过共享内存进行通讯和数据之间的传输。\nCUDA 并行程序,会被多个 Thread 同时执行。\nCUDA 与 GPU 硬件的对应关系:\n一个 Block 线程块只在一个 SM 上通过 warp 进行调度; 一旦在 SM 上调起了 Block 线程块,就会一直保留到执行完 kernel; 一个 SM 可以同时保存多个 Block 线程块,块间并行地执行。 五、算力计算 # ","date":"2024-08-26","externalUrl":null,"permalink":"/articles/nvidia-gpu-%E6%9E%B6%E6%9E%84-cuda-%E5%B9%B3%E5%8F%B0%E5%85%A5%E9%97%A8%E5%AD%A6%E4%B9%A0/","section":"Articles","summary":"","title":"NVIDIA GPU 架构 \u0026 CUDA 平台入门学习","type":"posts"},{"content":" 一、关于本书 # 《我有自己的宇宙》 心理学 作者:钱婧 简介:本书的作者钱婧老师是一位北师大的职场心理学教授,通过与广大年轻人进行交流沟通,她用自己多年的知识和经验为读者解答了许多关于职场、学业、恋爱、赚钱、社交以及家庭等诸多方面的疑惑。本书提出了一种叫做“中庸我”的思维模式,能够让我们在职场的风风雨雨中,甚至在更漫长的人生路上,坚守自己的一点本心,能够沉下心来朝着自己的目标踏实地做点实事。本书让我获益匪浅,也坚定了我在职场中的行事原则,让我摆脱了焦虑和内耗,变得更加自信和从容。\r二、内容分享 # 2.1 与“梦幻我”进行思维谈判 # 在本书中,与“中庸我”相反的思维模式叫做“梦幻我”,那什么是“中庸我”?什么是“梦幻我”?\n“中庸我”:相信在以自我为中心和顺从他人或环境这两极之间有一个平衡态的存在,而一切平衡的基础在于认识、认领和践行“核心我”; “梦幻我”:认为顺从他人或环境会导致失去自我,拥有自我意味着不能顺从。 在职场上,我们常常会被领导安排去做一些自己不想做的“杂活”或“脏活”,面对这种情况:\n“中庸我”思维会认为:如果一时的忍受和顺从是为了一个更内核的自我诉求和目标,比如升职加薪,那么耐着性子去把这些小事做好,这也是符合自己的价值实现的; “梦幻我”思维会认为:干这些杂活,自己有点儿委屈,想敷衍了事或者干脆推掉,但是心里又害怕得罪领导和同事,在行为和态度上扭扭捏捏,心不甘情不愿。这会让其它和自己交互的人觉得我们这个人配合度很低,还没有什么想法。在这种恶性循环中,自己的精神和能量也逐渐被侵蚀,导致每天上班都愤愤不平、郁郁寡欢。 以“中庸我”思维来看待职场中的糟心事,能够让我们少抱怨、多做事,以更加平和的心态面对自己当下的处境。当然,迎合领导的期待去做事,是在不撼动我们的底层内核为前提的,毕竟如果工作内容本身与我们的核心自我背道而驰,人就会变得撕裂扭曲,人根本快乐不起来,对工作没有激情,又何谈成长与发展呢?\n以我自己为例,在我刚参加工作的第一年间,便是常常被领导安排去做一些所谓的“杂活”,自己也常常陷入到内耗和自我怀疑之中。领导常说诸如“做这些活是有利于你的能力提升的”、“之后本来还打算给你们升职加薪的”。然而,面对投入了大量的时间和精力,自己的能力却没有什么大的变化,幻想中的回报也并没有兑现的现实,“自我感动”的谎言便被打碎了。\n这份经历也让我开始意识到,在职场上一味地听从领导的安排、没有自己的主见是不行的。我们应该对自己的目标有清晰的认知,对自己今后的发展有合理的规划,然后只需要围绕这一条主线去努力奋斗就好了,构建自己的核心竞争力才是第一目标。面对任何偏离自己主航道的安排和任务,有时候该佛系的就佛系,该摸鱼的就摸鱼得了,不过分纠结于别人对自己的评价,做好自己分内的事,拥有“被讨厌的勇气”也不失为一种职场中的生存智慧。\n“在职场这种混沌的环境里,冗思是最没用的行为,对他人的判断,也是“论迹不论心”比较好。别人的心思是猜不得也猜不全的。既然上班是为了获得发展,那么我们大可以放弃在职场中得到的情绪价值这种内分泌混乱的消费品。”\n总结一下,就是我们的行为逻辑要“自洽”,不要活得“拧巴”,不要“既要又要还要”。想清楚自己到底想要什么?(是舔好领导然后升职加薪?还是追求 work life balance?)哪些能力是值得花时间提升的?哪些事情是需要做好的?其它的事情就“let it go”吧。\n这也正如书中所说:\n“抓大放小。在大事上卷,在小事上躺。”\n而要做到这一点,首先就需要我们能够清楚地辨识并认领我们的“核心我”。\n2.2 辨识并认领自己的“核心我” # “中庸我”的主体叫做“核心我”,它是我们自我的起点,是人生的“基本盘”。\n“核心我”包括以下两个方面:\n“基底内核”:即我们当前暂时无法改变的客观条件,如:年龄、身体素质、性格、学历以及专业等; “精神内核”:即我们的思维模式,如:人生观、金钱观以及工作观等。 面对这些“既成事实”,我们应该要不卑不亢地认清并认领自己的“核心我”,比如当下我在自己的专业领域内到底是什么水平?我的核心诉求、我追求的生活方式是什么?\n“中庸我”思维能够接受自己的“核心我”,并在当下的现状上清醒地寻找所有可能的机会,努力地改变自己; “梦幻我”思维则很容易轻视自己,认为自己被过往所拖累,拧巴纠结,放不下过去,大把的时间和精力消耗在自怜自艾、怨天尤人上了,那成长的空间自然就小了。 当然,认清自己的“核心我”是需要时间的,是需要一定的经历过后才能发现的。\n“有一些人,在经历过一番折腾之后,会发现自己当时想要的,现在已经不想要了。不是吃不了苦意义上的不想要,而是发现真的不适合自己,并且自己也确实没那么喜欢。”\n看清了自己的“核心我”之后,则是需要积极地去践行它。在努力的路上,我们很多时候是孤独的、被否定的,这时候就需要耐得住性子,放弃短暂的享乐,聚焦长线的筹谋,持续提升自己的弹性能力。\n“在一无所有的时候,有一份不怠惰的耐心,这种能力将成为一件威力很大的武器。”\n2.3 围绕“核心我”提升弹性能力 # 围绕“核心我”提升弹性能力,就是指提升我们的“弹性我”,即构建我们自己的个人价值。\n“工作多年的职场人,在别人眼中所看中的,是掌握了什么跑赢工龄的专业技能,是能带来什么样的资源和人际关系,是综合实力和氛围感,代表的是一个立体的维度。”\n在“弹性我”中,需要我们不断去提升的能力包括:\n时间管理能力:聚焦主线,舍弃不重要的工作,用“核心我”去度量,做好平衡,杂中有序,忙而不乱; 积极注意力偏好:成长型思维,关注成长和发展,而不是负面和缺陷; 信息接收和处理能力:对外界信息保持敏感,主动寻求反馈,学会对信息进行提炼和取舍; 表达能力:主动提高沟通的频率可以帮助我们在职场中拿到属于自己的主动权,获得领导的信任,交流和汇报不要无脑抛问题,要带上自己的思考和方案; 领导能力:分好“蛋糕”,不空讲愿景和未来,敢于担责,建立信任,抓大放小,眼里容得下沙子,高效开会,保持坦诚; 展示能力:做好“印象管理”,经营好自己的人设,向上管理。 “当我们感觉目标离自己还比较遥远时,弹性就是解药——它可以让我们看得见自己的变化,了解自己的增量,体会到所谓的日子有功。”\n2.4 修补“隐性我”的创伤,或带伤前行 # “隐性我”指的是我们在成长的过程中,所遭遇过的一些伤痛(比如原生家庭造成的影响),从而让我们在今后的生活中变得更加敏感、自卑;以及自己过往的一些(可能不太正确的)价值和思维模式,从而在某些方面限制我们的发展。\n“能超脱介质成长的人是极少的,小时候依托于原生家庭,长大了依托工作。”\n“中庸我”思维会选择和“隐性我”共存,或者通过行为去影响这些底层认知。“中庸我”不会过度地去让“核心我”和“隐性我”纠缠,产生不必要的内耗; “梦幻我”思维则会把“核心我”和“隐性我”搅和到一起。当遇到困境的时候,“梦幻我”会让你怨念自己的过去和“核心我”,觉得自己就是这样,让自己憋得无处可发、无能为力。 我们的过去并不能决定我们的未来,人生是一个不断向前、无法后退的过程,只有一直朝前看,去积极探索人生的各种可能性,我们的生活才可能变得更加精彩。\n另外,本书中关于职场“PUA”的部分,让我对人们口中常说的“PUA”一词有了更加清晰的认识,比如并不是所有的批评都是 PUA。有的领导可能比较心直口快,但就事论事,目的是能让下属直观地认识到自己的问题并改进,这不是 PUA。那么什么是 PUA 呢?不同于一般的批评和指责,PUA 中的否定和贬低是不分缘由的,或者不成比例的,也就是俗话说的“对人不对事”。其目的是让下属产生“资格感缺失”,感觉到自己不配,最后形成隐性的自卑。\n“心理学中的“煤气灯效应”就是指通过施加情感虐待和控制,让受害者逐渐丧失自尊,产生自我怀疑,人格瓦解,无处可逃,最后人生崩塌。这其实就是我们现在讨论的比较多的“PUA”,即“Pick-up Artist”的缩写。”\n那么我们应该如何避免被 PUA 呢?书中给出了一些建议:\n明辨真相,客观看待事实; 建立并认领自己的“核心我”,不盲目地屈服; 不靠近、甚至远离令自己感到不适的职场环境。 2.5 应对“混沌我”的挑战,抬头向前走 # “我们人类首先存在于环境之中,我们不能脱离环境,环境塑造了我们,决定了我们的可能性。”\n“混沌我”指的就是面对外部大环境的变换,我们要如何凝聚自我,并坚定地走好自己的路。\n作为一名身处这个局势风云变幻的大环境中的从业者,我更加清晰地认识到,自己能够有当下的境遇首先是“时代+平台+运气”,最后才是个人能力所堆彻而成的结果,毕竟,这个时代从来不缺“卷王”。认识到这一点后,我明白我要更加努力地工作,珍惜自己现在的岗位,但同时我也知道,个人的努力在这个社会的大势面前是微不足道的。因此,我们要努力,但无需过度努力(这里的“过度努力”指的是过度加班从而伤及自己的身体健康、甚至心灵能量,个人认为有点类似于修仙小说中的“为了快速突破境界,然后以自己的寿元和根基为代价服用各种药物,或修炼邪功,从而获得不属于自己的实力”),踏踏实实地过好每一天,做好长远规划,有风险意识,持续学习和提升自己即可,每一片落叶自会有它的去处。\n另外,关于如何避免“内卷”?如何正确地去“卷”?本书中的一些建议给了我很大的启发:\n“少数事卷,多数事躺”:抓大放小,要在大事上卷,做好真正重要的事,至于其它的一些小事,有时候就躺一躺也无妨,不要把自己逼得太紧; “长期的事卷,短期的事躺”:聚焦长线目标,比如专业能力的提升、职场技能的锻炼,越是需要我们长期去做的事就越重要,越值得我们坚持,而至于一些临时的“支线任务”,摸摸鱼也是可以的; “行动上卷,情绪上躺”:不要“过度反思”,从而陷入情绪内耗的漩涡,可以让自己先行动起来,有些时候事情做着做着可能就“柳暗花明又一村”了。 “正确的卷法是去好好发展,快乐地卷,而不是当一个偏执的神经病。”\n三、心得体会 # 作为一名从校园进入职场刚满一年的小白,面对无尽的加班、干不完的杂活以及领导的否定,自己也常常陷入到迷茫和痛苦之中。如何在职场中活出自我,同时更好地平衡工作与生活?如何认清自己的目标,以更加恰当的方式去付出和努力?这些问题一直困扰着我。\n最近,我看了钱婧老师写的这本书,看完过后,我的不少疑惑得到了解答,内心的焦虑也在很大程度上得到了缓解。本书中提到的职场生存策略让我获益匪浅,在职场中我开始变得不再内耗和拧巴,对任何人和事都有了自己清晰的判断,也开始能够静下心来做好自己的事情,持续提升自己的技能,构建自己的竞争力。“在一无所有的时候,有一份不怠惰的耐心,这种能力将成为一件威力很大的武器”,这句话给了我很大的鼓舞,也是我在职场中砥砺前行、坚守自我的一份信仰。\n","date":"2024-08-10","externalUrl":null,"permalink":"/articles/%E6%88%91%E6%9C%89%E8%87%AA%E5%B7%B1%E7%9A%84%E5%AE%87%E5%AE%99%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E5%B9%B4%E8%BD%BB%E4%BA%BA%E7%9A%84%E8%81%8C%E5%9C%BA%E7%94%9F%E5%AD%98%E4%B9%8B%E9%81%93/","section":"Articles","summary":"","title":"《我有自己的宇宙》读书笔记 | 年轻人的职场生存之道","type":"posts"},{"content":"","date":"2024-08-10","externalUrl":null,"permalink":"/tags/%E8%81%8C%E5%9C%BA%E4%B9%8B%E9%81%93/","section":"Tags","summary":"","title":"职场之道","type":"tags"},{"content":" 一、关于本书 # 《睡眠革命》 个人健康 作者:尼克·利特尔黑尔斯 简介:本书的作者是英超曼联的运动睡眠教练,专门负责帮助运动员解决睡眠难题、改善睡眠质量。他将自己多年总结的方法论在书中进行了讲解,让人们能够正确认识人体的昼夜戒律并合理安排自己的睡眠周期。通过书中的方法,我们就能够彻底改善自己的睡眠质量,实现最大限度的身心修复,提高生活效率,让你自信且快乐地度过每一天。\r二、内容分享 # 2.1 什么是昼夜节律? # 在现代社会,由于学业、工作以及生存的压力,许多人长期压榨自己的睡眠时间,熬夜、作息不规律,这种违背人类昼夜节律的生活方式,正在逐渐摧毁现代人的健康。\n什么是昼夜节律?\n“如果作息时间非常“规律”,能在早晨按时起床,那么到了晚上,睡眠需求就会达到峰值。这完全符合昼夜节律,为我们提供了最佳入睡时机。我们会在凌晨 2~3 点进入一个高效的睡眠阶段(与此相对应,12 小时后又会出现一个睡意蒙眬的时段,它会以午后倦怠的形式出现),并且不久之后,在太阳升起、新的一天开始之前,我们的体温也会降到最低点。这时,就像按下了一个开关一样,人体会停止分泌褪黑素,因为我们将从黑暗慢慢进入光明。随着日光渐强,我们体内开始分泌血清素,一种刺激情绪的神经递质,它将和褪黑素此消彼长。”\n昼夜节律是大自然的法则,如果强行挑战规律,就会为自己的身体健康埋下诸多隐患。\n那么,我们应该如何掌控自己的睡眠,从而更好地适应昼夜节律呢?——了解自己的睡眠周期。\n2.2 什么是睡眠周期? # 在本书中,作者提出了一种 R90 方案,用于管理自己的睡眠周期:\n“R90 方案:R90 指的是以 90 分钟为一个周期,获得身体修复。从临床上说,90 分钟是一个人经历各个睡眠阶段所需的时间,这些睡眠阶段组成了一个睡眠周期。”\n在我们的睡眠周期中,根据睡眠质量可以分为:\n深睡眠 浅睡眠 快速眼动睡眠 “睡眠的生理修复功效大多产生于深睡眠阶段,比如生长激素分泌量的增加。人体生长激素是一种能促进新细胞生长和组织的修复、让人体能在日常劳作后获得休整、让人恢复生机与活力的关键成分,我们都离不开它。”\n因此,如果一直被困在浅睡眠中,那么我们将无法充分享受睡眠的种种益处。\n另外,每晚的各个睡眠周期是互不相同的:\n在较早的睡眠周期中,深睡眠所占的比重更大,因为此时身体希望我们尽快进入深睡眠状态; 而在较迟的睡眠周期中,快速眼动睡眠占有更大比重。 在理想状态下,我们应该以“睡眠—\u0026gt;醒来—\u0026gt;睡眠—\u0026gt;醒来”的模式,顺利地从一个睡眠周期过渡到下一个睡眠周期,并随着时间的流逝,逐渐减少深睡眠、增加快速眼动睡眠,直到清晨醒来。\n那么,我们应该如何管理自己的睡眠周期,并提高睡眠修复的质量呢?\n2.3 如何管理睡眠周期? # 2.3.1 设定固定的起床时间 # 在本书中,作者分享了一些制定最佳起床时间的建议:\n这个起床时间应该是每天都能实现的,并且除了某些特殊情况(比如要赶早晨的航班)之外,在你的日常生活中,并没有任何事情需要你起得比这个时间更早; 周末也要遵循这一起床时间,因此不要选择不太现实的时间,然后幻想你可以在周末大睡懒觉; 在理想状态下,你的固定起床时间,应该比你必须上班、上学或做其它事的时间早至少 90 分钟,这样你才有充分的准备时间。 下面分享一下我个人实践后的一些心得:\n要想养成一个固定的睡眠习惯,并在每天起床时都精神满满、自觉醒来,那么就需要计算自己的睡眠周期。一般我们需要睡 5 个睡眠周期(7.5 小时),但是之前自己并没有考虑到入睡也需要一个过程,不应该直接将上床关灯的时间作为计算睡眠周期的开始。我曾尝试过在 12 点准时关灯睡觉,早上 7 点半(我 9 点上班,起床时间应比上班时间提前 90 分钟)闹钟响起时,大脑仍然很困。因此,若想要每天在 7 点半自然醒来,并在早晨拥有更多的锻炼和学习时间,则需要提前 7.5 + 0.5 (缓冲时间) = 8 小时(一般是晚上 11 点半到 12 点之间)就关灯睡觉,因为你并不能马上入睡。另外,要想形成习惯,就应该每天坚持这个作息规律,包括周末。任何打破该作息的睡眠行为都会破坏早起习惯的养成,因此我不会在周末睡懒觉。\n2.3.2 从整体把控睡眠情况 # 许多人都会面临的一个问题就是,担心自己睡不好。不困就上床或是还没有准备好就上床,只会引发更多问题。而在夜间睡到一半时,因为睡不着了而压力重重、忧心忡忡,这也并不能让自己快速睡着。一旦开始担心这个问题,身体就会释放出肾上腺素和皮质醇等压力激素,反而更加清醒。\n面对这个问题,我们不妨以“周”为维度来统计我们的睡眠周期,而不是每晚睡了几小时。一个晚上并不会决定一切。所以,如果我们每晚需要 5 个睡眠周期,那完全可以将每周获得 35 个睡眠周期,设为自己的目标。\n“特殊情况(比如备战考试、熬夜加班)时可以不必强求非要睡满 5 个周期,但每周要争取获得至少 4 个睡眠充足的晚上。”\n2.3.3 注意睡眠前后的行为 # “睡觉前的行为会直接影响睡眠质量和持续时间,而醒来后的行为会对新的一天产生重大影响。在理想状态下,你需要 90 分钟的睡眠前适应时间和同等时长的睡眠后适应时间。”\n在本书中,作者给出了一些关于睡眠前后行为的建议,下面分别按“睡前行为”和“睡后行为”进行了归纳。\n睡前行为:\n如果你吃得很晚,就不应该马上上床睡觉,胃里堆满食物、忙着消化将会影响你的昼夜节律,从而影响睡眠质量; 尽管酒精会带来昏昏欲睡的感觉,但是不应摄入过量的酒精; 在睡觉前提前关闭电脑、平板电脑、智能手机和电视机,能减少你暴露在这些设备发出的蓝光下的时间(同时也有利于避免产生新的焦虑,各种杂乱思绪是破坏睡眠周期的一大干扰); 让卧室保持凉爽,并在睡前冲一个温水澡,从而能更好地适应从白天到夜间的温度变化; 让一切暗淡下来(在昏暗环境中,体内会分泌褪黑素,然后我们就会睡意朦胧); 准备一下明天的生活必需品,能放空你的大脑,为入睡做好准备; 避免剧烈运动。 睡后行为:\n起床后不要马上看手机,可以远离电子设备一会儿; 以一杯茶或一杯咖啡开启新的一天是一个不错的选择; 轻微的锻炼可以让你的身体慢慢适应新的一天; 到户外散散步,沐浴阳光,并呼吸一下新鲜的空气; 循序渐进地发动你的大脑,可以看看书、听听播客、做做家务,这些都是开始新的一天、再度融入这个世界的好办法。 恰当的睡后行为能让我们在新的一天更有效率,尽管完成这一系列程序需要花一点儿时间,但随后投入工作或社交时,我们能更加地清醒和从容。\n2.3.4 抓住日间小睡的机会 # “如果能在充分利用好夜间睡眠的同时,也利用好白天的时间,就能给身体和心灵带来一个不断重新启动的机会,帮助你满足现代社会的各种需求。我们并不是不加选择地随意打瞌睡,而是主动掌控并利用白天的各个机会,力争从中获取最大收益。”\n在午后小睡:\n午后时光是一天中次优的身心修复时段。\n如果夜间缺失了一个睡眠周期,午后就是最佳的弥补时机。这一时机不仅时间长,而且效率高。如果当天晚上你有可能会晚睡,那不妨利用好第二天的午睡时光。我们既可以利用这个时机,插入一个 90 分钟的睡眠周期(不推荐,容易出现睡眠惰性,睡醒后会表现出精神恍惚),也可以补充一个 30 分钟的可控修复期(推荐)。日间小睡有助于维持或改善随后的表现,提高生理和心理的灵敏度,并能有效改善情绪。\n即使你并没有真正进入睡眠状态也没有关系。重要的是,你能利用这段时间闭上眼睛、脱离这个世界片刻。能够睡着固然很棒,但徘徊在似睡非睡、似醒非醒的蒙眬、迷糊的状态中,同样也能达到休息的效果。\n在傍晚小睡:\n另外,我们可以利用傍晚的时间(下午 5~7 点之间)小睡一会儿,从而能够更好地利用晚上的时间。\n如果错过了午后的小睡,可以利用这一时机,小憩 30 分钟左右(插入一个可控修复期)。但此时不适合睡上 90 分钟(一个完整的睡眠周期),否则会影响夜间的睡眠。\n利用好日间的这两个休憩时机,将给你带来无穷信心,减轻睡眠压力,让你晚上可以晚点儿睡觉,并且不必为睡眠不足而过度担忧。\n从长远来看,这些日间小睡并不能代替夜间的睡眠。但这些日间小睡能和你的生理节律保持协调,补充夜间睡眠周期,促进身心的全面修复,并让你保持良好的精神状态和较高的工作效率。\n2.3.5 合理安排饮食和锻炼 # 如果我们在白天进行了体育锻炼,那么晚上躺在床上时,身体比平时更疲乏,会更容易睡着。合理的饮食和锻炼,有利于睡眠质量的提高。三者齐头并进,将给你的生活质量带来飞跃式的提高。\n三、心得体会 # 这本书是在我看完《早起的奇迹》之后紧接着阅读的一本书,因为当时自己想尝试早起,却因为总是犯困而很难坚持下去。本书的作者从人体的昼夜戒律开始讲解,让我对睡眠的生理学原理有了更具体的认识,作者也针对个人的睡眠管理给出了许多有用的意见,让我获益匪浅。通过合理地把控自己的睡眠周期,我们就能轻松实现早起,让每一天都变得更加高效。\n","date":"2024-07-19","externalUrl":null,"permalink":"/articles/%E7%9D%A1%E7%9C%A0%E9%9D%A9%E5%91%BD%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E4%BA%86%E8%A7%A3%E4%BD%A0%E7%9A%84%E6%98%BC%E5%A4%9C%E8%8A%82%E5%BE%8B/","section":"Articles","summary":"","title":"《睡眠革命》读书笔记 | 了解你的昼夜节律","type":"posts"},{"content":"","date":"2024-07-19","externalUrl":null,"permalink":"/tags/%E4%B8%AA%E4%BA%BA%E5%81%A5%E5%BA%B7/","section":"Tags","summary":"","title":"个人健康","type":"tags"},{"content":" 一、关于本书 # 《早起的奇迹》 个人成长 作者:哈尔·埃尔罗德 简介:这本书讲述了作者在遭遇了人生的重大挫折之后,通过每天坚持早起并执行“SAVERS”人生拯救计划,从而实现了人生的奇迹。本书对“SAVERS”人生拯救计划的内容进行了详细的介绍,通过长期实践该计划,就能彻底改变你的人生、健康、财富和人际关系。\r二、内容分享 # 2.1 为什么要早起?(Why) # 我自己曾经也经历过长达几年的晚睡晚起(或是晚睡但被迫早起)的时光,有时是因为学习和工作,但更多的时候却并没有特别重要的事,只是习惯性地熬到那个点才会睡,最终收获的只能是第二天一上午的混沌,甚至是一整天的低效。\n另外,有时我会想在睡前安排做一些事,比如:阅读、学英语,但往往事与愿违,我总是因为疲惫或别的借口而放弃。\n如果,我能将每天一些例行的安排放到早上上班之前进行,我便不能再有借口去逃避,并且还能以一个更加饱满的精神状态投入到这些任务上,于是我决定尝试早起,并通过阅读学习相关的一些方法论。\n“缺乏自我提升的意识和紧迫感是导致 95% 的人平庸、无能,无法过上理想生活最主要的原因之一。”\n将早起后的时光用于个人发展项目(比如:冥想、阅读、正念、学习、晨练),那么我们的内心将会变得更加充盈与自信,并开启充满激情的人生。\n2.2 早起后做什么?(What) # 在本书中,作者提出了一个叫做 **SAVERS(拯救者)**的早起计划,它包括以下内容:\nS: Silence (心静) A: Affirmations (自我肯定) V: Visualization (内心演练) E: Exercise (锻炼) R: Reading (阅读) S: Scribing (写作) S:Silence (心静):\n“心静,灵魂才能更清楚地看清前路,分辨善恶、认清真假,并抵达澄明之境。”\n“心静”可以提高自我觉醒度,并让我们认领自己的“核心我”(即什么事对我而言是最重要的,除此之外的事我都可以忍受或做出让步)。“心静”可以使我们的头脑变得明晰,每天都能专注自己的目标和优先事项,并缓解我们当下的焦虑。\n练习方式:冥想、祈祷、沉思、深呼吸、感恩。\nA:Affirmations (自我肯定):\n“自我肯定会影响你的思维方式和自我认知,帮你突破自己信念与行为上的局限,为成功打下坚实的基础。”\n我们应该停止为过去内疚,放下自卑,勇敢地朝着自己想要获得的成功前进。\n我会将自己平时看到的一些能够激励我的句子写下来,贴到自己的房间里,每天看一遍。\nV:Visualization (内心演练):\n内心演练是指将自己的主要目标、最深的欲望和最激动人心的梦想具象化,你的想象越是详细和具体,就越有可能采取必要的行动将理想变为现实。\nE:Exercise (锻炼):\n每天早晨,哪怕只锻炼几分钟,也能大大提高你的精气神,让你保持健康,使自己变得更加自信,情绪得到改善,思维变得清晰。\n晨练可以唤醒你的身体,激活你的大脑,帮助你在白天保持精力充沛。\nR:Reading (阅读):\n“读书给人以乐趣,给人以光彩,给人以才干。”\n坚持读书和思考,是一辈子的事。\n在开始读一本书之前,先问自己几个问题:\n我为什么想读这本书? 通过读这本书,我希望获得什么? 我打算将学到的知识运用到实际生活中吗? 另外,有时与其读一本完全陌生的新书,不如重读一本你认为对自己有用的书。只要一本书能够改变自己生活的某个方面,我就会再读一遍,或者至少重读那些标记了重点的地方,并输出读书笔记。\nS:Scribing (写作):\n写日记是练习写作的好方法,将自己的一些想法记录下来会有以下好处:\n使思维更加清晰:写作会迫使我们更深入地思考,可以让你的思维变得更清晰,激起头脑风暴,帮助你解决难题; 捕获灵感:写日记不仅能帮你拓展思维,还可以防止你遗忘重要的灵感; 回顾经验教训:写日记可以帮你回顾自己所学的知识; 检阅自己的进步:年终时重读自己一年的经历,感觉非常奇妙,你可以看到自己的进步,这是人生中最为励志、自豪、享受的体验之一。 2.3 如何坚持早起?(How) # 在本书中,作者给出了一套流程,从而能够帮助我们更好地实现早起,包括以下内容:\n睡前进行积极的心理暗示; 将闹钟放到离床较远的地方,并在闹钟响起后及时起床; 起床后立即刷牙洗脸(不要马上看手机),并以此作为缓冲,让大脑逐渐清醒; 喝水,及时补充水分能帮助我们恢复身体的活力; 晨练,让自己出一点汗,大脑将迅速变得清醒。 三、心得体会 # 在阅读本书之前,我是一个长期习惯于晚睡晚起的人。后来,有一次偶然间在微信读书的首页上刷到了这本书,在好奇心的驱使下我读了几页,然后就被其中的内容彻底吸引了。本书从为什么要早起、早起后应该做什么以及怎么实现早起等多个方面论述了与“早起”这一话题相关的方法论,并辅以作者的个人经历,让我下定决心要尝试一下书中的生活方式,改掉之前的恶习。\n然而,道理是简单的,但实践起来却总是充满了困难。书中的方法虽然确实有用,但在我刚开始实践时,却总是会感觉很困、起不来,这也引发了我对人类睡眠机制的好奇。因此,我看了《睡眠革命》这本书,它让我对人类的昼夜节律有了深刻的认识,并了解了掌控自己睡眠的方法。\n经过几周的尝试过后,我终于找到了自己的作息节奏。自从开始早起以来,我明显感觉自己的身体状况和情绪状况产生了积极的改变,对工作更加专注,心情更加愉悦,对生活更加充满信心。希望自己在今后的生活中,能够将“早睡早起”这一习惯长期坚持下去,像书中的作者一样,始终保持对生活乐观的态度。\n想要制定更加科学的早起计划,就需要对人体的昼夜节律有一定的了解,感兴趣的朋友可以阅读我写的另一篇文章:《睡眠革命》读书笔记。\n","date":"2024-07-09","externalUrl":null,"permalink":"/articles/%E6%97%A9%E8%B5%B7%E7%9A%84%E5%A5%87%E8%BF%B9%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E5%88%B6%E5%AE%9A%E4%BD%A0%E7%9A%84%E6%97%A9%E8%B5%B7%E8%AE%A1%E5%88%92/","section":"Articles","summary":"","title":"《早起的奇迹》读书笔记 | 制定你的早起计划","type":"posts"},{"content":" 一、关于本书 # 《认知觉醒》 个人成长 作者:周岭 简介:这是一本非常全面的个人成长指南,它打开了我的视野,提出了关于生活各个方面的认知方法,同时也解释了许多自己之前遇到过,却并不知道其底层原理的现象,让我从一个更高的维度认识到了许多事情的本质。当然,就像书中提到的,了解到这些知识只是开始,如何将学到的知识落实到行动,进而改变自己的生活才是最重要的。通过阅读本书,我们将从根本的认知上完成觉醒,进而开启不一样的人生。\r二、内容分享 # 2.1 学会应对焦虑 # “一切焦虑的根源都是因为自己的欲望大于能力,又极度缺乏耐心。”\n在生活中,我们总是会遇到一些各方面都比自己厉害很多的人,也会看到别人在互联网上晒出自己精致的生活,这时我们会幻想自己什么时候也能变成别人那样,但往往由于一些客观条件的限制,亦或者自身与别人的能力差距过大,从而让自己陷入到焦虑和内耗之中。\n“痛苦来源于与他人的比较。”\n本书中提到了几个可以缓解焦虑的办法,我认为很有帮助:\n克制欲望,不要让自己同时做很多事; 面对现实,看清自己真实的能力水平; 要事优先,想办法只做最重要的事情; 接受环境,在局限中做力所能及的事; 直面核心,狠狠逼自己一把去突破它。 另外,前不久从网上一位博主口中了解到了“持续努力”的概念,这位博主一开始只是一位普通学校的学生,却通过自己合理的规划以及长期的坚持,最终拿到了亚洲顶尖大学之一的全奖 cs phd offer。通过这个例子,我个人总结了一下,“成功 = 规划(信息/决策)+ 行动力 + 耐力(长期坚持/终身学习)+ 运气”,这也正符合本书中关于”舒适区理论“以及“复利效应”的内容。培养自己“延迟满足”的能力,将有利于在漫长的人生中收获更大的成功。\n2.2 了解自己的潜意识 # 什么是潜意识?\n“潜意识没有思维,只关心眼前的事物,喜欢即刻、确定、简单、舒适,这是属于天性的部分,同时,它处理信息的速度又极快,至少可达 11000000 次/秒,能极其敏锐地感知很多不易察觉的信息,这是属于感性的部分。”\n关于潜意识,书中让我印象最深刻的点是,潜意识处理信息的速度远远超过我们的理性思考,因此当我们第一次见到一个人、第一次来到一个新的环境以及第一次尝试一个新的事情时,潜意识往往会给出一些或正向或负向的反馈,这是因为潜意识往往已经在瞬间从周围的外部环境接收了大量的信息,从而产生了反馈,这应该就是人们常说的“第六感”或者说“直觉”。有些时候,选择相信自己的直觉(follow your heart),往往可以选择出最适合自己的路,并规避掉一些不必要的折磨。\n2.3 培养深度沉浸的能力 # 本书提到了一个概念叫做 “身心分离模式”,即我们常说的走神(身体在做 A 事情,脑子里却在想 B 事情)。据我观察,一个人所谓的学习能力,在很大程度上取决于其深度沉浸(专注)的能力。如果我们在做一件事情时,总是无法全身心地投入其中,那么我们做事的效率以及最后的结果恐怕都不会太好,人与人之间能力的差异也由此被拉开。\n如何提高自己的专注力?\n“慢慢练习收回感受,让注意力回到当下,进而改变自己的底层行为模式。”\n从现在开始,尝试在专心做事之前,将手机扔到一旁,抛掉脑中的杂念,专注于当下吧!\n2.4 形式主义的陷阱 # 如今,许多人都热衷于通过打卡的方式来督促自己去做某件事,比如学英语、健身等等,但是往往在初期做得热火朝天,却在后续的执行中陷入为了打卡而打卡的陷阱。当我们在完成一项任务时,如果心里只想着还差多少就能完成今天的任务,比如今天还要背几个单词、今天还要看几页书……,而不专注于事情本身,那就只是为了完成而完成,我们从这个过程中并没有真正收获什么。\n本书提到了一个概念叫做 “认知闭合需求”。所谓“认知闭合需求”,就是指当人们面对一个模糊的问题时,就有给问题找出一个明确的答案的欲望。\n将这一概念扩展到行为上也是一样的:\n“一件事若迟迟没有完成,心里就总是记挂,期盼着早点结束;此事一旦完成,做这件事的动机就会立即趋向于零。任务一旦闭合,大脑就会清理原先被占用的记忆空间,那件事很快就会退出脑海,行动的动机也就消失了。”\n如何避免这一现象?\n我们可以用“记录”代替“打卡”。目前,我个人采用的时间管理方法是:首先,列出今天想要完成的事件清单,并根据优先级分为(高、中、低);然后,我会优先集中精力,用相对比较大块的时间去完成高优先级的任务,同时用一些相对零散的时间去完成一些优先级为中或低的任务。这样便能够在一定程度上规避“认知闭合需求”,因为列出的任务清单只是为今天要做的事指明一些方向,并不需要全部完成,只要保证完成了优先级最高的项即可,这样我便不用想着今天还有多少卡没有打,从而能够更加专注地投入于手中的任务;最后,我会使用 App 记录每天用于各项任务的时长,这样我便能对自己的学习情况有一定的掌控,并在之后进行调整和改进。\n2.5 高效学习的秘诀 # “是否有及时、持续的正向反馈,正是产生学习效果差异的关键。”\n今天,在传统的学校教育中,在初期沉浸于大量的理论学习是大多数人的学习方式,然而只有输入没有输出,进而没有反馈的学习往往只是停留于表面,是低效的。如何最大程度地挖掘自己的主观能动性?——持续获取正反馈。我们要有 “作品意识”,即不管学到了什么东西,都可以尝试把知识整理为能够输出的东西(一篇文章、一个视频、……),或发表到网上,或讲给别人听,从而获取反馈,并促使自己在之后不断做得更好。\n另外,我从本书中学习到了 “极度专注 + 主动休息” 的学习模式。一个人每天的精力和专注力是有限的,正确的学习策略应该是:在学习和工作时全情投入,并在感到疲惫时及时休息、放松,以便在下一段时间中更好地投入到工作之中。\n“有效学习的关键是保持极度专注,而非一味比拼毅力和耐心。”\n目前,我除了每晚的例行睡眠之外,还会在每天中午 1-2 点和傍晚 6-7 点之间,插入一个时长在 30 分钟左右的日间修复性睡眠,这能让我短暂地放空因工作而变得迟钝的大脑,回复精力,并在之后以更好的精神状态投入到学习和工作之中。\n2.6 无痛自律的秘诀 # 如何变得自律?\n“真正的行动力并不完全来源于自制力。”\n本书提到了一个概念叫做 “注意力的增强回路”。它指的是每天早上起床后,我们都可以刷新前一天的精神状态,并获得一份纯净的注意力,这时如果你选择在起床后立马就去刷手机、沉迷于新闻和短视频,那么你的注意力就会持续地往负向的方向增强,你沉浸得越久,想要“刹车”就越难;反之,如果你选择在起床后立马投入到学习中,那么你将会更容易持续地专注下去,我把这种现象叫做大脑的“惯性”。因此,是否能够度过自律的一天,往往在一天的开始时就决定了。\n另外,书中还提到,实现自律的另一个关键还在于个人的 “认知层次”,我称之为“目标感”。即如果一个人心中有远大的目标,并愿意为之持久的奋斗,那么在追寻梦想的这条道路上,则大概率会比那些浑然度日的人拥有更强的自律性。\n2.7 坚持早起 # 本书中提到,个人成长最高效的方法便是 “早冥读写跑” 五件套。其中,关于“早起”的部分对我的影响最大,也最深刻地改变了我的生活。\n在此前的生活中,我总是习惯于晚睡晚起,上午的时光对我来说似乎总是迷糊和短暂的。通过本书,我认识到了早起的好处。早起能让自己在头脑最清醒的时候,拥有充足的时间进行学习和思考;早起后适当的运动,也有利于提高上午的工作效率和专注度;更重要的是,早起能够让自己以一个更加积极的心态来面对新的一天,减缓焦虑等负面情绪。\n为了实现我的“早起计划”,我还特意去阅读了相关的书籍,这里推荐两本书:《早起的奇迹》和《睡眠革命》。这两本书让我对人体的昼夜节律有了更加深刻的认识,让我学会了如何更好地掌控自己的睡眠习惯,从而实现更加美好的人生。\n从我坚持早起以来,在每天早晨正式开始工作之前,我有了更多的时间去学习英语、健身和阅读,我的心态也发生了积极的改变,能够以更加饱满的精神状态来面对我的工作。总之,希望自己能在今后的生活中,将早起这一习惯继续坚持下去!\n三、心得体会 # 初读这本书时,正值我刚毕业进入职场块一年的时候,也是我人生中最迷茫的几个时刻之一。刚入职那会儿,觉得自己毕业就进入大厂,自以为正是可以为一份事业而激情奋斗之时,却发现理想是好的,但现实却是残酷的。我进入了菊厂中最没有“技术”的几个部门之一,人称“华为公务员”,每天都干着没什么技术含量,也没什么成就感的工作,可能只有刚入职的头几个月里是自己成长最快的时候。在这样日复一日的“牛马”生活中,我曾尝试奋力挣扎过,企图有朝一日能摆脱这样的生活,进入更好的平台去追求自己心中所谓的事业,但更多的时候却是在现实的摧残下,开始对工作和生活变得越来越麻木。\n“救赎之道,就在其中”,这句话出自电影《肖申克的救赎》,也是支撑我度过这段灰暗时光的精神力量之一,它让我相信在人生的逆境中,只要坚持不懈地努力,总会有逃脱当下这个“牢笼”的一天(强烈推荐没有看过这部电影的可以去看看,经典老电影能流传下来果然是有其独到的魅力的)。而至于本书,则教会了我具体的做法,让我懂得了怎么去利用好每一天的时间,化焦虑为具体,默默努力,静待翻身的那一天。\n最后,写下这篇读书笔记时(2024 年 11 月),我已经通过自己的“洞察 + 规划 + 努力”摆脱了之前的处境,通过公司内部转岗来到了目前这个更好的平台,也希望自己在今后的生活中,能够铭记这一份经历,坚持“长期规划 + 持续努力”,并最终实现成长和蜕变。\n","date":"2024-06-05","externalUrl":null,"permalink":"/articles/%E8%AE%A4%E7%9F%A5%E8%A7%89%E9%86%92%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E5%BC%80%E5%90%AF%E5%BF%83%E6%99%BA%E6%8E%8C%E6%8E%A7%E8%87%AA%E5%B7%B1%E7%9A%84%E7%94%9F%E6%B4%BB/","section":"Articles","summary":"","title":"《认知觉醒》读书笔记 | 开启心智,掌控自己的生活","type":"posts"},{"content":"","date":"2024-06-05","externalUrl":null,"permalink":"/tags/%E5%AD%A6%E4%B9%A0%E6%96%B9%E6%B3%95/","section":"Tags","summary":"","title":"学习方法","type":"tags"},{"content":" 一、关于本书 # 《富爸爸 穷爸爸》 投资理财 作者:罗伯特·清崎 简介:本书是我读过的第一本理财相关的书籍,它不仅让我第一次认识到了财商教育的重要性,也让我明白了什么是好的消费观和理财观。书中关于“应该以什么样的态度来对待工作和生活”的部分,让我获益匪浅。通过阅读本书,我们可以对自己当下的消费方式有一个更加清醒的了解和认识。\r二、内容分享 # 2.1 财商教育的重要性 # “富人之所以越来越富,穷人之所以越来越穷,中产阶级之所以总是在债务的泥潭中挣扎,其中一个主要原因就是,他们对金钱的认识不是来自学校,而是来自家庭。遗憾的是,学校并没有开设有关“金钱”的课程。学校教育只专注于学术知识的传授和专业技能的培养,却忽视了理财技能的培训。所以众多精明的银行家、医生和会计师在学校时成绩优异,可还是要一辈子在财务问题上挣扎。如果我们继续把教孩子理财的重任交给那些濒于贫困边缘或已陷入贫困境地的父母,我们的国家又该怎么发展下去?”\n今天,我们大多数人的财商教育都来自于自己的父母,我们的消费观也大概率是由自己所处的原生家庭所塑造的。学校并没有将理财这项技能纳入到义务教育的范畴之中,以至于我自己长久以来并没有特意关注过自己的财务状况,有钱就存着,需要用钱就花。\n得益于我的母亲曾经是一名银行的会计,自己对于理财这件事的态度似乎也受到了家庭的影响。我并不排斥去理财,甚至有点喜欢和这些数字打交道。特别是现在自己已经工作,有了一定的收入和储蓄,我更加意识到了财务知识的重要性。如果我们只知道埋头挣钱,而不懂得如何管理好自己的财富,那么就极有可能使自己陷入到朝不保夕的财务风险之中。\n2.2 什么是资产?什么是负债? # “资产”:不管我是否工作,只需要我付出最低限度的劳动,就可以将钱放进我口袋里的东西; “负债”:把钱从我口袋里拿出去的东西。 今天,大家好像都特别热衷于买房买车,哪怕自己没有钱,也要贷款去买。通过本书,我了解到房子和车子都属于负债,而不是资产。在年轻的时候,我们应该做的,是尽力去积累自己的资产项(而不是去买入负债),从而可以在将来收获更多、更长久的财富。\n而大多数人都是随波逐流的,或是迫于社会压力,亦或是因为需要结婚等理由,在自己没有能力负担的情况下买了房和车,以至于早早地就背上巨额的负债,抗风险能力急剧降低,不敢轻易失业,自然也就没有了追寻自己真正所向往的生活的底气与条件。\n2.3 什么是好的消费观? # 通过本书,我知道了什么是好的消费观,这一点也是我认为最贴近自己当下的生活,最具有现实指导意义的收获。\n对于像我一样刚毕业的年轻人而言,我们首先应该尽量减少开支和借款,并勤劳地工作,从而打下一个坚实的资产基础。然后,我们要将自己挣的一部分钱用于构筑自己的资产,从而产生更多的现金流。当自己能够掌控的现金流足够多时,就可以去买房买车了。最后,不要妄图一夜暴富,平时多存钱,提高自己的抗风险能力,并且坚持学习财务知识,提高自己的理财能力。否则,即便自己拥有大量的财富,如果没有支配管理对应数额的金钱的能力,那么这些财富也终将会离你而去。\n2.4 构建多样化的收入来源 # “当你在自己的专业领域内变得越来越精通时,那么同时也意味着你被社会所异化了,你的选择变少了。”\n因此,作者鼓励我们除了做好自己的本职工作之外,一定要再去发展一项额外的技能,这样当你失业时,你还能有其他的经济来源维持自己的生活。目前,我也在积极探索工作以外的收入来源(在不影响本职工作的前提下),不让工资成为自己唯一的经济来源,并提高自己在财务上的抗风险能力。\n三、心得体会 # 这本书算是我的理财启蒙之作,也激起了我对投资理财相关知识的兴趣。在今后,希望我能继续阅读更多经济学相关的书籍,从而不断提高自己的风险意识和理财能力。\n","date":"2024-05-07","externalUrl":null,"permalink":"/articles/%E5%AF%8C%E7%88%B8%E7%88%B8-%E7%A9%B7%E7%88%B8%E7%88%B8%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%B4%A2%E5%95%86%E6%95%99%E8%82%B2%E5%90%AF%E8%92%99%E4%B9%8B%E4%BD%9C/","section":"Articles","summary":"","title":"《富爸爸 穷爸爸》读书笔记 | 财商教育启蒙之作","type":"posts"},{"content":"","date":"2024-05-07","externalUrl":null,"permalink":"/tags/%E6%8A%95%E8%B5%84%E7%90%86%E8%B4%A2/","section":"Tags","summary":"","title":"投资理财","type":"tags"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}] \ No newline at end of file diff --git a/public/roadmap/index.html b/public/roadmap/index.html index 1f05e2c..14e243e 100644 --- a/public/roadmap/index.html +++ b/public/roadmap/index.html @@ -870,8 +870,8 @@

    Contents diff --git a/public/tags/ai/index.html b/public/tags/ai/index.html index 5f0e466..8e86a18 100644 --- a/public/tags/ai/index.html +++ b/public/tags/ai/index.html @@ -628,7 +628,7 @@

    AI - ·1146 words + ·1157 words diff --git a/public/tags/llm/index.html b/public/tags/llm/index.html index 5b36caa..a51a8ec 100644 --- a/public/tags/llm/index.html +++ b/public/tags/llm/index.html @@ -628,7 +628,7 @@

    LLM<
    - ·1146 words + ·1157 words diff --git "a/public/tags/\345\244\247\346\250\241\345\236\213\345\276\256\350\260\203/index.html" "b/public/tags/\345\244\247\346\250\241\345\236\213\345\276\256\350\260\203/index.html" index 1c3cfe5..bd7a02c 100644 --- "a/public/tags/\345\244\247\346\250\241\345\236\213\345\276\256\350\260\203/index.html" +++ "b/public/tags/\345\244\247\346\250\241\345\236\213\345\276\256\350\260\203/index.html" @@ -628,7 +628,7 @@

    - ·1146 words + ·1157 words diff --git "a/public/tags/\350\256\272\346\226\207\347\262\276\350\257\273/index.html" "b/public/tags/\350\256\272\346\226\207\347\262\276\350\257\273/index.html" index a2bcba0..7f2678a 100644 --- "a/public/tags/\350\256\272\346\226\207\347\262\276\350\257\273/index.html" +++ "b/public/tags/\350\256\272\346\226\207\347\262\276\350\257\273/index.html" @@ -628,7 +628,7 @@

    - ·1146 words + ·1157 words