基于人体关节夹角的人体动作识别算法(代码+数据集)

news2024/11/15 23:35:47

为此本文提出了一个基于人体关节夹角的人体动作识别算法,主要做了以下工作:
(1)提出了一个可解释性强,耗费算力较少且鲁棒性较高的基于人体关节夹角的人体动作序列的特征抽取方法。
(2)本文所使用的分类模型是一个融合了SVM,lightgbm等6个分类模型的集成学习模型,并且使用了一个结合了Boosting和Bagging的集成学习策略。
最终本论文提出的方法在G3D数据集上的分类准确率为92.3%。该结果验证论文中方法的可行性

论文提出的算法的流程图

本文当中所提出的人体动作分类算法如上图所示,本算法主要分为人体3D关节点坐标获取,特征工程,模型集成,模型训练4个部分:
(1)人体关节点坐标获取部分:先使用Openpose的body25预训练模型结合训练集自带的深度图来提取出人体关键点3D坐标
(2)特征工程部分:先抽取人体动作特征向量,具体方法为:
①在每一个人体动作序列中等间距取50张图片
②基于得到的人体关键点3D坐标,每一张图片我们都可以得到 24 个肢体向量。然后通过24 个肢体向量两两之间的夹角的余弦值,可以得到一个长度为276的向量
③将50张图片得到的向量按照顺序拼接成长为13800的向量而后用基于方差的Filter方法和基于随机森林的RFE方法来特征选择。
(3)模型集成部分:因为logistic回归拟合能力较弱,所以先让它进行Adaboost增强拟合能力,而后再把它和SVM,KNN,随机森林,Xgboost,lightgbm一起进行基于软投票策略的Bagging方法后输出最终的分类结果。
(4)模型训练部分:按照训练集和测试集5:5的比例划分数据集,而后按照划分的数据集使用网格调参法调出每一个基分类模型的最佳参数,而后将其带入集成学习模型当中进行最终的模型分类预测。

论文方法的实验以及实验结果结果

本文先使用网格调参法调出基分类器的最佳参数,具体调参的过程如下图所示:

根据调参的结果,各个基分类器的最佳参数如下:
(1)logistic回归:“L2”惩罚项,C取0.214
(2)支持向量机:核函数取“poly”,C取0.238
(3)KNN:使用kdtree进行搜索,K值取3
(4)随机森林:estimators的最佳参数为142,criterion选择“entropy”这个选项
(5)Xgboost:最佳estimators的个数为25
(6)Lightgbm:最佳estimators的个数为67
(7)在实验当中Adaboost中的基分类器的个数设定为100
将调参结果输入到集成分类学习模型当中以后分类准确率达到了92.3%,得到的分类结果的混淆矩阵如下图所示:

一些重要东西的下载

①数据集使用的是G3D数据集:call me
②Openpose预处理以后的数据: call me 

 

代码运行

cd ./code
①集成分类模型运行结果 python proposed_method.py
②SVM画图 python SVM.py
①KNN画图 python KNN.py
④随机森林画图 python randomforest.py
⑤xgboost和Lightgbm画图 python xgboost_and_lightgbm.py
⑥logistic画图 python logistic.py
⑦特征抽取和特征选择:先把Openpose预处理以后的数据解压好以后放到code/data目录下,而后运行 python feature_extracting.py ,但是使用这个代码运行所得到的特征抽取结果来运行模型就可能得到和论文得到的结果不一样,因为RFE的运行结果每一次都会有细小的差异
⑧如果你想直接使用论文实验的数据的话就把Experimental data 文件夹里面的数据放到code文件夹下面,这样就不用再跑一遍特征抽取了.

feature_extracting.py

import  numpy as np
import pandas as pd
import os
import sklearn as sk
import matplotlib.pyplot as plt
import json
import math
import os
from PIL import Image
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.feature_selection import RFE
from sklearn.feature_selection import VarianceThreshold
def fun(x):
    return x >> 3
def depth2xyz(depth_map, depth_cam_matrix, flatten=False, depth_scale=1000):
    fx, fy = depth_cam_matrix[0, 0], depth_cam_matrix[1, 1]
    cx, cy = depth_cam_matrix[0, 2], depth_cam_matrix[1, 2]
    h, w = np.mgrid[0:depth_map.shape[0], 0:depth_map.shape[1]]
    z = depth_map/518
    x = w
    y = h
    xyz = np.dstack((x, y, z))
    return xyz

def getdata(filename1,filename2):
    jsonname = filename1
    picurename = filename2
    picurename = picurename.replace('Colour', 'Depth')
    with open(jsonname) as f:
        keypoints = json.load(f)
    body25 = keypoints['people'][0]['pose_keypoints_2d']
    img = Image.open(picurename)
    img = np.array(img)
    img = fun(img)
    img = np.where(img > 8192, img - 8192, img)

    depth_map = img.T

    depth_cam_matrix = np.array([[518, 0, 325.5],
                                 [0, 519, 253.5],
                                 [0, 0, 1]])
    targrt = depth2xyz(depth_map, depth_cam_matrix)

    x = []
    y = []
    z = []
    for i in range(0, 74, 3):
        D = targrt[int(body25[i])-1][int(body25[i + 1])-1]
        x.append(body25[i])
        y.append(body25[i + 1])
        z.append(D[2])

    return x, y, z


def VectorCosine(x, y):
    a = 0
    b = 0
    c = 0

    for i in range(0, len(x)):
        a += x[i] * y[i]
        b += x[i] * x[i]
        c += y[i] * y[i]
        c += 0.00001
        b += 0.00001
    result = a / (math.sqrt(b) * math.sqrt(c))
    return result


def cosvalues(filename1,filename2):
    x, y, z = getdata(filename1,filename2)
    result = []
    bodys = [
        [15, 17], [15, 0], [16, 0], [16, 18], [0, 1], [2, 1], [8, 1], [5, 1],
        [2, 3], [3, 4], [5, 6], [6, 7], [8, 9], [8, 12], [9, 10], [12, 13],
        [10, 11], [13, 14], [11, 24], [14, 21], [11, 22], [14, 19], [22, 23], [19, 20]
    ]
    i = 0
    bodys1 = bodys
    lengths = []

    for body in bodys:
        for body1 in bodys1[i + 1:24:1]:
            vector1 = np.array([x[body[0]] - x[body[1]], y[body[0]] - y[body[1]], z[body[0]] - z[body[1]]])
            vector2 = np.array([x[body1[0]] - x[body1[1]], y[body1[0]] - y[body1[1]], z[body1[0]] - z[body1[1]]])
            array = np.array([np.linalg.norm(vector1), np.linalg.norm(vector2)])

            cosine = VectorCosine(vector1, vector2)
            result.append(cosine)
        i = i + 1

    return result
Root="data/feature-extracting-result"
Sports=os.listdir(Root)
print(Sports)

for Sport in Sports:
    sportpath=Root+'/'+Sport
    Actors=os.listdir(sportpath)
    for Actor in Actors:
        Actionspath=sportpath+'/'+Actor
        Actions=os.listdir(Actionspath)
        for Action in Actions:
            Finalpath = Actionspath + '/' + Action
            for i in np.linspace(1, 1, 1):
                i = int(i)
                jsonpath = Finalpath+ '/output/' + str(i) + '_keypoints.json'
                depthpath = Finalpath+ '/depth/' + str(i) + '.png'

                Matrix = np.array(cosvalues(jsonpath, depthpath))

            for i in np.linspace(2, 50, 49):
                i = int(i)
                jsonpath = Finalpath+ '/output/' + str(i) + '_keypoints.json'
                depthpath = Finalpath+ '/depth/' + str(i) + '.png'

                Vector = np.array(cosvalues(jsonpath, depthpath))
                Matrix = np.vstack((Matrix, Vector))
            Matrixname = Finalpath + '/' + "Matrix.csv"
            Matrix.tofile(Matrixname, sep=',', format='%1.5f')
    print(Sport,"is Done")
Root="data/feature-extracting-result"
sports=os.listdir(Root)
Class=0
'''Bowling是,0,Drving是1.Fighting是2,FPS是3,Golf是4,Misc是5,Tennis是6'''
result=pd.read_csv("data/feature-extracting-result/Bowling/Actor_1/1/Matrix.csv",header=None)
result["class"]=1
data=pd.DataFrame(columns=result.columns)
for sport in sports:
    Actors=os.listdir(Root+"/"+sport)
    for Actor in Actors:
        bianshus=os.listdir(Root+"/"+sport+"/"+Actor)
        for bianshu in bianshus:
            filename=Root + "/" + sport + "/" + Actor + "/" + bianshu+"/Matrix.csv"
            data1=pd.read_csv(filename,header = None)
            data1["class"]=Class
            data=data.append(data1)
    Class=Class+1
data.to_csv("future_extracting_result.csv")
data=pd.read_csv("future_extracting_result.csv")
target=data["class"]
traindata=data.drop(["class"],axis=1)
sel = VarianceThreshold(threshold=0.16)
###选取0.16的原因:若特征是伯努利随机变量,假设p=0.8,即二分类特征中某种分类占到80%以上的时候删除特征
#因为有80%都是相似数据,对于数据的整体的特征已经没有太多的区分意义
traindata = sel.fit_transform(traindata)
print("variance filtler is Done")
RFC_ = RFC(n_estimators =25,random_state=0)
traindata = RFE(RFC_,n_features_to_select=500).fit_transform(traindata,target)
print("RFE via randomforest is Done")
selectdata=pd.DataFrame(traindata)
selectdata.to_csv("future_selection_result.csv",index=False)
print("Feature extraction is Done")

 logistic.py

import numpy
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import VarianceThreshold
from sklearn.svm import SVC
import numpy as np
import pandas as pd
from lightgbm import LGBMClassifier
from sklearn import svm
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC
from xgboost import XGBClassifier
from sklearn.neighbors import  KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.feature_selection import RFE
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']='SimHei' #设置字体
plt.rcParams["font.size"]=10 #设置字号
plt.rcParams["axes.unicode_minus"]=False #正常显示负号
data=pd.read_csv("future_extracting_result.csv")
target=data["class"]
traindata=pd.read_csv("future_selection_result.csv")
X_train,X_test, y_train, y_test=train_test_split(traindata,target,test_size=0.5,random_state=2)
model1=LogisticRegression(random_state=2)
C=np.linspace(0.0001,1,20)
A=[]
B=[]
for i in C:
    model=LogisticRegression(C=i,random_state=2)
    model.fit(X_train, y_train)
    y_pred=model.predict(X_test)
    A.append(i)
    B.append(accuracy_score(y_test,y_pred))
print(A)
print(B)
plt.ylabel("分类准确率")#横坐标名字
plt.xlabel("C")#纵坐标名字
plt.plot(A, B)
plt.show()

proposed_method.py

import numpy
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import VarianceThreshold
from sklearn.svm import SVC
import numpy as np
import pandas as pd
from lightgbm import LGBMClassifier
from sklearn import svm
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC
from xgboost import XGBClassifier
from sklearn.neighbors import  KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.feature_selection import RFE
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import VotingClassifier
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']='SimHei' #设置字体
plt.rcParams["font.size"]=10 #设置字号
plt.rcParams["axes.unicode_minus"]=False #正常显示负号
data=pd.read_csv("future_extracting_result.csv")
target=data["class"]
traindata=pd.read_csv("future_selection_result.csv")
print(traindata.shape)
X_train,X_test, y_train, y_test=train_test_split(traindata,target,test_size=0.5,random_state=2)
#--------------------------------(1)logistic回归------------------------------------------
logistic=LogisticRegression(random_state=2,penalty="l2",C=0.214)
model1=AdaBoostClassifier(estimator=logistic,n_estimators=100,random_state=1)
model1.fit(X_train,y_train)
y_pred1=model1.predict(X_test)
print("构建的logistic+Adaboost分类模型的准确率为:",accuracy_score(y_test,y_pred1))
#---------------------------------(2)LGBT-----------------------------------------------
lgbt=LGBMClassifier(n_estimators=67,booster="gbtree",random_state=2)
lgbt.fit(X_train,y_train)
y_pred2=lgbt.predict(X_test)
print("构建的LGBT分类模型的准确率为:",accuracy_score(y_test,y_pred2))
#-----------------------------------(3)XGboost---------------------------------------------
xgb=XGBClassifier(n_estimators=25,booster="gbtree",random_state=2)
xgb.fit(X_train,y_train)
y_pred3=xgb.predict(X_test)
print("构建的XGboost分类模型的准确率为:",accuracy_score(y_test,y_pred3))
#-----------------------------------(4)KNN---------------------------------------------
KNN=KNeighborsClassifier(n_neighbors=3)
KNN.fit(X_train,y_train)
y_pred4=KNN.predict(X_test)
print("构建的KNN分类模型的准确率为:",accuracy_score(y_test,y_pred4))
#-----------------------------------(5)RandomForest---------------------------------------------
RF=RFC(n_estimators=142,criterion="entropy",random_state=1)
RF.fit(X_train,y_train)
y_pred5=RF.predict(X_test)
print("构建的RandomFores分类模型的准确率为:",accuracy_score(y_test,y_pred5))
#-----------------------------------(6)SVM多分类器---------------------------------------------------
svm=SVC(probability=True)
svm.fit(X_train,y_train)
y_pred6=svm.predict(X_test)
print("构建的SVM分类模型的准确率为:",accuracy_score(y_test,y_pred6))
#------------------------------Bagging集成模型一-----------------------------------------------------
voting=VotingClassifier(estimators=[('log',model1),("lgbt",lgbt),('KNN',KNN),('RF',RF),('svc',svm),('xgb',xgb)],voting='soft',weights=[1,1,1,1,1,1])
voting.fit(X_train,y_train)
y_pred=voting.predict(X_test)
print("构建的Bagging分类模型的准确率为:",accuracy_score(y_test,y_pred))
confusion = confusion_matrix(y_test, y_pred)
plt.imshow(confusion, cmap=plt.cm.Blues)
indices = range(len(confusion))

plt.colorbar()

plt.xlabel("真实标签")

plt.ylabel("预测的标签")

plt.title("混淆矩阵")
plt.xticks(indices, ['打保龄球','驾驶',"搏击",'射击','高尔夫','混合动作',"网球"])
plt.yticks(indices, ['打保龄球','驾驶',"搏击",'射击','高尔夫','混合动作',"网球"])
for first_index in range(len(confusion)):
    for second_index in range(len(confusion)):
        plt.text(first_index, second_index, confusion[first_index][second_index])

plt.show()

randomforest.py

import numpy
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import VarianceThreshold
from sklearn.svm import SVC
import numpy as np
import pandas as pd
from lightgbm import LGBMClassifier
from sklearn import svm
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC
from xgboost import XGBClassifier
from sklearn.neighbors import  KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.feature_selection import RFE
from matplotlib import pyplot as plt
from matplotlib import font_manager
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']='SimHei' #设置字体
plt.rcParams["font.size"]=10 #设置字号
plt.rcParams["axes.unicode_minus"]=False #正常显示负号
data=pd.read_csv("future_extracting_result.csv")
target=data["class"]
traindata=pd.read_csv("future_selection_result.csv")
print(traindata)
X_train,X_test, y_train, y_test=train_test_split(traindata,target,test_size=0.5,random_state=2)
estimators=np.linspace(1,200,50).astype(int)
A=[]
B=[]
A1=[]
A2=[]
A3=[]
for i in estimators:
    model=RFC(n_estimators=i,criterion="gini",random_state=2)
    model.fit(X_train, y_train)
    y_pred=model.predict(X_test)
    A.append(i)
    B.append(accuracy_score(y_test,y_pred))
for i in estimators:
    model=RFC(n_estimators=i,criterion="entropy",random_state=2)
    model.fit(X_train, y_train)
    y_pred=model.predict(X_test)
    A2.append(accuracy_score(y_test,y_pred))
for i in estimators:
    model=RFC(n_estimators=i,criterion="log_loss",random_state=2)
    model.fit(X_train, y_train)
    y_pred=model.predict(X_test)
    A3.append(accuracy_score(y_test,y_pred))
print(estimators)
print(A2)
print(B)
plt.ylabel("分类准确率")#横坐标名字
plt.xlabel("基学习器个数")#纵坐标名字
plt.plot(A, B, c='green',  label="基尼",markersize = 2,alpha=0.7)
plt.plot(A,A2,c='blue', linestyle='--' ,label="熵",markersize = 2,alpha=0.5)
plt.grid(True, linestyle='--', alpha=0.5)
plt.legend(loc='lower right')
plt.show()

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

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

相关文章

PyInstaller实战:打包Python应用并间接指定输出文件名

在深入探讨如何使用PyInstaller打包Python应用并指定输出文件名称的过程中,我们不仅可以了解基本的命令行操作和参数设置,还可以深入了解PyInstaller的工作机制、状态变化以及它在处理复杂Python项目时的优势。下面,我们将详细展开这一过程&a…

提升多跳问答中的语言模型知识编辑能力

人工智能咨询培训老师叶梓 转载标明出处 大模型在静态知识库的更新上存在局限,特别是在面对需要多步骤推理的多跳问题时,难以提供准确和最新的回答。为了解决这一问题,来自美国佐治亚大学、纽约大学、莱斯大学、北卡罗来纳州立大学等机构的研…

STM32F103C8----GPIO(跟着江科大学STM32)

一,GPIO简介 GPIO(General Purpose Input Output)通用输入输出口 可配置为8种输入输出模式 引脚电平:0V~3.3V(0V),部分引脚可容忍5V 输出模式下可控制端口输出高低电平,用以驱动…

idea2021安装教程与常见配置(可激活至2099年)

idea2021安装教程与常见配置(可激活至2099年) 下载 官网下载地址:https://www.jetbrains.com/zh-cn/idea/download/other.html 这里我们选择压缩包安装方式,选择2021.3 - Windows x64 ZIP Archive (zip),也可以选择exe安装方式 安装 解压缩安装方式 创建非中文目录D:\idea…

Win32绕过UAC弹窗获取管理员权限

在早些年写一些桌面软件时,需要管理员权限,但是又不想UAC弹窗,所以一般是直接将UAC的级别拉到最低,或者直接禁用UAC的相关功能。 什么是UAC(User Account Control) 用户帐户控制 (UAC) 是一项 Windows 安全功能,旨在保…

行走挖机多路比例阀控制放大器

挖掘机比例多路阀是挖掘机液压系统中的关键部件,它负责控制挖掘机各执行元件的运动方向、速度和力矩,从而影响挖掘机的作业效果。比例多路阀由多个阀块组成,其中比例控制阀由BEUEC比例放大器控制。每个阀块都有特定功能,如换向阀用…

昇腾大模型性能分析思路

性能分析 模型训练优化流程 我们根据性能问题的场景,按照单机和集群场景进行分类,再明确性能问题属于哪一类,明确好性能问题背景之后,才方便进行下一步问题的定位; 在明确问题背景后,参考性能分析工具介绍…

004、架构_详解(重点)

GoldenDB 分布式数据库框架 DN和RDB增加了备节点;引入新模块CM,且GTM、MDS、PM、CM都增加备节点;MDS、PM、CM、RDB被统一在了管理节点之中;GTM和MDS间多了一条连线,因为GTM的切换由MDS把控;初步系统架构mysqld:一般称为DB节点,负责单个节点的数据处理; dbproxy:一般…

FreeRTOS学习笔记—③RTOS内存管理篇(正在更新中)

二、RTOS的核心功能 RTOS的核心功能块主要分为任务管理、内核管理、时间管理以及通信管理4部分,框架图如下所示: (1)任务管理:负责管理和调度任务的执行,确保系统中的任务能够按照预期运行。 (…

【SpringBoot】使用Nacos服务注册发现与配置管理

前提:需要提前部署好nacos服务,这里可以参考我的文章:Windows下Nacos安装与配置 0. 版本信息 Spring Boot3.2.8Spring Cloud2023.0.1Spring Cloud alibaba2023.0.1.0nacos2.3.2本地安装的nacos2.3.0 Spring Boot、Spring Cloud、Spring Clo…

黑盒闪清 v2.9.9 体积小巧,简洁高效的手机清理神器

黑盒闪清APP是安卓手机上的一款优质文件管理器,拥有存储分析、文件分类、大文件扫描、空文件夹扫描等功能,应用无广告、无推送,完全免费使用,让你手机中的文件管理就跟在电脑上管理一样简单。 链接:https://pan.quark…

C语言学习笔记 Day16(文件管理--下)

Day16 内容梳理: C语言学习笔记 Day14(文件管理--上)-CSDN博客 C语言学习笔记 Day15(文件管理--中)-CSDN博客 目录 Chapter 10 文件操作 10.5 文件状态 10.6 文件的随机读写 fseek()、rewind() (1&…

对同一文件夹下所有excel表进行相同操作(数据填充、删除、合并)

背景引入:如图所示,笔者需要对数十个表格的银行日记账工作簿合并成一个工作簿,以便与本月银行流水进行核对。 为了方便银行日记账与银行流水进行核对,需要再每个村或小组的表格中,将村或小组的名称放在J列。 clear c…

Java | Leetcode Java题解之第392题判断子序列

题目&#xff1a; 题解&#xff1a; class Solution {public boolean isSubsequence(String s, String t) {int n s.length(), m t.length();int[][] f new int[m 1][26];for (int i 0; i < 26; i) {f[m][i] m;}for (int i m - 1; i > 0; i--) {for (int j 0; j…

9月4日C++作业

#include <iostream> #include <string> using namespace std; class Human {private:string name;int age;public:Human(){} //无参构造函数//有参构造函数Human(string i_name,int i_age):name(i_name),age(i_age){cout<<"调用了Human有参构…

一篇文章讲清楚什么是Spring AOP

目录 1、什么是代理&#xff1f; 1.1静态代理 1.2动态代理 2、什么是AOP&#xff1f; 3、AOP术语名词介绍 4、Spring AOP框架使用教程 5、Spring AOP框架细节讲解 1、什么是代理&#xff1f; 在讲解AOP之前&#xff0c;我们要先了解下什么是代理。 代理是二十四种设计…

Flutter集成Firebase框架

本文档的插件版本 flutter&#xff1a;3.19.4dart版本&#xff1a;3.3.2 firebase_core&#xff1a;2.30.0 firebase&#xff1a;13.7.3flutterfire&#xff1a;1.0.0 前言 Flutter集成Firebase框架要完成以下内容 在Firebase网页端创建一个项目在终端全局安装Firebase工具&…

中断管理笔记

1、异常与中断的基本概念 异常是指任何打断处理器正常执行&#xff0c;并且迫使处理器进入一个由有特权的特殊指令执行的事件。 异常可以分为两类&#xff1a;同步异常和异步异常。 由内部事件&#xff08;像处理器指令运行产生的事件&#xff09;引起的异常称为同步异常。异…

雅思7分相当于六级多少?雅思考试怎么备考才能到7分?

雅思7分相当于六级多少&#xff1f;雅思考试怎么备考才能到7分? 六级500分也不能和雅思7分相提并论&#xff0c;毕竟两者压根不在一个层级。楼主两月自学雅思获得8分&#xff0c;系统总结了雅思速成的提分秘籍&#xff0c;跟着我的方法走&#xff0c;两月屠鸭7分问题不大。1.6…

你做的SEO为什么效果不够好?

SEO&#xff08;Search Engine Optimization&#xff09;即搜索引擎优化&#xff0c;指在不同的搜索引擎中&#xff0c;按照相应的规则与机制提高网站在特定的搜索引擎中的自然排名。作为对产品和服务进行数字宣传营销的重要手段&#xff0c;SEO已然成为各企业提高网站曝光和流…