【机器学习】半监督学习

news2024/11/19 9:29:14

de9ff55b88b0a39cc0902ef9d0fc2d28.png

0b813ea581265e59ed39d18593264d81.png

一、问题假设

要利用无标签样本进行训练,必须对样本的分布进行假设?

98ac9450ddfbacdfcdfd5631af469a22.png

231f060d7d9c8aa83fe5187ebbd53671.png

二、启发式算法

d3e5b4d9e8a0fa48025fc31800b7c5c3.png

a40837166de2801398905d8e3544c57a.png

61e7cd5cce3492c4d1627bb41699bb0e.png

自训练和协同训练是两种常用的半监督学习的方法,它们的主要区别在于使用的模型的数量和类型。

  • 自训练:自训练是一种使用单个模型的半监督学习的方法,它的过程是先用有标签的数据训练一个初始的模型,然后用这个模型对无标签的数据进行预测,选择一些预测结果最有信心的数据作为新的有标签的数据,加入到原来的有标签的数据集中,再用这个扩充的数据集训练一个新的模型,重复这个过程直到满足某个终止条件。自训练的优点是简单和高效,但是缺点是可能会放大模型的初始偏差,导致错误的标签传播。

  • 协同训练:协同训练是一种使用多个模型的半监督学习的方法,它的过程是先用有标签的数据训练两个或多个不同的模型,然后用这些模型分别对无标签的数据进行预测,选择一些预测结果最有信心且不一致的数据作为新的有标签的数据,分别加入到对应的有标签的数据集中,再用这些扩充的数据集训练新的模型,重复这个过程直到满足某个终止条件。协同训练的优点是能够利用模型之间的多样性和互补性,减少错误的标签传播,但是缺点是需要设计合适的模型和选择合适的数据,否则可能会降低协同训练的效果。

三、生成模型

787bdd69ded2d1b472b0da2a13167244.png

7054a16a1f95b6edd24cf56695a8a4d3.png

2cc1dc9fb665eceb1d4f5925618fab75.png

使用高斯混合模型的半监督学习生成模型的原理

d7946a08771ecfbec286f9ca200d4738.png

eb83e3dd6c40970323c3dfe21205336c.png

这是一个半监督学习的示例,使用高斯混合模型(GMM)来对数据进行聚类和分类

# 导入numpy库,用于进行数值计算和矩阵操作
import numpy as np
# 导入sklearn库中的make_blobs函数,用于生成模拟数据
from sklearn.datasets import make_blobs
# 导入sklearn库中的GaussianMixture类,用于创建和训练GMM模型
from sklearn.mixture import GaussianMixture


# 生成数据,其中有三个类别,每个类别有两个特征
# X是一个1000行2列的矩阵,表示1000个数据点的特征值
# y是一个长度为1000的向量,表示每个数据点的真实类别
X, y = make_blobs(n_samples=1000, n_features=2, centers=3, random_state=0)


# 随机选择一部分数据作为标记数据,其余的作为未标记数据
# n_labeled是标记数据的数量,这里设为100
n_labeled = 100
# indices是一个长度为1000的向量,表示对数据点的随机排列
indices = np.random.permutation(X.shape[0])
# X_labeled是一个100行2列的矩阵,表示标记数据的特征值
X_labeled = X[indices[:n_labeled]]
# y_labeled是一个长度为100的向量,表示标记数据的真实类别
y_labeled = y[indices[:n_labeled]]
# X_unlabeled是一个900行2列的矩阵,表示未标记数据的特征值
X_unlabeled = X[indices[n_labeled:]]


# 用标记数据来初始化GMM的参数,假设有三个高斯分布
# gmm是一个GaussianMixture对象,表示GMM模型
gmm = GaussianMixture(n_components=3, random_state=0)
# gmm.means_init是一个3行2列的矩阵,表示GMM模型的初始均值
# 这里用标记数据的均值来初始化GMM模型的均值
gmm.means_init = np.array([X_labeled[y_labeled == i].mean(axis=0) for i in range(3)])
# gmm.fit是一个方法,用于根据标记数据来训练GMM模型
# 这里用标记数据的特征值和类别来训练GMM模型
gmm.fit(X_labeled, y_labeled)


# 用未标记数据来更新GMM的参数,用EM算法来迭代优化
# gmm.fit是一个方法,用于根据未标记数据来更新GMM模型
# 这里用未标记数据的特征值来更新GMM模型
gmm.fit(X_unlabeled)


# 用更新后的GMM来计算每个数据点的后验概率
# probs是一个1000行3列的矩阵,表示每个数据点属于每个高斯分布的后验概率
# gmm.predict_proba是一个方法,用于根据GMM模型来计算后验概率
# 这里用GMM模型和所有数据的特征值来计算后验概率
probs = gmm.predict_proba(X)


# 用后验概率来对数据进行分类,将数据分配给后验概率最大的高斯分布所对应的类别
# y_pred是一个长度为1000的向量,表示每个数据点的预测类别
# probs.argmax是一个方法,用于沿着指定的轴返回最大值的索引
# 这里用后验概率的最大值所在的列的索引来表示预测类别
y_pred = probs.argmax(axis=1)


# 计算分类的准确率
# accuracy是一个浮点数,表示预测类别和真实类别的一致比例
# np.mean是一个函数,用于计算平均值
# 这里用预测类别和真实类别的相等情况的平均值来表示准确率
accuracy = np.mean(y_pred == y)
# print是一个函数,用于输出结果
# 这里用字符串格式化的方法,将准确率保留两位小数并输出
print(f"Accuracy: {accuracy:.2f}")

输出:Accuracy: 0.92

四、低密度分割

半监督支持向量机

7bf70995beaccb93814e3a85dea92b17.png

SSVM

474fc37c15d014cd809523e81de56361.png

# 导入相关的库
import numpy as np
from sklearn.datasets import make_blobs
from sklearn.svm import SVC


# 生成数据,其中有三个类别,每个类别有两个特征
X, y = make_blobs(n_samples=1000, n_features=2, centers=3, random_state=0)


# 随机选择一部分数据作为标记数据,其余的作为未标记数据
n_labeled = 100
indices = np.random.permutation(X.shape[0])
X_labeled = X[indices[:n_labeled]]
y_labeled = y[indices[:n_labeled]]
X_unlabeled = X[indices[n_labeled:]]


# 用标记数据来训练一个初始的SVM分类器,假设有三个类别
svm = SVC(C=1.0, kernel='rbf', gamma='auto', probability=True)
svm.fit(X_labeled, y_labeled)


# 用未标记数据来改进SVM分类器,用自训练的方法
n_iter = 10 # 迭代次数
n_add = 10 # 每次迭代添加的数据个数
for i in range(n_iter):
  # 用SVM分类器来对未标记数据进行预测,得到每个数据点属于每个类别的概率
  probs = svm.predict_proba(X_unlabeled)
  # 选择预测结果最有信心的一部分数据,即概率最大的数据
  max_probs = probs.max(axis=1)
  max_indices = max_probs.argsort()[-n_add:]
  # 将这些数据加入到标记数据中,用其预测的类别作为标签
  X_labeled = np.concatenate([X_labeled, X_unlabeled[max_indices]])
  y_labeled = np.concatenate([y_labeled, probs[max_indices].argmax(axis=1)])
  # 用这些数据来重新训练SVM分类器
  svm.fit(X_labeled, y_labeled)
  # 从未标记数据中移除这些数据
  X_unlabeled = np.delete(X_unlabeled, max_indices, axis=0)


# 用改进后的SVM分类器来对所有数据进行分类
y_pred = svm.predict(X)


# 计算分类的准确率
accuracy = np.mean(y_pred == y)
print(f"Accuracy: {accuracy:.2f}") # 输出 0.9

TSVM     Transductive SVM

52e2839a546a06bdfc04c0eb50389308.png

import numpy as np
import sklearn.svm as svm
import time
class TransductiveSVM(svm.SVC):
    def __init__(self,kernel="rbf",Cl=1,Cu=0.01,gamma=0.1,X2=None):
        '''
        Initial TSVM
        Parameters
        ----------
        kernel: kernel of svm
        Cl: Penalty Inductive SVM
        Cu: Penalty Unlabeled set
        gamma: gamma for rbf kernel
        X2: Unlabeled set(only features)
                np.array, shape:[n2, m], n2: numbers of samples without labels, m: numbers of features




        '''
        self.Cl=Cl
        self.Cu=Cu
        self.kernel = kernel
        self.gamma=gamma
        self.clf=svm.SVC(C=self.Cl,kernel=kernel,gamma=self.gamma,probability=True)
        self.Yresult=None
        self.X2=X2


    def fit(self, X1, Y1):
        '''
        Train TSVM by X1, Y1, X2(X2 is passed on init)
        Parameters
        ----------
        X1: Input data with labels
                np.array, shape:[n1, m], n1: numbers of samples with labels, m: numbers of features
        Y1: labels of X1
                np.array, shape:[n1, ], n1: numbers of samples with labels




        '''


        t=time.time()
        X2=self.X2
        Y1[Y1!=+1]=-1
        Y1 = np.expand_dims(Y1, 1)
        ratio=sum(1 for i in Y1 if i==+1)/len(X1)
        num_plus=int(len(X2)*ratio) #number of positive example as describe in joachims svm


        N = len(X1) + len(X2)


        sample_weight = np.zeros(N)
        sample_weight[:len(X1)] = self.Cl




        self.clf.fit(X1, Y1,sample_weight=sample_weight[:len(X1)])  #classify the num_plus examples with the highest value with +1, other -1
        Y2=np.full(shape=self.clf.predict(X2).shape,fill_value=-1)
        Y2_d = self.clf.decision_function(X2)
        index=Y2_d.argsort()[-num_plus:][::-1]
        for item in index:
            Y2[item]=+1




        #INIT CMINUS E CLUS
        #C_minus=.00001
        C_minus=.00001
        C_plus=.00001*(num_plus/(len(X2)-num_plus))
        for i in range(len(Y2)):
            if(Y2[i]==+1):
                sample_weight[len(X1)+i]=C_plus
            else:
                sample_weight[len(X1)+i]=C_minus




        Y2 = np.expand_dims(Y2, 1)
        X3 = np.vstack((X1, X2))
        Y3 = np.vstack((Y1, Y2))
        k=0




        while (C_minus<self.Cu or C_plus<self.Cu): #LOOP 1
            self.clf.fit(X3, Y3, sample_weight=sample_weight)
            Y3 = Y3.reshape(-1)
            #slack=Y3*(self.clf.decision_function(X3))
            slack = Y3*self.clf.decision_function(X3)
            idx=np.argwhere(slack<1)
            eslackD=np.zeros(shape=slack.shape)
            for index in idx:
                eslackD[index]=1-slack[index]
            eslack2=np.zeros(shape=Y2.shape)
            eslack=eslackD[:len(X1)] #EPSILON OF LABELLED DATA
            eslack2=eslackD[len(X1):] # EPSILON FOR UNLABELED DATA




            condition=self.checkCondition(Y2,eslack2) #CONDITION OF LOOP
            l=0
            while(condition): #LOOP 2
                l+=1


                i,j=self.getIndexCondition(Y2,eslack2)  #TAKE A POSITIVE AND NEGATIVE SET
                #print("Switching at loop "+str(k)+"."+str(l)+"     index: "+str(i)+" "+str(j))
                #print("Switching values: "+str(eslack2[i])+" "+str(eslack2[j]))
                Y2[i]=Y2[i]*-1 #SWITCHING EXAMPLE
                Y2[j]= Y2[j]*-1


                sample_weight[len(X1)+i],sample_weight[len(X1)+j]=sample_weight[len(X1)+j],sample_weight[len(X1)+i] #UPDATE THE WEIGHT


                Y3=np.concatenate((Y1,Y2),axis=0)
                self.clf.fit(X3, Y3, sample_weight=sample_weight) #TRAINING WITH NEW LABELLING
                Y3 = Y3.reshape(-1)
                #slack =Y3*(self.clf.decision_function(X3))
                slack = Y3*self.clf.decision_function(X3)
                idx = np.argwhere(slack < 1)
                eslackD = np.zeros(shape=slack.shape)


                for index in idx:
                    eslackD[index] = 1 - slack[index]


                eslack = eslackD[:len(X1)]
                eslack2 = np.zeros(shape=Y2.shape)
                eslack2 = eslackD[len(X1):]
                condition = self.checkCondition(Y2, eslack2)
            k+=1
            #print(eslack2)
            C_minus=min(2*C_minus,self.Cu)
            C_plus=min(2*C_plus,self.Cu)
            #print("Loop "+str(k)+" Ctest="+str(self.Cu)+"   Cplus="+str(C_plus)+"   Cminus="+str(C_minus))


            for i in range(len(Y2)):
                if (Y2[i] == 1):
                    sample_weight[len(X1)+i] = C_plus
                else:
                    sample_weight[len(X1)+i] = C_minus


        self.Yresult=Y2
        Y3 = np.concatenate((Y1, Y2), axis=0)
        Y3=Y3.reshape(-1)
        end=time.time()
        print("The training finish in  "+str(end-t)+"  seconds")
        return self


    def checkCondition(self,Y,slack):
        '''
        Check condition of the loop 2
        Parameters
        ----------


        Y: labels of X2
                np.array, shape:[n1, ], n1: numbers of samples with semi-labels


        slack: slack variable for unlabeled set
                np.array, shape:[n1, ], n1: numbers of with semi-labels




        '''
        condition=False
        M=len(Y)
        for i in range(M):
            for j in range(M):
                if((Y[i]!=Y[j]) and (slack[i]>0) and (slack[j]>0) and ((slack[i]+slack[j])>2.001)):
                    condition=True
                    return condition
        return condition


    def getIndexCondition(self,Y,slack):
        '''
        Get index that satisfies condition of loop 2
        Parameters
        ----------


        Y: labels of X2
                np.array, shape:[n1, ], n1: numbers of samples with semi-labels


        slack: slack variable for unlabeled set
                np.array, shape:[n1, ], n1: numbers of with semi-labels




        '''
        M=len(Y)
        for i in range(M):
            for j in range(M):
                if(Y[i]!=Y[j] and slack[i]>0 and slack[j]>0 and (slack[i]+slack[j]>2.001)):
                    return i,j


    def predict(self, X):
        return self.clf.predict(X)


    def predict_proba(self):
        return self.clf.predict_proba()


    def decision_function(self, X):
        return self.clf.decision_function(X)


    def getResult(self):
        return self.Yresult


if __name__ == '__main__':
    from sklearn.datasets import load_breast_cancer
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import accuracy_score, precision_score, recall_score,roc_curve,f1_score


    X,y = load_breast_cancer(return_X_y=True)
    y[y==0]=-1
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.985)
    print(X_train.shape,X_test.shape)
    gammas=[0.001,0.01,0.2,0.4,0.8,1.5,3]
    cls=[1,2,5,10,20]
    c=1
    g=0.0001
    clf1 = svm.SVC(C=c,kernel="linear",gamma=g)
    #y_train=np.ravel(y_train)
    clf1.fit(X_train, y_train)


    y_svm = clf1.predict(X_test)
    f2 = accuracy_score(y_true=y_test, y_pred=y_svm)
    print("ACCURACY SVM " + str(f2))
    print("F1 SVM  ",f1_score(y_true=y_test,y_pred=y_svm))
    print("                                ")
    #print(clf1.coef_[0])
    clf=TransductiveSVM(kernel="linear",Cl=c,Cu=0.5,X2=X_test,gamma=g)
    #y_train=np.ravel(y_train)
    clf.fit(X_train,y_train)
    y_predicted=clf.predict(X_test)
    f = accuracy_score(y_true=y_test, y_pred=y_predicted)
    print("ACCURACY TSVM " + str(f))
    print("F1 TSVM " + str(f1_score(y_true=y_test,y_pred=y_predicted)))
    #print(clf.clf.coef_[0])


    #print(y_svm)

输出:

(8, 30) (561, 30)

ACCURACY SVM 0.8645276292335116

F1 SVM   0.9025641025641026

The training finish in  4.932965517044067  seconds

ACCURACY TSVM 0.8413547237076648

F1 TSVM 0.8593996840442337

五、基于图的算法

e27cdc37a5e0233b748da8c2d089dcd9.png

78553cb048952285c3b3b9f2ca42e31a.png

使用Python实现的基于图的半监督学习算法的示例代码,它使用了标签传播(Label Propagation)的方法:

# 导入相关的库
import numpy as np
from sklearn.datasets import make_blobs
from sklearn.metrics.pairwise import rbf_kernel


# 生成数据,其中有三个类别,每个类别有两个特征
X, y = make_blobs(n_samples=1000, n_features=2, centers=3, random_state=0)


# 随机选择一部分数据作为标记数据,其余的作为未标记数据
n_labeled = 100
indices = np.random.permutation(X.shape[0])
X_labeled = X[indices[:n_labeled]]
y_labeled = y[indices[:n_labeled]]
X_unlabeled = X[indices[n_labeled:]]


# 用标记数据和未标记数据构建一个图,用高斯核函数作为相似度度量,用k近邻图作为图结构
k = 10 # 近邻的个数
sigma = 1.0 # 高斯核函数的参数
# 计算数据点之间的高斯核矩阵
K = rbf_kernel(X, gamma=1.0 / (2 * sigma ** 2))
# 对每个数据点,只保留最近的k个邻居的相似度,其余的设为0,得到一个稀疏的相似度矩阵
W = np.zeros_like(K)
for i in range(X.shape[0]):
  # 找到第i个数据点的最近的k个邻居的索引
  neighbors = K[i].argsort()[-(k + 1):-1]
  # 将这些邻居的相似度保留,其余的设为0
  W[i, neighbors] = K[i, neighbors]
# 使相似度矩阵对称,即如果i和j是邻居,那么j和i也是邻居
W = np.maximum(W, W.T)


# 用标记数据的类别信息来初始化图中的节点的标签,用一个矩阵Y表示,其中Y[i, j]表示第i个数据点属于第j个类别的概率
n_classes = len(np.unique(y)) # 类别的个数
Y = np.zeros((X.shape[0], n_classes)) # 初始化标签矩阵
# 对于标记数据,用one-hot编码表示其类别,即Y[i, j] = 1当且仅当第i个数据点属于第j个类别
for i in range(n_labeled):
  Y[indices[i], y_labeled[i]] = 1
# 对于未标记数据,用均匀分布表示其类别,即Y[i, j] = 1 / n_classes
Y[indices[n_labeled:]] = 1.0 / n_classes


# 用图中的边来传播节点的标签,用标签传播的方法
alpha = 0.99 # 平滑系数,介于0和1之间,越接近1表示越依赖于未标记数据的信息,越接近0表示越依赖于标记数据的信息
n_iter = 10 # 迭代次数
# 在每次迭代中,用以下的公式来更新标签矩阵:Y = alpha * W * Y + (1 - alpha) * Y
for i in range(n_iter):
  # 计算W * Y,即用每个数据点的邻居的标签的加权平均来更新其标签
  WY = W.dot(Y)
  # 将标记数据的标签保持不变,即用(1 - alpha) * Y来表示
  WY[indices[:n_labeled]] = Y[indices[:n_labeled]]
  # 用alpha * W * Y + (1 - alpha) * Y来更新标签矩阵
  Y = alpha * WY + (1 - alpha) * Y
  # 对每个数据点,将其标签归一化,使得其概率之和为1
  Y = Y / Y.sum(axis=1, keepdims=True)


# 用收敛后的标签矩阵来对数据进行分类,即将数据分配给概率最大的类别
y_pred = Y.argmax(axis=1)


# 计算分类的准确率
accuracy = np.mean(y_pred == y)
print(f"Accuracy: {accuracy:.2f}")  # Accuracy: 0.91

六、半监督深度学习

c5c319d9bfdd6e7f2811d7614eae8d3b.png

af859b7ae2453393a106012ad21007d8.png

142dfec5efda4e9c2e169eafd90d508f.png

ladderNet网络

186c24784c6df27fad1b767347e876c6.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1375703.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【EI会议征稿通知】2024年机器学习与智能计算国际学术会议(MLIC 2024)

2024年机器学习与智能计算国际学术会议&#xff08;MLIC 2024&#xff09; 2024 International Conference on Machine learning and intelligent computing 智能计算与机器学习被广泛应用于大数据分析、人工智能、智能制造、智能交通、智能电网、智慧城市、智慧医疗、金融科…

语境化语言表示模型-ELMO、BERT、GPT、XLnet

一.语境化语言表示模型介绍 语境化语言表示模型&#xff08;Contextualized Language Representation Models&#xff09;是一类在自然语言处理领域中取得显著成功的模型&#xff0c;其主要特点是能够根据上下文动态地学习词汇和短语的表示。这些模型利用了上下文信息&#xf…

人工智能行业的发展前景如何?

人工智能&#xff08;AI&#xff09;已经成为如今科技领域的热门话题之一&#xff0c;从图像识别到自动驾驶&#xff0c;从语音助手到智能机器人&#xff0c;AI技术正在改变我们的生活方式。随着技术的不断发展和应用的扩大&#xff0c;人工智能行业的发展前景无疑是非常广阔的…

ios 1x/2x/3x

asset文件下可以配置1x/2x/3x图&#xff0c;然后不同机型屏幕会根据[UIScreen mainScreen].scale,自动按需读取相关图片,imageView可以根据image自动适应,需求有个包体积优化&#xff0c;使用 3x图webp格式替换asset图片&#xff0c;由于代码没有根据image尺寸自适应没有进行si…

构建基于RHEL9系列(CentOS9,AlmaLinux9,RockyLinux9等)的Nginx1.24.0的RPM包

本文适用&#xff1a;rhel9系列&#xff0c;或同类系统(CentOS9,AlmaLinux9,RockyLinux9等) 文档形成时期&#xff1a;2023年 因系统版本不同&#xff0c;构建部署应略有差异&#xff0c;但本文未做细分&#xff0c;对稍有经验者应不存在明显障碍。 因软件世界之复杂和个人能力…

【JVM】初识 Jvm

目录 什么是JVM JVM 的功能 常见的JVM 什么是JVM JVM 的全程是 Java Virtual Machine ( java 虚拟机 &#xff09; JVM 是一种用于计算设备的规范&#xff0c;也是一个虚构出来的计算机&#xff0c;通过在实际的计算机上仿真模拟各种计算机功能实现&#xff0c;JVM 屏蔽了…

函数——自制函数(c++)

今天进入自制函数。 自制函数&#xff0c;需要自己定义其功能。比如&#xff0c;设置一个没有参数没有返回值的积木&#xff0c;叫“aaa”。那么&#xff0c;如果想要运行“aaa”&#xff0c;就需要以下代码&#xff1a; void aaa(); 告诉系统有“aaa”…

如何从多个文件夹里各提取相应数量的文件放一起到新文件夹中形成多文件夹组合

首先&#xff0c;需要用到的这个工具&#xff1a; 百度 密码&#xff1a;qwu2蓝奏云 密码&#xff1a;2r1z 说明一下情况 文件夹&#xff1a;1、2、3里面分别放置了各100张动物的图片&#xff0c;模拟实际情况的各种文件 操作&#xff1a;这里演示的是从3个文件夹里各取2张图…

恒创科技:解决Windows服务器磁盘空间不足的问题

​  服务器硬盘的大小是决定空间是否充足的主要因素。但在日常使用中&#xff0c;服务器和网站备份会消耗大量存储空间&#xff0c;如果维护不当&#xff0c;最终将耗尽您的容量。同样&#xff0c;日志文件、临时文件和数据库可以在硬盘驱动器上或回收站中无休止地建立。当您…

MidTool的GPT-4:开启智能语言新纪元

MidTool平台上的GPT-4是由OpenAI开发的最新一代语言预测模型。与前一代GPT-3相比&#xff0c;GPT-4在理解深度、文本生成的连贯性和创造性方面都有了显著的提升。这意味着用户可以期待更加自然、更加精准的交流体验&#xff0c;以及更高质量的内容创作。 1. 无缝对话体验 在M…

高效构建Java应用:Maven入门和进阶(四)

高效构建Java应用&#xff1a;Maven入门和进阶&#xff08;四&#xff09; 四. Maven聚合和继承特性4.1 Maven工程继承关系4.2 Maven工程聚合关系 四. Maven聚合和继承特性 4.1 Maven工程继承关系 继承概念 Maven 继承是指在 Maven 的项目中&#xff0c;让一个项目从另一个项目…

使用 Docker 部署 Halo 博客系统

:::info 项目地址&#xff1a;https://github.com/halo-dev/halo ::: 一、Halo 介绍 1&#xff09;Halo 简介 Halo 是一款强大易用的开源建站工具&#xff0c;它让你无需太多的技术知识就可以快速搭建一个博客、网站或者内容管理系统。具备可插拔架构、主题套用、富文本编辑器…

05-微服务Sentinel流量哨兵

一、Sentinel介绍 1.1 什么是Sentinel 分布式系统的流量防卫兵&#xff1a;随着微服务的普及&#xff0c;服务调用的稳定性变得越来越重要。Sentinel以“流量”为切入点&#xff0c;在流量控制、断路、负载保护等多个领域开展工作&#xff0c;保障服务可靠性。特点&#xff1…

MyBatis实战指南(二):工作原理与基础使用详解

MyBatis是一个优秀的持久层框架&#xff0c;它支持定制化SQL、存储过程以及高级映射。那么&#xff0c;它是如何工作的呢&#xff1f;又如何进行基础的使用呢&#xff1f;本文将带你了解MyBatis的工作原理及基础使用。 一、MyBatis的工作原理 1.1 MyBatis的工作原理 工作原理…

Vue基础-搭建Vue运行环境

这篇文章介绍了在Vue.js项目中进行开发环境搭建的关键步骤。包括node.js安装和配置、安装Vue及Vue CLI工具、安装webpack模板、安装vue-router、创建Vue项目等步骤。这篇文章为读者提供了清晰的指南&#xff0c;帮助他们快速搭建Vue.js开发环境&#xff0c;为后续的项目开发奠定…

小汪,TCP连接和断连夺命6连问你可能扛得住?

目录 TCP三次握手连接和四次挥手断连的几处疑问 一、建立连接&#xff0c;为什么是三次握手&#xff0c;而不是二次握手&#xff1f; 二、为什么每次建立 TCP 连接时&#xff0c;初始化的序列号都要求不一样呢&#xff1f; 三、断开连接&#xff0c;为什么是四次握手&#x…

HarmonyOS 开发基础(八)Row和Column

HarmonyOS 开发基础&#xff08;八&#xff09;Row和Column 一、Column 容器 1、容器说明&#xff1a; 纵向容器主轴方向&#xff1a;从上到下纵向交叉轴方向&#xff1a;从左到右横向 2、容器属性&#xff1a; justifyContent&#xff1a;设置子元素在主轴方向的对齐格式…

30道JVM综合面试题详解含答案(值得珍藏)

1. 描述一下JVM加载Class文件的原理机制? Java中的所有类&#xff0c;都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类&#xff0c;而它的工作就是把class文件从硬盘读取到内存中。在写程序的时候&#xff0c;我们几乎不需要关心类的加载&#xff0c;因为这些都…

流程控制详解

1、Java流程控制 主要涉及三大流程控制&#xff1a;顺序、分支、循环 如下图&#xff1a; 1&#xff09;流程2 存在对用户名和密码的校验&#xff0c;是否为空&#xff0c;存在分支控制 2&#xff09;流程3 用户名和密码在数据库是否存在&#xff0c;存在分支控制 3&#xf…

ant design vue Tree组件叶子节点横向排列

antdesignvue的树形组件要实现组件叶子节点横向排列有点坑&#xff0c;没有 配置属性&#xff0c;需要自己想办法。 要实现的效果 看tree组件的dom结构&#xff0c;父元素flex竖向布局&#xff0c;子项不论节点层级都在同一层&#xff01;&#xff01;&#xff01; 难点在于想…