基于keras与tensorflow手工实现ResNet50网络

news2024/11/15 13:57:52

前言

在文章 基于tensorflow的ResNet50V2网络识别动物,我们使用了keras已经提供的神经网络,完成了图像分类的。这个时候,小明同学就问了,那么我怎么自己去写一个神经网络来进行训练呢?
本文就基于tensorflow,自己定一个神经网络。

ResNet50网络

在这里插入图片描述
从结构上看,与我们之前的的区别在于,输入的格式变成(3,224,224)
ResNet50有两个基本的块,分别名为Conv BlockIdentity Block

整体架构

在这里插入图片描述

Conv Block架构

在这里插入图片描述

Identity Block架构

在这里插入图片描述

模型训练

手工实现模型

模型代码(resnet50.py)

# 根据模型进行引入
from keras import layers

from keras.layers import Input,Activation,BatchNormalization,Flatten
from keras.layers import Dense,Conv2D,MaxPooling2D,ZeroPadding2D,AveragePooling2D
from keras.models import Model

def identity_block(input_tensor, kernel_size, filters, stage, block):
    filters1, filters2, filters3 = filters

    name_base = str(stage) + block + '_identity_block_'

    x = Conv2D(filters1, (1, 1), name=name_base + 'conv1')(input_tensor)
    x = BatchNormalization(name=name_base + 'bn1')(x)
    x = Activation('relu', name=name_base + 'relu1')(x)

    x = Conv2D(filters2, kernel_size, padding='same', name=name_base + 'conv2')(x)
    x = BatchNormalization(name=name_base + 'bn2')(x)
    x = Activation('relu', name=name_base + 'relu2')(x)

    x = Conv2D(filters3, (1, 1), name=name_base + 'conv3')(x)
    x = BatchNormalization(name=name_base + 'bn3')(x)

    x = layers.add([x, input_tensor], name=name_base + 'add')
    x = Activation('relu', name=name_base + 'relu4')(x)
    return x

def conv_block(input_tensor, kernel_size, filters, stage, block, strides=(2, 2)):

    filters1, filters2, filters3 = filters

    res_name_base = str(stage) + block + '_conv_block_res_'
    name_base = str(stage) + block + '_conv_block_'

    x = Conv2D(filters1, (1, 1), strides=strides, name=name_base + 'conv1')(input_tensor)
    x = BatchNormalization(name=name_base + 'bn1')(x)
    x = Activation('relu', name=name_base + 'relu1')(x)

    x = Conv2D(filters2, kernel_size, padding='same', name=name_base + 'conv2')(x)
    x = BatchNormalization(name=name_base + 'bn2')(x)
    x = Activation('relu', name=name_base + 'relu2')(x)

    x = Conv2D(filters3, (1, 1), name=name_base + 'conv3')(x)
    x = BatchNormalization(name=name_base + 'bn3')(x)

    shortcut = Conv2D(filters3, (1, 1), strides=strides, name=res_name_base + 'conv')(input_tensor)
    shortcut = BatchNormalization(name=res_name_base + 'bn')(shortcut)

    x = layers.add([x, shortcut], name=name_base+'add')
    x = Activation('relu', name=name_base+'relu4')(x)
    return x

def ResNet50(input_shape=[224,224,3],classes=1000):
    img_input = Input(shape=input_shape)
    x = ZeroPadding2D((3, 3))(img_input)

    x = Conv2D(64, (7, 7), strides=(2, 2), name='conv1')(x)
    x = BatchNormalization(name='bn_conv1')(x)
    x = Activation('relu')(x)
    x = MaxPooling2D((3, 3), strides=(2, 2))(x)

    x = conv_block(x, 3, [64, 64, 256], stage=2, block='a', strides=(1, 1))
    x = identity_block(x, 3, [64, 64, 256], stage=2, block='b')
    x = identity_block(x, 3, [64, 64, 256], stage=2, block='c')

    x = conv_block(x, 3, [128, 128, 512], stage=3, block='a')
    x = identity_block(x, 3, [128, 128, 512], stage=3, block='b')
    x = identity_block(x, 3, [128, 128, 512], stage=3, block='c')
    x = identity_block(x, 3, [128, 128, 512], stage=3, block='d')

    x = conv_block(x, 3, [256, 256, 1024], stage=4, block='a')
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='b')
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='c')
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='d')
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='e')
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='f')

    x = conv_block(x, 3, [512, 512, 2048], stage=5, block='a')
    x = identity_block(x, 3, [512, 512, 2048], stage=5, block='b')
    x = identity_block(x, 3, [512, 512, 2048], stage=5, block='c')

    x = AveragePooling2D((7, 7), name='avg_pool')(x)

    x = Flatten()(x)
    x = Dense(classes, activation='softmax', name='fc1000')(x)

    model = Model(img_input, x, name='resnet50')
    return model

模型训练(resnet50_model_train.py)

将 基于tensorflow的ResNet50V2网络识别动物的模型训练代码进行少量改造

import os
import pandas as pd

# Model
import keras
from keras.preprocessing.image import ImageDataGenerator

# Callbacks
from keras.callbacks import EarlyStopping, ModelCheckpoint

# Pre-Trained Model
import tensorflow as tf
import resnet50

root_path = './animal/Animals_Classification/Animal-Data-V2/Data-V2/Training Data/'
valid_path = './animal/Animals_Classification/Animal-Data-V2/Data-V2/Validation Data/'
test_path = './animal/Animals_Classification/Animal-Data-V2/Data-V2/Testing Data/'
class_names = sorted(os.listdir(root_path))
n_classes = len(class_names)

print(f"Total Number of Classes : {n_classes} \nClass Names : {class_names}")


class_dis = [len(os.listdir(root_path+name)) for name in class_names]


train_gen = ImageDataGenerator(rescale=1/255., rotation_range=10, horizontal_flip=True)
valid_gen = ImageDataGenerator(rescale=1/255.)
test_gen = ImageDataGenerator(rescale=1/255)

# Load Data
train_ds = train_gen.flow_from_directory(root_path, class_mode='binary', target_size=(224,224), shuffle=True, batch_size=32)
valid_ds = valid_gen.flow_from_directory(valid_path, class_mode='binary', target_size=(224,224), shuffle=True, batch_size=32)
test_ds = test_gen.flow_from_directory(test_path, class_mode='binary', target_size=(224,224), shuffle=True, batch_size=32)



with tf.device("/GPU:0"):
    ## Pre-Trained Model
    model = resnet50.ResNet50()
    model.summary()

    model_file = "ResNet50_V1.h5"
    # 加载预训练模型
    if os.path.exists(model_file):
        model.load_weights(model_file)

    # Callbacks
    cbs = [EarlyStopping(patience=5, restore_best_weights=True), ModelCheckpoint(model_file, save_best_only=True)]

    # Model
    opt = tf.keras.optimizers.Adam(learning_rate=2e-3)
    model.compile(loss='sparse_categorical_crossentropy', optimizer=opt, metrics=['accuracy'])

    # Model Training
    history = model.fit(train_ds, validation_data=valid_ds, callbacks=cbs, epochs=200)

    data = pd.DataFrame(history.history)
    print(data)

模型执行

GPU基本跑满
在这里插入图片描述

Model: "resnet50"
__________________________________________________________________________________________________
 Layer (type)                   Output Shape         Param #     Connected to                     
==================================================================================================
 input_1 (InputLayer)           [(None, 224, 224, 3  0           []                               
                                )]                                                                
                                                                                                  
 zero_padding2d (ZeroPadding2D)  (None, 230, 230, 3)  0          ['input_1[0][0]']                
                                                                                                  
 conv1 (Conv2D)                 (None, 112, 112, 64  9472        ['zero_padding2d[0][0]']         
                                )                                                                 
                                                                                                  
 bn_conv1 (BatchNormalization)  (None, 112, 112, 64  256         ['conv1[0][0]']                  
                                )                                                                 
                                                                                                  
 activation (Activation)        (None, 112, 112, 64  0           ['bn_conv1[0][0]']               
                                )                                                                 
                                                                                                  
 max_pooling2d (MaxPooling2D)   (None, 55, 55, 64)   0           ['activation[0][0]']             
                                                                                                  
 2a_conv_block_conv1 (Conv2D)   (None, 55, 55, 64)   4160        ['max_pooling2d[0][0]']          
                                                                                                  
 2a_conv_block_bn1 (BatchNormal  (None, 55, 55, 64)  256         ['2a_conv_block_conv1[0][0]']    
 ization)                                                                                         
                                                                                                  
 2a_conv_block_relu1 (Activatio  (None, 55, 55, 64)  0           ['2a_conv_block_bn1[0][0]']      
 n)                                                                                               
                                                                                                  
 2a_conv_block_conv2 (Conv2D)   (None, 55, 55, 64)   36928       ['2a_conv_block_relu1[0][0]']    
                                                                                                  
 2a_conv_block_bn2 (BatchNormal  (None, 55, 55, 64)  256         ['2a_conv_block_conv2[0][0]']    
 ization)                                                                                         
                                                                                                  
 2a_conv_block_relu2 (Activatio  (None, 55, 55, 64)  0           ['2a_conv_block_bn2[0][0]']      
 n)                                                                                               
                                                                                                  
 2a_conv_block_conv3 (Conv2D)   (None, 55, 55, 256)  16640       ['2a_conv_block_relu2[0][0]']    
                                                                                                  
 2a_conv_block_res_conv (Conv2D  (None, 55, 55, 256)  16640      ['max_pooling2d[0][0]']          
 )                                                                                                
                                                                                                  
 2a_conv_block_bn3 (BatchNormal  (None, 55, 55, 256)  1024       ['2a_conv_block_conv3[0][0]']    
 ization)                                                                                         
                                                                                                  
 2a_conv_block_res_bn (BatchNor  (None, 55, 55, 256)  1024       ['2a_conv_block_res_conv[0][0]'] 
 malization)                                                                                      
                                                                                                  
 2a_conv_block_add (Add)        (None, 55, 55, 256)  0           ['2a_conv_block_bn3[0][0]',      
                                                                  '2a_conv_block_res_bn[0][0]']   
                                                                                                  
 2a_conv_block_relu4 (Activatio  (None, 55, 55, 256)  0          ['2a_conv_block_add[0][0]']      
 n)                                                                                               
                                                                                                  
 2b_identity_block_conv1 (Conv2  (None, 55, 55, 64)  16448       ['2a_conv_block_relu4[0][0]']    
 D)                                                                                               
                                                                                                  
 2b_identity_block_bn1 (BatchNo  (None, 55, 55, 64)  256         ['2b_identity_block_conv1[0][0]']
 rmalization)                                                                                     
                                                                                                  
 2b_identity_block_relu1 (Activ  (None, 55, 55, 64)  0           ['2b_identity_block_bn1[0][0]']  
 ation)                                                                                           
                                                                                                  
 2b_identity_block_conv2 (Conv2  (None, 55, 55, 64)  36928       ['2b_identity_block_relu1[0][0]']
 D)                                                                                               
           
*******略,大致意思是有50层,因为是ResNet50*********                                                                                         
                                                                                                  
 5c_identity_block_bn3 (BatchNo  (None, 7, 7, 2048)  8192        ['5c_identity_block_conv3[0][0]']
 rmalization)                                                                                     
                                                                                                  
 5c_identity_block_add (Add)    (None, 7, 7, 2048)   0           ['5c_identity_block_bn3[0][0]',  
                                                                  '5b_identity_block_relu4[0][0]']
                                                                                                  
 5c_identity_block_relu4 (Activ  (None, 7, 7, 2048)  0           ['5c_identity_block_add[0][0]']  
 ation)                                                                                           
                                                                                                  
 avg_pool (AveragePooling2D)    (None, 1, 1, 2048)   0           ['5c_identity_block_relu4[0][0]']
                                                                                                  
 flatten (Flatten)              (None, 2048)         0           ['avg_pool[0][0]']               
                                                                                                  
 fc1000 (Dense)                 (None, 1000)         2049000     ['flatten[0][0]']                
                                                                                                  
==================================================================================================
Total params: 25,636,712
Trainable params: 25,583,592
Non-trainable params: 53,120
__________________________________________________________________________________________________

在这里插入图片描述

训练结果

Epoch 1/200
625/625 [==============================] - 245s 376ms/step - loss: 0.3225 - accuracy: 0.8965 - val_loss: 0.7825 - val_accuracy: 0.7670
Epoch 2/200
625/625 [==============================] - 226s 361ms/step - loss: 0.2998 - accuracy: 0.9018 - val_loss: 0.9110 - val_accuracy: 0.7060
Epoch 3/200
625/625 [==============================] - 223s 357ms/step - loss: 0.2644 - accuracy: 0.9113 - val_loss: 1.2283 - val_accuracy: 0.6760
Epoch 4/200
625/625 [==============================] - 223s 357ms/step - loss: 0.2465 - accuracy: 0.9188 - val_loss: 0.9871 - val_accuracy: 0.7500
Epoch 5/200
625/625 [==============================] - 225s 360ms/step - loss: 0.2307 - accuracy: 0.9234 - val_loss: 1.1059 - val_accuracy: 0.6720
Epoch 6/200
625/625 [==============================] - 228s 365ms/step - loss: 0.2016 - accuracy: 0.9341 - val_loss: 0.5819 - val_accuracy: 0.8370
Epoch 7/200
625/625 [==============================] - 227s 363ms/step - loss: 0.1859 - accuracy: 0.9380 - val_loss: 0.8662 - val_accuracy: 0.7740
Epoch 8/200
625/625 [==============================] - 223s 356ms/step - loss: 0.1732 - accuracy: 0.9419 - val_loss: 0.6927 - val_accuracy: 0.8130
Epoch 9/200
625/625 [==============================] - 221s 353ms/step - loss: 0.1631 - accuracy: 0.9446 - val_loss: 0.7033 - val_accuracy: 0.8090
Epoch 10/200
625/625 [==============================] - 220s 352ms/step - loss: 0.1416 - accuracy: 0.9528 - val_loss: 0.8072 - val_accuracy: 0.8130
Epoch 11/200
625/625 [==============================] - 221s 352ms/step - loss: 0.1403 - accuracy: 0.9524 - val_loss: 0.9740 - val_accuracy: 0.7570
        loss  accuracy  val_loss  val_accuracy
0   0.322520   0.89655  0.782460         0.767
1   0.299801   0.90180  0.910993         0.706
2   0.264406   0.91130  1.228291         0.676
3   0.246451   0.91880  0.987062         0.750
4   0.230691   0.92340  1.105917         0.672
5   0.201550   0.93405  0.581927         0.837
6   0.185873   0.93800  0.866195         0.774
7   0.173195   0.94185  0.692714         0.813
8   0.163083   0.94460  0.703307         0.809
9   0.141572   0.95280  0.807219         0.813
10  0.140329   0.95240  0.974021         0.757

训练结果

从输出的训练结果来看,效果没有ResNet50V2的,对参数进行了一些调整,没有太多的效果。各位如果有兴趣可以对这样的网络进行修改,从而提升验证的正确性。

模型验证

模型验证代码

from keras.models import load_model
import tensorflow as tf
from tensorflow.keras.utils import load_img, img_to_array
import numpy as np
import os

import matplotlib.pyplot as plt

root_path = './animal/Animals_Classification/Animal-Data-V2/Data-V2/Training Data/'

class_names = sorted(os.listdir(root_path))

model = load_model('./ResNet50_V1.h5')
model.summary()

def load_image(path):
    '''This function will load the image present at the given location'''
    image = tf.cast(tf.image.resize(img_to_array(load_img(path))/255., (224,224)), tf.float32)
    #image = tf.cast(tf.image.resize(img_to_array(load_img(path)) / 255., (224, 224)), tf.float32)
    return image

i_path = './animal/Animals_Classification/Animal-Data-V2/Data-V2/Validation Data/Gorilla/Gorilla (3).jpeg'
image = load_image(i_path)
preds = model.predict(image[np.newaxis, ...])[0]

print(preds)

pred_class = class_names[np.argmax(preds)]

confidence_score = np.round(preds[np.argmax(preds)], 2)

# Configure Title
title = f"Pred : {pred_class}\nConfidence : {confidence_score:.2}"
print(title)

plt.figure(figsize=(25, 8))
plt.title(title)
plt.imshow(image)
plt.show()

while True:
    path =  input("input:")
    if (path == "q!"):
        exit()
    image = load_image(path)

    preds = model.predict(image[np.newaxis, ...])[0]
    print(preds)

    pred_class = class_names[np.argmax(preds)]

    confidence_score = np.round(preds[np.argmax(preds)], 2)

    # Configure Title
    title = f"Pred : {pred_class}\nConfidence : {confidence_score:.2}"
    print(title)

    plt.figure(figsize=(25, 8))
    plt.title(title)
    plt.imshow(image)
    plt.show()

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

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

相关文章

Redis原理篇——五种基本数据类型

一、Redis底层数据结构 1. SDS 获取字符串长度耗时: 由于Redis底层是C语言编写的,C语言中没有字符串这个概念,本质上都是字符数组,获取字符串长度就是遍历数组获取长度(遍历到 \0结束标识结束 )时间复杂度…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java高校毕业生信息采集系统05hj2

大四计算机专业的同学们即将面临大学4年的最后一次考验--毕业设计。通过完成毕业设计来对过去4年的大学学习生活做一个总结,也是检验我们学习成果的一种方式,毕业设计作品也是我们将来面试找工作的一个敲门砖。 选题前先看看自己掌握哪些技术点、擅长哪…

数据库常用的数据类型和约束条件

文章目录一. 数据库常用的数据类型1. 数字类型1.1 整数类型:INT(m)和BIGINT(m)1.2 浮点类型:DOUBLE(m,n)2. 字符类型2.1 定长字符:CHAR(n)2.2 变长字符:VARCHAR(n)2.3 变长字符:TEXT(n)3. 日期类型3.1 语法格式:3.2 注意事项二. 约束条件1.主键约束(PRIMARY KEY)1.1 注意事项1.…

[附源码]Python计算机毕业设计Django美发店会员管理系统

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

Web压测工具http_load原理分析

01、前言 http_load是一款测试web服务器性能的开源工具,从下面的网址可以下载到最新版本的http_load: http://www.acme.com/software/http_load/ 这个软件一直在保持着更新(不像webbench,已经是十年的老古董了。 webbench的源…

【Matplotlib绘制图像大全】(二十九):Matplotlib绘制热力图

前言 大家好,我是阿光。 本专栏整理了《Matplotlib绘制图像大全》,内包含了各种常见的绘图方法,以及Matplotlib各种内置函数的使用方法,帮助我们快速便捷的绘制出数据图像。 正在更新中~ ✨ 🚨 我的项目环境: 平台:Windows10语言环境:python3.7编译器:PyCharmMatp…

【经验分享】突然我的SM.MS的图床没法访问了(内附解决方法)

【经验分享】突然我的SM.MS的图床没法访问了(内附解决方法) 一大早写文章,发现Markdown里的图片全部都不能成功加载了,这个的确挺头疼的! 文章目录1 说一说现象2 简单排查一下3 查找解决方案4 实施解决方案5 总结6 更多…

高楼扔鸡蛋问题

1.对应letecode链接 高楼扔鸡蛋问题 2.题目描述 解题思路 题目是这样&#xff1a;你面前有一栋从 1 到 N 共 N 层的楼&#xff0c;然后给你 K 个鸡蛋&#xff08;K 至少为 1&#xff09;。现在确定这栋楼存在楼层 0 < F < N&#xff0c;在这层楼将鸡蛋扔下去&#xff…

Windows使用ssh协议远程连接ubuntu linux子系统

Windows使用ssh协议远程连接ubuntu linux子系统一、Windows远程连接ubuntu linux子系统二、开启ubuntu ssh服务三、获取ubuntu子系统的ip地址四、从windows上通过ssh连接到ubuntu子系统五、后记一、Windows远程连接ubuntu linux子系统 当我们在windows上安装好ubuntu子系统后&…

Linux命令总结详细

Linux命令总结详细1.前言2.基础知识2.1.执行命令格式2.2.帮助命令2.2.1.man命令2.3.部分快捷键2.3.1.Tab键2.3.2.Ctrlc组合键2.3.3.Ctrll组合键2.4.服务运行命令2.5.服务开机启动命令3.系统工作命令3.1.date时间命令3.1.1.命令解释3.1.2.命令参数3.1.3.案例3.2.reboot重启命令3…

ESP-01S使用AT指令连接阿里云

这次分享下ESP8266-01S使用AT指令连接阿里云&#xff0c;为了后面stm32--esp-01s-阿里云&#xff08;MQTT&#xff09;做铺垫 目录 步骤&#xff1a; 1.烧录阿里云固件 首先我们打开->安信可官网下载阿里云的固件&#xff0c;如图 1.1串口助手与esp-01s接线说明 注&am…

C语言——VS2019实用调试技巧

前言 要想成为一个合格的程序员&#xff0c;不仅仅要会写代码&#xff0c;更要会调试代码。咔咔一通敲代码&#xff0c;敲出了BUG&#xff0c;这时就分两种程序员&#xff0c;一种是质疑编译器的程序员&#xff0c;“什么&#xff1f;我写出了BUG&#xff0c;是不是机器出了问…

什么是混淆矩阵精度、召回率、准确性、F1 分数、FPR、FNR、TPR、TNR?

在你的数据科学生涯的开始,混淆矩阵会非常混乱,我们会有很多问题,比如什么时候使用精度?什么时候使用召回?在哪些情况下可以使用精度?因此,我将尝试在本博客中回答这些问题。 什么是混淆矩阵? 混淆矩阵是一种将预测结果和实际值以矩阵形式汇总的方法,用来衡量分类问题…

带头双向循环链表的实现

目录前言节点声明链表的初始化尾插打印链表头插尾删头删查找节点指定位置插入指定位置删除链表销毁前言 之前讲过单链表的实现&#xff0c;在实现的过程中&#xff0c;我们会发现每次删除或者在前面插入节点的时候&#xff0c;都要提前保存上一个节点的地址。这样做十分麻烦&a…

大一新生HTML期末作业个人介绍博客 使用html+css+javascript+jquery技术制作网页,含有动画,hover效果,含有表格布局

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

RV1126/RV1109 IPC板 + RK3568+鸿蒙AI视频解决方案

近年来&#xff0c;云终端产品在办公、教育、工控等行业被广泛应用&#xff0c;其具有实用性强、运维量小、数据存储更安全等特点&#xff0c;深受市场青睐。而面对复杂光照环境、人流与车流、多变人体动作等复杂场景&#xff0c;成像质量和画面效果以及细节呈现能力&#xff0…

Ansible自动化运维工具之playbook剧本编写(上)

内容预知 1.playbook的相关知识 1.1 playbook 的简介 1.2 playbook的 各部分组成 2. 基础的playbook剧本编写实例 实例1&#xff1a;playbook编写 apache的yum安装部署剧本 实例2&#xff1a;playbook编写nginx 的yum安装并且能修改其监听端口的剧本 3. playbook的定义、引…

网站如何快速变成灰色?,几行代码就搞定了!

当大家看到全站的内容都变成了灰色&#xff0c;包括按钮、图片等等。这时候我们可能会好奇这是怎么做到的呢&#xff1f;有人会以为所有的内容都统一换了一个 CSS 样式&#xff0c;图片也全换成灰色的了&#xff0c;按钮等样式也统一换成了灰色样式。但你想想这个成本也太高了&…

ThreadLocal笔记

并发的场景中&#xff0c;如果有多个线程同时修改公共变量&#xff0c;可能会出现线程安全问题&#xff0c;即该变量最终结果可能出现异常。 如果使用锁来保证资源隔离&#xff0c;会存在大量锁等待&#xff0c;会让响应时间延长很多。 ThreadLocal的核心思想是&#xff1a;共享…

云服务器centos8搭建网站 apache+php+mysql

由于对数据库容量要求比较大&#xff0c;年费用300左右的普通虚拟主机只能提供500M-1G的数据库&#xff0c;不能满足要求&#xff0c;故寻找到同样费用的云服务器单核、1G内存、系统盘50G&#xff0c;缺点是只提供基本系统centos&#xff0c;其他要自己搭建&#xff0c;经过一周…