<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Qingfeng Huang - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Qingfeng Huang - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 30 May 2026 19:37:08 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/author/qingfeng/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 如何编写出整洁的代码——开发者指南与示例 ]]>
                </title>
                <description>
                    <![CDATA[ 试想一下，一个房间里衣服、书籍和各种物品散落得到处都是。在这样的凌乱环境里，你觉得要找到一样东西会不会很困难呢？ 现在来想一想，当我们写出乱糟糟的代码时，它是不是也会使人感到困惑，甚至比凌乱的房间还要让人头疼呢！ 相反，简洁的代码就像一个井然有序的房间：你可以轻松找到所需的内容，和快速理解正在发生的事情，使你更快地完成工作。 现在，我们来瞧瞧这个图表。该图表呈现并对照了两种不同的代码编写方式，以及它们对添加更多代码行所需要时间的影响：  1. ⚠️ 仓促 & 混乱的代码     （红线）：这种代码是在没有规划和整理的情况下匆忙写出的代码。起初，这种方式看起来高效，但随着代码行数增加，代码结构变得越来越难以理解和维护。因此，随着时间推移，每添加一行代码所需的时间都会越来越长。            2. ⚡ 谨慎 & 整洁的代码     （蓝线）：这种代码是经过精心规划和整理的，使其更容易理解和修改。虽然一开始可能会稍微慢一些，但随着代码的增长，它依然保持清晰易读，因此添加新代码不会变得越来越困难。           简单来说，编写整洁的代码一开始可能会缓慢一些，但从长远 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-write-clean-code-tips-for-developers/</link>
                <guid isPermaLink="false">67dbea8e66481204770e045c</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Qingfeng Huang ]]>
                </dc:creator>
                <pubDate>Thu, 20 Mar 2025 10:18:11 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2025/03/5cb4369b-9f2e-4f97-9765-ac00a62800c6.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-write-clean-code-tips-for-developers/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Write Clean Code – Tips for Developers with Examples</a>
      </p><!--kg-card-begin: markdown--><p>试想一下，一个房间里衣服、书籍和各种物品散落得到处都是。在这样的凌乱环境里，你觉得要找到一样东西会不会很困难呢？</p>
<p>现在来想一想，当我们写出乱糟糟的代码时，它是不是也会使人感到困惑，甚至比凌乱的房间还要让人头疼呢！</p>
<p>相反，简洁的代码就像一个井然有序的房间：你可以轻松找到所需的内容，和快速理解正在发生的事情，使你更快地完成工作。</p>
<p>现在，我们来瞧瞧这个图表。该图表呈现并对照了两种不同的代码编写方式，以及它们对添加更多代码行所需要时间的影响：</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730728342241/3fa8f5a1-0af4-4ffd-aa3a-bb70001b026d.png" alt="3fa8f5a1-0af4-4ffd-aa3a-bb70001b026d" width="604" height="340" loading="lazy"></p>
<ol>
<li>
<p>⚠️ <strong>仓促 &amp; 混乱的代码</strong>（红线）：这种代码是在没有规划和整理的情况下匆忙写出的代码。起初，这种方式看起来高效，但随着代码行数增加，代码结构变得越来越难以理解和维护。因此，随着时间推移，每添加一行代码所需的时间都会越来越长。</p>
</li>
<li>
<p><strong>⚡ 谨慎 &amp; 整洁的代码</strong>（蓝线）：这种代码是经过精心规划和整理的，使其更容易理解和修改。虽然一开始可能会稍微慢一些，但随着代码的增长，它依然保持清晰易读，因此添加新代码不会变得越来越困难。</p>
</li>
</ol>
<p>简单来说，编写整洁的代码一开始可能会缓慢一些，但从长远来看，它能节省后续所耗费的时间，让工作更轻松。同时，还能提高软件的可靠性，打造更优质的产品。</p>
<p>编写整洁的代码是专业开发人员培养的一种良好习惯，体现了专业人员对质量的承诺和坚定的职业操守。在本文中，我将带领你了解一些保持代码整洁的最佳实践。</p>
<h3 id="">我们将涵盖的内容</h3>
<ol>
<li>
<p><a href="#heading-1-use-meaningful-names">使用有意义的名称</a></p>
</li>
<li>
<p><a href="#heading-2-follow-the-single-responsibility-principle-srp">遵循单一责任原则 (SRP)</a></p>
</li>
<li>
<p><a href="#heading-3-avoid-unnecessary-comments">避免不必要的注释</a></p>
</li>
<li>
<p><a href="#heading-4-make-your-code-readable">提高代码可读性</a></p>
</li>
<li>
<p><a href="#heading-5-write-unit-tests">编写单元测试</a></p>
</li>
<li>
<p><a href="#heading-6-be-careful-with-dependencies">谨慎处理依赖项</a></p>
</li>
<li>
<p><a href="#heading-7-organize-your-project">合理组织项目结构</a></p>
</li>
<li>
<p><a href="#heading-8-use-consistent-formatting">保持一致的代码格式</a></p>
</li>
<li>
<p><a href="#heading-9-avoid-hardcoding-values">避免硬编码</a></p>
</li>
<li>
<p><a href="#heading-10-limit-function-length">限制函数长度</a></p>
</li>
<li>
<p><a href="#heading-conclusion">总结</a></p>
</li>
</ol>
<h2 id="10">编写整洁代码的 10 个实用技巧</h2>
<p>为了帮助你开启编写整洁代码的旅程，这里为你提供了 10 个实用技巧，可以帮助你保持代码的可读性、条理性和高效性。</p>
<h3 id="1">1. 使用有意义的名称</h3>
<p>在为变量、函数和类命名时，选择能清晰描述其用途的名称。</p>
<p>不要简单地命名变量为 <code>b</code>，试着用 <code>numberOfUsers</code>这样的名称。这样，任何阅读你的代码的人都能毫不费力地理解其用途，而无需额外注释。一个有意义的名称可以减少猜测，避免产生混淆。</p>
<p><strong>示例</strong>：</p>
<pre><code>// 正确的例子
let numberOfUsers = 5; // 清晰易懂

// 错误的示范
let b = 5; // 含糊不清
</code></pre>
<p><strong>💡 命名技巧</strong></p>
<ul>
<li>
<p><strong>变量</strong>：使用描述数据的名词，例如 <code>userAge</code> 或 <code>totalAmount</code>。</p>
</li>
<li>
<p><strong>函数</strong>：使用动词，如 <code>calculateTotal()</code> 或 <code>fetchUserData()</code>。</p>
</li>
<li>
<p><strong>类</strong>：使用单数名词来表示其含义，如 <code>User</code> 或 <code>Order</code> 来表示它们的类型。</p>
</li>
</ul>
<pre><code>// 变量：描述其存储的数据
let userAge = 25;

// 函数：使用动词来描述其功能
function calculateTotal(price, quantity) {
    return price * quantity;
}

// 类：单数名词，表示一种对象类型
class User {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}
</code></pre>
<h3 id="2srp">2. 遵循单一责任原则（SRP）</h3>
<p><strong>单一职责原则</strong>意味着每个函数或方法都应该<strong>只负责一项特定的任务</strong>。</p>
<p>这样可以让你的函数保持简洁、专注，从而提高代码的可读性，有便于后续的测试和维护。</p>
<p>想象一下一个工具箱，其中每个工具都有其独特的用途——干净的代码函数也应如此运作。</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730728643183/77666f78-7ec9-4a5c-af4f-253e6de4acac.jpeg" alt="77666f78-7ec9-4a5c-af4f-253e6de4acac" width="474" height="355" loading="lazy"></p>
<p>例如，如果你有一个名为 <code>calculateTotal</code> 的函数，它就只应该负责计算总和。如果你给它添加额外的任务，可能会导致代码变得混乱，难以维护。</p>
<p>下面是一个示范例子，展示了为什么保持函数的单一职责很重要：</p>
<p>假定您期望计算一个总和并返回一个带有额外信息的对象，诸如谁进行了计算，以及何时计算的。与其直接将这些内容添加到 <code>calculateTotal</code> 中，不如我们创建第二个函数来处理额外的信息。</p>
<ol>
<li>
<p><strong>优良范例（任务分离）</strong></p>
<pre><code> // 此函数仅用于计算总和
 function calculateTotal(a, b) {
     return a + b;
 }

 // 此函数用于创建包含额外信息的对象
 function createCalculationRecord(a, b, user) {
     let sum = calculateTotal(a, b); // 调用上面的计算总和函数
     return {
         user: user,
         total: sum,
         timestamp: new Date()
     };
 }

 let record = createCalculationRecord(5, 10, "Shahan");
 console.log(record);
</code></pre>
</li>
</ol>
<p><strong>👍 为什么这样更好</strong>：这样使每个函数都有一个明确且集中的任务。其中 <code>calculateTotal</code> 只需负责数学运算，而 <code>createCalculationRecord</code> 则负责添加额外的细节。如果你想更改总数的计算方式，只需更新 <code>calculateTotal</code>函数；如果你想改变记录格式，只需更新 <code>createCalculationRecord</code>的函数。</p>
<ol start="2">
<li>
<p><strong>错误示例（在一个函数中混合多个任务）</strong></p>
<pre><code> // 此函数在一个步骤中同时计算总和创建对象
 function calculateTotalAndReturnRecord(a, b, user) {
     let sum = a + b;
     return {
         user: user,
         total: sum,
         timestamp: new Date()
     };
 }

 let record = calculateTotalAndReturnRecord(5, 10, "Shahan");
 console.log(record);
</code></pre>
</li>
</ol>
<p><strong>👎 为什么这样不好</strong>：函数名称 <code>calculateTotalAndReturnRecord</code> 表明它试图完成多项任务。如果你只想使用计算部分，就无法在不涉及记录部分的情况下复用这个函数。此外，单独更新和测试每一项任务也会更困难。</p>
<h3 id="3">3. 避免不必要的注释</h3>
<p>一个良好的代码应该能够具有自身的解释性，而不需要过多的注释来解释。而专注于编写清晰且易于理解的代码，便使其本身就能表达出意图。</p>
<p>注释在解释复杂逻辑或特殊实现时很有用，但同时过多的注释会使代码显得杂乱无章，难以阅读和理解。</p>
<p><strong>💬 什么时候使用注释</strong>：</p>
<ul>
<li>
<p>用于说明为什么要以特定方式进行操作。</p>
</li>
<li>
<p>在处理复杂的算法或计算时。</p>
</li>
<li>
<p>添加有关潜在局限性的注释。</p>
</li>
</ul>
<p><strong>示例</strong>：</p>
<pre><code>// 命名清晰，无需注释
let userAge = 25;

// 命名不清晰，需要注释
let a; // 用户的年龄
</code></pre>
<h3 id="4">4. 提高代码可读性</h3>
<p>可读性良好的代码使用<strong>缩进</strong>、<strong>换行</strong>和<strong>空格</strong>来保持整洁和有序。</p>
<p>可以把它想象成写一篇故事文章：段落的划分能让阅读变得更轻松，同样，在代码中，合理的换行可以起到相同的作用，使其更易于理解。</p>
<p><strong>示例</strong>：</p>
<pre><code>// 优良范例的代码结构
if (isLoggedIn) {
    console.log("欢迎回来!");
} else {
    console.log("请登入账号。");
}

// 错误示例的代码结构
if(isLoggedIn){console.log("欢迎回来!");}else{console.log("请登入账号。");}
</code></pre>
<p>在 VS Code 中，<strong>Prettier</strong> 和 <strong>Black</strong> 是常见的代码格式化工具，可以自动为多种编程语言应用整洁的代码风格。</p>
<p><strong>PyCharm</strong> 和 <strong>IntelliJ</strong> 具备强大的内置格式化功能，并支持自定义规则，例如 Python 中的 PEP 8 代码风格指南和其他标准规范。这些工具可以帮助项目保持一致的代码格式，减少手动调整的工作量，从而提高代码的可读性和维护性。</p>
<h3 id="5">5. 编写单元测试</h3>
<p>单元测试有助于确保代码的每个部分按预期运行。</p>
<p>通过测试小的、独立的部分（比如函数），你可以尽早发现错误，并防止其蔓延到代码的其他部分。</p>
<p>具体来说，单元测试实际上是对你代码的每个部分所进行的小型质量检查，从而确保它们按预期来工作。</p>
<p><strong>🍎 现实中的案例：</strong></p>
<p>让我们来看一下，如何测试具有多个方法的复杂 JavaScript 对象，以 <code>Calculator</code> 类为例。</p>
<p>这种方法将帮助你明白，为何要让每个方法专注于一项任务，并通过单元测试确保每个方法都能正确运行。</p>
<p>这里有一个 <code>Calculator</code> 类，其中包含执行基本算术运算的方法：加法、减法、乘法和除法。</p>
<pre><code>class Calculator {
    constructor() {
        this.result = 0;
    }

    add(a, b) {
        return a + b;
    }

    subtract(a, b) {
        return a - b;
    }

    multiply(a, b) {
        return a * b;
    }

    divide(a, b) {
        if (b === 0) throw new Error("无法除以零");
        return a / b;
    }
}
</code></pre>
<p>如你所见，每个方法都执行一个特定的操作。其中，<code>divide</code> 方法有额外的逻辑来处理除以零的情况，否则这会导致错误。</p>
<p>现在，我们将编写单元测试来验证每个方法是否按预期工作。🔬</p>
<p><strong>🧪 为每个方法编写单元测试</strong></p>
<p>为了测试我们的 <code>Calculator</code> 类，我们可以编写单元测试，涵盖正常情况和边缘情况。以下是如何为每个方法设置测试的示例：</p>
<pre><code>// 初始化 Calculator 实例
const calculator = new Calculator();

// 测试 add 方法
console.assert(calculator.add(2, 3) === 5, '测试失败: 2 + 3 应该等于 5');
console.assert(calculator.add(-1, 1) === 0, '测试失败: -1 + 1 应该等于 0');

// 测试 subtract 方法
console.assert(calculator.subtract(5, 3) === 2, '测试失败: 5 - 3 应该等于 2');
console.assert(calculator.subtract(0, 0) === 0, '测试失败: 0 - 0 应该等于 0');

// 测试 multiply 方法
console.assert(calculator.multiply(2, 3) === 6, '测试失败: 2 * 3 应该等于 6');
console.assert(calculator.multiply(-1, 2) === -2, '测试失败: -1 * 2 应该等于 -2');

// 测试 divide 方法
console.assert(calculator.divide(6, 3) === 2, '测试失败: 6 / 3 应该等于 2');
try {
    calculator.divide(1, 0);
    console.assert(false, '测试失败: 除以零应该抛出错误');
} catch (e) {
    console.assert(e.message === "无法除以零", '测试失败: 除以零的错误信息不正确');
}
</code></pre>
<p><strong>🫧 测试解释：</strong></p>
<ol>
<li>
<p><strong>加法</strong>（<code>add</code> 方法）：我们测试 <code>add(2, 3)</code> 返回 <code>5</code>，以及 <code>add(-1, 1)</code> 返回 <code>0</code>。如果这些测试通过，就说明我们的加法逻辑是正确的。</p>
</li>
<li>
<p><strong>减法</strong>（<code>subtract</code> 方法）：我们验证 <code>subtract(5, 3)</code> 返回 <code>2</code>，以及 <code>subtract(0, 0)</code> 返回 <code>0</code>。这些检查确保减法计算准确无误。</p>
</li>
<li>
<p><strong>乘法</strong>（<code>multiply</code> 方法）：我们测试乘法函数，包括正数和负数的情况，确保 <code>multiply(2, 3)</code> 返回 <code>6</code>，以及 <code>multiply(-1, 2)</code> 返回 <code>-2</code>。</p>
</li>
<li>
<p><strong>除法</strong>（<code>divide</code> 方法）：我们验证 <code>6</code> 除以 <code>3</code> 返回 <code>2</code>。对于除以零的情况，我们使用 <code>try...catch</code> 块来确保方法抛出正确的错误消息。这可以验证该方法是否能正确处理错误情况。</p>
</li>
</ol>
<p>你可以看到，如果任何方法失败，测试都会生成清晰的错误消息，使我们能够迅速识别并修复问题。单独测试方法有助于我们在项目发展过程中尽早发现错误，并保持可靠、整洁的代码。</p>
<h3 id="6">6. 谨慎处理依赖项</h3>
<p>依赖项，是指你的代码所依赖的外部软件组件。🔌</p>
<p>想象一下，您正在构建一个能发送电子邮件的网络应用程序。与其自己编写邮件发送功能，你可以使用像 <a href="https://nodemailer.com/"><strong>Nodemailer</strong></a> 这样的外部库。在这种情况下，Nodemailer就是一个<strong>依赖项</strong>——你的应用依赖它来处理邮件发送功能。</p>
<p><strong>示例：</strong></p>
<pre><code class="language-javascript">const nodemailer = require('nodemailer');

function sendEmail(to, subject, message) {
    const transporter = nodemailer.createTransport({
        service: 'gmail',
        auth: {
            user: 'your-email@gmail.com',
            pass: 'your-email-password'
        }
    });

    const mailOptions = {
        from: 'your-email@gmail.com',
        to: to,
        subject: subject,
        text: message
    };

    return transporter.sendMail(mailOptions);
}
</code></pre>
<p>在这段代码中，<code>nodemailer</code> 被引入并用于创建一个邮件发送的传输器。如果没有它，你将不得不从零编写整个邮件发送功能，这将非常复杂且耗时。通过使用 Nodemailer 作为依赖项，你的应用可以轻松实现邮件发送功能。</p>
<p>尽管依赖项非常有用，但过度依赖外部软件或库可能会带来问题。因此，只有当它们能简化你的工作或提供重要功能时，才应该使用依赖项。</p>
<p>有效管理依赖是编写简洁代码的关键。以下是一些提示：</p>
<ul>
<li>
<p><strong>限制依赖项</strong>：仅包含对项目真正必要的库或模块，避免不必要的依赖项。</p>
</li>
<li>
<p><strong>保持版本更新</strong>：使用最新版本的库，以减少安全风险。</p>
</li>
<li>
<p><strong>分离核心逻辑</strong>：尽可能自己编写核心功能，这样即使未来需要移除某个依赖，也不会影响你代码的正常运行。</p>
</li>
</ul>
<p>让我用之前的 Nodemailer 代码来给你举个例子，说明如何在代码中实现逻辑分离。</p>
<p>你可以创建一个封装函数，将发送电子邮件的细节抽象出来。这样，你就可以更改底层的电子邮件服务或移除对 Nodemailer 的依赖，而不影响到代码的其他部分。</p>
<p>以下是如何组织代码以实现此目的：</p>
<pre><code class="language-javascript">const nodemailer = require('nodemailer');

// 用于发送电子邮件的核心函数
function sendEmail(to, subject, message) {
    const transporter = createTransporter();
    const mailOptions = createMailOptions(to, subject, message);
    return transporter.sendMail(mailOptions);
}

// 创建邮件发送器的函数
function createTransporter() {
    return nodemailer.createTransport({
        service: 'gmail',
        auth: {
            user: 'your-email@gmail.com',
            pass: 'your-email-password'
        }
    });
}

// 创建邮件选项的函数
function createMailOptions(to, subject, message) {
    return {
        from: 'your-email@gmail.com',
        to: to,
        subject: subject,
        text: message
    };
}

// 示例用法
sendEmail('recipient@example.com', 'Test Subject', 'Hello, this is a test email.')
    .then(() =&gt; {
        console.log('Email sent successfully!');
    })
    .catch((error) =&gt; {
        console.error('Error sending email:', error);
    });
</code></pre>
<p><strong>🗝️ 关键要点：</strong></p>
<ol>
<li>
<p><strong>核心函数</strong>：<code>sendEmail</code>、<code>createTransporter</code> 和 <code>createMailOptions</code> 的函数是相互独立的，这样你就可以修改其中一个而不影响其他函数。</p>
</li>
<li>
<p><strong>轻松修改</strong>：如果您希望在将来切换到其它电子邮件服务，你只需修改 <code>createTransporter</code> 的函数即可。</p>
</li>
<li>
<p><strong>可维护性</strong>：这种结构使你的代码更易于维护和理解。</p>
</li>
</ol>
<h3 id="7">7. 合理组织项目结构</h3>
<p>一个良好的项目结构与代码本身同样重要。</p>
<p>可以这样想，就像整理你的工作空间一样——你需要为每样东西指定位置，这样就能轻松找到它们。对于编码项目，应该为不同的部分创建专门的文件夹，例如 <code>components</code>、<code>utils</code> 和 <code>services</code>等格式。</p>
<p><strong>📂 如何组织你的项目</strong></p>
<p>为了建立一个整洁有序的项目，你应当将代码的不同部分的分类放入指定的文件夹中。以下是一个组织良好的项目结构的简单示例：</p>
<pre><code>myProject # 我的项目
├── src # 资源文件夹
│   ├── components # 组件文件夹
│   ├── services # 服务文件夹
│   ├── utils # 实用工具文件夹
└── tests # 测试文件夹
</code></pre>
<h4 id="">项目结构解析：</h4>
<ol>
<li>
<p><strong>myProject（我的项目）</strong>：这是你项目的根文件夹。包含与您的应用所有相关的内容。</p>
</li>
<li>
<p><strong>src（资源）</strong>：此文件夹包含您项目的所有源代码。您大部分时间都将在这里编码。</p>
</li>
<li>
<p><strong>components（组件）</strong>：此子文件夹用于存放可复用的 UI 组件。例如，如果你正在构建网络应用程序，你可能会在这里存放按钮、导航栏、表单等组件的单独文件。每个组件都应有独立的文件，以保持模块化。</p>
<ul>
<li><code>components</code> 中的示例结构：</li>
</ul>
</li>
</ol>
<pre><code>    components
    ├── Button.js # 按钮组件文件
    ├── Header.js # 头部导航组件文件
    └── Form.js # 表单组件文件
</code></pre>
<ol start="4">
<li>
<p><strong>services（服务）</strong>：此文件夹用于存放执行特定任务或处理业务逻辑的函数。例如，如果你的应用涉及发送邮件，你可以在这里创建一个专门的文件来管理所有与邮件相关的功能。</p>
<ul>
<li><code>services</code> 中的示例结构：</li>
</ul>
</li>
</ol>
<pre><code>    services
    ├── emailService.js # 邮件服务文件
    ├── userService.js # 用户服务文件
    └── productService.js # 产品服务文件
</code></pre>
<ol start="5">
<li>
<p><strong>utils（实用工具）</strong>：在这里，你可以放置一些可以跨项目使用的辅助函数。这些通常包括格式化日期、验证输入或其他不属于特定组件或服务的通用任务。</p>
<ul>
<li><code>utils</code> 目录内的示例结构：</li>
</ul>
</li>
</ol>
<pre><code>    utils
    ├── formatDate.js # 格式化日期文件
    ├── validateEmail.js # 验证邮箱格式文件
    └── generateId.js # 生成客户ID号码文件
</code></pre>
<ol start="6">
<li>
<p><strong>tests（测试）</strong>：此文件夹专用于存放测试文件。保持测试的有序排列有助于确保在构建新功能时，你可以轻松地测试它们，而无需在代码库中四处查找相关文件。</p>
<ul>
<li><code>tests</code> 目录内的示例结构：</li>
</ul>
</li>
</ol>
<pre><code>    tests
    ├── emailService.test.js # 测试邮件服务的功能
    ├── userService.test.js # 测试用户服务的功能
    └── component.test.js # 测试 UI 组件的功能
</code></pre>
<p><strong>📨 真实案例：使用 Nodemailer 进行邮件发送</strong></p>
<p>假设你正在构建一个向用户发送电子邮件的应用程序。你可以按照以下结构组织项目：</p>
<pre><code>myEmailApp # 我的邮件应用程序
├── src # 资源文件夹
│   ├── components # 存放组件的文件夹
│   │   ├── EmailForm.js # 邮件表单组件
│   │   └── SuccessMessage.js # 邮件发送成功消息的组件
│   ├── services # 存放服务组件的文件夹
│   │   └── emailService.js # 处理邮件服务功能的组件
│   ├── utils # 存放工具组件的文件夹
│   │   └── validateEmail.js  # 验证邮箱格式的组件
└── tests # 存放测试组件的文件夹
    ├── emailService.test.js # 测试邮件服务的组件
    └── EmailForm.test.js # 测试邮件表单组件
</code></pre>
<ul>
<li>
<p><strong>EmailForm.js</strong>：此组件负责处理发送电子邮件的用户界面，例如收件人、主题和邮件正文的输入字段。</p>
</li>
<li>
<p><strong>SuccessMessage.js</strong>：这个组件在邮件发送成功后显示一条成功消息。</p>
</li>
<li>
<p><strong>emailService.js</strong>：该服务包含使用 Nodemailer 发送电子邮件的逻辑，使你的代码保持模块化和清晰。</p>
</li>
<li>
<p><strong>validateEmail.js</strong>：一个实用的工具函数，用于检查电子邮件地址格式是否正确。</p>
</li>
<li>
<p><strong>tests</strong>：该文件夹用于编写测试代码，以确保你的电子邮件服务和组件按预期运行。</p>
</li>
</ul>
<p><strong>🍱 良好项目组织的优势</strong></p>
<ol>
<li>
<p><strong>易于导航</strong>：查看你的项目的任何人都可以快速了解，在哪里找到特定部分的代码，帮助理解其结构。</p>
</li>
<li>
<p><strong>更好的协作</strong>：如果你与他人合作，清晰的结构可以帮助团队成员明确各自的贡献范围，避免相互干扰。</p>
</li>
<li>
<p><strong>良好的扩展性</strong>：随着项目的发展，保持清晰的结构有助于管理复杂性，并保持代码库的整洁。</p>
</li>
<li>
<p><strong>提高可维护性</strong>：当你需要更新或修复某些内容时，可以快速找到相关文件，从而节省时间并减少错误。</p>
</li>
</ol>
<h3 id="8">8. 保持一致的代码格式</h3>
<p>一致的代码格式 有助于提高可读性，使代码更加清晰、有条理。</p>
<p>为你的项目确立一种编写代码的模式，例如使用两个空格缩进，或者在注释前始终包括一个换行符。</p>
<p>遵循一致的代码格式可以让代码更易读、更易维护，并提升项目的整体质量。</p>
<p><strong>🛠️ 代码格式化工具</strong></p>
<ul>
<li>
<p><a href="https://prettier.io/"><strong>Prettier</strong></a>：基于一套规则自动格式化代码，确保代码风格一致。<a href="https://www.freecodecamp.org/news/how-to-use-prettier-in-visual-studio-code/">这里有一个教程</a>，其中讲解如何在 VSCode 中设置和使用 Prettier，从而来帮助提高你的代码的可读性。</p>
</li>
<li>
<p><a href="https://eslint.org/"><strong>ESLint</strong></a>：用于强制执行编码规范，突出潜在问题。<a href="https://www.freecodecamp.org/news/how-to-set-up-eslint-prettier-stylelint-and-lint-staged-in-nextjs/#heading-set-up-eslint">这里有一个教程</a>，其中包含关于为你的项目设置 ESLint 的实用且深入的部分。</p>
</li>
</ul>
<h3 id="9">9. 避免硬编码</h3>
<p>硬编码指的是，直接在代码中嵌入数据值，例如将用户 ID 设置为 <code>123</code> 而不是使用变量。</p>
<p>避免硬编码可以提高代码的复用性，减少手动修改的需求。相反，你应该将值存储在变量、常量或配置文件中，以便更灵活地调整和管理数据。</p>
<p>以下是硬编码可能导致问题的一个场景：：</p>
<pre><code>// 错误示例：硬编码的用户限制
function createUser(name) {
    let numberOfUsers = 100; // 硬编码值
    if (numberOfUsers &gt;= 100) {
        return '用户数量已达上限。';
    }
    // 创建用户的代码
    return '用户已创建。';
}
</code></pre>
<p>在这个错误示例中，<code>numberOfUsers</code> 被硬编码为 <code>100</code>。如果您想要更改用户限制，您必须在代码中找到并修改此值。如果这个值在多个地方被重复使用，那么修改起来会变得繁琐且容易出错。</p>
<h4 id=""><strong>🏗️ 使用常量的优化示例</strong></h4>
<p>现在，让我们对代码进行重构，使用常量来替代硬编码：</p>
<pre><code>// 良好示例：使用常量
const MAX_USERS = 100; // 将限制存储在常量中

function createUser(name) {
    let numberOfUsers = getCurrentUserCount(); // 从函数或数据库获取当前计数
    if (numberOfUsers &gt;= MAX_USERS) {
        return '用户数量已达上限。';
    }
    // 创建用户的代码
    return '用户已创建。';
}

// 获取当前用户数量的示例函数
function getCurrentUserCount() {
    // 模拟从数据库获取当前计数
    return 90; // 示例计数
}
</code></pre>
<p><strong>🥣 优化示例解析：</strong></p>
<ol>
<li>
<p><strong>使用常量</strong>：<code>MAX_USERS</code> 常量被定义在代码顶部。如果未来需要更改最大用户数，只需修改这一处，而不必在代码中四处查找并修改硬编码的值</p>
</li>
<li>
<p><strong>动态值</strong>：<code>getCurrentUserCount()</code> 函数会从数据库或其他数据源中动态获取当前的用户数量。这种方法避免了对数量进行硬编码，便于轻松更改。</p>
</li>
<li>
<p><strong>可维护性</strong>：将值存储在常量中，使你的代码更易于维护。如果业务需求发生变化，并且您需要将用户限制增加到 <code>150</code>，只需将 <code>MAX_USERS</code> 从 <code>100</code> 更改为 <code>150</code>，整个应用程序都会反映出这一更改。</p>
</li>
<li>
<p><strong>清晰性</strong>：为常量使用描述性的名称（如 <code>MAX_USERS</code>）可提高代码的可读性。任何查看您代码的人都能迅速明白这个值所代表的含义。</p>
</li>
</ol>
<p><strong>🤐 何时使用配置文件</strong></p>
<p>在大型应用程序中，你可能还会考虑使用配置文件（如 JSON、YAML 或环境变量）来存储在不同环境（开发、预发布、生产）之间可能会改变的值。</p>
<p>例如，在你的 <strong>config.json</strong> 文件中，你可以将 <code>maxUsers</code> 硬编码（但请注意，在 config.json 中，建议使用驼峰命名法，以保持格式一致性）：</p>
<pre><code>{
    "maxUsers": 100,
    "emailService": {
        "service": "gmail",
        "user": "your-email@gmail.com",
        "pass": "your-email-password"
    }
}
</code></pre>
<p><strong>🪴 在代码中使用配置文件：</strong></p>
<pre><code>const config = require('./config.json');

function createUser(name) {
    let numberOfUsers = getCurrentUserCount(); 
    if (numberOfUsers &gt;= config.maxUsers) {
        return 'User limit reached.';
    }
    // 代码以创建用户
    return 'User created.';
}
</code></pre>
<h3 id="10">10. 限制函数长度</h3>
<p>较长的函数难以理解和维护。</p>
<p>虽然没有严格的规定，但通常来说，函数的长度最好控制在 20 到 30 行以内。如果一个函数承担了多个职责或包含过多的步骤，这往往意味着它太长了。需将这些函数拆分成为较小的“辅助”函数，可以使代码更易管理和理解。</p>
<p>下面是一个冗长而复杂的函数示例：</p>
<pre><code>function updateCart(cart, item, discountCode) {
    // 将商品加入购物车
    cart.items.push(item);

    // 计算新的总价
    let total = 0;
    cart.items.forEach(cartItem =&gt; {
        total += cartItem.price * cartItem.quantity;
    });

    // 如果有折扣码则应用折扣
    if (discountCode) {
        total = applyDiscount(total, discountCode);
    }

    // 记录交易
    console.log(`Item added: ${item.name}, New total: $${total}`);

    return total;
}
</code></pre>
<p>⚠️ <strong>这个函数执行了多个任务：</strong></p>
<ol>
<li>
<p>将商品加入购物车。</p>
</li>
<li>
<p>计算总价格。</p>
</li>
<li>
<p>如果有折扣码，则应用折扣。</p>
</li>
<li>
<p>记录交易信息。</p>
</li>
</ol>
<p>虽然该函数目前看起来可以管理，但如果继续增加任务，它会迅速变得难以调试和维护。</p>
<p>让我们将这个冗长的函数拆分为更小、单一职责的函数：</p>
<pre><code>function updateCart(cart, item, discountCode) {
    addItemToCart(cart, item); 
    let total = calculateTotal(cart);

    if (discountCode) {
        total = applyDiscount(total, discountCode);  
    }

    logTransaction(item, total); 
    return total;  
}

function addItemToCart(cart, item) {
    cart.items.push(item);
}

function calculateTotal(cart) {
    return cart.items.reduce((total, cartItem) =&gt; total + cartItem.price * cartItem.quantity, 0);
}

function logTransaction(item, total) {
    console.log(`Item added: ${item.name}, New total: $${total}`);
}
</code></pre>
<h4 id="">🧩 详细解释：</h4>
<ol>
<li>
<p><code>addItemToCart</code>：该函数仅负责将商品添加到购物车。它简单且目标明确，不包含任何额外逻辑。</p>
</li>
<li>
<p><code>calculateTotal</code>：函数计算购物车内所有商品的总价。这样代码更易读、易理解，如果你未来需要修改计算方式，只需调整这个函数即可。</p>
</li>
<li>
<p><code>logTransaction</code>：负责记录详细信息。如果你以后需要更改日志内容（例如，添加时间戳），只需修改该函数，而不会影响其他部分。</p>
</li>
<li>
<p><code>updateCart</code>：作为主函数，它现在更像是一个流程总结——先添加商品，然后计算总价、再应用折扣，最好记录交易结果。这种方式使代码一目了然，更清晰易懂。</p>
</li>
</ol>
<p><strong>📒 让我们总结一下限制函数长度的要点：</strong></p>
<ol>
<li>
<p><strong>🎯 专注于单一任务</strong>: 每个函数最好只执行一个任务。如果一个函数同时处理多个任务，不妨考虑将其拆分成更小的函数。。</p>
</li>
<li>
<p><strong>🩼 使用辅助函数</strong>: ：辅助函数是小型且专注的函数，用于执行特定任务并支持主函数的运行。例如，上述示例中的<code>addItemToCart</code>、<code>calculateTotal</code> 和 <code>logTransaction</code> 就是辅助函数。</p>
</li>
<li>
<p><strong>🪦 使用描述性命名</strong>: 函数名称应该能够清楚表达其功能（例如 <code>addItemToCart</code>），这样可以让代码自解释，减少对额外注释的依赖。</p>
</li>
</ol>
<h2 id="">整洁代码的最佳实践</h2>
<p>现在我们已经介绍了一些重要的技巧，现在让我们来看一下整洁代码背后的核心原则：</p>
<ol>
<li>
<p><strong>🎏 简单性</strong>: 始终让代码尽可能简单，避免不必要的复杂度。</p>
</li>
<li>
<p><strong>🧂 一致性</strong>: 保持代码在风格和结构上的统一性，使其更易读和维护。</p>
</li>
<li>
<p><strong>🌾 清晰性</strong>: 代码应该清楚地表达它的功能，无需额外的解释。</p>
</li>
<li>
<p><strong>⛽ 高效性</strong>: 在不牺牲可读性的前提下，编写优化的代码，以提升性能。</p>
</li>
</ol>
<p>这些原则表明，编写代码不仅仅是写代码，更是设计解决方案。整洁代码是一种需要不断练习的技能，所以请保持学习，不断提升自己！</p>
<p><strong>🔌 关于依赖项的说明</strong></p>
<p>与其直接在代码中硬编码依赖项，不如使用包管理工具来管理它们，如 <a href="https://www.npmjs.com/"><strong>npm</strong></a>（用于 JavaScript）或 <strong>pip</strong>（用于 Python）这样的包管理器来统一规划它们。这样，你可以轻松更新或移除依赖项，确保代码的灵活性和可维护性。</p>
<h3 id="">总结 🏁</h3>
<p>编写整洁的代码就像为房子打下坚实的地基。它能让整个项目井然有序，使你在项目扩展时轻松添加新功能或修复问题。</p>
<p>掌握这些技巧后，你可以逐步养成让代码更易读、更易维护的良好习惯，同时让编写代码变得更加高效且愉悦。</p>
<h3 id="">建议的下一步 📘</h3>
<p>如果你想在六个月内成为后端开发者，可以参考我的<a href="https://codewithshahan.gumroad.com/l/pcela">后端开发人员路线图</a>。该路线图旨在帮助初学者按照每周目标稳步学习，其中涵盖关键的技能、工具和技术。这个路线图可以让你的学习过程更加有条理、易坚持。</p>
<p><strong>你可以在</strong> <a href="https://x.com/shahancd"><strong>𝕏</strong></a> <strong>上关注我，以获取实时更新。</strong></p>
<p>期待我们的下次再见！</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Python 中的聚类——机器学习工程手册 ]]>
                </title>
                <description>
                    <![CDATA[ 您是否想学习如何发现与分析数据中隐藏的模式呢？聚类，作为一项基本的无监督机器学习技术，能够助力您揭示极具价值的洞察，进而从根本上转变您对复杂数据集的认知。 在这本全面的手册中，我们将深入探讨一些必须掌握的聚类算法和技术，并辅以相关理论作为支撑。随后，您将通过大量实例、Python 实现方法和其可视化效果来知晓其工作原理。 无论您是刚入门的初学者，或者是富有经验的数据科学家，这本手册都是熟悉掌握聚类技术的宝贵资源。您也能够在此下载这本手册 [https://join.lunartech.ai/clustering-in-python]。 倘若您同样喜欢通过听来学习，这里还有个 15 分钟的播客，在其中我们更详细地讨论了聚类。在这一期里，我们探索了聚类的基本理念，从而帮助您更深入地了解如何将这些技术应用于现实世界的数据。 以下是我们将要涵盖的内容：  1.  无监督学习导论              2.  监督学习与无监督学习的区别              3.  核心术语解析      ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/clustering-in-python-a-machine-learning-handbook/</link>
                <guid isPermaLink="false">67ced2e93d55950475baa158</guid>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Qingfeng Huang ]]>
                </dc:creator>
                <pubDate>Mon, 10 Mar 2025 12:08:03 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2025/03/0f8cd7d3-54d4-49a3-b864-e3e477446089.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/clustering-in-python-a-machine-learning-handbook/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Learn Clustering in Python – A Machine Learning Engineering Handbook</a>
      </p><!--kg-card-begin: markdown--><p>您是否想学习如何发现与分析数据中隐藏的模式呢？聚类，作为一项基本的无监督机器学习技术，能够助力您揭示极具价值的洞察，进而从根本上转变您对复杂数据集的认知。</p>
<p>在这本全面的手册中，我们将深入探讨一些必须掌握的聚类算法和技术，并辅以相关理论作为支撑。随后，您将通过大量实例、Python 实现方法和其可视化效果来知晓其工作原理。</p>
<p>无论您是刚入门的初学者，或者是富有经验的数据科学家，这本手册都是熟悉掌握聚类技术的宝贵资源。您也能够<a href="https://join.lunartech.ai/clustering-in-python">在此下载这本手册</a>。</p>
<p>倘若您同样喜欢通过听来学习，这里还有个 15 分钟的播客，在其中我们更详细地讨论了聚类。在这一期里，我们探索了聚类的基本理念，从而帮助您更深入地了解如何将这些技术应用于现实世界的数据。</p>
<iframe width="100%" height="152" src="https://open.spotify.com/embed/episode/2O3KSW25GbqCJXl6LfUmyw" title="Spotify embed" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" allowfullscreen="" loading="lazy"></iframe>
<h3 id="">以下是我们将要涵盖的内容：</h3>
<ol>
<li>
<p><a href="#heading-introduction-to-unsupervised-learning">无监督学习导论</a></p>
</li>
<li>
<p><a href="#heading-supervised-vs-unsupervised-learning">监督学习与无监督学习的区别</a></p>
</li>
<li>
<p><a href="#heading-important-terminology">核心术语解析</a></p>
</li>
<li>
<p><a href="#heading-how-to-prepare-data-for-unsupervised-learning">无监督学习的数据预处理方法</a></p>
</li>
<li>
<p><a href="#heading-clustering-explained">聚类概念解析</a></p>
</li>
<li>
<p><a href="#heading-k-means-clustering">K-均值聚类</a></p>
<ul>
<li>
<p><a href="#heading-k-means-clustering-python-implementation">K-均值聚类：Python 的实现方法</a></p>
</li>
<li>
<p><a href="#heading-k-means-clustering-visualization">K 均值聚类：可视化的实现方法</a></p>
</li>
</ul>
</li>
<li>
<p><a href="#heading-elbow-method-for-optimal-number-of-clusters-k">肘部法则：选择最佳聚类数(K)</a></p>
</li>
<li>
<p><a href="#heading-hierarchical-clustering">层次聚类</a></p>
<ul>
<li>
<p><a href="#heading-hierarchical-clustering-python-implementation">层次聚类：Python 的实现方法</a></p>
</li>
<li>
<p><a href="#heading-hierarchical-clustering-visualization">层次聚类: 可视化的实现方法</a></p>
</li>
</ul>
</li>
<li>
<p><a href="#heading-dbscan-clustering">DBSCAN 聚类</a></p>
<ul>
<li>
<p><a href="#heading-dbscan-clustering-python-implementation">DBSCAN 聚类: Python 的实现方法</a></p>
</li>
<li>
<p><a href="#heading-dbscan-clustering-visualization">DBSCAN 聚类: 可视化的实现方法</a></p>
</li>
</ul>
</li>
<li>
<p><a href="#heading-how-to-use-t-sne-for-visualizing-clusters-with-python">如何使用 t-SNE 在 Python 中可视化聚类</a></p>
</li>
<li>
<p><a href="#heading-more-unsupervised-learning-techniques">探索更多无监督学习技巧方法</a></p>
</li>
</ol>
<h3 id=""><strong>在通过本手册的学习探索后，你将具备以下技能：</strong></h3>
<ol>
<li>
<p><strong>理解无监督学习基础原理</strong> – 您将掌握监督学习与无监督学习的关键差异，并知晓聚类怎样融入更广泛的机器学习范畴.</p>
</li>
<li>
<p><strong>精通聚类相关的重要术语</strong> – 您将熟悉数据点、簇中心、距离度量和聚类评估方法等关键概念.</p>
</li>
<li>
<p><strong>数据预处理以适用于聚类</strong> – 您将会学习怎样处理缺失值、对数据集进行规范化、去除异常值，以及应用像 PCA 和 t-SNE 这类降维技术。</p>
</li>
<li>
<p><strong>深入领会聚类技术</strong> – 您将探索各种各样的聚类方法, 包括 K 均值算法、层次聚类和 DBSCAN，并了解何时使用每种方法。</p>
</li>
<li>
<p><strong>在 Python 中实现 K-均值聚类</strong> – 您将学习如何使用 Python 应用 K-均值算法，同时使用肘部法则优化聚类数量，并有效地可视化聚类结果。</p>
</li>
<li>
<p><strong>应用层次聚类</strong> – 您将理解聚合聚类和分裂聚类，学习如何构建树状图，并使用 Python 实现层次聚类。</p>
</li>
<li>
<p><strong>使用 DBSCAN 进行基于密度的聚类</strong> – 您将掌握 DBSCAN 的聚类方法，包括其识别噪声点和任意形状聚类的能力。</p>
</li>
<li>
<p><strong>可视化聚类结果</strong> – 您将能够使用 Matplotlib、Seaborn 和 t-SNE 等库为聚类结果生成有意义的可视化效果，从而有效地分析和解释数据。</p>
</li>
<li>
<p><strong>评估聚类性能</strong> – 您将学习如何使用轮廓分数、戴维森堡丁指数和方差比准则等技术评估聚类质量。</p>
</li>
<li>
<p><strong>使用真实世界的数据集来进行工作</strong> – 您将获得在真实世界的数据集上应用聚类技术的实践经验，包括客户细分、异常检测和模式识别。</p>
</li>
<li>
<p><strong>扩展您在聚类之外的知识</strong> – 您将接触到其他无监督学习技术，例如混合模型和主题建模，从而拓宽您在机器学习方面的专业知识。</p>
</li>
</ol>
<p>通过学习本手册，您将在聚类和无监督学习方面打下坚实的基础，这将使您能够自信地分析复杂数据集并发现其隐藏的模式！</p>
<h3 id=""><strong>前提条件</strong></h3>
<p>在深入研读这本关于聚类和无监督学习的手册之前，您应当对机器学习概念、数据预处理技术以及基础的 Python 编程技能有扎实的了解。这些前提条件将有助于您理解本书中涵盖的理论基础和实际应用。</p>
<p>首先也是最重要的是，要熟悉 <em><strong>机器学习的基础知识</strong></em>。您应当了解有监督学习和无监督学习之间的区别，以及聚类技术背后的核心原理。</p>
<p>诸如数据点、特征、距离度量（欧几里得距离、曼哈顿距离）， 以及相似性度量等概念在聚类算法中的重要作用. 而掌握概率、统计学和线性代数的基础知识也将对帮助您理解有所帮助，因为这些数学概念构成了许多机器学习模型的基础。</p>
<p>接下来，<strong>数据预处理技术</strong> 对于处理真实世界的数据集起到至关重要的作用。由于聚类算法严重依赖于结构良好的数据，您需要了解如何处理缺失值、对数值特征进行归一化或标准化以及去除可能扭曲聚类结果的异常值。</p>
<p>诸如特征缩放（最小 - 最大归一化、标准化）和降维（PCA, t-SNE）之类的技术能够提高聚类的准确性和效率，让您更易于解读结果。</p>
<p>最后，要跟上本手册中的实践操作，需要具备 <strong>熟练的Python 编程和掌握数据科学库能力</strong>。您应当能够熟悉使用诸如 NumPy 和 Pandas 等库来进行数据处理，使用 Matplotlib 和 Seaborn 进行数据可视化，以及使用 Scikit-learn 实现机器学习算法。</p>
<p>由于您将应用诸如 K-均值算法、层次聚类和 DBSCAN 等聚类技术，熟悉使用 Jupyter Notebooks 编写和执行 Python 脚本以及解读聚类结果，将有助于提升您的学习体验</p>
<p>在这些领域打下坚实的基础后，您将能够充分发挥聚类的优势，从数据中获取更深入的洞察。</p>
<h2 id=""><strong>无监督学习导论</strong></h2>
<p>无监督学习是机器学习中的一项强大技术，它能够在没有预定义标签或目标变量的情况下揭示数据中的隐藏模式和结构。不同于依赖标签数据进行训练的监督学习，无监督学习使我们能够探索和理解无标签数据集的内在结构。</p>
<p>无监督学习的一个重要应用是聚类。聚类是一种根据数据点的内在特征和相似性将其分组的过程。通过识别数据集中的模式和关系，聚类能够帮助我们提取有价值的见解，并理解复杂数据的结构。</p>
<p>聚类在多个领域具有重要应用，包括客户分群、异常检测、图像识别和推荐系统。它能够识别数据中的不同群组，将数据分类为有意义的类别，并揭示数据集背后的潜在趋势。</p>
<p>在后续章节里，我们将深入探讨不同的聚类算法，包括 K-Means、层次聚类 和 DBSCAN, 分析其理论基础、实现方法与可视化呈现。在本手册结束时，您将全面掌握无监督学习的相关知识，能够熟练运用各类聚类技术到自己的数据分析工作中，具备相应的知识与技能。</p>
<p>请记住，聚类只是无监督学习的一个方面，它还包含许多其他技术和应用。让我们深入探索无监督学习的精彩世界，挖掘它在从无标签数据中提取洞察方面的强大能力！</p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://dataexpertise.in/wp-content/uploads/2023/12/Supervised-vs.-Unsupervised-Learning-1.jpg" alt="监督学习与无监督学习之间的区别 " width="1600" height="880" loading="lazy"></a></p>
<h2 id="vs">监督学习 vs. 无监督学习</h2>
<p>谈到机器学习，主要有两种方法：有监督学习和无监督学习。理解这两种方法之间的差异对于根据您的数据分析需求选择合适的技术至关重要。</p>
<p>监督学习，顾名思义，是指在有标签的数据上训练机器学习模型。在这种方法中，输入数据包含特征（也称为属性或变量）以及相应的目标值或标签。模型从这些有标签的数据中学习，并根据新的、未见过的数据进行预测或分类。</p>
<p>而另一方面，无监督学习完全是关于探索未标记的数据。在无监督学习中，数据没有预定义的标签或目标值。相反，算法自行在数据中寻找模式、结构和关系。其目标是发现隐藏的见解，并更深入地了解数据的潜在结构。</p>
<p>值得一提的是，无监督学习的一个关键优势是能够发现此前未知的模式和关系。因为由于不受标签数据的限制，无监督算法可以揭示其他分析方法可能难以察觉的有价值信息。这使得无监督学习在探索性数据分析、异常检测和聚类等领域尤为有用。</p>
<p>在监督学习中，目标变量起到引导作用，使模型能够进行准确的预测或分类。然而，这种对标签数据的依赖也会限制模型的能力，因为它可能难以处理训练数据中未出现的全新模式或未被充分表示的数据。</p>
<p>相比之下，无监督学习提供了一种更灵活和自适应的方法。即使没有明确的标签，它仍能捕捉数据的内在结构和关系。通过利用聚类算法和降维技术，无监督学习为解析复杂数据集提供了强大的工具。</p>
<p>总而言之，监督学习适用于有标签数据的任务，目标是进行精准的预测或分类。而无监督学习在探索数据中的隐藏模式和关系时尤为重要，特别是在标签数据稀缺或不存在的情况下。</p>
<p>通过理解这两种方法之间的差异，您可以有效地选择正确的技术，从而充分发挥数据分析工作的潜力。</p>
<h2 id=""><strong>关键术语</strong></h2>
<p>想要全面理解无监督学习和聚类，熟悉与这些概念相关的关键术语至关重要。以下是一些您应该了解的重要术语：</p>
<p><strong>1. 数据点</strong></p>
<p>数据点指的是数据集中单个的观测值或实例。每个数据点包含描述特定对象或事件的各种特征或属性。</p>
<p><strong>2. 聚类数量</strong></p>
<p>聚类的数量代表了在聚类过程中，数据将被划分成不同组别的期望数量或估计数量。这是一个关键参数，它决定了最终聚类的结构。</p>
<p><strong>3. 无监督算法</strong></p>
<p>无监督算法是一种数学方法，用于在没有标记或预分类示例的情况下识别数据中的模式或关系。这些算法能够探索数据集的内在结构，挖掘隐藏的规律。</p>
<p>理解并运用这些术语，将为您的无监督学习和聚类之旅奠定坚实的基础。而在接下来的部分中，我们将更深入地探讨如何在 Python 中实现聚类技术的实际应用。</p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://cdn.letterdrop.co/pictures/fe3db832-862f-4a35-be7c-37231ad814bb.png" alt="这张图展示了从数据收集到清理、转换、降维和拆分的准备过程。出自机器学习的数据准备: 终极指南| Pecan AI" width="1024" height="576" loading="lazy"></a></p>
<h2 id=""><strong>如何为无监督学习做数据预处理</strong></h2>
<p>在实施无监督学习算法之前，确保数据已经过适当的预处理至关重要。这包括采取某些步骤来优化输入数据，使其适合使用聚类技术进行分析。以下是为无监督学习准备数据时的重要考虑因素：</p>
<h3 id=""><strong>数据归一化</strong></h3>
<p>数据准备的一个关键方面是归一化，即把所有特征都缩放到一致的范围。这是必要的，因为数据集中的变量可能具有不同的单位或量级。</p>
<p>归一化有助于在聚类过程中避免对任何特定特征产生偏向。常见的归一化方法包括最小 - 最大缩放和标准化。</p>
<h3 id=""><strong>处理缺失值</strong></h3>
<p>处理缺失值是数据预处理中的关键步骤。在应用聚类算法之前，必须识别并解决数据集中存在的缺失值。</p>
<p>处理缺失值的方法多种多样，其中一种常见技术是插值，即使用统计方法或算法估算缺失值并进行填充。</p>
<h3 id=""><strong>异常值检测与处理</strong></h3>
<p>异常值可能会显著影响聚类结果，因为它们可能会干扰簇边界的确定。因此，及时检测和处理异常值至关重要。常见的方法包括 Z-score 分析和四分位距（IQR）分析，用于识别并处理异常值。</p>
<h3 id=""><strong>降维</strong></h3>
<p>在某些情况下，数据集可能具有很高的维度，这意味着它包含大量的特征。高维数据可能难以有效地可视化和分析。可以采用诸如主成分分析（PCA）之类的降维技术来减少特征的数量，同时保留数据中最具信息量的部分。</p>
<p>通过仔细准备数据、标准化变量、处理缺失值、解决异常值以及在必要时降低维度，您可以优化无监督学习算法的输入数据质量。这能确保聚类结果准确且有意义，从而在数据中发现有价值的见解和模式。</p>
<p>请记住，数据准备是无监督学习过程中至关重要的步骤，它为成功的聚类分析奠定基础。</p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://cdn.analyticsvidhya.com/wp-content/uploads/2019/08/An-Introduction-to-K-Means-Clustering-.webp" alt="K 均值聚类的可视化图，彩色数据点在坐标平面上按聚类排列。周围有图表和数学公式，说明了聚类分配和质心。- Analytics Vidhya" width="872" height="473" loading="lazy"></a></p>
<h2 id=""><strong>聚类解析</strong></h2>
<p>聚类是无监督学习中的一项基本技术，在揭示数据中的隐藏模式方面发挥着关键作用。它通过基于数据点的相似性对其进行分组，使我们能够识别数据集中的不同子集或簇。通过分析这些簇的结构，我们可以获得有价值的见解并做出基于数据的决策。</p>
<h3 id=""><strong>聚类的概念</strong></h3>
<p>从本质上讲，聚类旨在找出数据点之间的相似性或关系，而无需任何预定义的标签或目标变量。其目标是在每个聚类内部最大化相似性，同时在不同聚类之间最大化差异性。这一过程使我们能够识别数据中的模式和内在结构。</p>
<p>聚类可以通过多种因素来定义，例如距离、连通性或密度。聚类中的每个数据点与其他同一聚类中的点的相似度高于与其他聚类中的点的相似度。这种分组方式使我们能够对数据进行细分，这在诸如客户细分、异常检测和图像识别等各种领域中都具有极大的用处。</p>
<h3 id=""><strong>聚类算法的类型</strong></h3>
<p>有几种聚类算法可供选择，每种算法都有其自身将数据划分成簇的方法。一些流行的算法包括 K 均值聚类、层次聚类和 DBSCAN（基于密度的空间聚类算法，用于处理噪声和离群点）。</p>
<h4 id="1k"><strong>1. K-均值聚类</strong></h4>
<p>K 均值聚类是一种被广泛使用的算法，旨在将数据划分成 K 个不同的簇。它通过迭代地将每个数据点分配给最近的簇中心，然后重新计算簇中心来实现这一目标。这个过程会一直持续到收敛，从而形成定义明确的簇。</p>
<h4 id="2"><strong>2. 层次聚类</strong></h4>
<p>层次聚类通过基于特定标准递归地划分或合并聚类来创建聚类的层次结构。这种方法可以用树状图来表示，树状图能为聚类的层次结构以及聚类之间的关系提供有价值的见解。</p>
<h4 id="3dbscan"><strong>3. DBSCAN 聚类</strong></h4>
<p>DBSCAN 是一种基于密度的算法，它根据数据点的密度和连通性对其进行分组。该算法特别适用于识别任意形状的簇以及处理噪声数据。</p>
<p>这些只是聚类算法的几个例子，每种算法都有其自身的优势和适用于特定场景的情况。根据数据特征和问题领域选择最合适的算法是很重要的。</p>
<p>在接下来的部分中，我们将更深入地探讨这些聚类算法的理论、实现和可视化，以便为您提供对其工作原理以及何时使用它们的全面理解。</p>
<p>需要谨记的是，聚类是一种强大的技术，它能让我们挖掘出数据中隐藏的结构，从而获得宝贵的见解并做出明智的决策。所以就让我们一起走进聚类的世界，探索它所蕴含的潜力。</p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://images.squarespace-cdn.com/content/v1/5acbdd3a25bf024c12f4c8b4/1608407348392-22767PJ7RQ85BD5RLSLZ/k-means-clustering.png" alt="K-均值聚类 — 机器学习与人工智能的科学" width="1127" height="867" loading="lazy"></a></p>
<h2 id="k"><strong>K-均值聚类</strong></h2>
<p>K 均值聚类是一种常见的无监督学习算法，基于数据点之间的相似性将其划分到不同的簇中。在本节中，我们将深入探讨 K 均值聚类的理论原理，并使用 scikit-learn 库在 Python 中实现该算法。</p>
<p>在数据科学和数据分析领域，我们常常希望将观察结果归类到一系列的 <strong>分段</strong> 或者 <strong>簇</strong> 以满足不同的分析需求。例如，一家公司可能希望根据客户的交易历史或购买频率，将客户分为 3 到 5 组。这通常是一种 <strong>无监督</strong> <strong>学习</strong> 方法，因为这些标签(组别/分段/簇）在分析前是未知的。</p>
<p>将观测值聚类分组的最流行方法之一是无监督聚类算法<strong>K-均值</strong>. 以下是 K 均值聚类的条件：</p>
<ul>
<li>
<p>需要提前指定聚类的数量：K</p>
</li>
<li>
<p>每个观察结果都需要至少属于一个类别</p>
</li>
<li>
<p>每个观测值都必须只属于一个类别（类别之间不能有重叠）</p>
</li>
<li>
<p>任何观测值都不应属于超过一个类别。</p>
</li>
</ul>
<p>K 均值算法背后的理念在于 <strong>最小化簇内方差，并最大化簇间方差</strong> 因此，K 均值算法将观测值划分成 K 个簇，使得所有 K 个簇的总簇内方差尽可能小。</p>
<p>其背后的动机是将观测值进行聚类，使得聚类到同一组的观测值尽可能相似，而来自不同组的观测值尽可能不同。</p>
<p>从数学角度而言，簇内变异的定义取决于您自行选择的距离度量方式。例如，您可以选用欧几里得距离、曼哈顿距离等作为距离度量方式。</p>
<p>K 均值聚类在簇内变异最小的情况下是最优的 簇 C_k 的簇内差异量度 W(C_k) 反映了同一簇内观测点之间的差异程度。因此，需要解决以下优化问题：</p>
<p>$$\min_{C_1, \dots, C_K} \sum_{k=1}^{K} W(C_k)$$</p>
<p>其中，基于欧几里得距离的簇内差异可以表示如下：</p>
<p>$$W(C_k) = \frac{1}{|C_k|} \sum_{i,i' \in C_k} \sum_{j=1}^{p} (x_{ij} - x_{i'j})^2$$</p>
<p>第 k 个聚类中的观测值数量用 |C_k | 表示。因此，K 均值的优化问题可以描述如下：</p>
<p>$$\min_{C_1, \dots, C_K} \left\{ \sum_{k=1}^{K} \frac{1}{|C_k|} \sum_{i,i' \in C_k} \sum_{j=1}^{p} (x_{ij} - x_{i'j})^2 \right\}$$</p>
<h3 id="k"><strong>K-均值算法</strong></h3>
<p>K 均值算法的伪代码可描述如下：</p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://miro.medium.com/v2/resize:fit:1400/1*0DjFFWY4tY74Z8EMXggEMA.png" alt="替换文本: 该图展示了 K 均值算法的伪代码，包含两个主要步骤。步骤 1：在初始条件下，将每个数据点随机分配到一个簇中。步骤 2：当簇发生变化时，更新簇的质心并重新分配数据点，直至收敛。" width="1400" height="718" loading="lazy"></a></p>
<p>K 均值算法是一种非确定性方法，其随机性体现在第一步，即所有观测值都被随机分配到 K 个类别中的一个。</p>
<p>在第二步中，对于每个聚类，通过计算该聚类中所有数据点的平均值来计算聚类质心。第 <em>Kth</em> 个聚类的质心是一个长度为 <em>p</em> 的向量，其中包含第  <em>kth</em> 个聚类中所有观测值的变量均值，而 <em>p</em> 为变量的数量。</p>
<p>然后，在下一步中，对观测值的簇进行更新，使得每个观测值都被分配到其质心最近的簇中，通过迭代最小化 <strong>总簇内平方和</strong> 来实现。也就是说，我们反复执行步骤 2 和 3，直到簇的质心不再变化或者达到最大迭代次数为止。</p>
<h3 id="kpython"><strong>K 均值聚类：Python 的实现方法</strong></h3>
<p>让我们来看一个将观测值分类为 4 类的例子。原始数据如下所示：</p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://miro.medium.com/v2/resize:fit:1200/1*QRRqHu4MATa7piwcPHmsSA.png" alt="标题为 &quot;原始数据可视化,&quot; 的散点图，显示绿色点分布在 X 值从 0 到 3 的列中，Y 值范围为 0 到 10。" width="600" height="600" loading="lazy"></a></p>
<pre><code># 导入项目所需要的库
# KMeans 是 scikit-learn 提供的聚类算法
from sklearn.cluster import KMeans  
# Metrics 模块用于评估聚类性能
from sklearn import metrics  
# 用于数值计算和数组操作
import numpy as np  
# Pandas 用于以结构化 DataFrame 格式处理数据
import pandas as pd  

# 生成用于 K-Means 聚类的合成数据
# 创建一个 100×2 的数组，元素为 0 到 9 之间的随机整数
df = np.random.randint(0, 10, size=[100, 2])  
# 生成一个 300×1 的数组，元素为 0 到 3 之间的随机整数
X1 = np.random.randint(0, 4, size=[300, 1])  
# 生成一个 300×1 的数组，元素为 0 到 10 之间的随机浮点数
X2 = np.random.uniform(0, 10, size=[300, 1])  
# 沿第二个轴（列方向）合并 X1 和 X2，形成具有两个特征的数据集
df = np.append(X1, X2, axis=1)  

# 在生成的数据集上应用 K-Means 聚类算法
# 调用 KMeans_Algorithm 函数，并设置簇数 K=4
Clustered_df = KMeans_Algorithm(df=df, K=4)  
# 将聚类后的数据转换为 Pandas DataFrame
df = pd.DataFrame(Clustered_df)  


# 执行 K-Means 聚类的函数
def KMeans_Algorithm(df, K):
    """
    在给定数据集上执行 K-Means 聚类。

    参数:
    df (array-like): 待聚类的输入数据集。
    K (int): 聚类的簇数.

    返回:
    df (DataFrame): 原始数据集，新增一列用于存储聚类标签。
    """

    # 使用指定参数初始化 K-Means 模型
    # 将聚类数量设置为 K
    # 使用 k-means++ 初始化以提高收敛速度
    # 将最大迭代次数设置为 300
    # 设置固定的随机种子以确保结果可复现
    KMeans_model = KMeans(
        n_clusters=K,  
        init='k-means++',  
        max_iter=300,  
        random_state=2021  
    )

    # 在数据集上拟合 K-Means 模型
    KMeans_model.fit(df)

    # 提取聚类质心（每个簇的中心点）。
    centroids = KMeans_model.cluster_centers_

    # 将质心转换为 DataFrame，并设置列名为 "X" 和 "Y"
    centroids_df = pd.DataFrame(centroids, columns=["X", "Y"])

    # 获取分配给每个数据点的聚类标签
    labels = KMeans_model.labels_

    # 将输入数据转换为 Pandas DataFrame（如果尚未转换）
    df = pd.DataFrame(df)

    # 添加新列以存储分配的聚类标签
    df["labels"] = labels

    # 返回包含聚类标签的更新后的 DataFrame
    return d
</code></pre>
<p><a href="https://lunartech.ai"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738528849086/9891484a-a8b0-45eb-a8e3-f1a76c038b73.png" alt="这是 Python 中 K-Means 聚类的代码截图。其中包括导入 scikit-learn、numpy 和 pandas 等库，生成合成数据，以及定义一个带有参数和 K-Means 模型初始化的聚类函数。该代码处理数据集，并返回带有聚类标签的 DataFrame。- lunartech.ai" width="1750" height="3052" loading="lazy"></a></p>
<p>此脚本旨在生成合成数据，应用 K-Means 聚类，并为每个数据点分配聚类标签。K-Means 聚类算法是一种无监督机器学习方法，它根据特征空间中数据点的接近程度将相似的数据点分组到不同的簇中。以下是该脚本的工作原理的逐步分解。</p>
<p>第一步是导入必要的库。 该脚本使用<code>sklearn.cluster</code> 中的 <code>KMeans</code> 来实现 K-均值聚类算法。虽然  <code>metrics</code> 模块在 <code>sklearn</code> 已经存在, 但是在本脚本中未被使用, 不过它对于评估聚类质量非常有用. <code>NumPy</code> 负责数值计算和数组操作，而 <code>Pandas</code>用于将数据组织成 DataFrame，以便更方便地操作和处理。</p>
<p>接下来，脚本生成合成数值数据。创建一个 NumPy 数组 <code>df</code>，其维度为 100×2，包含 0 到 9 之间的随机整数。此外，还分别生成两个额外的数组 <code>X1</code> and <code>X2</code>。其中 <code>X1</code> 为 300×1 的数组，包含 0 到 3 之间的随机整数，而 <code>X2</code> 为 300×1 的数组，包含 0 到 10 之间的随机浮点数。随后，这两个数组沿第二个轴（列方向）合并，形成一个具有两个特征的数据集，使其可以用于聚类分析。</p>
<p>在合成数据准备完成后，脚本应用 K-Means 聚类算法。调用 <code>KMeans_Algorithm</code> 函数，并设置 <code>K=4</code>, 即算法将数据分为四个簇。该函数返回聚类后的数据集，并将其转换为 Pandas DataFrame 进行进一步处理。</p>
<p><code>KMeans_Algorithm</code> 函数接受两个参数：数据集 <code>df</code> 和簇的数量 <code>K</code>. 在该函数内部，使用 <code>KMeans()</code> 初始化 K-均值模型. 簇的数量设为 <code>K</code>, 同时 <code>init='k-means++'</code> 参数用于优化初始化，以加快收敛速度。<code>max_iter=300</code> 限制最大迭代次数，以防止计算时间过长，而 <code>random_state=2021</code> 用于确保结果的可复现性。</p>
<p>初始化后，使用<code>KMeans_</code><a href="http://model.fit"><code>model.fit</code></a><code>(df)</code> 在数据集上拟合 K-均值模型。这一步骤处理数据集，识别聚类中心，并将数据点归入相应的簇。训练完成后，通过 <code>KMeans_model.cluster_centers_</code>提取聚类质心，并将其存储在一个 Pandas DataFrame 中，列名设为 "X" and "Y"，以便更直观地解析聚类结果。</p>
<p>每个数据点都会被分配一个聚类标签，该标签可通过 <code>KMeans_model.labels_</code>获取。 脚本随后确保数据集被存储为 Pandas DataFrame（如果尚未转换），并添加一个新的 <code>"labels"</code>列来存储分配的聚类标签。最终，返回包含原始特征及聚类结果的更新数据集。</p>
<p>该脚本的输出是一个 Pandas DataFrame，包含三列：两列数值特征列表示生成的数据点，另一列 <code>"labels"</code> 列指示每个数据点所属的聚类。 例如，简化的输出可能如下所示：一个数据点 <code>[2.0, 7.4]</code> 被分配到簇 <code>0</code>，而另一个数据点 <code>[1.0, 3.2]</code> 归属于簇 <code>1</code>.</p>
<p>该脚本成功创建了一个结构化数据集，将数据划分为四个不同的簇，并为每个数据点分配了相应的聚类标签。 结果可以通过散点图等可视化技术进一步分析，以更直观地理解聚类分布。未来的优化方向可能包括使用 轮廓系数评估聚类质量，或尝试不同的簇数量，以找到最优的聚类方案。</p>
<h3 id="k"><strong>K 均值聚类：可视化的实现方法</strong></h3>
<p>K-均值的关键优势之一在于其处理大型数据集时的简单性和高效性。它是一种在众多领域广泛使用的聚类算法，包括客户细分、图像压缩、异常检测和模式识别。</p>
<p>尽管K-均值算法简单，但它在发现数据中固有的群组结构方面非常有效，使其成为无监督学习中的重要工具。但和任何算法一样，它也有局限性，比如对初始质心选择的敏感性以及难以检测非球形簇。了解这些优缺点有助于在将K-均值应用于实际数据集时做出明智的决策。</p>
<p>在本节中，我们将探讨如何在 Python 中实现 K-均值聚类并可视化结果。通过逐步的代码实现，您将看到数据点是如何被分组到各个簇中的，以及算法是如何迭代地优化其簇分配的。我们还将讨论选择最优簇数的最佳实践以及如何评估聚类质量。</p>
<h3 id="k">洞悉K-均值算法</h3>
<p>在深入探讨实现方法之前，让我们先简要了解一下K-均值算法的工作原理。该算法遵循以下步骤：</p>
<ol>
<li>
<p><strong>步骤 1：初始化</strong> – 随机选择 K 个质心，其中 K 表示期望的聚类数量。</p>
</li>
<li>
<p><strong>步骤 2：分配任务</strong> – 根据欧几里得距离，将每个数据点分配给最近的质心。</p>
</li>
<li>
<p><strong>步骤 3: 更新</strong> – 通过取分配给每个聚类的所有数据点的平均值来重新计算质心</p>
</li>
<li>
<p><strong>步骤 4: 重复</strong> – 重复步骤 2 和 3，直至满足收敛标准（例如，质心移动极小）。</p>
</li>
</ol>
<pre><code>fig, ax = plt.subplots(figsize=(6, 6))

# 针对具有不同标签的观测数据，使用第 1 列和第 2 列进行可视化。
plt.scatter(df[df["labels"] == 0][0], df[df["labels"] == 0][1],
c='black', label='cluster 1')
plt.scatter(df[df["labels"] == 1][0], df[df["labels"] == 1][1],
c='green', label='cluster 2')
plt.scatter(df[df["labels"] == 2][0], df[df["labels"] == 2][1],
c='red', label='cluster 3')
plt.scatter(df[df["labels"] == 3][0], df[df["labels"] == 3][1],
c='y', label='cluster 4')
plt.scatter(centroids[:, 0], centroids[:, 1], marker='*', s=300, c='black', label='centroid')
plt.legend()
plt.xlim([-2, 6])
plt.ylim([0, 10])
plt.xlabel('X')
plt.ylabel('Y')
plt.title('聚类数据的可视化')
ax.set_aspect('equal')
plt.show()
</code></pre>
<p><a href="https://lunartech.ai"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738529023579/d49a6f1c-93fa-42ab-ac99-4e168d30c44c.png" alt="一张使用 Matplotlib 库的 Python 脚本的截图，该脚本用于可视化聚类数据。它在一个散点图中用不同的颜色和标签绘制了聚类，并包含一个黑色的质心标记。- lunartech.ai" width="1868" height="1116" loading="lazy"></a></p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://miro.medium.com/v2/resize:fit:1400/1*Isl-76ShvTNwa35Xu50yHA.png" alt="标题为“聚类数据可视化”的散点图，用四种不同颜色表示四个聚类：黑色代表聚类 1，绿色代表聚类 2，红色代表聚类 3，黄色代表聚类 4。黑色星号标记网格上的质心，X 轴和 Y 轴分别标注为 -2 至 6 和 0 至 10。图中还包含图例。" width="969" height="705" loading="lazy"></a></p>
<p>在上图中，K 均值算法已将这些观测值聚类为 4 组。从可视化效果来看，这些观测值的聚类方式甚至从图表上看也显得很自然，而且合乎情理。</p>
<h3 id="k"><strong>肘部法则确定最优聚类数（K）</strong></h3>
<p>使用 K 均值算法时面临的最大挑战之一是聚类数目的选择。有时这是业务决策，但大多数时候我们希望选择一个最优且合理的 K 值（即聚类数量）。确定这个最优 K 值（或聚类数量）最常用的方法之一是 <strong>肘部法则</strong>。</p>
<p>要使用这种方法，您需要了解什么是 <strong>惯性</strong>。惯性是样本到其最近聚类中心的平方距离之和。因此，惯性或 <strong>聚类内平方和</strong> 值可以表明不同聚类的连贯性或纯度如何。惯性可以这样描述：</p>
<p>$$\sum_{i=1}^{N} (x_i - C_k)^2$$</p>
<p>其中 N 是数据集中的样本数量，C 是一个聚类的中心，k 是聚类的索引。因此，惯性只是计算每个聚类中样本到其聚类中心的平方距离，并将它们相加。</p>
<p>然后我们可以计算不同聚类数量 K 的惯性。我们可以像下面的图一样绘制出来，其中我们考虑 K = 1, 2, ..., 10。然后从图中我们可以选择肘部出现时对应的 K 值。在这种情况下，肘部出现在 K = 3 时。</p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://miro.medium.com/v2/resize:fit:1400/1*S9wmsHzA4nVnZ7zSi9WfLA.png" alt="折线图展示了 K 均值肘部方法，其中 x 轴表示从 1 到 9 的聚类数量，y 轴表示惯性。该图表明在大约 3 个聚类时惯性急剧下降。" width="1400" height="667" loading="lazy"></a></p>
<pre><code>def Elbow_Method(df):
    inertia = []
    # 考虑 K = 1, 2,..., 10 作为 K
    K = range(1, 10)
    for k in K:
        KMeans_Model = KMeans(n_clusters=k, random_state = 2022)
        KMeans_Model.fit(df)
        inertia.append(KMeans_Model.inertia_)
    return(inertia)

K = range(1, 10)
inertia = Elbow_Method(df)
plt.figure(figsize = (17,8))
plt.plot(K, inertia, 'bx-')
plt.xlabel("K: 聚类数量")
plt.ylabel("惯性")
plt.title("K 均值：肘部法则")
plt.show()
</code></pre>
<p><a href="https://lunartech.ai"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738529158688/f8c4892b-962b-416d-9795-c442b149deee.png" alt="这段代码片段展示了 Python 中 K-Means 聚类的肘部法则。该函数计算了从 1 到 10 个聚类的惯性，并使用 Matplotlib 绘制结果，以确定最佳聚类数量。- lunartech.ai" width="1380" height="1042" loading="lazy"></a></p>
<p>K 均值算法是一种非确定性方法，其随机性体现在第一步，即所有观测值都被随机分配到 K 个类别中的一个。</p>
<p>正如您所见，K 均值聚类提供了一种基于相似性对数据点进行分组的高效且有效的方法。通过在 Python 中实现 K 均值算法，您可以轻松地将此技术应用于自己的数据集，并从数据中获得有价值的见解。</p>
<p>Python 提供了强大的工具来实现和可视化 K-Means 聚类。借助 scikit-learn 库和 matplotlib，您可以轻松地将 K-Means 应用于您的数据集，并从生成的聚类中获得很多有用的信息。</p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://media.geeksforgeeks.org/wp-content/uploads/20230427165259/Distance-Matrix-in-Hierarchical--Clustering.webp" alt="图示展示了层次聚类中的距离矩阵比较。图中说明了四种方法：最小法、最大法、组平均法和沃德法，每种方法都用圆圈和标有数字的点来表示数据簇。" width="1000" height="500" loading="lazy"></a></p>
<h2 id=""><strong>层次聚类理论</strong></h2>
<p>另一种常见的聚类技术是层次聚类。 这是一种无监督学习方法，可用于将观测数据聚类成不同的分组。但与 K-Means 不同，层次聚类最初将每个观测数据视为一个独立的簇。</p>
<h3 id="vs"><strong>凝聚层次聚类 vs. 分裂层次聚类</strong></h3>
<p>层次聚类主要有两种类型：凝聚层次聚类和分裂层次聚类。</p>
<p>凝聚聚类首先将每个数据点分配到其自身的簇中。然后，它基于选定的距离度量标准，迭代地合并最相似的簇，直到形成一个包含所有数据点的单个簇。</p>
<p>这种自下而上的方法会生成一种类似二叉树的结构，也称为树状图，其中每个节点的高度代表正在合并的簇之间的差异程度。</p>
<p>另一方面，分裂聚类从包含所有数据点的一个单一簇开始。然后，它递归地将簇划分为更小的子簇，直到每个数据点都在自己的簇中。这种自上而下的方法生成了一个树状图，提供了有关簇层次结构的见解。</p>
<h3 id=""><strong>层次聚类的距离度量</strong></h3>
<p>要确定聚类或数据点之间的相似性，您可以使用多种距离度量方法。常用的度量方法包括欧几里得距离、曼哈顿距离和余弦相似度。这些度量方法量化了成对数据点之间的差异或相似性，并指导聚类过程。</p>
<p>在这种技术中，最初每个数据点都被视为一个单独的簇。在每次迭代中，最相似或差异最小的簇合并为一个簇，此过程一直持续到只剩下一个簇为止。因此，该算法反复执行以下步骤：</p>
<ul>
<li>
<p>1: 找出距离最近的两个聚类。</p>
</li>
<li>
<p>2: 合并两个最相似的簇。</p>
</li>
<li>
<p>然后它继续这种迭代过程，直到所有的簇都合并在一起。</p>
</li>
</ul>
<p>两个簇之间的相异性或相似性计算取决于所选择的链接（Linkage）方式。常见的五种链接方法包括：</p>
<ul>
<li>
<p><strong>完全链接:</strong> 计算两个簇（K1 和 K2）之间的最大簇间差异性。具体而言，需要计算 K1 中所有观测点与 K2 中所有观测点之间的成对差异性，并选择其中最大的值作为簇间距离。</p>
</li>
<li>
<p><strong>单链接:</strong> 计算两个簇（K1 和 K2）之间的最小簇间差异性。具体而言，需要计算 K1 中所有观测点与 K2 中所有观测点之间的成对差异性，并选择其中最小的值作为簇间距离。</p>
</li>
<li>
<p><strong>平均链接:</strong> 计算两个簇（K1 和 K2）之间的平均簇间差异性。具体而言，需要计算 K1 中所有观测点与 K2 中所有观测点之间的成对差异性，并取这些差异值的平均值作为簇间距离。</p>
</li>
<li>
<p><strong>质心链接:</strong> 计算簇 K1 的质心与簇 K2 的质心之间的差异性。（这种链接方式通常较少被使用，因为它可能导致较多的簇重叠。）</p>
</li>
<li>
<p><strong>沃德法:</strong> 通过最小化每个观测点与簇内平均观测点之间的平方和距离，来决定如何合并簇。该方法倾向于生成大小相近的簇，并在层次聚类中常用于优化聚类结果。</p>
</li>
</ul>
<h3 id="python"><strong>层次聚类：Python 的实现方法</strong></h3>
<p>层次聚类是一种强大的无监督学习技术，可根据数据点之间的相似性将其分组为不同的簇。 在本节中，我们将探讨如何在 Python 中实现层次聚类。</p>
<p>以下是在 Python 中实现层次聚类的示例：</p>
<pre><code>import scipy.cluster.hierarchy as HieraarchicalClustering
from sklearn.cluster import AgglomerativeClustering
import numpy as np
import pandas as pd

# 生成层次聚类的数据
df = np.random.randint(0,10,size = [100,2])
X1 = np.random.randint(0,4,size = [300,1])
X2 = np.random.uniform(0,10,size = [300,1])
df = np.append(X1,X2,axis = 1)
hierCl = HieraarchicalClustering.linkage(df, method='ward')

Hcl= AgglomerativeClustering(n_clusters = 7, affinity = 'euclidean', linkage ='ward')
Hcl_fitted = Hcl.fit_predict(df)
df = pd.DataFrame(df)
df["labels"] = Hcl_fitted
</code></pre>
<p><a href="https://lunartech.ai"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738529216677/9b71d1c5-4847-4cc3-b847-0620409119d6.png" alt="使用 scipy、sklearn、numpy 和 pandas 等库进行层次聚类的 Python 代码截图。该代码生成随机数据，使用函数执行聚类，并输出带有标签的 DataFrame。- lunartech.ai " width="1734" height="968" loading="lazy"></a></p>
<p>这段代码使用了 Scipy 的层次聚类模块和 Scikit-learn 的凝聚聚类算法来实现层次聚类。该脚本的目的是生成一个合成数据集，应用层次聚类，并为数据点分配聚类标签。</p>
<p>脚本的第一部分导入了所需的库。导入了 Scipy 的层次聚类模块 (<code>scipy.cluster.hierarchy</code>) 并将其命名为 <code>HieraarchicalClustering</code>,用于执行基于链接的聚类。还导入了 Scikit-learn 的 <code>AgglomerativeClustering</code> 类来实现一种特定类型的层次聚类。此外，NumPy 用于数值运算和生成随机数据，而 Pandas 则用于将数据结构化为 DataFrame。</p>
<p>接下来，脚本生成合成的数值数据。创建一个 100×2 的矩阵 (<code>df</code>)，其中包含 0 到 9 之间的随机整数。然后分别创建两个额外的数据集 <code>X1</code> and <code>X2</code>。<code>X1</code> 包含 300 个 0 到 3 之间的随机整数，而<code>X2</code> 包含 300 个 0 到 10 之间的随机浮点数。然后使用 <code>np.append()</code>沿着第二轴将这两个数据集合并，形成一个具有两个特征的数据集，该数据集将用于聚类。</p>
<p>一旦数据集准备就绪，便使用 Ward 连接法进行层次聚类，该方法可使合并后的聚类之间的方差最小化。通过 <code>HieraarchicalClustering.linkage(df, method='ward')</code> 创建链接矩阵 <code>hierCl</code>，该函数用于计算层次聚类的解决方案。</p>
<p>生成层次聚类链接矩阵后，应用凝聚聚类将数据分为七个簇（<code>n_clusters=7</code>）。 <code>affinity='euclidean'</code> 参数指定使用欧几里得距离作为度量点之间相似性的距离度量。<code>linkage='ward'</code> 参数确保使用 Ward 方法合并簇，以最小化方差。然后使用 <a href="http://Hcl.fit"><code>Hcl.fit</code></a><code>_predict(df)</code> 将模型拟合到数据集，为每个数据点分配一个簇标签。</p>
<p>最后，数据集被转换为一个 Pandas 数据框，并添加了一个新列“labels”来存储分配的聚类标签。生成的数据框现在既包含原始数据点，也包含其对应的聚类分配，从而便于进一步分析或可视化。</p>
<p>总之，此脚本生成随机数据，使用 Scipy 的 linkage 方法和 Scikit-learn 的 Agglomerative Clustering 进行层次聚类，并为每个数据点分配聚类标签。最终的数据集可用于分析聚类结构、可视化结果或验证聚类效果。</p>
<h3 id=""><strong>层次聚类: 可视化的实现方法</strong></h3>
<p>层次聚类的一个关键优势在于其能够创建聚类的层次结构，这能够为数据点之间的关系提供有价值的见解。</p>
<p>在 Python 中可视化层次聚类，我们可以使用诸如 Scikit-learn、SciPy 和 Matplotlib 等多种库。这些库提供了易于使用的函数和工具，从而简化了可视化过程。</p>
<p>因此，在执行层次聚类之后，通常很有帮助的是将聚类结果可视化。我们可以使用各种可视化技术，例如树状图或热图。</p>
<p>正如我们上面所讨论的，树状图是一种树形图，用于展示聚类之间的层次关系。它可以通过 Python 中的 Scipy 库生成。</p>
<p>以下是一个在 Python 中可视化树状图和聚类点的示例：</p>
<pre><code># 生成一个树状图以帮助确定最佳聚类数量
# 树状图展示了层次聚类如何逐步合并数据点。
dendrogram = HieraarchicalClustering.dendrogram(hierCl)

# 设置树状图的标题
plt.title('树状图')

# 在 x 轴上标注以表明观测值（数据点）
plt.xlabel("观察结果")

# 在 y 轴上标注出各聚类之间的欧几里得距离
plt.ylabel('欧几里得距离')

# 显示树状图
plt.show()


# 使用散点图可视化聚类数据
# 每种颜色代表一个不同的类别。

# 将属于聚类 1 的所有点用黑色绘制出来
plt.scatter(df[df["labels"] == 0][0], df[df["labels"] == 0][1], 
            c='black', label='cluster 1')

# 将属于第 2 类的所有点都用绿色标出。
plt.scatter(df[df["labels"] == 1][0], df[df["labels"] == 1][1], 
            c='green', label='cluster 2')

# 将属于第 3 类的所有点都用红色标出。
plt.scatter(df[df["labels"] == 2][0], df[df["labels"] == 2][1], 
            c='red', label='cluster 3')

# 将属于第 4 类的所有点用洋红色绘制出来
plt.scatter(df[df["labels"] == 3][0], df[df["labels"] == 3][1], 
            c='magenta', label='cluster 4')

# 将属于第 5 类的所有点都用紫色标出。
plt.scatter(df[df["labels"] == 4][0], df[df["labels"] == 4][1], 
            c='purple', label='cluster 5')

# 将属于第 6 类的所有点用黄色标出
plt.scatter(df[df["labels"] == 5][0], df[df["labels"] == 5][1], 
            c='y', label='cluster 6')

# 将属于第 7 类的所有点用黑色标出
plt.scatter(df[df["labels"] == 6][0], df[df["labels"] == 6][1], 
            c='black', label='cluster 7')

# 在图中显示图例以标注每个聚类
plt.legend()

# 将代表特征 1（第一维度）的 x 轴标注出来
plt.xlabel('X')

# 将代表特征 2（第二维度）的 y 轴标注出来
plt.ylabel('Y')

# 设置散点图的标题
plt.title('层级聚类')

# 显示聚类散点图
plt.show()
</code></pre>
<p><a href="https://lunartech.ai"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738529338003/d04605b0-8c9e-46d9-8aac-0f62dc0a67d3.png" alt="一段用于在 Python 中可视化层次聚类的代码片段。它包括生成树状图以及创建散点图来表示聚类，每个聚类用不同的颜色表示。X 轴和 Y 轴都有标签，并且设置了图的标题以增强清晰度。该代码使用了 Matplotlib 库中的 、 、 和 等函数。 - lunartech.ai" width="1682" height="2680" loading="lazy"></a></p>
<p>以下是在 Python 中可视化层次聚类的分步指南：</p>
<p><strong>步骤 1：预处理数据</strong></p>
<p>在进行层次聚类的可视化之前，对数据进行预处理（如缩放或标准化）是很重要的。这能确保所有特征具有相似的范围，并防止对特定特征产生任何偏见。</p>
<p><strong>步骤 2: 执行层次聚类</strong></p>
<p>接下来，我们使用选定的算法，例如 Scikit-learn 中的 AgglomerativeClustering 执行层次聚类。该算法计算数据点之间的相似度，并根据特定的链接准则将它们合并为簇。</p>
<p><strong>步骤 3: 创建一个树状图</strong></p>
<p>我们可以使用 SciPy 库中的树状图函数来创建这种可视化效果。树状图能够让我们直观地看到各个聚类之间的距离和关系。</p>
<p><strong>步骤 4: 绘制聚类图</strong></p>
<p>最后，我们可以使用散点图或其他合适的可视化技术来绘制聚类。这有助于我们直观地看到每个聚类中的数据点，并深入了解每个聚类的特征。</p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://miro.medium.com/v2/resize:fit:1400/1*wIrFoLxUBv-8Y_cuskgukQ.png" alt="一张展示观测值层次聚类的树状图，采用欧几里得距离。图表用蓝色、绿色和橙色标注了聚类编号和分支。 - lunartech.ai" width="839" height="684" loading="lazy"></a></p>
<p>此树状图随后可帮助我们确定更优的聚类数量。如您所见，在这种情况下，我们似乎应该使用 7 个聚类。</p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://miro.medium.com/v2/resize:fit:1280/1*WBByBnOzYgVVhTvTc-d7PA.png" alt="标题为“层次聚类”的散点图，沿 X 轴显示七个垂直的聚类。每个聚类都有不同的颜色，并配有图例，标明聚类编号及对应颜色。- lunartech.ai" width="640" height="480" loading="lazy"></a></p>
<p>通过在 Python 中对层次聚类进行可视化，我们可以更好地理解数据内部的结构和关系。这种可视化技术在处理复杂数据集时特别有用，并且有助于决策过程和模式发现。</p>
<p>请记得根据您的数据集和目标调整具体的参数和设置。尝试不同的可视化方法和技术能够让您对数据有更深入的了解。</p>
<h2 id="dbscan"><strong>DBSCAN 聚类理论</strong></h2>
<p>DBSCAN (基于密度的空间聚类算法，用于处理噪声数据)是一种用于聚类分析的无监督学习算法。它特别擅长识别任意形状的聚类，并能处理噪声数据。</p>
<p>与 K-均值 或层次聚类不同，DBSCAN 不需要预先指定聚类的数量。相反，它根据数据中的密度和连通性来定义聚类。</p>
<h3 id="dbscan"><strong>DBSCAN 工作原理:</strong></h3>
<p><strong>基于密度的聚类</strong>: DBSCAN 会将彼此距离较近且具有足够数量近邻的数据点归为一组。它将数据点密集的区域识别为聚类，并将稀疏区域视为噪声。</p>
<p><strong>核心点、边界点和噪声点</strong>: DBSCAN 将数据点分为三种类型：核心点、边界点和噪声点。</p>
<ul>
<li>
<p>核心要点: 在指定距离（由 <code>eps</code> 参数定义）内具有最少数量邻近点（由 <code>min_samples</code> 参数定义）的数据点。</p>
</li>
<li>
<p>边界点: 位于核心点的 <code>eps</code> 距离内但相邻点数量不足，而不能被视为核心点的数据点。</p>
</li>
<li>
<p>噪声点：既非核心点也非边界点的数据点。</p>
</li>
</ul>
<p><strong>可达性与连通性</strong>:  DBSCAN 通过可达性和连通性的概念来定义簇。如果一个数据点可以通过一条由核心点（Core Points）连接的路径到达另一个数据点，则认为该数据点是可达的（reachable）。如果两个数据点是可达的，它们属于同一个簇。</p>
<p><strong>簇扩展</strong>: DBSCAN 从一个任意数据点开始，通过检查其邻居及其邻居的邻居，不断扩展簇，从而形成一个连通的数据点群。</p>
<h3 id="dbscan">** DBSCAN 聚类的核心优势:**</h3>
<ul>
<li>
<p><strong>检测复杂结构的能力</strong>: DBSCAN 能够发现各种形状和大小的簇，使其非常适合处理具有非线性关系或不规则模式的数据集。</p>
</li>
<li>
<p><strong>抗噪声能力强</strong>: DBSCAN 能够有效地处理含噪声的数据，通过将噪声点与聚类点区分开来。</p>
</li>
<li>
<p><strong>自动确定聚类数量</strong>: DBSCAN 算法无需预先指定聚类的数量，这使其更方便且能更好地适应不同的数据集。</p>
</li>
<li>
<p><strong>适用于大规模数据集</strong>: 与某些其他聚类算法相比，DBSCAN 的时间复杂度相对较低，这使其能够很好地扩展到大规模数据集。</p>
</li>
</ul>
<p>在下一节中，我们将深入探讨如何在 Python 中实现 DBSCAN 算法，并提供分步指导和示例。</p>
<h3 id="dbscanpython"><strong>DBSCAN 聚类: Python 的实现方法</strong></h3>
<p>在本节中，我将指导您如何使用 Python 实现 DBSCAN。</p>
<h4 id="dbscan">DBSCAN 聚类的关键步骤</h4>
<ol>
<li>
<p><strong>数据预处理:</strong> 在应用 DBSCAN 之前，对数据进行预处理非常重要。这包括处理缺失值、对特征进行标准化以及选择合适的距离度量。</p>
</li>
<li>
<p><strong>定义参数:</strong> DBSCAN 需要两个主要参数 epsilon (eps) 和最小点数 (MinPts). Epsilon 决定了将两点视为邻点的最大距离，而 MinPts 则指定了形成密集区域所需的最少点数。</p>
</li>
<li>
<p><strong>执行基于密度的聚类:</strong> DBSCAN 算法首先随机选取一个数据点，并找出其在指定的 epsilon 距离内的邻点。如果邻点数量超过 MinPts 阈值，则形成一个新的聚类。该算法通过迭代添加新点来扩展这个聚类，直到无法再添加新的点为止。</p>
</li>
<li>
<p><strong>执行噪声检测:</strong> 不属于任何聚类的点被视为噪声或异常值。这些点未被分配到任何聚类中，对于识别数据中的异常情况至关重要。</p>
</li>
</ol>
<p>要在 Python 中执行 DBSCAN 聚类，我们可以使用 scikit-learn 库。首先，需要导入所需的库并加载要聚类的数据集。然后，可以创建 DBSCAN 类的一个实例，并设置 epsilon (eps) 和最小样本数 (min_samples) 参数。</p>
<p>以下是一个示例代码片段，可帮助您入门：</p>
<pre><code>import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN

# 生成示例数据
X, _ = make_moons(n_samples=500, noise=0.05, random_state=0)

# 应用 DBSCAN 聚类
db = DBSCAN(eps=0.3, min_samples=5, metric='euclidean')
y_db = db.fit_predict(X)
</code></pre>
<p><a href="https://lunartech.ai"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738529451227/4b01ac7c-a9f9-4666-8fe5-e457a18ad160.png" alt="紫色背景上的代码片段展示了使用 DBSCAN 聚类算法的过程。代码导入了 NumPy 和 Matplotlib 等库，生成示例数据，并使用指定参数应用 DBSCAN 进行聚类。- lunartech.ai" width="1312" height="782" loading="lazy"></a></p>
<p>请记得将 <code>X</code> 替换为您的实际数据集。您可以调整 <code>eps</code> 和 <code>min_samples</code> 参数以获得不同的聚类结果。<code>eps</code> 参数是将一个样本视为另一个样本邻域内的最大距离。<code>min_samples</code> 是将一个点视为核心点的邻域中的样本数或总权重。</p>
<p>DBSCAN 相较于其他聚类算法具有诸多优势，比如无需预先设定聚类的数量。这使得它适用于聚类数量未知的数据集。DBSCAN 还能够识别出形状和大小各异的聚类，使其在捕捉复杂结构方面更具灵活性。</p>
<p>但是 DBSCAN 在处理数据集中的不同密度时可能会遇到困难，并且对 epsilon (eps)和最小点数参数的选择较为敏感。要获得最佳的聚类结果，必须对这些参数进行精细调整。</p>
<p>通过在 Python 中实现 DBSCAN，您可以利用这一强大的聚类算法来发现数据中具有意义的模式和结构。</p>
<p>在我们探讨 DBSCAN 与其他聚类技术之间的差异之前，让我们更仔细地研究一下影响 DBSCAN 性能和结果的关键参数。</p>
<h3 id="dbscan">理解 DBSCAN 的关键参数</h3>
<p><strong>eps</strong> (epsilon) 参数定义了两个数据点之间的最大距离，使其中一个点被视为另一个点的邻居。这意味着，在核心点 eps 半径范围内的所有点都属于同一个簇。选择合适的 eps 值至关重要，因为 eps 过小可能会导致簇数量过多且规模较小，而 eps 过大可能会将原本独立的簇合并为一个簇。</p>
<p><strong>min_samples</strong> 参数决定形成高密度区域所需的最小数据点数. 如果一个点在 eps  半径范围内至少有 min_samples 个邻居，则被分类为 <strong>核心点</strong> 。如果一个点落在某个核心点的 eps 半径内，但自身不满足 min_samples 要求，则被分类为 <strong>边界点</strong>. 任何既不是核心点也不是边界点的数据点都会被标记为噪声或异常值。</p>
<h3 id="dbscan">DBSCAN 如何对数据点进行分组</h3>
<p>DBSCAN 通过识别核心点并围绕它们扩展簇来运行。它根据密度将紧密分布的点（即簇）归为一组，并将低密度点标记为异常值（或噪声）。其过程如下：</p>
<ol>
<li>
<p><strong>选择一个未访问的数据点</strong> 并检查其 <code>eps</code> 半径范围内是否至少有 <code>min_samples</code> 个邻居。</p>
</li>
<li>
<p>如果满足条件，该点被标记为核心点，并以其为中心形成一个新的簇。</p>
</li>
<li>
<p><strong>扩展簇</strong> 将所有<code>eps</code>范围内直接可达的点加入簇中。如果这些点也是核心点，则将其邻居点一并加入。</p>
</li>
<li>
<p><strong>继续扩展</strong> 直到没有更多数据点满足密度准则。</p>
</li>
<li>
<p><strong>移动到下一个未访问的数据点</strong> ，并重复该过程。</p>
</li>
<li>
<p><strong>对剩余数据点进行分类。</strong> 将剩余数据点分类为边界点（属于某个簇但不是核心点）或噪声点（不属于任何簇的异常值）。</p>
</li>
</ol>
<h3 id="dbscan">DBSCAN 示例实现</h3>
<p>在该实现中:</p>
<ul>
<li>
<p><code>eps=0.3</code>: 定义数据点之间的最大距离，决定它们是否被视为邻居。</p>
</li>
<li>
<p><code>min_samples=5</code>: 设置形成高密度区域所需的最小数据点数</p>
</li>
<li>
<p><code>fit_predict(X)</code>: 为每个数据点分配一个聚类标签。</p>
</li>
</ul>
<p>应用 DBSCAN 算法后，数据点会被分配标签。如果两个点属于同一个聚类，它们在 <code>y_db</code>中将具有相同的标签。 被识别为离群点的数据点将被标记为<code>-1</code> 并且不被聚类。</p>
<p>生成的散点图直观地展示了 DBSCAN 如何识别出两个新月形的簇。与假定簇为球形的 K-均值不同，DBSCAN 能够有效地检测出任意形状的簇。</p>
<pre><code>plt.scatter(X[y_db == 0, 0], X[y_db == 0, 1],
            c='lightblue', marker='o', s=40,
            edgecolor='black', 
            label='cluster 1')
plt.scatter(X[y_db == 1, 0], X[y_db == 1, 1],
            c='red', marker='s', s=40,
            edgecolor='black', 
            label='cluster 2')
plt.legend()
plt.show()
</code></pre>
<p><a href="https://lunartech.ai"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738529515628/a5c2861e-1263-4cad-84f2-9e026261942f.png" alt="这是使用 Matplotlib 绘制散点图的 Python 代码截图。该代码定义了两个具有不同颜色和标记的簇，添加了图例，并显示了图形。- lunartech.ai" width="1058" height="744" loading="lazy"></a></p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://miro.medium.com/v2/resize:fit:1400/1*ymoTCnR3H-WBs8ShoTrYNg.png" alt="散点图显示两个聚类：簇 1 绿色圆点在上方形成一条曲线, 簇 2 红色方块在下方形成一条曲线。图片来源: The Author" width="1054" height="637" loading="lazy"></a></p>
<p>生成的图表将显示两个呈月牙状的簇，分别以绿色和红色呈现，这表明 DBSCAN 成功地识别并分离了这两个相互交织的半圆。</p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a97d1f6-3c00-4493-b430-1d8e3cb8d270_3327x1350.png" alt="该插图展示了未标注的数据点在两个不同图表中被聚类成两组。其中一个简笔人物问道 &quot;没有标签，如何评估这些呢?&quot; - lunartech.ai" width="3327" height="1350" loading="lazy"></a></p>
<h2 id=""><strong>如何评估聚类算法的性能</strong></h2>
<p>评估聚类模型的性能可能颇具挑战性，因为在无监督学习中没有可用的真实标签。不过，有几种评估指标可以为聚类结果的质量提供见解。</p>
<ul>
<li>
<p><strong>轮廓系数</strong>: 衡量每个数据点与其所属聚类的契合程度，同时与其他聚类进行比较。轮廓系数越高，表明聚类效果越好。</p>
</li>
<li>
<p><strong>戴维森堡丁指数:</strong> 该指标衡量每个聚类与其最相似聚类之间的平均相似度，同时考虑聚类之间的分离度。值越低表示聚类效果越好。</p>
</li>
<li>
<p><strong>方差比准则:</strong> 评估簇间离散度与簇内离散度的比值，较高的值表示聚类效果更清晰。</p>
</li>
<li>
<p><strong>可视化评估</strong>: 检查聚类结果的可视化表示，例如散点图或树状图，也能为聚类的质量和意义提供有价值的见解。</p>
</li>
</ul>
<p>我建议您结合使用评估指标和视觉评估，以全面评估聚类模型的性能。</p>
<h2 id="kdbscan"><strong>K 均值聚类、层次聚类和 DBSCAN 之间的区别</strong></h2>
<p>K 均值、层次聚类和 DBSCAN 是三种广泛使用的聚类算法，每种算法都有其独特的数据分组方法。理解它们之间的差异对于根据数据特征和分析目标选择最合适的算法至关重要。</p>
<h3 id="k"><strong>K-均值聚类</strong></h3>
<p>K 均值聚类是一种基于质心的算法，它根据相似性将数据划分为 K 个簇。该算法首先随机初始化 K 个质心，然后迭代地将每个数据点分配给最近的质心。一旦所有数据点都被分配，就根据每个簇内点的均值重新计算质心。这个过程会一直持续到达到收敛为止。</p>
<h4 id="k"><strong>K-均值聚类的核心优势:</strong></h4>
<ul>
<li>
<p>对于大型数据集而言，高效且可扩展。</p>
</li>
<li>
<p>当聚类呈球形且分布均匀时效果良好。</p>
</li>
<li>
<p>与层次聚类相比，计算速度更快。</p>
</li>
<li>
<p>易于实施和理解。</p>
</li>
</ul>
<h4 id="k"><strong>K-均值聚类的局限性:</strong></h4>
<ul>
<li>
<p>需要提前指定聚类（K）的数量。</p>
</li>
<li>
<p>对初始质心位置敏感，导致结果不同。</p>
</li>
<li>
<p>假定各聚类大小相等且呈球形，但实际情况并非总是如此。</p>
</li>
<li>
<p>在处理异常值和非线性形状的聚类时遇到的难题。</p>
</li>
</ul>
<h3 id=""><strong>层次聚类</strong></h3>
<p>层次聚类创建了一个嵌套的聚类层次结构，无需预先定义聚类的数量。它从将每个数据点视为单独的聚类开始，然后根据相似性逐步合并或拆分聚类。其结果通常用树状图来可视化，这有助于确定最佳的聚类数量。</p>
<h4 id=""><strong>层次聚类的核心优势:</strong></h4>
<ul>
<li>
<p><strong>无需</strong> 预先指定聚类数量。</p>
</li>
<li>
<p>捕捉集群之间的层级关系。</p>
</li>
<li>
<p>能够处理不同类型的数据，包括数值型和分类型。</p>
</li>
<li>
<p>对于探索性分析很有用，带有树状图以提高可解释性。</p>
</li>
</ul>
<h4 id=""><strong>层次聚类的局限性:</strong></h4>
<ul>
<li>
<p>计算成本高，在大规模数据集上效率低（O(n²)时间复杂度）。</p>
</li>
<li>
<p>由于内存限制，难以扩展到大规模数据集。</p>
</li>
<li>
<p>确定树状图的合适截断点较具挑战性。</p>
</li>
<li>
<p>对噪声和异常值敏感，可能导致层次结构失真。</p>
</li>
</ul>
<h3 id="dbscan"><strong>DBSCAN（基于密度的应用空间聚类算法）</strong></h3>
<p>DBSCAN 是一种基于密度的聚类算法，它根据数据点之间的接近程度和密度来对数据点进行分组，而不是基于预先定义的聚类。与 K-Means 和层次聚类不同，DBSCAN 不需要指定聚类的数量。相反，它使用两个关键参数：eps (两个点被视为邻居的最大距离) 和 min_samples (形成高密度簇所需的最小点数)。不满足这些条件的点会被标记为噪声。</p>
<h4 id="dbscan"><strong>DBSCAN 的核心优势:</strong></h4>
<ul>
<li>
<p>无需预先指定聚类的数量。</p>
</li>
<li>
<p>能够检测任意形状的簇，这与假定簇为球形的 K-均值 算法不同。</p>
</li>
<li>
<p>能够有效地处理异常值，将其标记为噪声，而不是强行将其归入某个聚类。</p>
</li>
<li>
<p>适用于密度不均或具有非线性结构的数据集，在复杂数据分布下表现良好。</p>
</li>
</ul>
<h4 id="dbscan"><strong>DBSCAN 的局限性:</strong></h4>
<ul>
<li>
<p>难以处理不同密度的聚类，因为单一的 eps 值可能无法适用于所有簇。</p>
</li>
<li>
<p>对参数调整敏感 (eps 和 min_samples)，参数选择会影响聚类性能。</p>
</li>
<li>
<p>不适用于高维数据，因为在高维空间中，欧几里得距离的区分度会降低。</p>
</li>
<li>
<p>在超大规模数据集上可能表现欠佳，尽管其可扩展性优于层次聚类。</p>
</li>
</ul>
<h3 id=""><strong>选择合适的聚类算法</strong></h3>
<table>
<thead>
<tr>
<th>特征</th>
<th>K-均值</th>
<th>层次聚类</th>
<th>DBSCAN</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>簇形</strong></td>
<td>假设聚类为球形</td>
<td>适用于层次结构</td>
<td>适用于任意形状的聚类</td>
</tr>
<tr>
<td><strong>可扩展性</strong></td>
<td>高度可扩展（适用于大规模数据集，计算速度快）</td>
<td>不具备良好扩展性 (时间复杂度 O(n²))</td>
<td>度可扩展（处理超大规模数据集可能存在挑战）</td>
</tr>
<tr>
<td><strong>簇的数量</strong></td>
<td>必须预定义</td>
<td>无需指定</td>
<td>无需指定</td>
</tr>
<tr>
<td><strong>处理异常值</strong></td>
<td>较差</td>
<td>对噪声敏感</td>
<td>良好，可检测异常值作为噪声</td>
</tr>
<tr>
<td><strong>计算复杂度</strong></td>
<td>O(n) 至 O(n log n)</td>
<td>O(n²)</td>
<td>O(n log n)</td>
</tr>
<tr>
<td><strong>可解释性</strong></td>
<td>结果输出易于理解</td>
<td>树状图提供良好的可视化解析</td>
<td>直观性较低，需要调整参数</td>
</tr>
</tbody>
</table>
<p>每种聚类算法都有其优缺点。 <strong>K-均值</strong> 在处理大型数据集以及簇呈球形且彼此分离时效果最佳。<strong>层次聚类</strong> 在存在层次关系或簇的数量未知时很有用。<strong>DBSCAN</strong> 在检测任意形状的簇和处理噪声方面表现出色，但需要仔细调整参数。</p>
<p>通过了解每种算法的特点，您可以做出明智的决定，选择最适合您数据分析需求的聚类方法。</p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://miro.medium.com/v2/resize:fit:1400/1*HpMauXQZe0ByFFSHs4wNLw.png" alt="-SNE 可视化图，困惑度为 50，展示了数据点的聚类情况。标注的聚类突出了不同的年份、评分以及电影类型，如爱情、惊悚、动作和冒险。- lunartech.ai" width="1400" height="1103" loading="lazy"></a></p>
<h2 id="tsnepython"><strong>如何使用 t-SNE 在 Python 中可视化聚类</strong></h2>
<p>在应用 K-Means、层次聚类和 DBSCAN 等聚类算法后，通常需要对聚类结果进行可视化，以更好地理解数据的内在结构。</p>
<p>而对于二维或三维数据集，散点图可以很好地展示聚类结果。然而，在实际应用中，数据通常具有高维特征，直接可视化是较为困难的。</p>
<p>为应对这一挑战，您可以使用诸如<strong>t-SNE</strong> (t-Distributed Stochastic Neighbor Embedding) 之类的降维技术将高维数据投影到低维空间，同时保留其结构。这使您能够更有效地可视化聚类，并识别出在原始数据中可能不那么明显的隐藏模式。</p>
<p>在本节中，我们将探讨 t-SNE 的理论基础及其在 Python 中的实现。</p>
<h3 id="tsne"><strong>深入解析 t-SNE</strong></h3>
<p>t-SNE 由 Laurens van der Maaten 和 Geoffrey Hinton 于 2008 年提出，作为一种用于可视化复杂数据结构的方法。其目标是在低维空间中表示高维数据点，同时保留数据点之间的局部结构和成对相似性。</p>
<p>t-SNE 通过在高维空间和低维空间中建模数据点之间的相似性来实现这一目标。</p>
<h3 id="tsne"><strong>t-SNE 算法</strong></h3>
<p>t-SNE 算法的执行步骤如下：</p>
<ol>
<li>
<p>计算高维空间中数据点的两两相似度。通常使用高斯核函数（Gaussian kernel），基于欧几里得距离（ Euclidean distances）来衡量数据点之间的相似性。</p>
</li>
<li>
<p>随机初始化低维嵌入。</p>
</li>
<li>
<p>定义一个成本函数，用于衡量高维空间与低维空间中数据点相似性的匹配程度。</p>
</li>
<li>
<p>使用梯度下降优化成本函数，以最小化高维空间与低维空间相似性之间的差异。</p>
</li>
<li>
<p>重复执行步骤 3 和 4，直至成本函数收敛。</p>
</li>
</ol>
<p>借助 scikit-learn 等库，用 Python 实现 t-SNE 相对简单。scikit-learn 库为将 t-SNE 应用于您的数据提供了用户友好的 API。通过遵循 scikit-learn 的文档和示例，您可以轻松地将 t-SNE 集成到您的机器学习流程中。</p>
<h3 id="tsne"><strong>二维 t-SNE 可视化</strong></h3>
<pre><code>import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.manifold import TSNE

# 加载数据集
digits = datasets.load_digits()
X, y = digits.data, digits.target

# 应用 t-SNE 算法
tsne = TSNE(n_components=2, random_state=0)
X_tsne = tsne.fit_transform(X)

# 在二维平面上可视化结果
plt.figure(figsize=(10, 6))
scatter = plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, edgecolor='none', alpha=0.7, cmap=plt.cm.get_cmap('jet', 10))
plt.colorbar(scatter)
plt.title("数字数据集的 t-SNE 可视化")
plt.show()
</code></pre>
<p><a href="https://lunartech.ai"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738529609503/e4a5dac2-0c31-4e9c-b8cd-9d243736ee67.png" alt="使用 Matplotlib 和 scikit-learn 可视化数字数据集 t-SNE 转换的 Python 代码片段。该代码加载数据集，应用 t-SNE，并在二维平面上绘制结果。- lunartech.ai" width="2048" height="1080" loading="lazy"></a></p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://miro.medium.com/v2/resize:fit:1400/1*vFccfsJFgXl3rulHs93MKA.png" alt="这个散点图展示了 Digits 数据集的 t-SNE 可视化结果。不同颜色的点群代表不同的数字，颜色从深红色到浅蓝色渐变，分别对应数字 0 到 9。右侧的颜色条显示了每种颜色所代表的数字。- lunartech.ai" width="1000" height="600" loading="lazy"></a></p>
<p>在这个例子中：</p>
<ol>
<li>
<p>我们将加载 <code>digits</code> 数据集.</p>
</li>
<li>
<p>我们应用 t-SNE 将数据从 64 维（因为每张图像为 8×8）降维至 2 维。</p>
</li>
<li>
<p>然后我们绘制转换后的数据，根据每个点的真实数字标签为其着色。</p>
</li>
</ol>
<p>生成的可视化结果将展示出若干簇，每个簇对应一个数字（从 0 到 9）。这有助于了解不同数字在原始高维空间中的分离程度。</p>
<h3 id=""><strong>高维数据可视化</strong></h3>
<p>t-SNE 的主要优势之一在于其能够将高维数据在低维空间中进行可视化。通过降低数据的维度，t-SNE 使我们能够识别出在原始高维空间中可能不明显的聚类和模式。由此产生的可视化结果能够为数据的结构提供有价值的见解，从而有助于人们的决策过程。</p>
<pre><code>import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.manifold import TSNE
from mpl_toolkits.mplot3d import Axes3D

# 加载数据集
digits = datasets.load_digits()
X, y = digits.data, digits.target

# 应用 t-SNE 算法
tsne = TSNE(n_components=3, random_state=0)
X_tsne = tsne.fit_transform(X)

# 在三维平面上实现可视化结果
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
scatter = ax.scatter(X_tsne[:, 0], X_tsne[:, 1], X_tsne[:, 2], c=y, edgecolor='none', alpha=0.7, cmap=plt.cm.get_cmap('jet', 10))
plt.colorbar(scatter)
plt.title("数字数据集的三维 t-SNE 分析图")
plt.show()
</code></pre>
<p><a href="https://lunartech.ai"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738529676545/772f6b94-655b-4ae3-bdb5-a5334442c970.png" alt="这是一个使用 Python 编写的代码片段，其中用到了 matplotlib、sklearn 和 mpl_toolkits.mplot3d 等库。它加载了数字数据集，应用 t-SNE 进行降维，并在三维平面上实现可视化结果。 - lunartech.ai" width="2048" height="1154" loading="lazy"></a></p>
<p>在这段修订后的代码中：</p>
<ol>
<li>
<p>我们将 t-SNE 的<code>n_components=3</code> 设置为 3，以获得三维变换。</p>
</li>
<li>
<p>我们使用 <code>mpl_toolkits.mplot3d.Axes3D</code> 来创建一个三维散点图。</p>
</li>
</ol>
<p>执行此代码后，您将看到一个 3D 散点图，其中的点根据其 t-SNE 坐标进行定位，并根据其真实的数字标签进行着色。</p>
<p>而旋转三维可视化图像，会有助于我们更好地理解数据点的空间分布情况。</p>
<p><a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><img src="https://miro.medium.com/v2/resize:fit:1400/1*aw8wAIvC2CXwXO7Ixjy1JQ.png" alt="用于数字数据集的 t-SNE 投影的 3D 散点图。数据点呈簇状分布，不同颜色代表不同的数字。右侧的颜色条表示从 0 到 9 的数值。" width="844" height="692" loading="lazy"></a></p>
<p>t-SNE 是一种强大的降维和高维数据可视化工具。通过利用其功能，您可以更深入地了解复杂的数据集，并发现那些并非一目了然的隐藏模式。而凭借其 Python 实现方法和易用性，t-SNE 对于任何数据科学家或机器学习从业者来说都是一项宝贵的资源。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741790800643/da4e7d4f-4030-4b8a-9dc1-d8cb669a4bbb.gif" alt="散点矩阵图展示了鸢尾花属植物中的三种物种（山鸢尾（蓝色）、变色鸢尾（红色）和维吉尼亚鸢尾（绿色））的花萼宽度、花萼长度、花瓣宽度和花瓣长度之间的关系。- lunartech.ai" class="kg-image" width="1014" height="596" loading="lazy">
    <figcaption></figcaption>
</figure>
<h2 id=""><strong>有关更多的无监督学习技术</strong></h2>
<p>除了我们在此讨论的聚类技术之外，还有一些重要的无监督学习技术值得探索。虽然我们在此不会进行详细的探讨，但让我们简要提及其中两种技术：混合模型和主题建模。</p>
<h3 id=""><strong>混合模型</strong></h3>
<p>混合模型是一类概率模型，用于建模复杂的数据分布。它假设整体数据集可以表示为多个潜在子群或成分的组合，每个成分由其自身的概率分布描述。</p>
<p>混合模型在数据点不属于明确的独立聚类且可能存在重叠特征的情况下特别有用。</p>
<h3 id=""><strong>主题建模</strong></h3>
<p>主题建模是一种用于从文档集合中提取潜在主题的方法，它可以帮助探索和发现文本数据中的隐含语义模式。</p>
<p>通过分析单词在不同文档中的共现情况并识别常见主题，主题建模能够实现大规模文本数据的自动分类和摘要生成。该技术广泛应用于自然语言处理(NLP)、信息检索和内容推荐系统等领域。</p>
<p>尽管这些技术的深入研究超出了本手册的范围，但是值得进一步探索，因为它们是很有价值的工具，从而有助于挖掘和洞察隐藏的模式从您的数据中，从而帮助您获得见解。</p>
<p>请一定要牢记，掌握无监督学习是需要持续的学习和实践。在熟悉上述不同技术后，您将能够更好地应对各种数据分析挑战，并在多个领域内灵活运用这些方法。</p>
<h2 id=""><strong>常见问题解答</strong></h2>
<h3 id=""><strong>小问答: 请问监督学习和无监督学习之间的区别是什么？</strong></h3>
<p>首先，监督学习会是在带有标签的数据上训练模型，其中输入与对应的输出配对，目标是预测新数据的输出结果。</p>
<p>而相比之下，无监督学习处理的是无标签数据，旨在发现数据中的模式、结构或聚类，而无需来预定义其输出结果。</p>
<p>所以在本质上，监督学习的目标是学习一个映射函数，而无监督学习则侧重于发现数据中的隐藏关系或分组结构。</p>
<h3 id=""><strong>小问答: 如何知道哪种聚类算法最适合我的数据类型?</strong></h3>
<p>聚类算法的适用性取决于多种因素，例如数据的性质、期望的聚类数量以及您试图解决的具体问题。</p>
<p>在本手册中，我们将讨论三种常用的聚类算法:</p>
<ul>
<li>
<p><strong>K-均值</strong> 是一种常用的聚类算法，旨在将数据划分为 K 个簇，并将每个数据点分配给最临近的质心。该算法适用于分布均匀、接近球形的聚类，但需要预先指定簇的数量。</p>
</li>
<li>
<p><strong>层次聚类</strong> 通过迭代合并或拆分数据点来构建聚类层次结构。它提供树状图来可视化聚类过程，并能够处理不同形状和规模的聚类。</p>
</li>
<li>
<p><strong>DBSCAN</strong> 是一种基于密度的聚类算法，它将相互接近的数据点归为一类，并识别并分离异常值。该算法能够发现任意形状的聚类，并且无需预先指定聚类数量。</p>
</li>
</ul>
<p>要确定最适合您用例的算法，我建议您尝试不同的技术，并根据诸如聚类质量、计算效率和可解释性等指标来评估其性能。</p>
<h3 id=""><strong>小问答: 请问无监督学习可以用于预测分析吗?</strong></h3>
<p>尽管无监督学习主要关注在没有特定输出标签的情况下发现数据中的模式和关系，但它也可以间接支持预测分析。通过来揭示数据中的隐藏结构和聚类，无监督学习能够提供有价值的洞察，从而优化特征工程、异常检测或数据分群，这些都能进一步提升预测模型的性能。</p>
<p>最明显的一个例子是，聚类等无监督学习技术可以帮助识别数据中的不同群组或模式，这些信息可作为预测模型的输入特征，或者用于生成新的预测变量。因此，无监督学习在预测分析中发挥着重要作用，它能够加深对数据的理解，并提升预测模型的准确性和有效性。</p>
<h2 id=""><strong>数据科学与人工智能资源</strong></h2>
<p>想要更详细的了解有关数据科学、机器学习和人工智能方面的职业，或者学习如何获得一份数据科学的工作吗？您可以下载这本 <a href="https://downloads.tatevaslanyan.com/six-figure-data-science-ebook">免费的数据科学与人工智能职业手册</a>.</p>
<p>想从零开始学习机器学习，或者想巩固已有知识吗? 下载这本 <a href="https://www.freecodecamp.org/news/machine-learning-handbook/">免费的机器学习基础手册</a> 一次性获取所有与 Python 示例相结合的机器学习基础知识。</p>
<h2 id=""><strong>关于作者</strong></h2>
<p><a href="https://www.linkedin.com/in/tatev-karen-aslanyan/"><strong>Tatev Aslanyan</strong></a> 是一位高级机器学习和人工智能工程师，以及 <a href="https://www.lunartech.ai/"><strong>LunarTech</strong></a>的首席执行官兼联合创始人。其领导的 LunarTech 是一家致力于让数据科学和人工智能在全球普及的深度技术创新初创公司。Tatev 在人工智能工程和数据科学领域拥有超过 6 年的工作经验，曾在美国、英国、加拿大和荷兰工作过，她将自己的专业知识应用于推进不同行业的人工智能解决方案。</p>
<p>Tatev 本人不仅拥有<a href="https://www.lunartech.ai/">顶尖荷兰大学</a>的[硕士]计量经济学和[本科]运筹学学位<a href="https://www.linkedin.com/in/tatev-karen-aslanyan/">51</a>,还曾发表了多篇科学学术文章有关于自然语言处理（NLP）、机器学习和推荐系统在美国权威的学术期刊上。</p>
<p>作为顶级开源项目的贡献者, Tatev 共同参与撰写了多门课程和书籍, 其中包括 <strong>2024 年的 freeCodeCamp</strong> 项目, 并且还在<a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><strong>LunarTech</strong> 项目</a>中发挥了关键作用，帮助了<strong>来自于 144 个国家的 30,000 多名学习者</strong>。</p>
<p><a href="https://www.lunartech.ai/">LunarTech</a> 作为一家深度技术创新公司，其不断研发人工智能驱动的产品，并同时提供教育工具来帮助企业和个人推动创新，从而实现让其能降低运营成本并提升盈利能力。</p>
<h2 id=""><strong>联系我们</strong></h2>
<ul>
<li>
<p><a href="https://www.linkedin.com/in/tatev-karen-aslanyan/">关注我的 LinkeIn 账户</a></p>
</li>
<li>
<p><a href="https://www.youtube.com/@LunarTech_ai">访问 YouTube 频道</a></p>
</li>
<li>
<p>订阅 <a href="https://substack.com/@lunartech"><strong>LunarTech Newsletter</strong></a> 或者 <a href="https://lens.lunartech.ai/"><strong>LENS</strong></a> - 我们的资讯频道。</p>
</li>
</ul>
<p>想要更全面了解数据科学、机器学习和人工智能的职业发展，并学习如何成功获得数据科学岗位吗？那就立即下载这本免费的数据科学与人工智能职业手册。</p>
<p>感谢您选择本手册作为您的学习伙伴。在您继续探索广阔的人工智能领域时，我希望您能够保持自信、精准思考，并怀揣创新精神！</p>
<h2 id="lunartech"><strong>由 LunarTech 打造的人工智能工程师训练营</strong></h2>
<p>如果您坚定地想成为一名人工智能工程师，并寻找一门既涵盖深层理论又注重实践的全方位训练营, 那么一定要看看专注于生成式人工智能的<a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp"><strong>LunarTech 人工智能工程师训练营</strong></a>. 这虽然不是在有关人工智能工程上最全面和最高级的项目, 但是该训练营将为您提供在最具竞争力的人工智能领域和行业中脱颖而出的全部技能和知识。</p>
<figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.25%;" class="fluid-width-video-wrapper">
            <iframe width="560" height="315" src="https://www.youtube.com/embed/g6KQHEeZVQY" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy" name="fitvid0"></iframe>
          </div>
        </div>
      </figure>
<p>在 3 至 6 个月内，你可以选择自定进度或参与小组学习，掌握生成式人工智能及其基础模型，如 变分自编码器、生成式对抗网络、Transformers 和大型语言模型。深入学习数学、统计学、模型架构，以及使用 PyTorch 和 TensorFlow 等行业标准框架训练这些模型的技术细节。</p>
<p>该课程涵盖大型模型的预训练、微调、提示工程、量化和大模型优化，以及诸如检索增强生成（RAGs）等前沿技术。</p>
<p>本次训练营将帮助您弥合研究与实际应用之间的差距，赋能您能够设计出有影响力的解决方案, 并同时打造出包含前沿项目的优秀作品集。</p>
<p>该课程还同时考虑人工智能伦理，帮助您构建可持续并且合乎道德规范的人工智能模型，与负责任的人工智能原则相契合。这不单单是一门课程 —— 它还将是一段全面的旅程，旨在让您成为人工智能革命中的领军人物 <a href="https://www.lunartech.ai/bootcamp/ai-engineering-bootcamp">立即浏览课程详情</a>。</p>
<p>由于名额有限，我们对于人工智能工程师的需求比以往任何时候都更高。所以请不要犹豫，您的人工智能工程师之路，将从现在开始！您可以<a href="https://forms.fillout.com/t/frSHf9HUZCus">报名参加</a>。</p>
<blockquote>
<p><em>“让我们一起共创未来!“ - Tatev Aslanyan, LunarTech 的首席执行官兼联合创始人</em></p>
</blockquote>
<h2 id="tatevkarensubstack"><a href="https://tatevaslanyan.substack.com/?source=post_page-----f9fb36a94a05--------------------------------"><strong>数据科学与人工智能电子期刊 | Tatev Karen | Substack</strong></a></h2>
<p>如果想从零开始学习机器学习，或者想要巩固已有知识? 可下载<a href="https://join.lunartech.ai/machine-learning-fundamentals--3f64f"><strong>免费的机器学习基础手册</strong></a></p>
<p>如果还想全面了解数据科学、机器学习和人工智能行业，并学习如何进入数据科学领域？可下载<a href="https://downloads.tatevaslanyan.com/six-figure-data-science-ebook"><strong>免费的数据科学与人工智能职业手册</strong></a>。</p>
<p>感谢您选择本手册作为您的学习伙伴。在您继续探索广阔的机器学习领域时，希望您充满信心、严谨细致，并秉持创新精神。祝您未来一切顺利！</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
