[HITML]哈工大2020秋机器学习复习笔记

1 基础

查准率 = 预测正确的正例 / 预测出的所有正例

查全率 = 预测正确的正例 / 真实的所有正例

1.1 机器学习中的概率

机器学习任务涉及不确定性下的推理,不确定性的来源/随机性:

  • 噪声——传感器测量的变异性,部分可观测性,不正确的标签

  • 有限样本大小——训练和测试数据是随机抽取的实例

概率量化不确定性!

分布:二项分布、beta分布、狄利克雷分布

1.2 最大似然估计、最大后验概率

  • 频率论的MLE方法:θ为未知常数,由资料估计

    Maximum Likelihood Estimate (MLE): choose θ \theta θ that maximizes probability of observed data D \mathcal{D} D
    θ ^ = arg ⁡ max ⁡ θ P ( D ∣ θ ) \displaystyle \widehat{\theta}=\arg \max _{\theta} P(\mathcal{D} \mid \theta) θ =argθmaxP(Dθ)

  • 贝叶斯的MAP方法:θ为随机变量,假设为概率分布

    Maximum a Posteriori (MAP) estimate: choose θ \theta θ that is most probable given prior probability and the data

    θ ^ = arg ⁡ max ⁡ θ P ( θ ∣ D ) = arg ⁡ max ⁡ θ = P ( D ∣ θ ) P ( θ ) P ( D ) \begin{aligned} \hat{\theta} &=\arg \max _{\theta} \quad P(\theta \mid \mathcal{D}) \\ &=\arg \max _{\theta}=\frac{P(\mathcal{D} \mid \theta) P(\theta)}{P(\mathcal{D})} \end{aligned} θ^=argθmaxP(θD)=argθmax=P(D)P(Dθ)P(θ)

缺点:

  • MLE:如果数据集太小就会过拟合

  • MAP:两个有着不同先验的人将会得到不同的估计

详见 https://blog.csdn.net/weixin_44940258/article/details/109731281

1.3 最小二乘法、梯度下降法、共轭梯度法

最小二乘指的是预测值与样本值距离的平方和最小,在高斯分布的条件下,最下二乘和最大似然可以得到相同的结果。

2 决策树

同样一个训练数据集,可以有多棵树与其一致

决策树的归纳:贪心策略——基于一个可以最优化某项准则的属性来切分示例集合

确定最好的切分:一个好的属性切分是将示例集合分成若干子集,最理想情况是每个子集为“皆为正例”或“皆为反例”,那么需要对结点混杂度(impurity)进行测量。测量由于切分带来的熵减少量,选择具有最大减少量的切分(最大增益)

停止切分准则

(1) 当前结点包含的样本全属于同一类别,无需划分;

(2) 当前属性集为空,或是所有样本在所有属性上取值相同,无法划分;

(3) 当前结点包含的样本集合为空,不能划分.

当获得最小可接受的决策树时则停止

Occam’s 剃刀:选择适合训练集合数据的最简单假设

解决过拟合问题

  • 预剪枝(早期停止规则):
    • 在算法变成一棵完全成熟的树之前停止它,节点的典型停止情况:
    • 如果所有实例都属于同一个类,则停止
    • 如果所有属性值都相同,则停止
    • 更多的限制条件:
    • 如果实例数量少于用户指定的阈值,则停止
    • 如果实例的类分布与可用的特征无关,停止(例如,使用χ2检验)
    • 如果扩展当前节点不能改善杂质度量(例如,基尼系数或信息增益),停止。
  • 后剪枝:使决策树完整生长,以自底向上的方式修剪决策树的节点,如果修剪后泛化误差有所改善,则用叶节点替换子树,叶节点的类标签由子树中的大多数实例类确定,可以使用MDL (Minimum Description Length)进行后剪枝。

缺失属性值的处理

  • 在属性值缺失的情况下进行划分属性选择:去除所有缺失的样本,但是计算无缺失值样本所占的比例,在计算信息增益时只看在无缺失样本上的值,但结果需要乘以无缺失值样本所占的比例。
  • 给定划分属性,若样本在该属性上的值缺失,如何对样本进行划分:让同一个样本以不同的概率划入到不同的子结点中去。让同一个样本划到所有的子结点中,同时设置样本的权值为无缺失值样本中在该属性上取结点值的样本所占的比例

2.1 熵

信息量:当一个不可能发生的事件发生了,我们就说这个信息的信息量大;而当一个一定会发生的事件发生了,我们说这个信息几乎没有信息量。这符合我们的直觉。可以使用函数 − ln ⁡ p ( x ) -\ln p(x) lnp(x) 来描述信息量的大小。

因此,我们用这个概念来描述一个事件能够带给我们的信息量的期望,即
H ( X ) = − ∑ i = 1 n p ( x i ) ln ⁡ p ( x i ) H(X)=-\sum_{i=1}^n p(x_i)\ln p(x_i) H(X)=i=1np(xi)lnp(xi)

2.2 条件熵

条件熵 H ( Y ∣ X ) H(Y|X) H(YX) 表示在已知随机变量 X X X 的条件下随机变量 Y Y Y 的不确定性。条件熵 H ( Y ∣ X ) H(Y|X) H(YX) 定义为 X X X 给定条件下 Y Y Y 的条件概率分布的熵对 X X X 的数学期望:

条件熵 H ( Y ∣ X ) H(Y|X) H(YX) 相当于联合熵 H ( X , Y ) H(X,Y) H(X,Y) 减去单独的熵 H ( X ) H(X) H(X),即 H ( Y ∣ X ) = H ( X , Y ) − H ( X ) H(Y|X)=H(X,Y)-H(X) H(YX)=H(X,Y)H(X)

互信息

信息增益:目标类变量与属性A变量在S(样本集)上的互信息

2.3 相对熵(KL散度)

相对熵用于描述两个分布之间的差异,假设 P P P 代表真实分布, Q Q Q 代表预测分布,那么这两个分布之间就有一个“相似度”,那么就可以描述它们之间的差异的大小,这就是相对熵。计算公式:
D K L ( p ∥ q ) = ∑ i = 1 n p ( x i ) ln ⁡ ( p ( x i ) q ( x i ) ) D_{K L}(p \| q)=\sum_{i=1}^{n} p\left(x_{i}\right) \ln \left(\frac{p\left(x_{i}\right)}{q\left(x_{i}\right)}\right) DKL(pq)=i=1np(xi)ln(q(xi)p(xi))
显然, D K L ( p ∣ ∣ q ) ≠ D K L ( q ∣ ∣ p ) D_{KL}(p||q)\ne D_{KL}(q||p) DKL(pq)=DKL(qp)

2.4 交叉熵

在机器学习中,评估一个模型的好坏,只需要计算KL散度即可,而对KL散度公式做一下变形,我们发现,只需要关注交叉熵即可:
D K L ( p ∥ q ) = ∑ i = 1 n p ( x i ) ln ⁡ ( p ( x i ) ) − ∑ i = 1 n p ( x i ) ln ⁡ ( q ( x i ) ) = − H ( p ( x ) ) + [ − ∑ i = 1 n p ( x i ) ln ⁡ ( q ( x i ) ) ] \begin{aligned} D_{K L}(p \| q) &=\sum_{i=1}^{n} p\left(x_{i}\right) \ln \left(p\left(x_{i}\right)\right)-\sum_{i=1}^{n} p\left(x_{i}\right) \ln \left(q\left(x_{i}\right)\right) \\ &=-H(p(x))+\left[-\sum_{i=1}^{n} p\left(x_{i}\right) \ln \left(q\left(x_{i}\right)\right)\right] \end{aligned} DKL(pq)=i=1np(xi)ln(p(xi))i=1np(xi)ln(q(xi))=H(p(x))+[i=1np(xi)ln(q(xi))]
等式的前一部分恰巧就是p的熵,这一部分是不会变化的,而等式的后一部分,就是交叉熵
H ( p , q ) = − ∑ i = 1 n p ( x i ) ln ⁡ ( q ( x i ) ) H(p,q) = -\sum_{i=1}^{n} p\left(x_{i}\right) \ln \left(q\left(x_{i}\right)\right) H(p,q)=i=1np(xi)ln(q(xi))

关于条件熵、相对熵、交叉熵参考 https://www.cnblogs.com/kyrieng/p/8694705.html

交叉熵参考 https://blog.csdn.net/tsyccnh/article/details/79163834

3 贝叶斯判别

3.1 最优分类器

贝叶斯决策论,在分类任务中是在所有相关概率都已知的理想情形下,考虑如何基于这些概率和误判损失来选择最优的类别标记。

假设类别标记 Y = { c 1 , c 2 , . . . , c N } Y=\{c_1,c_2,...,c_N\} Y={ c1,c2,...,cN},记 λ i j \lambda_{ij} λij 为真实类别为 c j c_j cj 的样本点被错误的标记为类别 c i c_i ci 所造成的损失,因此,对于已知后验概率的点 x x x,我们有以下的期望损失 (风险):
R ( c i ∣ x ) = ∑ j = 1 N λ i j P ( c j ∣ x ) R\left(c_{i} \mid x\right)=\sum_{j=1}^{N} \lambda_{i j} P\left(c_{j} \mid x\right) R(cix)=j=1NλijP(cjx)
我们的目的就是要找一个判定准则 h : X → Y h:X\rightarrow Y h:XY,使得总体风险最小,即 R ( h ) = E x [ R ( h ( x ) ∣ x ) ] R(h)=E_{x}[R(h(x) \mid x)] R(h)=Ex[R(h(x)x)] 最小,显然,对每个样本 x x x,若 h h h 能最小化条件风险 R ( h ( x ) ∣ x ) R(h(x) \mid x) R(h(x)x),则总体风险 R ( h ) R(h) R(h) 也将被最小化。这就产生了贝叶斯判定准则 (Bayes decision rule):为最小化总体风险,只需在每个样本上选择那个能使条件风险R(c|x)最小的类别标记。此时我们有一个最优分类器
h ∗ ( x ) = arg ⁡ min ⁡ c ∈ Y R ( c ∣ x ) h^*(x)=\arg \min_{c\in Y} R(c|x) h(x)=argcYminR(cx)
这就是贝叶斯最优分类器。

若目标是最小化分类错误率,则误判损失 λ i j \lambda_{ij} λij 可写为 λ i j = { 0 ,  if  i = j 1 ,  otherwise  \lambda_{i j}=\left\{\begin{array}{ll} 0, & \text { if } i=j \\ 1, & \text { otherwise } \end{array}\right. λij={ 0,1, if i=j otherwise 

于是真实类别为 c j c_j cj 的点 x x x 的期望损失是 R ( c i ∣ x ) = 1 − P ( c i ∣ x ) R(c_i|x)=1-P(c_i|x) R(cix)=1P(cix)

我们的目的仍是使每个点的损失最小,所以相当于使 P ( c i ∣ x ) P(c_i|x) P(cix) 最大,即 arg ⁡ max ⁡ c i P ( c i ∣ x ) \displaystyle\arg \max_{c_i} P(c_i|x) argcimaxP(cix)

故得到的最优分类器为 ∀ x ∈ X , h ∗ ( x ) = arg ⁡ max ⁡ c ∈ Y P ( c ∣ x ) \displaystyle \forall x\in X,\quad h^*(x)= \arg\max_{c\in Y} P(c|x) xX,h(x)=argcYmaxP(cx)

3.2 线性判别

线性判别分析 (LDA),原理同PCA,相当于PCA在二维向一维投影降维的特殊情况。

3.3 生成式模型与判别式模型

  • 判别式模型:给定 x x x,可通过直接建模 P ( c ∣ x ) P(c|x) P(cx) 来预测 c c c

    决策树、SVM、线性回归、逻辑回归、kNN

  • 生成式模型:先对联合概率分布 P ( x ∣ c ) P(x|c) P(xc) 建模,然后再由此获得 P ( c ∣ x ) P(c | x) P(cx)

    生成式模型:朴素贝叶斯、GMM

3.4 KNN

kNN的意思是取k个最近的邻居的信息来做预测,对于分类任务来说,最近的k个邻居中,哪个类的邻居最多就把它也标为这个类,而对于回归任务来说,可以将这k个邻居的的平均值作为结果输出。总之,就是用最近的k的邻居的特征来描述测试点的特征。

关于距离的度量:

  • 欧式距离
  • 1范数
  • 无穷范数
  • 马氏距离: D ( x , x ′ ) = ∑ i σ i 2 ( x i − x i ′ ) 2 ⇔ D ( x , x ′ ) = ( x − x ′ ) T Σ − 1 ( x − x ′ ) D(x, x')=\sqrt{\sum_i\sigma^2_i(x_i-x_i')^2} \Leftrightarrow D(x, x')=\sqrt{(x-x')^T\Sigma^{-1}(x-x')} D(x,x)=iσi2(xixi)2 D(x,x)=(xx)TΣ1(xx)
  • 相关系数
  • 马氏距离
  • 曼哈顿距离
  • 汉明距离

4 线性回归、朴素贝叶斯与逻辑回归

4.1 线性回归

目的是构造一个线性函数,使得输入一个观测值x,能够输出其对应的预测值y,由于y的取值是连续的,所以称其为回归问题,这就是线性回归

为了判断我们得到的函数中哪个是最好的,我们需要一个指标,这就是损失函数

在多项式拟合的实验中,我们使用了一个关于x的高阶多项式来拟合正弦函数,虽然在二维空间中,我们得到的不是一个线性函数,但在高位空间看,我们得到的实际上是一个关于 X = ( x , x 2 , . . . , x n ) X=(x, x^2, ... ,x^n) X=(x,x2,...,xn) 的线性函数。在求解过程中,我们使用的损失函数是预测值与样本值的欧氏距离之和,当损失函数最小时,我们取到最优的函数,即回归直线。

4.2 过拟合

过拟合,指的是模型在训练集上表现的很好,但是在交叉验证集合测试集上表现一般,也就是说模型对未知样本的预测表现一般,泛化(generalization)能力较差。

对于过拟合的处理有以下几种常见的方式:

  • 增加训练数据集
  • 正则化,增加正则项
  • 设置提前停止的条件:Early stopping便是一种迭代次数截断的方法来防止过拟合的方法,即在模型对训练数据集迭代收敛之前停止迭代来防止过拟合。

4.3 偏置与方差

4.4 特征变换

从一组已有的特征通过一定的数学运算得到一组新特征

用于数据降维

  • LDA (线性判别分析):有监督的。设法将样例投影到一条直线上,使得同类样例的投影点尽可能接近、 异类样例的投影点尽可能远离;在对新样
    本进行分类时,将其投影到同样的这条直线上,再根据投影点的位置来确定新样本的类别。

  • PCA (主成分分析):无监督的。主要用于对数据的降维压缩,不属于类别判别。

4.5 朴素贝叶斯

在贝叶斯中,有 P ( Y ∣ X ) = P ( X ∣ Y ) P ( Y ) P ( X ) P(Y|X)=\frac{P(X|Y)P(Y)}{P(X)} P(YX)=P(X)P(XY)P(Y),即对 ∀   i , j \forall\ i,j  i,j,有
P ( Y = y i ∣ X = x j ) = P ( X = x j ∣ Y = y i ) P ( Y = y j ) P ( X = x j ) = P ( X = x j ∣ Y = y i ) P ( Y = y i ) ∑ k P ( X = x j ∣ Y = y k ) P ( Y = y k ) P(Y=y_i|X=x_j)=\frac{P(X=x_j|Y=y_i)P(Y=y_j)}{P(X=x_j)}=\frac{P(X=x_j|Y=y_i)P(Y=y_i)} {\sum_k P(X=x_j|Y=y_k)P(Y=y_k)} P(Y=yiX=xj)=P(X=xj)P(X=xjY=yi)P(Y=yj)=kP(X=xjY=yk)P(Y=yk)P(X=xjY=yi)P(Y=yi)
在朴素贝叶斯中,给定观测值 Y Y Y,假设 X = < X 1 , X 2 , . . . , X n > X=<X_1, X_2, ..., X_n> X=<X1,X2,...,Xn>,对于 ∀   1 ≤ i < j ≤ n \forall\ 1\le i<j\le n  1i<jn X i X_i Xi X j X_j Xj 满足条件独立,即
P ( X 1 , X 2 , . . . , X n ∣ Y ) = ∏ i P ( X i ∣ Y ) P(X_1, X_2, ..., X_n|Y)=\prod_i{P(X_i|Y)} P(X1,X2,...,XnY)=iP(XiY)

条件独立:X和Y条件独立,即 P ( X ∣ Y , Z ) = P ( X ∣ Z ) P(X|Y,Z)=P(X|Z) P(XY,Z)=P(XZ)

所以,在朴素贝叶斯条件独立的假设下给定 X n e w = < X 1 , X 2 , . . . , X n > X^{new}=<X_1, X_2, ..., X_n> Xnew=<X1,X2,...,Xn> 估计该点的类别 Y n e w Y^{new} Ynew,有
P ( Y = y k ∣ X 1 , X 2 , . . . , X n ) = P ( X 1 , X 2 , . . . , X n ∣ Y = y k ) P ( Y = y k ) ∑ j P ( X 1 , X 2 , . . . , X n ∣ Y = y j ) P ( Y = y j ) = P ( Y = y k ) ∏ i P ( X i ∣ Y = y k ) ∑ j P ( Y = y k ) ∏ i P ( X i ∣ Y = y j ) P ( Y = y j ) P(Y=y_k|X_1, X_2, ..., X_n)=\frac{P(X_1, X_2, ..., X_n|Y=y_k)P(Y=y_k)} {\sum_j P(X_1, X_2, ..., X_n|Y=y_j)P(Y=y_j)} =\displaystyle\frac{P(Y=y_k)\displaystyle \prod_i P(X_i|Y=y_k)} {\displaystyle\sum_j P(Y=y_k) \prod_i P(X_i|Y=y_j)P(Y=y_j)} P(Y=ykX1,X2,...,Xn)=jP(X1,X2,...,XnY=yj)P(Y=yj)P(X1,X2,...,XnY=yk)P(Y=yk)=jP(Y=yk)iP(XiY=yj)P(Y=yj)P(Y=yk)iP(XiY=yk)
因此原问题 Y n e w = arg ⁡ max ⁡ y k P ( Y = y k ∣ X 1 , X 2 , . . . , X n ) \displaystyle Y^{new}=\arg \max_{y_k} P(Y=y_k|X_1, X_2, ..., X_n) Ynew=argykmaxP(Y=ykX1,X2,...,Xn) 就转化为

Y n e w = arg ⁡ max ⁡ y k P ( Y = y k ) ∏ i P ( X i ∣ Y = y k ) (4.5.1) \displaystyle Y^{new}=\arg \max_{y_k} P(Y=y_k) \prod_i P(X_i|Y=y_k)\tag{4.5.1} Ynew=argykmaxP(Y=yk)iP(XiY=yk)(4.5.1)

也就是说,当各个属性之间条件独立时,考虑最大化各个属性取值时的类别,而为了能够从整体上看最大化的情况,取了各个属性值上的概率积。

在朴素贝叶斯中,我们需要对两类参数进行估计

  • 先验概率: π k = P ( Y = y k ) \pi_k = P(Y=y_k) πk=P(Y=yk)

  • 条件概率: θ i j k = P ( X i = x i j ∣ Y = y k ) \theta_{ijk} = P(X_i=x_{ij}|Y=y_k) θijk=P(Xi=xijY=yk)

因此,分别采用MLE和MAP来看效果

# D { A } \#D\{A\} #D{ A} 表示数据集D中满足条件A的样本数量

  • MLE: π ^ k = P ^ ( Y = y k ) = # D { Y = y k } ∣ D ∣ , θ ^ i j k = P ^ ( X i = x i j ∣ Y = y k ) = # D { X i = x i j ∧ Y = y k } # D { Y = y k } \displaystyle\hat\pi_k = \hat P(Y=y_k)=\frac{\#D\{Y=y_k\}}{|D|},\quad \hat\theta_{ijk} = \hat P(X_i=x_{ij}|Y=y_k)=\frac{\#D\{X_i=x_{ij} \wedge Y=y_k\}}{\#D\{Y=y_k\}} π^k=P^(Y=yk)=D#D{ Y=yk},θ^ijk=P^(Xi=xijY=yk)=#D{ Y=yk}#D{ Xi=xijY=yk}

    但是有个问题,若某个属性值在训练集中没有与某个类同时出现过,则直接基于上式进行概率估计,再根据式 ( 4.5.1 ) (4.5.1) (4.5.1)进行判别将出现问题。假设对于类别 y 0 y_0 y0 来说,这个类别中的所有数据都没有出现 X 2 = x 23 X_2=x_{23} X2=x23 的取值,因此有 P ( X 2 = x 23 ∣ Y = y 0 ) = 0 P(X_2=x_{23}|Y=y_0)=0 P(X2=x23Y=y0)=0,而由于式 ( 4.5.1 ) (4.5.1) (4.5.1)中的连乘,最终会得到一个拥有 X 2 = x 23 X_2=x_{23} X2=x23 属性值的点被判断为属于 y 0 y_0 y0 类的可能性为0,而其他属性无法起到相应的作用,显然这是不合理的。因此,在各个估计中加入平滑项来消除这个问题,于是可以得到与MAP中的式子相同的结论。

  • MAP: π ^ k = P ^ ( Y = y k ) = # D { Y = y k } + α k ∣ D ∣ + ∑ m α m , θ ^ i j k = P ^ ( X i = x i j ∣ Y = y k ) = # D { X i = x i j ∧ Y = y k } + α k ′ # D { Y = y k } + ∑ m α m ′ \displaystyle\hat\pi_k = \hat P(Y=y_k)=\frac{\#D\{Y=y_k\}+\alpha_k}{|D|+\sum_m \alpha_m},\quad \hat\theta_{ijk} = \hat P(X_i=x_{ij}|Y=y_k)=\frac{\#D\{X_i=x_{ij} \wedge Y=y_k\}+\alpha_k'}{\#D\{Y=y_k\}+\sum_m \alpha_m'} π^k=P^(Y=yk)=D+mαm#D{ Y=yk}+αk,θ^ijk=P^(Xi=xijY=yk)=#D{ Y=yk}+mαm#D{ Xi=xijY=yk}+αk

    α i \alpha_i αi 是一些虚样本点。假的,人为加入的,当 α i = 0 \alpha_i=0 αi=0 时为MLE;当 α i = 1 \alpha_i=1 αi=1 时为拉普拉斯平滑,这是常用的方法。

朴素贝叶斯在不满足假设(各属性之间不条件独立)下的分类效果仍然很好

i i i 个属性 X i X_i Xi 取值是连续的时:高斯朴素贝叶斯

4.6 逻辑回归

分类器做分类问题的实质是预测一个已知样本的位置标签,即 P ( Y = 1 ∣ X = < X 1 , . . . , X n > ) P(Y=1|X=< X_1,...,X_n>) P(Y=1X=<X1,...,Xn>)。按照朴素贝叶斯的方法,可以用贝叶斯概率公式将其转化为类条件概率(似然)和类概率的乘积。但通过以下的推导可以直接求该概率。

假设分类问题是一个0/1二分类问题:
P ( Y = 1 ∣ X ) = P ( Y = 1 ) P ( X ∣ Y = 1 ) P ( X ) = P ( Y = 1 ) P ( X ∣ Y = 1 ) P ( Y = 1 ) P ( X ∣ Y = 1 ) + P ( Y = 0 ) P ( X ∣ Y = 0 ) = 1 1 + P ( Y = 0 ) P ( X ∣ Y = 0 ) P ( Y = 1 ) P ( X ∣ Y = 1 ) = 1 1 + exp ⁡ ( ln ⁡ P ( Y = 0 )

本实现基于JavaEE的SSM框架,使用MySQL数据库。 1. 创建数据库 在MySQL中创建一个名为`ssm_login`的数据库,执行以下SQL语句: ```sql CREATE DATABASE ssm_login; ``` 在该数据库中创建一个名为`user`的表,执行以下SQL语句: ```sql CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `password` varchar(50) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; ``` 2. 配置Struts2 在项目中添加Struts2依赖,例如使用Maven,在`pom.xml`中添加以下依赖: ```xml <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.5.26</version> </dependency> ``` 在`web.xml`中添加Struts2的过滤器和监听器: ```xml <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class>org.apache.struts2.tiles.StrutsTilesListener</listener-class> </listener> ``` 在`struts.xml`中配置Struts2的Action和Result: ```xml <struts> <constant name="struts.devMode" value="true" /> <package name="login" extends="struts-default"> <action name="login" class="com.example.action.LoginAction"> <result name="success" type="tiles">welcome</result> <result name="input" type="tiles">login</result> </action> </package> </struts> ``` 其中,`LoginAction`为登录操作的Action,`welcome`为登录成功后跳转的页面,`login`为登录页面。 3. 编写Action 在`com.example.action`包下创建`LoginAction`类,代码如下: ```java package com.example.action; import com.example.model.User; import com.opensymphony.xwork2.ActionSupport; import org.apache.struts2.convention.annotation.Action; import org.apache.struts2.convention.annotation.Namespace; import org.apache.struts2.convention.annotation.ParentPackage; import org.apache.struts2.convention.annotation.Result; import org.springframework.stereotype.Controller; import javax.servlet.http.HttpSession; import java.util.ArrayList; import java.util.List; @Controller @ParentPackage("struts-default") @Namespace("/") public class LoginAction extends ActionSupport { private String username; private String password; private List<User> userList = new ArrayList<>(); @Action(value = "login", results = { @Result(name = "success", location = "/WEB-INF/views/welcome.jsp"), @Result(name = "input", location = "/WEB-INF/views/login.jsp") }) public String login() { if ("admin".equals(username) && "123456".equals(password)) { HttpSession session = ServletActionContext.getRequest().getSession(); Integer count = (Integer) session.getAttribute("count"); count = count == null ? 1 : count + 1; session.setAttribute("count", count); session.setAttribute("username", username); return SUCCESS; } else { addActionError("用户名或密码错误!"); return INPUT; } } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public List<User> getUserList() { return userList; } public void setUserList(List<User> userList) { this.userList = userList; } } ``` 其中,`login()`方法为登录操作,如果用户名和密码都为`admin`和`123456`,则将用户名和登录次数存入Session中,跳转到`welcome.jsp`页面;否则向页面传递错误信息,跳转回`login.jsp`页面。 4. 编写JSP页面 在`webapp`目录下创建`WEB-INF/views`目录,用于存放JSP页面。创建`login.jsp`和`welcome.jsp`页面,代码如下: login.jsp ```jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE html> <html> <head> <title>登录页面</title> </head> <body> <s:form action="login" method="post"> <s:textfield name="username" label="用户名" /> <s:password name="password" label="密码" /> <s:submit value="登录" /> </s:form> <s:if test="hasActionErrors()"> <div style="color: red"> <s:actionerror /> </div> </s:if> </body> </html> ``` welcome.jsp ```jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE html> <html> <head> <title>欢迎页面</title> </head> <body> <div>欢迎您,<s:property value="#session.username" />!</div> <div>您是本系统的第<s:property value="#session.count" />个登录用户。</div> </body> </html> ``` 其中,`login.jsp`页面使用了Struts2的标签,简化了表单的编写;`welcome.jsp`页面使用了OGNL表达式,直接获取Session中的用户名和登录次数。 5. 运行程序 启动Tomcat服务器,访问`http://localhost:8080/ssm_login/login`,即可进入登录页面。输入正确的用户名和密码,即可跳转到欢迎页面,并显示登录用户的用户名和登录次数。如果输入错误的用户名或密码,页面会提示错误信息,重新输入即可。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值