常见算法原理(线性回归、决策树、SVM、聚类等)

文章目录

机器学习算法是人工智能领域的核心工具,不同的算法适用于不同的问题场景。本文将深入讲解几种最常用的机器学习算法,包括线性回归、决策树、支持向量机和聚类算法,帮助读者理解它们的原理、应用场景以及实现方式。

线性回归:最简单却最实用的算法

线性回归是机器学习中最基础也是最重要的算法之一。虽然它看起来很简单,但在实际应用中却非常有效,很多复杂的算法都是在线性回归的基础上发展而来的。

线性回归的基本原理

线性回归的核心思想是找到一条直线(或者高维空间中的超平面),使得这条线能够最好地拟合数据点。用数学语言来说,就是找到一个函数关系,使得预测值和真实值之间的误差最小。

对于最简单的一元线性回归,我们要找的是这样一个函数:

y = wx + b

其中,x是输入特征,y是预测值,w是权重(斜率),b是偏置(截距)。我们的目标是找到最优的w和b,使得所有样本点到这条直线的距离之和最小。

这个距离通常用均方误差来衡量:

MSE = (1/n) * Σ(y_i - ŷ_i)²

其中,y_i是真实值,ŷ_i是预测值,n是样本数量。

梯度下降法求解

要找到最优的参数,最常用的方法就是梯度下降法。梯度下降的思想很直观:想象你站在山顶,想要下到山谷,最快的方法就是沿着最陡的方向往下走。在数学上,这个最陡的方向就是梯度的反方向。

具体的更新规则是:

w = w - α * ∂L/∂w

b = b - α * ∂L/∂b

其中,α是学习率,控制每次更新的步长。学习率太大可能导致震荡,太小则收敛速度慢。

下面用代码来实现一个简单的线性回归:

python 复制代码
import numpy as np
import matplotlib.pyplot as plt

class LinearRegression:
    def __init__(self, learning_rate=0.01, iterations=1000):
        self.lr = learning_rate
        self.iterations = iterations
        self.w = None
        self.b = None
        self.losses = []
    
    def fit(self, X, y):
        # 初始化参数
        n_samples, n_features = X.shape
        self.w = np.zeros(n_features)
        self.b = 0
        
        # 梯度下降
        for i in range(self.iterations):
            # 前向传播:计算预测值
            y_pred = np.dot(X, self.w) + self.b
            
            # 计算损失
            loss = np.mean((y - y_pred) ** 2)
            self.losses.append(loss)
            
            # 计算梯度
            dw = -(2/n_samples) * np.dot(X.T, (y - y_pred))
            db = -(2/n_samples) * np.sum(y - y_pred)
            
            # 更新参数
            self.w -= self.lr * dw
            self.b -= self.lr * db
            
            if i % 100 == 0:
                print(f"Iteration {i}: Loss = {loss:.4f}")
    
    def predict(self, X):
        return np.dot(X, self.w) + self.b

# 生成示例数据
np.random.seed(42)
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)

# 训练模型
model = LinearRegression(learning_rate=0.1, iterations=1000)
model.fit(X, y.ravel())

# 预测
X_test = np.array([[0], [2]])
predictions = model.predict(X_test)
print(f"预测结果: {predictions}")

# 可视化
plt.scatter(X, y, color='blue', label='真实数据')
plt.plot(X_test, predictions, color='red', linewidth=2, label='拟合直线')
plt.xlabel('X')
plt.ylabel('y')
plt.legend()
plt.title('线性回归拟合结果')
plt.show()

多元线性回归

在实际应用中,我们通常需要处理多个特征的情况。多元线性回归的形式是:

y = w₁x₁ + w₂x₂ + ... + wₙxₙ + b

用向量形式表示就是:

y = w^T · x + b

求解方法和一元线性回归类似,只是梯度计算变成了向量形式。上面的代码实现实际上已经支持多元线性回归了。

线性回归的应用场景

线性回归适用于以下场景:

  • 房价预测:根据面积、地段、楼层等特征预测房价
  • 销售预测:根据广告投入、季节等因素预测销售额
  • 温度预测:根据历史数据预测未来温度
  • 股票趋势分析:分析股票价格的线性趋势

需要注意的是,线性回归假设特征和目标之间存在线性关系,如果关系是非线性的,需要进行特征工程或使用其他算法。

决策树:像人一样做决策

决策树是一种非常直观的算法,它的决策过程就像人类的思维方式一样,通过一系列的判断来得出结论。

决策树的基本原理

决策树通过树形结构来表示决策过程。每个内部节点表示一个特征的判断,每个分支表示判断的结果,每个叶子节点表示最终的决策结果。
晴天
阴天
雨天

正常
天气如何?
湿度如何?
去打球
不去打球
不去打球
去打球

构建决策树的关键问题是:如何选择最优的特征进行分裂?这就需要用到信息增益或基尼系数等指标。

信息熵与信息增益

信息熵是衡量数据不确定性的指标。熵越大,数据越混乱;熵越小,数据越纯净。

信息熵的计算公式:

H(D) = -Σ p_i * log₂(p_i)

其中,p_i是第i类样本在数据集中的比例。

信息增益表示使用某个特征进行分裂后,信息熵的减少量:

Gain(D, A) = H(D) - Σ (|D_v|/|D|) * H(D_v)

其中,D_v是特征A取值为v的样本子集。

我们选择信息增益最大的特征进行分裂,这样可以使得分裂后的子节点尽可能纯净。

基尼系数

除了信息增益,还可以使用基尼系数来选择分裂特征。基尼系数表示从数据集中随机抽取两个样本,其类别不一致的概率:

Gini(D) = 1 - Σ p_i²

基尼系数越小,数据集的纯度越高。

决策树的实现

下面用代码实现一个简单的决策树分类器:

python 复制代码
import numpy as np
from collections import Counter

class DecisionTreeNode:
    def __init__(self, feature=None, threshold=None, left=None, right=None, value=None):
        self.feature = feature      # 分裂特征的索引
        self.threshold = threshold  # 分裂阈值
        self.left = left           # 左子树
        self.right = right         # 右子树
        self.value = value         # 叶子节点的类别

class DecisionTree:
    def __init__(self, max_depth=10, min_samples_split=2):
        self.max_depth = max_depth
        self.min_samples_split = min_samples_split
        self.root = None
    
    def _entropy(self, y):
        """计算信息熵"""
        counter = Counter(y)
        probs = np.array(list(counter.values())) / len(y)
        return -np.sum(probs * np.log2(probs + 1e-10))
    
    def _information_gain(self, X, y, feature, threshold):
        """计算信息增益"""
        # 父节点熵
        parent_entropy = self._entropy(y)
        
        # 分裂
        left_mask = X[:, feature] <= threshold
        right_mask = ~left_mask
        
        if np.sum(left_mask) == 0 or np.sum(right_mask) == 0:
            return 0
        
        # 子节点熵
        n = len(y)
        n_left, n_right = np.sum(left_mask), np.sum(right_mask)
        e_left = self._entropy(y[left_mask])
        e_right = self._entropy(y[right_mask])
        child_entropy = (n_left / n) * e_left + (n_right / n) * e_right
        
        return parent_entropy - child_entropy
    
    def _best_split(self, X, y):
        """找到最优分裂点"""
        best_gain = -1
        best_feature = None
        best_threshold = None
        
        n_features = X.shape[1]
        for feature in range(n_features):
            thresholds = np.unique(X[:, feature])
            for threshold in thresholds:
                gain = self._information_gain(X, y, feature, threshold)
                if gain > best_gain:
                    best_gain = gain
                    best_feature = feature
                    best_threshold = threshold
        
        return best_feature, best_threshold
    
    def _build_tree(self, X, y, depth=0):
        """递归构建决策树"""
        n_samples, n_features = X.shape
        n_classes = len(np.unique(y))
        
        # 停止条件
        if depth >= self.max_depth or n_samples < self.min_samples_split or n_classes == 1:
            leaf_value = Counter(y).most_common(1)[0][0]
            return DecisionTreeNode(value=leaf_value)
        
        # 找到最优分裂点
        best_feature, best_threshold = self._best_split(X, y)
        
        if best_feature is None:
            leaf_value = Counter(y).most_common(1)[0][0]
            return DecisionTreeNode(value=leaf_value)
        
        # 分裂数据
        left_mask = X[:, best_feature] <= best_threshold
        right_mask = ~left_mask
        
        # 递归构建左右子树
        left_subtree = self._build_tree(X[left_mask], y[left_mask], depth + 1)
        right_subtree = self._build_tree(X[right_mask], y[right_mask], depth + 1)
        
        return DecisionTreeNode(best_feature, best_threshold, left_subtree, right_subtree)
    
    def fit(self, X, y):
        """训练决策树"""
        self.root = self._build_tree(X, y)
    
    def _predict_sample(self, x, node):
        """预测单个样本"""
        if node.value is not None:
            return node.value
        
        if x[node.feature] <= node.threshold:
            return self._predict_sample(x, node.left)
        else:
            return self._predict_sample(x, node.right)
    
    def predict(self, X):
        """预测多个样本"""
        return np.array([self._predict_sample(x, self.root) for x in X])

# 使用示例
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

# 加载数据
iris = load_iris()
X, y = iris.data, iris.target

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 训练模型
tree = DecisionTree(max_depth=5)
tree.fit(X_train, y_train)

# 预测
y_pred = tree.predict(X_test)
accuracy = np.mean(y_pred == y_test)
print(f"准确率: {accuracy:.4f}")

决策树的优缺点

优点:

  • 易于理解和解释,可以可视化
  • 不需要数据标准化
  • 可以处理数值型和类别型数据
  • 可以处理多输出问题

缺点:

  • 容易过拟合,需要剪枝
  • 对数据的微小变化敏感
  • 可能创建偏向某些类别的树
  • 训练时间较长

决策树的改进:随机森林

为了克服决策树容易过拟合的问题,可以使用集成学习方法,其中最著名的就是随机森林。随机森林通过构建多棵决策树,然后对它们的预测结果进行投票或平均,从而提高模型的泛化能力。
训练数据
决策树1
决策树2
决策树3
...
决策树N
投票/平均
最终预测

支持向量机:寻找最优分界线

支持向量机是一种非常强大的分类算法,它的核心思想是找到一个最优的超平面,使得不同类别的样本被正确分开,并且分类间隔最大。

SVM的基本原理

对于线性可分的数据,SVM要找的是这样一个超平面:

w^T · x + b = 0

使得所有样本点到这个超平面的距离之和最大。这个距离称为间隔(margin)。
SVM核心思想
找到最优超平面
最大化分类间隔
支持向量
距离超平面最近的点
决定超平面位置

数学上,这个问题可以表示为:

最小化:(1/2) ||w||²

约束条件:y_i(w^T · x_i + b) ≥ 1

其中,y_i是样本的类别标签(+1或-1),x_i是样本特征。

软间隔与惩罚参数

在实际应用中,数据往往不是完全线性可分的,可能存在一些噪声点。为了处理这种情况,引入了软间隔的概念,允许一些样本点违反间隔约束。

引入松弛变量ξ_i,优化目标变为:

最小化:(1/2) ||w||² + C * Σξ_i

约束条件:y_i(w^T · x_i + b) ≥ 1 - ξ_i,ξ_i ≥ 0

其中,C是惩罚参数,控制对误分类的惩罚程度。C越大,对误分类的惩罚越大,模型越复杂;C越小,模型越简单,但可能欠拟合。

核函数:处理非线性问题

当数据不是线性可分时,可以通过核函数将数据映射到高维空间,使其在高维空间中线性可分。

常用的核函数有:

  • 线性核:K(x, y) = x^T · y
  • 多项式核:K(x, y) = (x^T · y + c)^d
  • 高斯核(RBF):K(x, y) = exp(-γ||x - y||²)
  • Sigmoid核:K(x, y) = tanh(αx^T · y + c)

其中,RBF核是最常用的核函数,它可以将数据映射到无限维空间。

SVM的实现

下面用代码演示如何使用SVM:

python 复制代码
import numpy as np
from sklearn import svm
from sklearn.datasets import make_classification, make_circles
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# 生成线性可分数据
X_linear, y_linear = make_classification(n_samples=100, n_features=2, n_redundant=0, 
                                         n_informative=2, n_clusters_per_class=1, 
                                         random_state=42)

# 生成非线性数据
X_nonlinear, y_nonlinear = make_circles(n_samples=100, noise=0.1, factor=0.5, random_state=42)

def plot_svm_decision_boundary(X, y, model, title):
    """绘制SVM决策边界"""
    plt.figure(figsize=(10, 6))
    
    # 创建网格
    h = 0.02
    x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))
    
    # 预测网格点
    Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    
    # 绘制决策边界
    plt.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.RdYlBu)
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.RdYlBu, edgecolors='black')
    
    # 绘制支持向量
    plt.scatter(model.support_vectors_[:, 0], model.support_vectors_[:, 1],
                s=200, linewidth=1, facecolors='none', edgecolors='k', label='支持向量')
    
    plt.xlabel('特征1')
    plt.ylabel('特征2')
    plt.title(title)
    plt.legend()
    plt.show()

# 线性SVM
print("训练线性SVM...")
svm_linear = svm.SVC(kernel='linear', C=1.0)
svm_linear.fit(X_linear, y_linear)
print(f"线性SVM准确率: {svm_linear.score(X_linear, y_linear):.4f}")
plot_svm_decision_boundary(X_linear, y_linear, svm_linear, "线性SVM")

# RBF核SVM
print("\n训练RBF核SVM...")
svm_rbf = svm.SVC(kernel='rbf', C=1.0, gamma='auto')
svm_rbf.fit(X_nonlinear, y_nonlinear)
print(f"RBF核SVM准确率: {svm_rbf.score(X_nonlinear, y_nonlinear):.4f}")
plot_svm_decision_boundary(X_nonlinear, y_nonlinear, svm_rbf, "RBF核SVM")

# 比较不同C值的影响
print("\n比较不同C值的影响...")
for C in [0.1, 1, 10, 100]:
    model = svm.SVC(kernel='linear', C=C)
    model.fit(X_linear, y_linear)
    print(f"C={C}: 准确率={model.score(X_linear, y_linear):.4f}, "
          f"支持向量数量={len(model.support_vectors_)}")

SVM的应用场景

SVM适用于以下场景:

  • 文本分类:垃圾邮件检测、情感分析
  • 图像识别:人脸识别、手写数字识别
  • 生物信息学:蛋白质分类、基因分类
  • 金融预测:信用评分、股票预测

SVM的优点是在高维空间中表现良好,对于小样本数据也能取得不错的效果。但缺点是训练时间较长,对大规模数据不太适用。

聚类算法:发现数据的内在结构

聚类是无监督学习的典型代表,它的目标是将相似的样本归为一类,不同的样本分到不同的类。聚类不需要标签数据,完全依靠数据本身的特征进行分组。

K-Means聚类

K-Means是最常用的聚类算法,它的思想很简单:将数据分成K个簇,使得每个簇内的样本尽可能相似,不同簇之间的样本尽可能不同。


开始
随机初始化K个中心点
分配样本到最近的中心点
更新中心点位置
中心点是否变化?
结束

K-Means算法的步骤:

  1. 随机选择K个样本作为初始聚类中心
  2. 计算每个样本到各个聚类中心的距离,将样本分配给最近的聚类中心
  3. 重新计算每个簇的中心点(簇内所有样本的均值)
  4. 重复步骤2和3,直到聚类中心不再变化或达到最大迭代次数

K-Means的实现

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs

class KMeans:
    def __init__(self, n_clusters=3, max_iters=100):
        self.n_clusters = n_clusters
        self.max_iters = max_iters
        self.centroids = None
        self.labels = None
    
    def fit(self, X):
        # 随机初始化聚类中心
        n_samples = X.shape[0]
        random_indices = np.random.choice(n_samples, self.n_clusters, replace=False)
        self.centroids = X[random_indices]
        
        for i in range(self.max_iters):
            # 分配样本到最近的聚类中心
            distances = np.sqrt(((X - self.centroids[:, np.newaxis])**2).sum(axis=2))
            self.labels = np.argmin(distances, axis=0)
            
            # 更新聚类中心
            new_centroids = np.array([X[self.labels == k].mean(axis=0) 
                                     for k in range(self.n_clusters)])
            
            # 检查是否收敛
            if np.allclose(self.centroids, new_centroids):
                print(f"在第{i+1}次迭代后收敛")
                break
            
            self.centroids = new_centroids
        
        return self
    
    def predict(self, X):
        distances = np.sqrt(((X - self.centroids[:, np.newaxis])**2).sum(axis=2))
        return np.argmin(distances, axis=0)

# 生成示例数据
X, y_true = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=42)

# 训练K-Means
kmeans = KMeans(n_clusters=4, max_iters=100)
kmeans.fit(X)

# 可视化结果
plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)
plt.scatter(X[:, 0], X[:, 1], c=y_true, cmap='viridis', alpha=0.6)
plt.title('真实分布')
plt.xlabel('特征1')
plt.ylabel('特征2')

plt.subplot(1, 2, 2)
plt.scatter(X[:, 0], X[:, 1], c=kmeans.labels, cmap='viridis', alpha=0.6)
plt.scatter(kmeans.centroids[:, 0], kmeans.centroids[:, 1], 
           c='red', marker='X', s=200, edgecolors='black', label='聚类中心')
plt.title('K-Means聚类结果')
plt.xlabel('特征1')
plt.ylabel('特征2')
plt.legend()

plt.tight_layout()
plt.show()

如何选择K值

选择合适的K值是K-Means算法的关键问题。常用的方法有:

  1. 肘部法则:绘制不同K值对应的簇内误差平方和(SSE),选择曲线出现拐点的K值
python 复制代码
def elbow_method(X, max_k=10):
    """肘部法则选择K值"""
    sse = []
    for k in range(1, max_k + 1):
        kmeans = KMeans(n_clusters=k)
        kmeans.fit(X)
        # 计算簇内误差平方和
        sse_k = sum([np.sum((X[kmeans.labels == i] - kmeans.centroids[i])**2) 
                     for i in range(k)])
        sse.append(sse_k)
    
    plt.figure(figsize=(8, 5))
    plt.plot(range(1, max_k + 1), sse, marker='o')
    plt.xlabel('聚类数量K')
    plt.ylabel('簇内误差平方和SSE')
    plt.title('肘部法则选择K值')
    plt.grid(True)
    plt.show()

elbow_method(X, max_k=10)
  1. 轮廓系数:衡量样本与其所在簇的相似度,取值范围[-1, 1],值越大表示聚类效果越好

层次聚类

除了K-Means,层次聚类也是一种常用的聚类方法。层次聚类不需要预先指定簇的数量,它通过构建一个树状结构来表示样本之间的层次关系。

层次聚类分为两种:

  • 自底向上(凝聚):开始时每个样本是一个簇,然后逐步合并最相似的簇
  • 自顶向下(分裂):开始时所有样本在一个簇中,然后逐步分裂
python 复制代码
from scipy.cluster.hierarchy import dendrogram, linkage
import matplotlib.pyplot as plt

# 生成数据
X, _ = make_blobs(n_samples=50, centers=3, random_state=42)

# 层次聚类
linkage_matrix = linkage(X, method='ward')

# 绘制树状图
plt.figure(figsize=(12, 6))
dendrogram(linkage_matrix)
plt.title('层次聚类树状图')
plt.xlabel('样本索引')
plt.ylabel('距离')
plt.show()

DBSCAN:基于密度的聚类

DBSCAN是一种基于密度的聚类算法,它可以发现任意形状的簇,并且能够识别噪声点。

DBSCAN的核心概念:

  • 核心点:在半径ε内至少有MinPts个邻居的点
  • 边界点:不是核心点,但在某个核心点的ε邻域内
  • 噪声点:既不是核心点也不是边界点
python 复制代码
from sklearn.cluster import DBSCAN
from sklearn.datasets import make_moons

# 生成月牙形数据
X, _ = make_moons(n_samples=200, noise=0.05, random_state=42)

# DBSCAN聚类
dbscan = DBSCAN(eps=0.3, min_samples=5)
labels = dbscan.fit_predict(X)

# 可视化
plt.figure(figsize=(10, 6))
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', alpha=0.6)
plt.title('DBSCAN聚类结果')
plt.xlabel('特征1')
plt.ylabel('特征2')
plt.colorbar(label='簇标签')
plt.show()

print(f"发现{len(set(labels)) - (1 if -1 in labels else 0)}个簇")
print(f"噪声点数量: {list(labels).count(-1)}")

聚类算法的应用场景

聚类算法在实际中有广泛的应用:

  • 客户细分:根据购买行为将客户分组
  • 图像分割:将图像分成不同的区域
  • 异常检测:识别与正常数据不同的异常点
  • 文档聚类:将相似的文档归为一类
  • 基因分析:根据基因表达模式对基因分组

算法选择指南

面对不同的问题,如何选择合适的算法呢?下面给出一些建议:






机器学习问题
有标签数据?
预测连续值?
聚类算法
回归算法
分类算法
线性回归

岭回归

Lasso回归
数据线性可分?
逻辑回归

线性SVM
决策树

随机森林

核SVM

神经网络
K-Means

层次聚类

DBSCAN

选择算法时需要考虑的因素:

  • 数据规模:大数据集适合线性模型、小数据集可以用SVM
  • 特征维度:高维数据适合线性模型或降维后使用
  • 数据分布:线性可分用线性模型,非线性用核方法或树模型
  • 可解释性:需要解释性用线性模型或决策树
  • 训练时间:时间紧张用简单模型,时间充足可以用复杂模型

总结

本文详细介绍了机器学习中最常用的几种算法:线性回归、决策树、支持向量机和聚类算法。每种算法都有其适用的场景和特点:

线性回归简单高效,适合处理线性关系的问题,是入门机器学习的最佳选择。通过梯度下降法,我们可以找到最优的参数,使得预测误差最小。

决策树直观易懂,可以处理非线性问题,通过信息增益或基尼系数选择最优分裂特征。虽然单棵决策树容易过拟合,但通过随机森林等集成方法可以大大提高性能。

支持向量机通过最大化分类间隔来找到最优分界线,对于高维数据表现优异。通过核函数,SVM可以处理非线性问题,是一种非常强大的分类算法。

聚类算法不需要标签数据,可以自动发现数据的内在结构。K-Means简单高效,层次聚类可以展示数据的层次关系,DBSCAN可以发现任意形状的簇并识别噪声点。

在实际应用中,没有一种算法是万能的,需要根据具体问题选择合适的算法。通常的做法是尝试多种算法,通过交叉验证等方法比较它们的性能,最终选择效果最好的模型。同时,特征工程、参数调优等技巧也对模型性能有重要影响。

掌握这些基础算法的原理和实现,是深入学习机器学习的重要基础。在此基础上,可以进一步学习深度学习、强化学习等更高级的技术。

相关推荐
美酒没故事°12 小时前
Open WebUI安装指南。搭建自己的自托管 AI 平台
人工智能·windows·ai
鸿乃江边鸟12 小时前
Nanobot 从onboard启动命令来看个人助理Agent的实现
人工智能·ai
本旺13 小时前
【Openclaw 】完美解决 Codex 认证失败
ai·codex·openclaw·小龙虾·gpt5.4
张張40813 小时前
(域格)环境搭建和编译
c语言·开发语言·python·ai
乐鑫科技 Espressif14 小时前
使用 MCP 服务器,把乐鑫文档接入 AI 工作流
人工智能·ai·esp32·乐鑫科技
语戚14 小时前
Stable Diffusion 入门:架构、空间与生成流程概览
人工智能·ai·stable diffusion·aigc·模型
俊哥V14 小时前
每日 AI 研究简报 · 2026-04-08
人工智能·ai
rrrjqy14 小时前
什么是RAG?
ai
Flittly15 小时前
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
java·笔记·spring·ai·springboot
Flittly15 小时前
【SpringAIAlibaba新手村系列】(14)MCP 本地服务与工具集成
java·spring boot·笔记·spring·ai