RomanticQq

保持对生活的热爱,唯有生活不可被辜负

0%

  1. 在pytorch中,如果用交叉熵损失,则模型的最后一层不能是nn.Softmax(),因为在交叉熵损失中已经内置了nn.Softmax(),用两次会造成损失收敛缓慢甚至不收敛;
  2. 给文件起名字的时候不要与安装的包的名字相同,否则导包时可以会导本地同名的文件,调用包里的方法就会报,没有发现该模块;
  3. 安装完augly,发现导包失败,pip uninstall python-magic; pip install python-magic-bin==0.4.14
  4. 训练vgg16时,学习率不能设置太大,比如设置为0.001时,就会造成损失函数不下降,模型不收敛,可以设置为0.0001;

Here's something encrypted, password is required to continue reading.
阅读全文 »

安装docker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apt update

apt-get install ca-certificates curl gnupg lsb-release

curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -

sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"

sudo apt-get install docker-ce docker-ce-cli containerd.io

systemctl start docker

apt-get -y install apt-transport-https ca-certificates curl software-properties-common

service docker restart

sudo docker version

sudo docker images

将容器打包成镜像

1
2
3
4
5
6
# -a:作者
# -m:提交成镜像时的说明信息
# -p:在commit时,将容器暂停
# -c:使用Dockerfile指令来创建容器

docker commit -a "fuqiang" -m "描述" 容器名 新镜像名:标识

创建包含镜像的容器

1
2
3
4
5
docker run -it --name 新容器名 -p 宿主端口:容器端口 -v 宿主空间位置:容器空间位置 镜像名 /bin/bash

# 第一次要启动容器
docker start 容器名
docker exec -it 容器名 /bin/bash

其他常用docker命令

1
2
3
4
5
6
7
8
9
10
# 显示在运行的容器
docker ps
# 显示所有容器
docker ps -a
# 搜索镜像
docker search 镜像名
# 拉取镜像
docker pull 镜像名
# 显示当前docker所有镜像
docker images

参考博客

参考视频

image-20230316150350681

2

  1. Patch Partition中,对于图片的下采样不是采用卷积的形式,而是采用真正分割的方式;
  2. 在进行注意力计算时,token的长度和通道数相等,比如[h,w,c],那么就有h*w个token;
  3. 添加偏置项是在Q*K之后进行的;
  4. SW-MSA中的mask是在添加完偏置项以后加上去的,W-MSA省略这一步;
  5. 每个stage只创建一次mask,因此尺寸大小相等,不用重复创建;

学习内容

image-20230307143026001

image-20230307143104645

image-20230307143616391

  • 要实现高性能,对于预处理和后处理要写cuda核函数;

驾驭tensorRT的方案介绍

image-20230307143935065

image-20230307144103465

image-20230307144215385

image-20230307145015509

image-20230307145149847

image-20230307145550909

image-20230307145737203

  • torch2trt是由个人来维护的;
  • 生成的trt engine是和设备绑定的,在一个型号的显卡上编译的模型不一定能在另一个显卡上好好的执行;
  • 这种方法必须在设备上安装pytorch,然后再导出这个模型,因为从别的地方导出的模型是不一定能用的;

image-20230307151111960

image-20230307151648573

image-20230307151917782

image-20230307152326146

如何正确导出onnx并在C++中正确推理

image-20230307153402167

image-20230307153657443

image-20230307153756859

image-20230307154026675

image-20230307154108420

image-20230307154302209

image-20230307155126651

封装

public、protected、private的区别

  • public在类内和类外都可以被访问;
  • protected和private只能在类内被访问;
  • protected可以被子类访问,而private不能;

struct和class的区别

  • struct的默认权限为公有;
  • class的默认权限为私有;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class C1
{
int m_A; //默认是私有权限
};

struct C2
{
int m_A; //默认是公共权限
};

int main() {

C1 c1;
c1.m_A = 10; //错误,访问权限是私有

C2 c2;
c2.m_A = 10; //正确,访问权限是公共

system("pause");

return 0;
}

对象的初始化和清理

构造函数和析构函数

  • 我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现;
  • 构造函数和析构函数编译器会自动调用;
  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用;
  • 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作;
  • 构造函数语法:类名(){};
  • 构造函数可以有参数,因此可以发生重载;
  • 析构函数语法:~类名(){};
  • 析构函数不可以有参数,因此不能发生重载;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Person
{
public:
//构造函数
Person()
{
cout << "Person的构造函数调用" << endl;
}
//析构函数
~Person()
{
cout << "Person的析构函数调用" << endl;
}

};

void test01()
{
Person p;
}

int main() {

test01();

system("pause");

return 0;
}

构造函数

构造函数两种分类方式:按参数分为有参构造无参构造;按类型分为有参构造无参构造

1
2
3
4
5
6
7
//构造函数的三种调用方法
//括号法
Person p('张三')
//显示法
Person p = Person('张三');
//隐式转换法
Person p = '张三';

拷贝构造函数(它是一种函数)

1
2
3
4
5
//拷贝构造函数,可以拷贝一个实例化对象的任何内容
Person(const Person& p) {
age = p.age;
cout << "拷贝构造函数!" << endl;
}

拷贝构造函数调用的时机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<iostream>
using namespace std;
class Person {
public:
int p_age;
Person() {
cout << "调用默认构造函数" << endl;
}
Person(int age) {
p_age = age;
cout << "调用有参构造函数" << endl;
}
Person(const Person& p) {
cout << "调用拷贝构造函数" << endl;
}
~Person() {
cout << "调用析构函数" << endl;
}
};
void fun1(Person p){}
Person fun2()
{
Person p;
return p;
}
void test03()
{
Person p1 = fun2();
}
int main()
{
//情况1:使用一个已经创建完毕的对象初始化一个新对象
//Person p1;
//Person p2(10);
//Person p3(p2);
//情况2:值传递的方法给函数传值
//Person p;
//Person p1 = p; //这样也是可以调用拷贝函数
//fun1(p); //这一行和上一行都可以调用拷贝函数,值传递就相当于进行赋值操作
//情况3:以值方式返回局部对象 没有测试成功
//但是当调用fun2()给p1赋值时,应该是调用拷贝函数的;
test03();
return 0;
}

构造函数的调用规则

  • 默认情况下,编译器会默认创建三个函数:构造函数,析构函数,拷贝构造函数;
  • 在有有参构造函数的情况下,编译器不会创建无参构造函数;
  • 在有拷贝构造函数的情况下,编译器不会自动提供构造函数;

深浅拷贝

  • 浅拷贝带来的问题是堆区内存会被重复释放;

image-20230305162512220

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<iostream>
#include<string.h>
using namespace std;
class Student {
public:
string s_name;
int* s_age;
Student(string name, int age)
{
s_name = name;
s_age = new int(age);
}
Student(const Student& p)
{
s_name = p.s_name;
//s_age = p.s_age; //这是浅拷贝,会带来内存重复释放的问题
s_age = new int(*p.s_age); //深拷贝
}
~Student()
{
if (s_age != NULL)
delete s_age;
}
};
int main()
{
Student s1("张三", 18);
Student s2(s1);
cout <<"s1的name:" << s1.s_name << "age:" << *s1.s_age << endl;
cout <<"s2的name:" << s2.s_name << "age:" << *s2.s_age << endl;
return 0;
}

总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。

列表初始化

1
2
3
4
5
6
class Student{
public:
string s_name;
int s_age;
Student(string name, int age):s_name(name),s_age(age){}
};

类对象作为类成员

  • 当类中成员是其他类对象时,我们称该对象为对象成员
  • 构造函数调用顺序:先调用对象成员的构造函数,在调用本类的构造函数;
  • 析构函数的调用顺序跟构造函数的调用顺序相反;

静态成员

  • 静态成员分为:静态成员变量和静态成员函数;
  • 静态成员变量
    • 所有对象共享同一份数据
    • 在编译阶段分配内存
    • 类内声明,类外初始化
  • 静态成团函数
    • 所有对象共享同一个函数
    • 静态成员函数只能访问静态成员变量
  • 静态成员也是有访问权限的;
1
2
3
4
5
6
//静态成员的访问方式
//方式1 通过对象
Person p;
p.name;
//方式2 通过类名
Person::name;

C++模型和this指针

成员变量和成员函数分开存储

在C++中,成员变量和成员函数是分开存储的,只有非静态成员变量才是属于对象的;

this指针的用途

  • 当形参和成员变量同名时,可用this指针来区分;
  • 在类的非静态成员函数中返回对象本身,可使用return *this;
1
2
3
4
5
Person(int age)
{
//1、当形参和成员变量同名时,可用this指针来区分
this->age = age;
}
1
2
3
4
5
6
Person& PersonAddPerson(Person p)
{
this->age += p.age;
//返回对象本身,哪个对象对象调用的这个函数,返回哪个对象上
return *this;
}

空指针访问成员函数

  • 空指针可以访问成员函数,但是当成员函数中有this时就会报错;
  • 为了保持代码的健壮性,可以在成员函数中进行限制;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//空指针访问成员函数
class Person {
public:

void ShowClassName() {
cout << "我是Person类!" << endl;
}

void ShowPerson() {
if (this == NULL) {
return;//如果去掉return,或则把return改成别的就报错了;
}
cout << mAge << endl;
}

public:
int mAge;
};

void test01()
{
Person * p = NULL;
p->ShowClassName(); //空指针,可以调用成员函数
p->ShowPerson(); //但是如果成员函数中用到了this指针,就不可以了
}

int main() {

test01();

system("pause");

return 0;
}

const修饰成员函数

常函数:

  • 成员函数后加const后我们称为这个函数为常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:

  • 声明对象前加const称该对象为常对象;
  • 常对象只能调用常函数;
  • 常对象可以修改加mutable的成员属性的值;

继承

继承的基本语法

class 子类 : 继承方式 父类

继承后的权限和访问权限

clip_image002

  • 继承后的权限是父类中的权限和继承方式中安全性最高的权限为主;
  • 访问权限由父类决定,在父类中是private的权限不可被访问;

继承中构造函数和析构函数的顺序

  • 构造函数的调用顺序是先调用父类构造函数,再调用子类构造函数;
  • 析构函数的调用顺序和构造函数相反;

继承同名成员处理方式

  • 当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数;
  • 如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域;
1
2
s.func();//调用子类
s.Base::func();//调用父类

继承同名静态成员处理方式

和上述方法一致。

多继承语法

class 子类 :继承方式 父类1 , 继承方式 父类2...

多态

虚函数和纯虚函数

1
2
3
4
5
6
7
//虚函数
virtual void speak()
{
cout << "动物在说话" << endl;
}
//纯虚函数
virtual void speak() = 0;

虚函数一般都是没有用的,所哟一般都用纯虚函数;

抽象类

有纯虚函数的类叫抽象类。

切记,有虚函数的类不一定是抽象类,一定要是纯虚函数,有虚函数的类是可以被实例化的。

函数默认参数

1
2
3
int func(int a, int b = 10, int c = 10) {
return a + b + c;
}

函数占位参数

1
2
3
4
5
6
7
8
int func(int a, int) {
return a;
}
int main()
{
int c = 0;
c = func(1,2);
}

函数占位参数可以没有实际意义,但是传参时必须要传。

函数重载的条件

  • 同一作用域下;
  • 函数名必须相同;
  • 函数参数类型不同,个数不同和顺序不同,要满足其一;

函数重载的注意事项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//情况1:引用作为重载条件
void func(int& a)
{
cout << "func (int &a) 调用 " << endl;
}

void func(const int& a)
{
cout << "func (const int &a) 调用 " << endl;
}
int main()
{
int a = 10;
func(10);//调用带const
func(a);//调用无const
}
1
//情况2:带有默认参数作为重载条件,容易产生歧义,应当避免。

  1. C++函数中定义的返回值类型和return的返回值类型不同时,会把return的返回值类型强转为定义的返回值类型,即使定义的是返回的引用,return的是一个普通数值也可以强转,但是如果return的是一个普通数值,定义的是一个指针,这样程序就会报错;
  2. 在函数中return一个值,并不是把这个值返回了,而是重新创建了一个对象,然后把这个值给释放掉;
  3. 调用无参构造不能加括号;
  4. 通过new开辟的空间要用指针接收,int * p = new int(10);

内存分区模型

C++程序在执行时,将内存大方向划分为4个区域

  • 代码区:存放函数体的二进制代码,由操作系统进行管理的(所有的代码都会放在代码区,包括英文字母和中文注释)
  • 全局区:存放全局变量和静态变量以及常量
  • 栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等
  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

内存四区意义:

不同区域存放的数据,赋予不同的生命周期, 给我们更大的灵活编程

程序运行前

在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域

代码区:

存放 CPU 执行的机器指令

代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可

代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令

全局区:

全局变量和静态变量存放在此.

全局区还包含了常量区, 字符串常量和其他常量也存放在此.

==该区域的数据在程序结束后由操作系统释放==.

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//全局变量
int g_a = 10;
int g_b = 10;

//全局常量
const int c_g_a = 10;
const int c_g_b = 10;

int main() {

//局部变量
int a = 10;
int b = 10;

//打印地址
cout << "局部变量a地址为: " << (int)&a << endl;
cout << "局部变量b地址为: " << (int)&b << endl;

cout << "全局变量g_a地址为: " << (int)&g_a << endl;
cout << "全局变量g_b地址为: " << (int)&g_b << endl;

//静态变量
static int s_a = 10;
static int s_b = 10;

cout << "静态变量s_a地址为: " << (int)&s_a << endl;
cout << "静态变量s_b地址为: " << (int)&s_b << endl;

cout << "字符串常量地址为: " << (int)&"hello world" << endl;
cout << "字符串常量地址为: " << (int)&"hello world1" << endl;

cout << "全局常量c_g_a地址为: " << (int)&c_g_a << endl;
cout << "全局常量c_g_b地址为: " << (int)&c_g_b << endl;

const int c_l_a = 10;
const int c_l_b = 10;
cout << "局部常量c_l_a地址为: " << (int)&c_l_a << endl;
cout << "局部常量c_l_b地址为: " << (int)&c_l_b << endl;

system("pause");

return 0;
}

打印结果:

1545017602518

总结:

  • C++中在程序运行前分为全局区和代码区
  • 代码区特点是共享和只读
  • 全局区中存放全局变量、静态变量、常量
  • 常量区中存放 const修饰的全局常量 和 字符串常量

程序运行后

栈区:

由编译器自动分配释放, 存放函数的参数值,局部变量等

注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int * func()
{
int a = 10;
return &a;
}

int main() {

int *p = func();

cout << *p << endl;
cout << *p << endl;

system("pause");

return 0;
}

堆区:

由程序员分配释放,若程序员不释放,程序结束时由操作系统回收

在C++中主要利用new在堆区开辟内存

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int* func()
{
int* a = new int(10);
return a;
}

int main() {

int *p = func();

cout << *p << endl;
cout << *p << endl;

system("pause");

return 0;
}

总结:

堆区数据由程序员管理开辟和释放

堆区数据利用new关键字进行开辟内存

1.3 new操作符

C++中利用==new==操作符在堆区开辟数据

堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符 ==delete==

语法:new 数据类型

利用new创建的数据,会返回该数据对应的类型的指针

示例1: 基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int* func()
{
int* a = new int(10);
return a;
}

int main() {

int *p = func();

cout << *p << endl;
cout << *p << endl;

//利用delete释放堆区数据
delete p;

//cout << *p << endl; //报错,释放的空间不可访问

system("pause");

return 0;
}

示例2:开辟数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//堆区开辟数组
int main() {

int* arr = new int[10];

for (int i = 0; i < 10; i++)
{
arr[i] = i + 100;
}

for (int i = 0; i < 10; i++)
{
cout << arr[i] << endl;
}
//释放数组 delete 后加 []
delete[] arr;

system("pause");

return 0;
}

  1. 结构体的定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include<iostream>
    #include<string>
    using namespace std;
    struct student //结构体定义
    {
    string name;
    int age;
    }std2,std3; //std2,std3相当于提前声明的结构体变量,这是可选的,可以不声明,也可以声明一个或多个
    int main()
    {
    struct student std1 = { "张三",18 }; //初始化赋值
    cout << std1.name << endl;
    std2.name = "李四"; // 结构体用.去赋值或取值
    std2.age = 19;
    std3.name = "王五";
    std3.age = 20;
    cout << std2.name << endl;
    cout << std3.name << endl;
    return 0;
    }
  2. 结构体数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include<iostream>
    #include<string>
    using namespace std;
    struct student
    {
    string name;
    int age;
    };
    int main()
    {
    struct student stds[] = {
    {"张三", 18},
    {"李四", 19},
    {"王五", 20}
    };
    cout << stds[0].name << endl;
    return 0;
    }
  3. 结构体指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include<iostream>
    #include<string>
    using namespace std;
    struct student
    {
    string name;
    int age;
    };
    int main()
    {
    struct student stds = { "张三", 18 };
    struct student* p = &stds;
    cout << p << endl;
    cout << p->name << endl; //对于结构体指针,通过->去取值
    return 0;
    }
  4. 结构体嵌套结构体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    //学生结构体定义
    struct student
    {
    //成员列表
    string name; //姓名
    int age; //年龄
    int score; //分数
    };

    //教师结构体定义
    struct teacher
    {
    //成员列表
    int id; //职工编号
    string name; //教师姓名
    int age; //教师年龄
    struct student stu; //子结构体 学生
    };


    int main() {

    struct teacher t1;
    t1.id = 10000;
    t1.name = "老王";
    t1.age = 40;

    t1.stu.name = "张三";
    t1.stu.age = 18;
    t1.stu.score = 100;

    cout << "教师 职工编号: " << t1.id << " 姓名: " << t1.name << " 年龄: " << t1.age << endl;

    cout << "辅导学员 姓名: " << t1.stu.name << " 年龄:" << t1.stu.age << " 考试分数: " << t1.stu.score << endl;

    system("pause");

    return 0;
    }
  5. 结构体做函数传参

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    //学生结构体定义
    struct student
    {
    //成员列表
    string name; //姓名
    int age; //年龄
    int score; //分数
    };

    //值传递
    void printStudent(student stu )
    {
    stu.age = 28;
    cout << "子函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl;
    }

    //地址传递
    void printStudent2(student *stu)
    {
    stu->age = 28;
    cout << "子函数中 姓名:" << stu->name << " 年龄: " << stu->age << " 分数:" << stu->score << endl;
    }

    int main() {

    student stu = { "张三",18,100};
    //值传递
    printStudent(stu);
    cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl;

    cout << endl;

    //地址传递
    printStudent2(&stu);
    cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl;

    system("pause");

    return 0;
    }
  6. 结构体中const使用场景

    可以用const来防止误操作;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    //学生结构体定义
    struct student
    {
    //成员列表
    string name; //姓名
    int age; //年龄
    int score; //分数
    };

    //const使用场景
    void printStudent(const student *stu) //加const防止函数体中的误操作
    {
    //stu->age = 100; //操作失败,因为加了const修饰
    cout << "姓名:" << stu->name << " 年龄:" << stu->age << " 分数:" << stu->score << endl;

    }

    int main() {

    student stu = { "张三",18,100 };

    printStudent(&stu);

    system("pause");

    return 0;
    }