09. 机器学习- 逻辑回归

文章目录

    • 线性回归回顾
    • 逻辑回归

茶桁的AI秘籍 09

Hi,你好。我是茶桁。

上一节课,在结尾的时候咱们预约了这节课一开始对上一节课的内容进行一个回顾,并且预告了这节课内容主要是「逻辑回归」,那我们现在就开始吧。

线性回归回顾

在上一节课中,我们定义了model,loss函数以及求导函数。最后我们用for循环来完成了求导过程。本节课一开始,咱们先来对上一节课的代码做一次优化,优化后的代码也会上传到课程代码仓库内。

此部分代码依然在08.ipynb中。

首先,我们将之前的model重新更名为linear, 以便知道我们这个函数是要做什么的。接着,我们把for循环内对w和b的偏导封装为一个函数,便于我们之后调用:

def optimize(w, b, x, y, yhat, pw, pb, learning_rate):w = w + -1 * pw(x, y, yhat) * learning_rateb = b + -1 * pb(x, y, yhat) * learning_ratereturn w, b

然后我们将整个for循环封装一下:

def train(model_to_be_train, target, loss, pw, pb):w = np.random.random_sample(size = (1, 2))# w normalb = np.random.random()learning_rate = 1e-5epoch = 200for i in range(epoch):for batch in range(len(rm)):# batch trainningindex = random.choice(range(len(rm)))rm_x, lstat_x = rm[index], lstat[index]x = np.array([rm_x, lstat_x])y = target[index]yhat = model_to_be_train(x, w, b)loss_v = loss(yhat, y)batch_loss.append(loss_v)w, b = optimize(w, b, x, y, yhat, pw, pb, learning_rate)if batch % 100 == 0:print('Epoch: {} Batch: {}, loss: {}'.format(i, batch, loss_v))return model_to_be_train, w, b

在最后呢,我们可以在调用函数之前,导入所需第三方库,然后将之前的数据处理在执行函数前获取并处理一遍:

import matplotlib.pyplot as plt
import random
import pandas as pd
import numpy as np
from sklearn.datasets import fetch_openml
dataset = fetch_openml(name='boston', version=1, as_frame=True, return_X_y=False, parser='pandas')data = dataset['data']
target = dataset['target']
columns = dataset['feature_names']
dataframe = pd.DataFrame(data)
dataframe['price']  = targetrm = dataframe['RM']
lstat = dataframe['LSTAT']model, w, b = train(linear, target, loss, partial_w, partial_b)

为什么我们每次都要随机取一个数字呢?

你也可以把所有的x全部输入进去,所有的y全部输入进去。但是实际上在整个场景下,比方说我们有很多的训练数据,每个训练数据都有一个x,有一个y。

loss函数本来写的是i属于所有的N,y_i减去yhat_i的平方。但是现在如果把所有的x和所有的y在真实的场景下输入进去的话,假设现在x有100万个或者200万个,输入进去之后整个求解过程可能loss函数这个程序加载都加载不出来,会非常非常慢。

所以在实际的工作中,假如说i属于D: ∑ i ∈ D \sum_{i \in D} iD,D就是distribution的意思,就是随机取一些数据,然后再把随机取的一些数据求解。这样的话每一次就可以保证它可以运行。

但是这样的一个区别是什么?

每次把所有的x和y都输入进去,这种梯度下降方式中loss下降是一个很顺滑的样子,这个叫做BGD。

还有一种情况就是咱们课上用的这种剃度下降方式,每次随机取了一个随机值,叫随机剃度下降,就随机取一个数字做梯度下降,简称SGD。这个loss下降就会上下波动很厉害。如我们上面展示的图。

再下来呢还有一种,它是取这两者之间,每次不是取一个,是取了多个。我们把这个叫做MBGD。

这是三种梯度下降方式。在实际的工作中SGD用的最多,因为可以快速的进行梯度下降学习。

我们可以将代码修改一下:

batch_size = 10
for i in range(epoch):...for batch in range(len(rm) // batch_size):indices = np.random.choice(range(len(rm)))rm_x, lstat_x = rm[indices], lstat[indices]x = np.array([rm_x, lstat_x])y = target[indices]...

关于这一部分内容,这里仅仅是提一下,在后面的课程中,我们还会更详细的来讲解。

我们在循环中,将原来的次数50替换成了epoch, epoch在机械学习里边指的是运行了整整一遍。

在第二个循环内,里面是rm个东西,每次都是随机取,我们随机取了多少次呢?取了rm个。也就是说平均每个样本会被取样一次。

这就是数据量大的好处,当数据量很大的时候,有个别的点没有取到或者说有个别的点取了多次其实对最终的效果是不影响的。

也就是说因为数量很大,所以一两次的变化,一两个数值取的少了或者取的多了,其实不是非常影响。

我们把每次epoch的batch打出来,我们来看一下:

def train(...):...losses = []for i in range(epoch):batch_loss = []for batch in range(len(rm)):...batch_loss.append(loss_v)...losses.append(np.mean(batch_loss))return model_to_be_train, w, b, lossesmodel, w, b, losses = train(linear, target, loss, partial_w, partial_b)
plt.plot(losses)
plt.show()

image-20231012120642561

那如果是上面我们更改的代码,使用了batch_size控制之后,图形就完全不一样。

image-20231012141443971

可以看到,这个loss下降还是挺明显的。

这个时候,我们假设知道一组的rm等于19,lstat等于7。而此时其实已经有了w和b,求到最终的w和b,就能够有一个预测值了。

predicate = model(np.array([19, 7]), w, b)
print(predicate)---
Epoch: 0 Batch: 0, loss: 46.17245060319155
...
Epoch: 199 Batch: 0, loss: 0.2053457975383563

我们在这个实例中,只用了两个最显著的特征,如果把x的维度变多一些,其实就会更加接近了。

好,这个线性回归的过程,其中包括线性函数的定义,为什么要用线性函数,loss函数的意义,梯度下降的意义就都讲完了。

这个内容是我从斯坦福大学的参考书上弄过来的。

除了定义一个这样一个平方值的loss,可以定一个绝对值loss,都是一样的,都可以实现找到最优值。

只不过这个二次方的这个loss对于结果, 它的惩罚会更大一些。

经过这一段代码的洗礼,对于之前的那个数学式子应该能看的更明白一些了。

逻辑回归

我们讲完了线性回归,下面再跟大家来讲一下逻辑回归。

逻辑回归是什么?假如还是如上那个问题,前面代码都没变。当然,库需要再导入一遍:

import random
import pandas as pd
import numpy as np
from sklearn.datasets import fetch_openmldataset = fetch_openml(name='boston', version=1, as_frame=True, return_X_y=False, parser='pandas')

现在咱们要变一个问题场景, 我们先打印一下np.percentile(), 这是要求百分位,比方说我们填入一下target, 其实我们数据预处理的时候知道,就是dataframe['price']:

print(np.percentile(target, 66))

我们写入一个target, 其实就是price,然后我们在后面写了一个66,也就是说,我们将这里所有的price,也就是房价,做了一次排序,然后,我取从0到100中的第66%个位置的数值, 就是大于2/3的房价。同样的,如果我这里填了一个50,那么就是取最中间的那个值。

输出的结果为23.53, 是23万美金。还是比较便宜,23万美金折合100多万。

好,现在我们来做一个判断:

greater_then_most = np.percentile(target, 66)
dataframe['expensive'] = dataframe['price'].apply(lambda p: int(p > greater_then_most))print(dataframe[:20])

image-20231012161144568

我们定义了一个expensive, 在dataframe中加入了这个特征。这个特征在房价大于2/3房价的时候int为1, 否则为0。

做了这样一件事之后,就是问这个房子是不是贵房子,如果是1,就是贵房子,0就不是贵房子。根据我们添加的特征来进行判断。

那接着呢,问题发生了改变。我们不知道这个房子的price, 现在需要进行预测这个房子是不是属于一个高档小区。在预测中,假如是1, 就表示是高档小区,0就表示不是高档小区。现在要根据它的一些特征来猜测它是不是高档小区。

我们刚刚其实已经知道,所谓的高档小区其实是和价格有一定关系的。

假如说现在咱们有一个问题要求解,现在要有一个模型能够预测它到底是1还是0,或者我们要预测是开心还是难过,咱们现在只要做一件事情就可以,就是把我们期望目标标成1, 把另外那个相对的目标标成0。

如果我们能够拟合一个函数,这个函数的输出要么是1,要么是0,我们让这个模型的值越接近于实际的值就可以了。

image-20231012163700062

比方说刚刚回顾完的线性回归,给定的(x, y)里边,y这个值它是一个实数。如果现在变成了0、1。比方说1就是happy,0就是sad。或者还是用咱们之前定义的:1就是expensive,0就是not expensive。

把1和0认为是概率,如果是概率的话,1就是100%是,0就100%不是。

那么咱们之前的model输出的是实数\in R, 这次需要的model就是输出的是0~1。这个模型的任务就变成了如果x给定的是1,那么model输出最后要尽可能的接近1。

怎么样才能让我们的model输出是0到1之间呢?有一个方法,一个函数叫做logistic函数, logistic function:
J ( θ ) = − ∑ i ( y ( i ) l o g ( h θ ( x ( i ) ) ) + ( 1 − y ( i ) ) l o g ( 1 − h θ ( x ( i ) ) ) ) \begin{align*} J(\theta)=-\sum_i(y^{(i)}log(h_{\theta}(x^{(i)}))+(1-y^{(i)})log(1-h_{\theta}(x^{(i)}))) \end{align*} J(θ)=i(y(i)log(hθ(x(i)))+(1y(i))log(1hθ(x(i))))

这个函数其实在复杂系统里面是一个很重要的函数, 人们其实是期望获得一种导数,数学家们研究的是这个:y’ = y(1-y), 就是y的导数等于y乘以1-y。研究完了之后发现有一种函数就满足这个特征:
f ( x ) = 1 1 + e − x \begin{align*} f(x) = \frac{1}{1+e^{-x}} \end{align*} f(x)=1+ex1

这个函数的值画出来,就是这个样子:

image-20231012173054474

值全部是从0到1之间,中间与y轴交点为0.5。

给大家讲一下这个原理,逻辑函数原本是想研究y’=y(1-y),求解出来有这样一个函数满足这样的特征:f(x) = 1/1+e^{-x}。那我们这里需要注意一下,这个特征以后会有大用。把它的图形画出来呢,就是如上图这样的一种函数, 这个函数值就是在0~1之间。

为什么我们要用逻辑函数来做概率预测呢?首先第一个原因就是因为它的值本身输出就是0~1之间,天然的适合做概率这块,第二,他还处处可导, 逻辑函数它是处处可导的。

所以我们就可以用这个函数来进行分类,可以把原来的模型f(x)=wx+b, 这整个模型写成:
f ( x ) = 1 1 + e − ( w x + b ) \begin{align*} f(x) = \frac{1}{1+e^{-(wx+b)}} \end{align*} f(x)=1+e(wx+b)1

原来的f(x)拟合的是等于wx+b, 现在把这个f(x)变成如上式的样子。这个输出的就变成0-1了,就能够让它的值在0到1之间变换。

这就是为什么我们把这种方法叫做逻辑回归的原因。就是它是在回归曲线上加了一个逻辑函数,所以我们称其为逻辑回归。

加上逻辑函数虽然输出的值是0~1之间,但其实是在做分类。越接近于1就越近于一类,越近于0就越近于另一类。逻辑回归本质上就是在做分类。

接着,咱们来上代码给大家详细的讲解一遍。

其实我们整个代码和之前实现的线性回归非常的像,唯一的区别是我们需要一个叫做sigmoid的函数,也就是逻辑函数。

def sigmoid(x):return 1/(1+np.exp(-x))

我们来把这个函数画出来看看是什么样的:

plt.plot(sigmoid(np.linspace(-10, 10)))
plt.show()

image-20231012184251402

其实, 机器学习是个很简单的问题。机器学习其实是计算机里面最简单的几个部分,哪些比这更复杂呢?第一个、编译器原理,还有程序设计语言与自动机,还有计算机图形学,复杂系统,还有计算复杂性,操作系统。其实这些都比
深度学习复杂的多。

为什么我们现在深度学习用的多,就是因为深度学习简单。所以我说这些题外话是想告诉大家,在学习这个的时候不要有什么顾虑和负担,放松一点,放开膀子撸起袖子干就完了。

好,我们现在把model写出来:

def model(x, w, b):return sigmoid(np.dot(x, w.T) + b)

那么来看, 我们现在如果要预测, 给一个rm和lstat输入进去, 一个RM和LSTAT的值输入进去。

我们先来看一下真实的值是怎样的:

rm = dataframe['RM']
lstat = dataframe['LSTAT']
target = dataframe['expensive']
epoch = 200
for i in range(epoch):for b in range(len(rm)):index = random.choice(range(len(rm)))x = np.array([rm[index], lstat[index]])y = target[index]print(x, y)

这个呢就是我们在训练时候每个给的数据,每次给他给一组数据,然后给它的这个值到底是0还是1。 我们期望的是求解一组(w,b),能够让它输入x的时候也能得到0或者1。就它真实的时候是0,期望的是这一组输入进去之后,根据(w,b)运行完了之后也是0。这个就是我们的目标。

假如已经获得线性回归了,然后要通过线性回归加一些东西想实现0和1的分类。之前我们在线性回归那里是不是先定义了一个loss函数?把loss函数定义清楚之后再对loss求偏导就可以了。那这里也是一样,需要定义loss,只要把loss定义出来之后给loss求偏导就可以了,和之前一模一样。

现在的问题就转变成,咱们怎么求loss呢?

我们的目标给定如果是0那么yhat也要是0。y是1的时候yhat也得是1。如果y等于1的情况下,yhat等于0,就意味着错的很厉害啊。相对的,y等于1,yhat等于0也同样是错的很厉害。

那么,如果y等于1的时候,yhat等于0.9, 错的就比较少。yhat等于1的时候,错误就是0,也就是没错误。

那么我们就可以写-log(yhat), 把这个写出来就是这样一个函数:当它越接近于0的时候,loss值会接近于无穷大, 当它接近于1的时候, loss会接近于0。

当y等于1的时候,loss可以等于-log(yhat)。如果y等于0,lose值就越接近于无穷大。这个时候loss就可以写成-log(1-yhat)。

那么现在这里就出现一个问题,也是通常面试时候的一个高频题:为什么在逻辑回归里,loss函数不直接写成1-yhat?

就是,如果y等于1的情况下,loss函数不直接写成1-y, 当y等于0的时候,loss不直接写成y。

因为这样会导致这条线呈现出一个直线,所有的偏导结果都是一致没有发生变化。

就好比有一个孩子考试成绩特别差,假如现在的目标是等于1,他的成绩特别特别差,0.001。现在的这个梯度还是比较小,他考特别好的时候这个梯度还是一样。

但是我们知道,梯度代表了接下来的变化方向和力度。这个就是我们为什么要用这个的原因。

当然,其实还有一些概率上的解释,这里就不继续延展着讲了。

对于上面讲的,当y=1和y=0的两个不同的loss函数,可以做一个归纳,写成一个loss函数:

l o s s = − ( y l o g y ^ + ( 1 − y ) l o g ( 1 − y ^ ) ) loss = - (ylog\hat y + (1-y) log(1-\hat y)) loss=(ylogy^+(1y)log(1y^))

为什么能够变成这样呢?我们来分析一下,如果y=0的时候,那ylog(yhat)就等于0,也就是说仅剩下(1-y)log(1-yhat), 反过来,当y=1的时候,等式后面部分就等于0,仅剩下ylog(yhat)。

那下面,我们就来完成代码来实现:

def loss(yhat, y):return -np.sum(y*np.log(yhat) + (1-y)*np.log(1-yhat))

lose函数求解出来之后,对于(w,b)怎么求偏导呢?

那么其实式子就可以变成:
− ( y l o g σ ( w x + b ) + ( 1 − y ) l o g σ ( 1 − ( w x + b ) ) ) − ( y l o g σ ( w 1 x 1 + w 2 x 2 + b ) + ( 1 − y ) l o g σ ( 1 − ( w 1 x 1 + w 2 x 2 + b ) ) ) \begin{align*} & -(ylog \sigma (wx+b) + (1-y)log \sigma (1-(wx+b))) \\ & -(ylog \sigma (w_1x_1+w_2x_2+b) + (1-y)log \sigma (1-(w_1x_1+w_2x_2+b))) \end{align*} (ylogσ(wx+b)+(1y)logσ(1(wx+b)))(ylogσ(w1x1+w2x2+b)+(1y)logσ(1(w1x1+w2x2+b)))
那么对其求偏导, 一系列推导完成后就可以变成:
∂ l o s s ∂ w i = ∑ ( y ^ − y ) x i \begin{align*} \frac{\partial loss}{\partial w_i} & = \sum(\hat y - y) x_i \end{align*} wiloss=(y^y)xi
我们来完成其函数代码,和线性部分一样,包含对w和b求导两部分:

def partial_w(x, y, yhat):return np.array([np.sum((yhat-y) * x[0]), np.sum((yhat-y) * x[1])])def partial_b(x, y, yhat):return np.sum((yhat - y))

那接下来我们干嘛?上节课的内容还有印象吗?接下来我们要给w,b随机值对吧?

w = w = np.random.random_sample((1,2))
b = np.random.random()

接着我们修改上面实现过的对真实值的实现代码, 删掉我们曾经打印的(x,y),然后利用我们实现的loss函数和偏导函数来计算预测值。

rm = dataframe['RM']
lstat = dataframe['LSTAT']
target = dataframe['expensive']learning_rate = 1e-5
epoch = 200
losses = []for i in range(epoch):batch_loss = []for batch in range(len(rm)):index = random.choice(range(len(rm)))x = np.array([rm[index], lstat[index]])y = target[index]# print(x, y)yhat = model(x, w, b)loss_v = loss(yhat, y)w = w + -1 * partial_w(x, y, yhat) * learning_rateb = b + -1 * partial_b(x, y, yhat) * learning_rateif batch % 100 == 0:print('Epoch: {}, Batch: {}, loss:{}'.format(i, batch, loss_v))losses.append(np.mean(batch_loss))

执行完之后,我们可以看到loss在慢慢的变小。

现在我们在数据中随机取一些数据,比如说我们去100个吧,用于去预测,检验我们的模型:

random_test_indices = np.random.choice(range(len(rm)), size=100)for i in random_test_indices:print('RM:{}, STAT:{}, TARGET:{}, PRE:{}'.format(rm[i], lstat[i], target[i], model(np.array([rm[i], lstat[i]]), w, b)))---
RM:6.425, STAT:12.03, TARGET:0, PRE:[0.15662289]
...
RM:5.0, STAT:31.99, TARGET:0, PRE:[4.87033539e-06]
...
RM:8.247, STAT:3.95, TARGET:1, PRE:[0.9623407]
...
RM:7.686, STAT:3.92, TARGET:1, PRE:[0.95077171]

我随机展示了一些数据,我们从这里能看到,预测值内有的值偏向0,有的值甚至比1还要大。对比前面的TARGET真实值来看,预测的大部分还是准确的。

不过这个时候还是有问题,我们做这个预测的初衷是为了要做分类,也就是到底是0还是1,那PRE值到底是什么,怎么分类呢?咱们就要牵扯到一个东西:dicision boundary

也就是决策的边界,咱们假定为0.5,让我们拿到的预测值去和这个边界值做对比,大于它的就是1,小于的就是0:

dicision_boundary = 0.5
predicate_label = int(predicate > decision_boundary)

有了这个之后,我们需要更改下我们之前的代码:

random_test_indices = np.random.choice(range(len(rm)), size=100)
decision_boundary = 0.5for i in random_test_indices:x1, x2, y = rm[i], lstat[i], target[i]predicate = model(np.array([x1, x2]), w, b)predicate_label = int(predicate > decision_boundary)print('RM:{}, LSTAT:{}, EXPENSIVE:{}, Predicated:{}'.format(x1, x2, y, predicate_label))

image-20231013142817740

更改完之后我们执行,和真实值进行对比,我们发现整个预测的还算事准确。当然也有部分预测错误的。

现在这个模型能够预测出来了,根据两个值能够预测出来它到底属于一个高档房子,还是不属于一个高档房子。但是我们会发现其实还有算错的地方。那么现在要问,如何衡量模型的好坏?以下就是我们要继续研究的问题:

  1. accuracy 准确度
  2. precision 精确度
  3. recall 召回率
  4. f1, f2 score
  5. AUC-ROC 曲线

这些就是我们用于衡量模型的一些指标,通过这个,我们要引出一个非常重要的概念,就是过拟合和欠拟合(over-fitting and under-fitting)。我们可以说,整个机器学习的过程,就是在不断的进行过拟合和欠拟合的调整。那么这些呢,就是我们下面课程的内容了。

目前来讲,我们学习了监督学习里面最重要的线性回归和逻辑回归,接下来什么我们要去学的LSTM,CNN等等,其实都是为了提高这个准确度所要做的事情。

也就是,现在我们发现虽然模型还是稍微有一些错误,这个时候就需要一起来再研究一下如何衡量模型的好坏。只有知道了如何衡量模型的好坏,才知道怎么样去调整它,怎么去优化它。

好,那下节课记得不见不散。

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

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

相关文章

Go语言入门心法(一)

Go语言入门心法(一) Go语言入门心法(二): 结构体 Go语言入门心法(三): 接口 一: go语言中变量认知 go语言中变量的定义: (要想飞|先会走)||(翻身仗|抹遗憾 ) |(二八定律)(先量变)|(再质变)||&#x…

【Matlab】二维绘图函数汇总

目录 1. plot() 2. subplot() 3. fplot() 4. polarplot() 1. plot() plot() 函数是 Matlab 中最常用的绘图函数,用于在平面直角坐标系中绘制直线或曲线。 用法: plot(X,Y) plot(X,Y,LineSpec) plot(X1,Y1, ... ,Xn,Yn) 说明: plot(X,Y) …

Ubuntu下安装Python

Ubuntu下安装Python 预备知识一、Python安装Python 二、Anaconda安装Anaconda卸载Anaconda 三、Miniconda安装Miniconda 四、异同比较 预备知识 (1) Python是一种编程语言。 (2) Anaconda是一款包管理工具,用来管理Python及其他语言的安装包,预装了很多…

【考研408真题】2022年408数据结构41题---判断当前顺序存储结构树是否是二叉搜索树

文章目录 思路408考研各数据结构C/C代码(Continually updating) 思路 很明显,这是一个顺序存储结构的树的构成方法。其中树的根节点位置从索引0开始,对于该结构,存在有:如果当前根节点的下标为n&#xff0c…

凉鞋的 Unity 笔记 108. 第二个通识:增删改查

在这一篇,我们来学习此教程的第二个通识,即:增删改查。 增删改查我们不只是一次接触到了。 在最先接触的场景层次窗口中,我们是对 GameObject 进行增删改查。 在 Project 文件窗口中,我们是对文件&文件夹进行增删…

Jetpack:007-Kotlin中的Button

文章目录 1. 概念介绍2. 使用方法2.1 Button2.2 IconButton2.3 ElevatedButton2.4 OutlinedButton2.5 TextButton2.6 FloatingActionButton 3. 示例代码4. 内容总结 我们在上一章回中介绍了Jetpack中输入框相关的内容,本章回中将要介绍 Button。闲话休提&#xff0…

使用【Blob、Base64】两种方式显示【文本、图片、视频】 使用 video 组件播放视频

Blob 显示 Blob 对象的类型是由 MIME 类型(Multipurpose Internet Mail Extensions)来确定的。MIME 类型是一种标准,用于表示文档、图像、音频、视频等多媒体文件的类型。以下是一些常见的 Blob 对象类型: text/plain&#xff1…

drone如何发布docker服务

上篇主要实现了drone在物理机上进行发布程序,这次介绍drone如何发布docker类型的服务。 一 drone.yml文件配置 前提:需要提前在drone里添加文件里面所引用的密钥 kind: pipeline # 定义对象类型,还有secret和signature两种类型 type: dock…

SaaS人力资源管理系统的Bug

SaaS人力资源管理系统的Bug Bug1【18】 这里我是直接把代码复制过来的&#xff0c;然后就有一个空白 这是因为它的代码有问题&#xff0c;原本的代码如下所示 <el-table-column fixed type"index" label"序号" width"50"></el-table…

Android之SpannableString使用

文章目录 前言一、效果图二、实现代码总结 前言 在开发中&#xff0c;往往有些需求是我们不愿意遇到的&#xff0c;但是也不得不处理的事情&#xff0c;比如一段文案&#xff0c;需要文案中某些文字变颜色或者点击跳转&#xff0c;所以简单写了几句代码实现&#xff0c;没什么…

java气候分析平台天气预报系统springboot+vue

保护措施 (自动编号、图片、措施简介、措施地点、措施时间、创建时间、措施详情、措施名称)&#xff1b; 报名信息 (自动编号、活动地点、图片、活动名称、活动时间、参与人数、活动详情、审核回复、创建时间、报名时间、手机、活动简介、是否审核、姓名、账号)&#xff1b; 配…

uniapp 一次性上传多条视频 u-upload accept=“video“ uni.chooseMedia uni.uploadFile

方式 一 部分安卓机 只能一条一条传视频 文档地址 uview 2.0 Upload 上传组件 html <view class"formupload"><u-upload accept"video":fileList"fileList3" afterRead"afterRead" delete"deletePic" name"…

Elasticsearch集群管理原理

Elasticsearch 是一个开源的、基于 Lucene 的分布式搜索和分析引擎&#xff0c;设计用于云计算环境中&#xff0c;能够实现实时的、可扩展的搜索、分析和探索全文和结构化数据。它具有高度的可扩展性&#xff0c;可以在短时间内搜索和分析大量数据。 Elasticsearch 不仅仅是一个…

算法练习13——跳跃游戏II

LeetCode 45 跳跃游戏 II 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回…

【漏洞复现】安全云平台存在任意文件下载getshell

漏洞描述 深圳市强鸿电子有限公司鸿运主动安全云平台存在任意文件下载漏洞,攻击者可通过此漏洞下载敏感文件信息。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害国家安全、荣誉和利益,未经授权…

el-table合计行合并

效果如下 因为合计el-table的合并方法是不生效的,所以需要修改css下手 watch: {// 应急物资的合计合并planData: {immediate: true,handler() {setTimeout(() > {const tds document.querySelectorAll(".pro_table .el-table__footer-wrapper tr>td");tds[0]…

C++笔记之std::future的用法

C笔记之std::future的用法 code review! 文章目录 C笔记之std::future的用法1.C中std::future和std::async总是一起出现吗&#xff1f;2.主要特点和用法3.一个完整的例子4.std::future 存放的是一个结果吗&#xff1f;5.cppreference——std::future 1.C中std::future和std::a…

数据分析基础:数据可视化+数据分析报告

数据分析是指通过对大量数据进行收集、整理、处理和分析&#xff0c;以发现其中的模式、趋势和关联&#xff0c;并从中提取有价值的信息和知识。 数据可视化和数据分析报告是数据分析过程中非常重要的两个环节&#xff0c;它们帮助将数据转化为易于理解和传达的形式&#xff0…

C++ - git 命令行

为什么要有 git 你很可能遇到过这种情况&#xff0c;在写项目的时候&#xff0c;要我不符合 leader 的要求&#xff0c;因而修改了很多个版本&#xff0c;但是修改到最后一版的时候&#xff0c;leader 有觉得当前这版还没有之前 某一版好&#xff0c;现在又要求你在 之前的那一…

OJ项目——用户的登录拦截,我是如何实现的?

目录 前言 1、关于Session该如何处理 简单session回顾&#xff1a; 回顾session的setAttribute、getAttribute : 项目中如何做&#xff1f; 2、登陆拦截器实现 自定义拦截器&#xff1a; 自定义拦截&#xff1a; 前言 博主之前也有出过一期关于拦截器的&#xff0c;大…