# Random Forest: Khi nhiều cây quyết định cùng bỏ phiếu


Một [cây quyết định (Decision Tree)]({{< ref "decision-tree.md" >}}) có cách suy luận dễ theo dõi: đặt một câu hỏi về dữ liệu, chia các quan sát thành hai nhóm, rồi lặp lại. Tuy nhiên, cây có thể khá **nhạy với dữ liệu huấn luyện**. Chỉ vài quan sát thay đổi cũng có thể làm phép chia gần gốc đổi chỗ, kéo theo một cấu trúc cây khác.

**Rừng ngẫu nhiên (Random Forest)** xử lý điểm yếu ấy bằng một ý tưởng đơn giản: tạo nhiều cây hơi khác nhau rồi gộp dự đoán của chúng. Mỗi cây được huấn luyện trên một mẫu bootstrap, đồng thời chỉ được xét một tập con ngẫu nhiên của các đặc trưng tại mỗi nút. Với bài toán phân loại, các cây bỏ phiếu; với bài toán hồi quy, ta lấy trung bình các dự đoán.

Điều quan trọng không chỉ là có *nhiều* cây. Các cây còn phải đủ khác nhau để sai số của chúng không cùng xuất hiện theo một kiểu.

## 1. Nhắc lại từ một cây quyết định

Giả sử cần phân loại một quan sát từ hai đặc trưng $x_1,x_2$. Ở mỗi nút, cây thử những câu hỏi dạng

$$
x_j \le s,
$$

trong đó $j$ là đặc trưng được xét và $s$ là ngưỡng chia. Với bài toán hai lớp, một thước đo thường dùng cho độ hỗn tạp của nút $t$ là [chỉ số Gini (Gini Impurity)]({{< ref "/posts/math/stats/gini-impurity.md" >}})

$$
G(t)=1-p_0(t)^2-p_1(t)^2,
$$

với $p_k(t)$ là tỷ lệ quan sát thuộc lớp $k$ trong nút. Một phép chia tạo hai nút con $L,R$ được đánh giá qua

$$
\frac{n_L}{n_t}G(L)+\frac{n_R}{n_t}G(R).
$$

Cây chọn phép chia làm đại lượng này nhỏ nhất trong số các ứng viên đang được xét. Quá trình tiếp tục cho đến khi gặp điều kiện dừng như độ sâu tối đa, số quan sát tối thiểu ở nút lá, hoặc nút đã đủ thuần nhất.

Cách chia theo từng trục tạo ra các miền dự đoán hình chữ nhật. Một cây sâu có thể bám rất sát mẫu huấn luyện, kể cả những dao động ngẫu nhiên. Đây là một biểu hiện của [quá khớp]({{< ref "/glossary.md#overfitting" >}}), nhưng không có nghĩa mọi cây sâu đều cho dự đoán ngoài mẫu kém; kết quả còn phụ thuộc dữ liệu và cách chính quy hóa cây.

Phần này chỉ nhắc nhanh viên gạch cơ sở. Bài [Cây quyết định: Một câu hỏi chia đôi dữ liệu]({{< ref "decision-tree.md" >}}) trình bày trực giác, cách chọn ngưỡng bằng chỉ số Gini và hoạt ảnh cây lớn dần trước khi đi tiếp tới cơ chế của rừng.

<div class="interactive-pane">
<link rel="stylesheet" href="/css/random-forest-interactive.css?v=1">
<figure class="rf-process" id="rf-process" data-step="0" aria-labelledby="rf-process-title rf-process-caption">
<div class="rf-process__head">
<div>
<p class="rf-eyebrow">HOẠT ẢNH CƠ CHẾ</p>
<h3 id="rf-process-title">Từ một mẫu dữ liệu đến lá phiếu của cả rừng</h3>
</div>
<button type="button" class="rf-button" data-action="play-process">Chạy hoạt ảnh</button>
</div>
<p class="rf-scroll-hint">Vuốt hình sang trái để xem các bước tiếp theo.</p>
<svg viewBox="0 0 760 360" role="img" aria-labelledby="rf-process-svg-title rf-process-svg-desc">
<title id="rf-process-svg-title">Quy trình huấn luyện và dự đoán của Random Forest</title>
<desc id="rf-process-svg-desc">Ba mẫu bootstrap được rút từ dữ liệu gốc, mỗi mẫu huấn luyện một cây với tập con đặc trưng ngẫu nhiên, sau đó ba cây bỏ phiếu cho dự đoán cuối.</desc>
<defs>
<marker id="rf-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto">
<path d="M0,0 L8,4 L0,8 Z"></path>
</marker>
</defs>
<g class="rf-stage rf-stage--data">
<text x="99" y="34" class="rf-label">Dữ liệu gốc</text>
<rect x="24" y="52" width="150" height="246" rx="14" class="rf-panel"></rect>
<g class="rf-dots">
<circle cx="55" cy="90" r="9" class="class-a"></circle><circle cx="93" cy="82" r="9" class="class-a"></circle>
<circle cx="136" cy="110" r="9" class="class-b"></circle><circle cx="64" cy="145" r="9" class="class-a"></circle>
<circle cx="112" cy="158" r="9" class="class-b"></circle><circle cx="145" cy="184" r="9" class="class-b"></circle>
<circle cx="52" cy="212" r="9" class="class-a"></circle><circle cx="96" cy="225" r="9" class="class-b"></circle>
<circle cx="138" cy="255" r="9" class="class-b"></circle>
</g>
</g>
<path d="M174 175 C205 175 205 95 236 95" class="rf-arrow rf-arrow--1"></path>
<path d="M174 175 C205 175 205 182 236 182" class="rf-arrow rf-arrow--1"></path>
<path d="M174 175 C205 175 205 269 236 269" class="rf-arrow rf-arrow--1"></path>
<g class="rf-stage rf-stage--samples">
<text x="305" y="34" class="rf-label">Mẫu bootstrap</text>
<g transform="translate(242 63)">
<rect width="126" height="64" rx="10" class="rf-sample"></rect>
<circle cx="22" cy="22" r="7" class="class-a"></circle><circle cx="48" cy="20" r="7" class="class-a"></circle><circle cx="75" cy="24" r="7" class="class-b"></circle><circle cx="101" cy="20" r="7" class="class-a"></circle>
<circle cx="35" cy="46" r="7" class="class-b"></circle><circle cx="63" cy="44" r="7" class="class-b"></circle><circle cx="92" cy="46" r="7" class="class-b"></circle>
</g>
<g transform="translate(242 150)">
<rect width="126" height="64" rx="10" class="rf-sample"></rect>
<circle cx="22" cy="22" r="7" class="class-b"></circle><circle cx="48" cy="20" r="7" class="class-a"></circle><circle cx="75" cy="24" r="7" class="class-b"></circle><circle cx="101" cy="20" r="7" class="class-b"></circle>
<circle cx="35" cy="46" r="7" class="class-a"></circle><circle cx="63" cy="44" r="7" class="class-a"></circle><circle cx="92" cy="46" r="7" class="class-b"></circle>
</g>
<g transform="translate(242 237)">
<rect width="126" height="64" rx="10" class="rf-sample"></rect>
<circle cx="22" cy="22" r="7" class="class-a"></circle><circle cx="48" cy="20" r="7" class="class-b"></circle><circle cx="75" cy="24" r="7" class="class-b"></circle><circle cx="101" cy="20" r="7" class="class-a"></circle>
<circle cx="35" cy="46" r="7" class="class-a"></circle><circle cx="63" cy="44" r="7" class="class-b"></circle><circle cx="92" cy="46" r="7" class="class-a"></circle>
</g>
</g>
<path d="M368 95 C400 95 425 80 455 80" class="rf-arrow rf-arrow--2"></path>
<path d="M368 182 C400 182 425 176 455 176" class="rf-arrow rf-arrow--2"></path>
<path d="M368 269 C400 269 425 272 455 272" class="rf-arrow rf-arrow--2"></path>
<g class="rf-stage rf-stage--trees">
<text x="485" y="34" class="rf-label">Cây khác nhau</text>
<g class="rf-tree" transform="translate(478 60)"><path d="M0 30 L-34 67 M0 30 L34 67 M-34 67 L-48 98 M-34 67 L-18 98"></path><circle cx="0" cy="20" r="12"></circle><circle cx="-34" cy="66" r="10"></circle><circle cx="34" cy="66" r="10"></circle><rect x="-58" y="96" width="20" height="14" rx="3" class="leaf-a"></rect><rect x="-28" y="96" width="20" height="14" rx="3" class="leaf-b"></rect><rect x="24" y="88" width="20" height="14" rx="3" class="leaf-b"></rect></g>
<g class="rf-tree" transform="translate(478 156)"><path d="M0 30 L-34 67 M0 30 L34 67 M34 67 L18 98 M34 67 L50 98"></path><circle cx="0" cy="20" r="12"></circle><circle cx="-34" cy="66" r="10"></circle><circle cx="34" cy="66" r="10"></circle><rect x="-44" y="88" width="20" height="14" rx="3" class="leaf-a"></rect><rect x="8" y="96" width="20" height="14" rx="3" class="leaf-a"></rect><rect x="40" y="96" width="20" height="14" rx="3" class="leaf-b"></rect></g>
<g class="rf-tree" transform="translate(478 252)"><path d="M0 30 L-34 67 M0 30 L34 67"></path><circle cx="0" cy="20" r="12"></circle><rect x="-44" y="62" width="20" height="14" rx="3" class="leaf-a"></rect><rect x="24" y="62" width="20" height="14" rx="3" class="leaf-b"></rect></g>
</g>
<path d="M526 126 C558 126 578 119 608 119" class="rf-arrow rf-arrow--3"></path>
<path d="M532 195 C560 195 578 173 608 173" class="rf-arrow rf-arrow--3"></path>
<path d="M526 272 C560 272 578 227 608 227" class="rf-arrow rf-arrow--3"></path>
<g class="rf-stage rf-stage--vote">
<text x="675" y="34" class="rf-label">Bỏ phiếu</text>
<rect x="615" y="78" width="120" height="220" rx="14" class="rf-panel"></rect>
<text x="675" y="119" class="rf-vote class-a-text">A</text><text x="675" y="173" class="rf-vote class-b-text">B</text><text x="675" y="227" class="rf-vote class-b-text">B</text>
<path d="M640 250 H710" class="rf-divider"></path>
<text x="675" y="282" class="rf-result">B: 2/3</text>
</g>
</svg>
<figcaption id="rf-process-caption"><b>Hình 1.</b> Hai nguồn ngẫu nhiên tạo khác biệt giữa các cây: mẫu huấn luyện và tập đặc trưng được phép xét tại từng nút.</figcaption>
</figure>
</div>

## 2. Hai lớp ngẫu nhiên tạo nên “rừng”

Với tập huấn luyện gồm $n$ quan sát và $p$ đặc trưng, một phiên bản Random Forest cho bài toán phân loại được xây dựng như sau:

1. Rút ngẫu nhiên có hoàn lại $n$ quan sát để tạo một mẫu bootstrap. Một quan sát có thể xuất hiện nhiều lần, còn một số quan sát không xuất hiện.
2. Trồng một cây quyết định trên mẫu đó. Tại **mỗi nút**, chỉ chọn ngẫu nhiên $m_{\text{try}}$ trong số $p$ đặc trưng, rồi tìm phép chia tốt nhất trong tập con này.
3. Lặp lại hai bước trên để có $B$ cây.
4. Khi gặp quan sát mới $x$, cho cả $B$ cây dự đoán và lấy lớp nhận nhiều phiếu nhất:

$$
\widehat y(x)=\operatorname{mode}\{T_1(x),\ldots,T_B(x)\}.
$$

Trong hồi quy, phép gộp thường là trung bình

$$
\widehat f(x)=\frac{1}{B}\sum_{b=1}^{B}T_b(x).
$$

Bước lấy mẫu có hoàn lại chính là ý tưởng [Bootstrap]({{< ref "bootstrap-method.md" >}}) dùng trong **Bagging** (*bootstrap aggregating*). Lớp ngẫu nhiên thứ hai — giới hạn đặc trưng được xét tại từng nút — làm các cây bớt giống nhau. Nếu một đặc trưng rất mạnh luôn được phép tham gia, nhiều cây có thể chọn cùng một phép chia gần gốc; khi ấy việc lấy trung bình nhiều cây tương quan cao đem lại ít lợi ích hơn.

## 3. Vì sao trung bình hóa có thể ổn định hơn?

Giả sử sai số của $B$ cây có cùng phương sai $\sigma^2$ và tương quan cặp xấp xỉ $\rho$. Khi đó, phương sai của trung bình có dạng gợi ý

$$
\operatorname{Var}\!\left(\frac{1}{B}\sum_{b=1}^{B}T_b\right)
=\rho\sigma^2+\frac{1-\rho}{B}\sigma^2.
$$

Số cây $B$ lớn làm giảm hạng thứ hai, nhưng không xóa được hạng $\rho\sigma^2$. Vì vậy Random Forest theo đuổi đồng thời hai mục tiêu:

- mỗi cây vẫn phải có năng lực dự đoán đủ tốt;
- các cây không nên mắc sai số quá giống nhau.

Chọn ít đặc trưng hơn tại mỗi nút thường giảm tương quan giữa các cây, nhưng nếu chọn quá ít thì từng cây có thể yếu đi. $m_{\text{try}}$, độ sâu, số quan sát tối thiểu ở lá và số cây đều là tham số cần đánh giá trên dữ liệu ngoài mẫu hoặc bằng một quy trình kiểm định phù hợp; không có một cấu hình tốt nhất cho mọi tập dữ liệu.

<div class="interactive-pane">
<section class="rf-lab" id="rf-lab" aria-labelledby="rf-lab-title">
  <div class="rf-lab__head">
    <div>
      <p class="rf-eyebrow">PHÒNG THÍ NGHIỆM TƯƠNG TÁC</p>
      <h3 id="rf-lab-title">Từ một cây đến ranh giới của cả rừng</h3>
    </div>
    <button type="button" class="rf-button" data-action="grow-forest">Cho rừng lớn dần</button>
  </div>
  <div class="rf-controls" aria-label="Điều khiển Random Forest">
    <label class="rf-control" for="rf-tree-count">
      <span>Số cây <output id="rf-tree-count-value" for="rf-tree-count">1</output></span>
      <input type="range" id="rf-tree-count" min="1" max="81" step="2" value="1">
    </label>
    <label class="rf-control" for="rf-feature-count">
      <span>Đặc trưng xét tại mỗi nút</span>
      <select id="rf-feature-count">
        <option value="1" selected>1 trong 2 — cây khác nhau hơn</option>
        <option value="2">2 trong 2 — cây giống nhau hơn</option>
      </select>
    </label>
  </div>
  <div class="rf-metrics" aria-live="polite">
    <div><span>Độ chính xác huấn luyện</span><strong id="rf-train-accuracy">—</strong></div>
    <div><span>Sai số out-of-bag (OOB)</span><strong id="rf-oob-error">—</strong></div>
    <div><span>Điểm đang xét</span><strong id="rf-query-vote">Bấm vào hình</strong></div>
  </div>
  <div class="rf-canvas-wrap">
    <canvas id="rf-canvas" role="img" aria-label="Ranh giới phân loại của Random Forest trên dữ liệu hai lớp; bấm vào mặt phẳng để xem tỷ lệ phiếu"></canvas>
  </div>
  <div class="rf-legend" aria-hidden="true"><span><i class="class-a"></i>Lớp A</span><span><i class="class-b"></i>Lớp B</span><span><i class="boundary"></i>Biên quyết định</span><span><i class="query"></i>Điểm đang xét</span></div>
  <p class="rf-hint">Kéo số cây, đổi số đặc trưng hoặc bấm vào mặt phẳng để xem tỷ lệ phiếu. Mô phỏng dùng cây CART giới hạn độ sâu trên một tập dữ liệu nhỏ, nên các con số chỉ minh họa cơ chế.</p>
</section>
<script src="/js/random-forest-interactive.js?v=1"></script>
<p class="rf-caption"><i><b>Hình 2.</b> Ranh giới của một cây thường thành các khối lớn. Khi thêm cây, tỷ lệ phiếu được trung bình hóa; ranh giới có thể ổn định hơn nhưng không nhất thiết cải thiện đơn điệu sau từng cây.</i></p>
</div>

## 4. Sai số out-of-bag đến từ đâu?

Trong một mẫu bootstrap kích thước $n$, xác suất một quan sát cụ thể không được rút lần nào là

$$
\left(1-\frac{1}{n}\right)^n \longrightarrow e^{-1}\approx 36{,}8\%.
$$

Các quan sát không xuất hiện trong mẫu huấn luyện của cây $b$ được gọi là **out-of-bag** (OOB) đối với cây đó. Để dự đoán OOB cho quan sát $i$, ta chỉ gộp phiếu từ những cây chưa dùng $i$ khi huấn luyện. So sánh các dự đoán này với nhãn thật cho ta một ước lượng nội bộ về sai số ngoài mẫu.

OOB hữu ích vì tận dụng ngay dữ liệu huấn luyện, nhưng không phải là phép bảo đảm cho dữ liệu tương lai. Nếu ta thử rất nhiều cách tiền xử lý hoặc siêu tham số rồi chọn cấu hình tốt nhất bằng cùng một sai số OOB, ước lượng cuối có thể trở nên lạc quan. Với quyết định quan trọng, vẫn nên giữ một tập kiểm tra độc lập hoặc dùng kiểm định lồng nhau.

## 5. Đọc kết quả mà không đòi hỏi quá nhiều từ mô hình

Random Forest thường là một mô hình cơ sở mạnh cho dữ liệu dạng bảng vì nó mô tả được quan hệ phi tuyến và tương tác giữa các đặc trưng mà không cần chỉ định sẵn công thức. Tuy nhiên, một vài giới hạn cần được giữ gần kết luận:

- **Không ngoại suy tốt trong hồi quy:** dự đoán của rừng là trung bình các giá trị ở nút lá, nên khó kéo dài một xu hướng vượt xa miền giá trị đã quan sát.
- **Xác suất bỏ phiếu chưa chắc đã được hiệu chỉnh:** tỷ lệ 80% số cây bỏ phiếu cho lớp B không tự động có nghĩa biến cố B xảy ra với xác suất đúng 80%.
- **Độ quan trọng của đặc trưng cần được diễn giải thận trọng:** các thước đo dựa trên mức giảm độ hỗn tạp có thể thiên lệch; permutation importance cũng bị ảnh hưởng khi các đặc trưng tương quan mạnh.
- **Dữ liệu mất cân bằng cần thước đo phù hợp:** độ chính xác tổng thể có thể che khuất việc mô hình bỏ sót lớp hiếm.
- **Nhiều cây hơn chủ yếu làm phép trung bình ổn định hơn:** sai số tổng quát hóa có xu hướng tiến tới một giới hạn với quy trình sinh cây cố định, chứ không được bảo đảm giảm sau mỗi cây mới.

So với [hồi quy Logistic]({{< ref "logistic-regression.md" >}}), Random Forest linh hoạt hơn về hình dạng biên quyết định nhưng khó tóm tắt bằng một bộ hệ số ngắn gọn. Lựa chọn hợp lý phụ thuộc mục tiêu: diễn giải tác động theo một cấu trúc tham số, hay dự đoán quan hệ phi tuyến với ít giả định hơn về dạng hàm.

## 6. Tóm tắt

Random Forest có thể được nhớ bằng ba động tác:

1. **Đổi dữ liệu:** mỗi cây nhận một mẫu bootstrap khác nhau.
2. **Đổi góc nhìn:** tại mỗi nút, cây chỉ xét một tập con ngẫu nhiên của các đặc trưng.
3. **Gộp dự đoán:** bỏ phiếu cho phân loại, lấy trung bình cho hồi quy.

Nguồn ngẫu nhiên thứ nhất tạo nhiều phiên bản của tập huấn luyện; nguồn thứ hai làm các cây bớt tương quan. Việc gộp dự đoán có thể giảm phương sai, nhưng hiệu quả thực tế vẫn phải được đánh giá trên dữ liệu phù hợp với mục tiêu sử dụng.

### Tài liệu tham khảo

- Leo Breiman (1996), [*Bagging Predictors*](https://www.stat.berkeley.edu/~breiman/bagging.pdf).
- Tin Kam Ho (1998), [*The Random Subspace Method for Constructing Decision Forests*](https://doi.org/10.1109/34.709601).
- Leo Breiman (2001), [*Random Forests*](https://www.stat.berkeley.edu/~breiman/randomforest2001.pdf).

