09. 机器学习- 逻辑回归

news2024/12/26 20:50:04

文章目录

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

茶桁的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_rate
    b = b + -1 * pb(x, y, yhat) * learning_rate

    return w, b

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

def train(model_to_be_train, target, loss, pw, pb):
    w = np.random.random_sample(size = (1, 2))# w normal
    b = np.random.random()
    learning_rate = 1e-5
    epoch = 200

    for i in range(epoch):
        for batch in range(len(rm)):
            # batch trainning
            index = 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']  = target

rm = 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, losses

model, 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_openml

dataset = 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_rate
        b = b + -1 * partial_b(x, y, yhat) * learning_rate

        if 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.5

for 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.coloradmin.cn/o/1093607.html

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

相关文章

【Linux】基本指令-入门级文件操作(二)

目录 基本指令 7 cp指令(重要) 8 mv指令(重要) 9 nano指令 10 cat指令 11 echo指令与重定向(重要) 12 more指令 13 less指令 基本指令 7 cp指令(重要) 功能:复…

React高级特性之context

例1&#xff1a; createContext // 跨组件通信Context引入createContext import React, { createContext } from react// App传数据给组件C App -- A -- C// 1. 创建Context对象 const { Provider, Consumer } createContext()function SonA () {return (<div>我是…

分布式存储系统Ceph应用详解

Ceph的应用 一、Ceph 存储池(Pool)1.1 Ceph存储池的基本概念1.2 原理1.3 一个Pool资源池应该包含多少PG数&#xff1f;1.4 Ceph 存储池相关管理命令1.4.1 创建1.4.2 查看1.4.3 修改1.4.4 删除 二、 CephFS文件系统MDS接口三、创建CephFS文件系统MDS接口3.1 服务端操作Step1 在管…

【Java学习之道】线程的概念与作用

引言 今天我们将探索多线程编程的基础概念和作用。对于初学者来说&#xff0c;掌握多线程编程是迈向Java高级技能的重要一步。通过本章的学习&#xff0c;你将了解线程是什么以及它在程序开发中的重要性&#xff0c;为你进一步深入学习和实际工作打下坚实的基础。让我们一起来…

学信息系统项目管理师第4版系列27_项目集管理和项目组合管理

1. 项目集发起人 1.1. 负责承诺将组织的资源应用于项目集&#xff0c;并致力于使项目集取得成功的人 1.2. 典型职责 1.2.1. 为项目集提供资金&#xff0c;确保项目集目标与战略愿景保持一致&#xff1b; 1.2.2. 使效益实现交付 1.2.3. 消除项目集管理与交付的困难和障碍 …

单目3D自动标注

这里介绍两种 1. 基于SAM的点云标注 Seal&#xff1a;是一个多功能的自监督学习框架&#xff0c;能够通过利用视觉基础模型的现成知识和2D-3D的时空约束分割自动驾驶数据集点云 Scalability&#xff1a;可拓展性强&#xff0c;视觉基础模型蒸馏到点云中&#xff0c;避免2D和…

应对互联网用户激增与IP地址短缺的挑战

互联网用户激增 随着互联网技术的飞速发展&#xff0c;互联网已经深刻改变了我们的生活方式和商业模式。无论是个人用户还是企业&#xff0c;都越来越依赖互联网进行沟通、娱乐、工作和学习。这一现象导致了互联网用户数量的快速激增。 IP地址的有限性 然而&#xff0c;与此…

码蹄集2230--square 高精度乘低精度,组合数

有种走法&#xff0c;因为需要向上走m步&#xff0c;向右走n步。 显然分子分母分别算出&#xff0c;再相除不太可能&#xff0c;那么分别求出分子和分母的质因子相乘的形式。分子存入up数组中&#xff0c;分母存入down数组中&#xff0c;数组中的元素对应之差final_数组即代表…

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

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

STL容器适配器以及仿函数的简单认识

在学c的过程中&#xff0c;我们必不可少的一大工具就是STL&#xff0c;并且要一定程度的了解STL背后的原理。 今天我们来探讨一下STL中stack和queue的容器适配器&#xff0c;以及priority_queue是什么&#xff0c;以及一点仿函数的知识。1.容器适配器 1). 容器适配器 在我们了…

React添加文件路径时使用@符号代替src目录(非creae-react-app)

在其它项目中看到的可以用符号来代替src目录&#xff0c;那么在自己的react项目中也必须得尝试一下。本人的项目不是通过create-react-app脚手架来创建的&#xff0c;无法使用craco或者的方案来实现。 jsconfig.json配置 用的vscode进行开发&#xff0c;查看项目当中是否存在js…

与HTTP相关的各种协议

TCP/IP TCP/IP协议是目前网络世界“事实上”的标准通信协议&#xff0c;实际上是一系列网络通信协议的统称&#xff0c;其中最核心的两个协议是 TCP和IP&#xff0c;其他的还有 UDP、ICMP、ARP 等等&#xff0c;共同构成了一个复杂但有层次的协议栈。 这个协议栈有四层&#x…

Nginx:动静分离(示意图+配置讲解)

示意图&#xff1a; 动静分离 动静分离是指将动态内容和静态内容分开处理的一种方式。通常&#xff0c;动态内容是指由服务器端处理的&#xff0c;例如动态生成的网页、数据库查询等。静态内容是指不需要经过服务器端处理的&#xff0c;例如图片、CSS、JavaScript文件等。通过…

C++之make_unique、namespace、class类总结(二百四十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

React-Router-6学习笔记

一、新建react项目 1、新建项目命令&#xff1a; yarn create vite react-router --template react 2、在vsCode中打开项目 3、在vsCode打开命令终端&#xff0c;输入 yarn 4、执行yarn dev启动当前项目 yarn dev 5、删除多余的东西&#xff0c;保留app.jsx和main.jsx文…

高校教务系统登录页面JS分析——皖西学院

高校教务系统密码加密逻辑及JS逆向 本文将介绍皖西学院教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的过程。通过本文&#xff0c;你将了解到密码加密的基本概念、常用加密算法以及如何通过逆向分析来破解密码。 本文仅供交流学习&#xff0c;勿用于非法用途。 一、密…

NLP算法面经 | 腾讯 VS 美团

作者 | 曾同学 编辑 | NewBeeNLP 面试锦囊之面经分享系列&#xff0c;持续更新中 后台回复『面试』加入讨论组交流噢 lz从3月初脚因打球扭伤了开始&#xff0c;投递简历&#xff0c;接二连三的面试鞭尸又面试&#xff0c;昨天才终于上岸了&#xff0c;分享经验~ 腾讯PCG看点&…

SSH 基础学习使用

什么是SSH 1.SSH SSH&#xff08;Secure Shell&#xff09; 是较可靠&#xff0c;专为远程登录会话和其他网络服务提供安全性的协议&#xff0c;利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。 实际应用中&#xff0c;主要用于保证远程登录和远程通信的安全&#…

数字技术助力智慧公厕,让公厕变身为全新创新应用

在如今数字化的时代&#xff0c;数字技术的集成应用已经渗透到了生活的方方面面。其中一个令人瞩目的领域就是智慧公厕。以前只是简单的厕所&#xff0c;如今借助数字技术的力量&#xff0c;智慧公厕变得功能强大、智能高效。接下来&#xff0c;我们将以智慧公厕源头领航厂家广…

Retrieve Anything To Augment Large Language Models

简介 论文主要介绍了一套通过对比学习和蒸馏学习的方法&#xff0c;来增强学习了embedding向量&#xff0c;然后能够在知识增强&#xff0c;长上下文建模&#xff0c;ICL和工具学习等方面来增强大模型能力。