工作中常用的100个知识点
1. Permission deniedGit解决Permission denied, please try again问题_git permission denied, please try again.-CSDN博客
cd ~/.ssh/ 回车,进入.ssh路径下;
接下来在.ssh路径下配置全局的name和email,输入以下命令:
git config --global user.name “xxxxx”
git config --global user.email “xxxxx@xx.com”
ssh-keygen -t rsa -C “xxxx@xxxx.com”
就生成了两个文件:id_rsa和id_rsa.pub
在SSH Keys页面里,在页面的右上角点击添加add SSH keys,将之前生成的两个文件中的id_rsa.pub文件里的内容粘贴到key文本框里
2. conda 会安装对应的依赖深度学习基础——pip和conda - 知乎
Pip install 和Conda install 的区别和使用场景_conda install和pip install-CSDN博客
Pip 只能安装 python 语言写的包,而 conda 可以安装任何语言写的包
sudo apt-get install python3-opencv
# or
conda install -n your-env-name opencv
3. 没有CXXABI1.3.8CXXABI_1.3.8 not found-腾讯云开发者社区-腾讯云
strings /usr/lib64/libstdc++.so.6|grep CXXABI
4. opencv版本
5.pip freeze >requirements.txt,conda list也可以看到当前环境的安装包
conda create -n demo python==3.10.8
conda activate your_env_name
pip install -r requirements.txt #根据文件类安装包
conda deactivate
6. torchrun命令进行容错处理Pytorch 多卡并行(2)—— 使用 torchrun 进行容错处理_51CTO博客_pytorch多gpu并行
7. vim 大写G跳转到最后一行。
8.利用卷积实现ssim计算。https://github.com/Po-Hsun-Su/pytorch-ssim/blob/master/pytorch_ssim/__init__.py
类似于协方差
9. 把laplace,sobel,
roberts图像分析:边缘检测中的梯度算子 - 简书
sobel
laplace
10.torch.nn.functional.interpolate(
基本等效于opencv中的resize,可以选择放大系数,也可以指定放大到的尺寸。
但是需要注意的是tensor中的尺寸一般是四维,NCHW,interpolate也只是对后面的两个维度操作。
此外还有一个align_corners参数:F.interpolate——数组采样操作-CSDN博客
11. state_dict,保存模型结构信息,比如卷积权重和偏置。optimizer对象也会有它自己的state_dict
【PyTorch技巧1】详解pytorch中的state_dict - 知乎
比如conv1这个结构,
self.conv1 = nn.Conv2d(3, 6, 5)
输入通道3,输出通道6,那么就意味着有6个卷积,每个卷积有三个通道,尺寸是(5,5).
不光是模型,模型训练相关的配置文件parse也可以以字典的形式,使用torch.save进行保存
torch.save(parse.__dict__,"./xxx.pkl")
12. pkl和pth
本质没什么区别,主要看保存的时候是否连同模型结构一起保存了,还是说只保存了参数。
torch.save(model.state_dict(), 'mymodel.pth')
torch.save(model, mymodel.pth)
只保存了参数便于灵活加载部分参数。如果只保存了参数,就要先实例化一个模型,然后再load参数。load参数后再用load_state_dict加载参数:PyTorch框架训练的几种模型区别_.pth.tar-CSDN博客
import torch
model = MyModel() # init your model class, build the graph shape
state_dict = torch.load('model_state_dict.pth') // 先load字典
model.load_state_dict(state_dict,strict=True) // 再更新到模型中,要求模型和参数严格对应
torch.save(model.state_dict(), 'model_state_dict1.pth')
13.optimizer = torch.optim.Adam(model.parameters(), lr=args.lr, weight_decay=args.weight_decay)
14.lsb_release -a,可以看到linux的发型版本是centos,使用yum安装,而不上apt-get
15. docker run --gpus all 时提示unknown flags,docker -v查看版本,发现clinet版本是17.12.1-ce,server版本是24.0.5,然后sudo apt-get install docker-ce安装最新的社区版,这时两个版本都是24.0.2,这时就可以在docker中使用gpu了
16.服务器往本机传输数据。先在本机上开启ssh服务。
sudo apt-get install openssh-server
sudo service ssh start
在本机上测试ssh 可以连接本机,
然后在服务器上使用scp传输到本机。
17. nohup command > output.log 2>&1 &
其中 2>&1是用来将标准错误2重定向到标准输出1中。1前面的&是为了让bash将1解释成标准输出而不是文件1。而最后一个&是为了让bash在后台执行。
18.把已有的conda虚拟环境打包成docker
将conda环境打包成docker步骤_将conda环境打包成docker镜像-CSDN博客
19.make命令和makefile
【Linux】 自动化编译工具make_make编译-CSDN博客
makefile文件使得文件之间的依赖关系更清楚,使用make编译时可以高效地重新编译改动的文件。
make -j8会以8个线程编译,make会在当前目录下找名字叫“Makefile”或“makefile”。
依赖关系以冒号的形式指定,依赖关系的下一行tab之后通常就是调用gcc的依赖方法,是真正要执行的动作。
Android底层开发(4)_android底层开发所需资源-CSDN博客
https://www.cnblogs.com/fog2012/p/5849207.html
如果是使用NDK编译,需要android.mk文件,这个本质也是makefile文件?
分析一个最简单的Android.mk;
LOCAL_PATH := $(call my-dir) # 定义了当前模块的相对路径;
include $(CLEAR_VARS) # 清空当前环境变量
LOCAL_MODULE := test # 编译生成的目标名称
LOCAL_SRC_FILES :=test.c # 编译该模块需要的源文件
include$(BUILD_EXECUTABLE) # 编译所生成的目标文件格式
makefile可能包含多个目标,如target1,target2,make target1可以指定只编译target1.
20.Python中subprocess和os.system
Python中subprocess和os.system的差异及用途_Python 笔记_设计学院
后者是系统自带的,一般只原来实现文件删除等操作。
21. subprocess.run时遇到"("unexpected,因为转义没有识别,可以在shell脚本指定解释器为base:#!/bin/bash,也可以通过replace把(替换为\(,)替换为\)
22. 计算直方图,hist = cv2.calcHist([img],[0],None,[16],[0,255]),注意第一个参数必需是[]括起来,因为第二个参数是第一个参数的索引,当直接传入img作为第一个通道时,直方图计算的是第一行的统计。可以np.sum(hist)计算直方图的总数。
23. python中使用字符串string,前面会有r或者f作为前缀,前者表示不会被转义,后者表明是格式化字符串,这样的字符串里可以使用{}直接嵌入值。
python 字符串前加f、r、b、u 分别代表什么?_python路径前面加f-CSDN博客
24.字典的键和集合的元素必须是不可变的(hashable)类型,因为它们需要保持唯一性和不变性。列表是可变的,因此不能被哈希(即不能保证其唯一性和不变性),所以不能用作字典键或集合元素。否则会遇到TypeError: unhashable type: 'list'
25.collections模块常用类型有:默认字典(defaultdict),双向队列(deque),有序字典(OrderedDict)。defaultdict和dict比,会为一个不存在的键提供默认值,从而避免KeyError异常。如果使用dict,,就要额外用一个表来判断字典里是否有这个key。
26.str被sorted之后,返回的是字符组成的list
eat ['a', 'e', 't'] tea ['a', 'e', 't'] tan ['a', 'n', 't'] ate ['a', 'e', 't']
27. 多线程操作时,文件夹最好每个进程使用自己的。os.getpid()得到进程号
28.Python字符串index()函数将引发ValueError,而find()函数将返回-1。 这是这些功能之间的唯一区别。
s ='abcd1234dcba'
print(s.index('a'))
print(s.index('cd'))
print(s.index('1', 0, 5))
29.rgb到bgr,就是最后一个维度的逆序,所以可以得到bgr=rgb[:,:,::-1]
30.python函数中的*,**分别将参数解包成元组和字典。*号的作用在于收集参数或者分配参数
31. 把字典写入txt时,使用for循环,把每一个item按照冒号写成一行:
for key,value in dict.items():
f.write(f"{key}:{value}")
32. 读取图像imread和imdecode,使用imdecode的优点在于可以从内存中直接解码图像数据,而不需要将数据保存到磁盘再进行读取。在某些场景中是有用的,如网络传输的数据源,摄像头捕获的图像数据。
33. lmdb读取图像得到的是rgb通道的数据?和opencv读出来的bgr不一样?
34.头文件循环包含
35.
position = s.find("jk"); 13 if (position != s.npos)
36. lmdb写入时的key和val都需要是baye类型,所以需要使用encode().
import lmdb
env = lmdb.open("./students") # 打开数据库
txn = env.begin(write=True) # 开启事务
txn.put(str(1).encode(), "xiaoming".encode()) # put 可以写入数据,也可以修改数据
txn.put(str(2).encode(), "xiaogang".encode())
txn.put(str(3).encode(), "xiaohong".encode())
txn.commit() # 提交更改
env.close()
37.python输出固定长度的整数
{:05}.format(42)--->00042
"%05d" % number
38.在JSON文件中添加注释通常不被推荐,因为JSON格式本身不支持注释。
39.img = np.rot90(img, k=n) # n=0,1,2,3,... 即旋转0,90,180,270,正数代表逆时针旋转,负数代表顺时针旋转
import cv2
import numpy as np
img = cv2.imread(image_path)
img = np.rot90(img, k=n) # n=0,1,2,3,... 即旋转0,90,180,270,
m = np.arange(8).reshape((2,2,2))
np.rot90(m, 1, (1,2))
#array([[[1, 3],
# [0, 2]],
# [[5, 7],
# [4, 6]]])
40.在 PyTorch 中,ConcatDataset
是一个用来组合多个数据集的工具,把多个数据集合并在一起。因为dataset主要的几个函数是get_item,get_len等,其实就是重写了这几个函数。要注意几个数据集需要是相同结构的,如图像尺寸,如果不一致的话就要进行数据预处理,或者是在get_item的时候进行处理。
41.np.arange([start, ]stop, [step, ]dtype=None)
42. which python或者whereis python,得到解释器的路径
43. ubunti终端中:docker search ubuntu2就可以搜索到ubuntu20的相关docker,选择一个,执行:docker pull xxxxx就可以安装成功,这时候docker images就可以看到了。
docker pull 命令来载入 ubuntu 镜像
docker run -it --gpus all xxxx,找不到gpu:
nvidia-container-cli: initialization error:loadlibrary failed: libnvidia-ml.so.1: cannot open shared object file: no such file or director
首先, 需要确保你的docker是使用apt安装的
输入sudo systemctl status docker.
如果是apt安装的会显示docker的详细信息, 如果是snap安装的会报错
docker load -i xxx.tar
docker import xx.tar xx:xxx // 加载的同时指定镜像名和版本号
docker tar xx xx:xxx // 更改原来的镜像名和版本号
44.np.prod(),计算数组中所有数字的乘积。
45. tensor和array都是共享内存的,b=a这样的赋值,改变其中一个会y影响另外一个。
a.numpy()把tensor转换为array,内存仍然是共享的;使用b = torch.from_numpy(a),内存也是共享的。
46.学习率和batsize有线性相关,同比例设置,和模型大小无关?常见的学习率是分段的,余弦退火不需要设置参数;CV算法工程师面试 高频基础知识点-CSDN博客
47.getattr()
是 Python 内置的一个函数,可以用来获取一个对象的属性值或方法。相当于返回那个值或者调用那个函数。
48.docker ps查看运行中的容器
49. sudo su切换到超级用户身份
50.nvidia-smi -pm ENABLED
export PATH=/home/yan/share/usr/local/arm/3.4.1/bin:$PATH
source .bashrc
docker -ce
docker commit命令的使用-CSDN博客
docker build -t xxx.
sudo systemctl restart docker
51.https://www.cnblogs.com/shine-lee
52.Python3二分查找库函数bisect(),二分查找要求序列是有序的。bisect.bisect_right(lut_list,i)
53.np.var(arr)算出来的方差是MSE的公式算出来的,是高斯分布下的最大似然估计,为了无偏,需要修正分母,np.var(arr,ddof=1),分母就会减1.What is the difference between numpy var() and statistics variance() in python? - Stack Overflow
54.rgb2hsv,要注意取值范围rgb应该是0~255的。必需乘完awb之后再转换颜色空间,因为转换的过程中会涉及到rgb相对大小。
55.https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering
56.leakey relu比relu更耗时,而relu因为梯度可能为0,方向传播造成神经元失活,效果可能会差。leakey relu的负数部分的斜率一般为0.01,即两段斜率是1:100
relu的实现就是一个max函数。
57.batch normaliztion有scale and shift两个可学习参数。这两个参数平衡了两个极端,即原始分布和标准方差分布,有利于权重和分布相互协调,增强了网络的表达能力。否则后面一层的分布要依赖于前面所学习的参数。
预测阶段的均值和方差使用收敛阶段的mini-batch的统计值。
Batch Normalization(BN)超详细解析_batchnorm在预测阶段需要计算吗-CSDN博客
58.self attention中QKV都来源于同一个数据集,那么对于encoder-decoder attention是encoder-decoder之间的,
59.处理JSON文件写入换行问题,
# 将学生信息写入文件
with open('data.json', 'w') as file:
json.dump(student_info, file, indent=4)
60.x = np.full(shape=[3, 3], fill_value=2)
x = np.ones([3, 3]) * 2
61. 绘制棋盘格,要遍历一遍,可以以(x+y)%2==0判断是黑色还是白色。两个反色的棋盘格图像的ssim是-1.
62. torch.bmm(tensor_a, tensor_b),bmm 是 batched matrix multiple 的简写,即批量矩阵乘法
63. 细节增强,将blur前后的差加在原始图像上。图像增强:多尺度的图像细节提升(multi-scale detail boosting)实现方法_多尺度增强-CSDN博客
借鉴了多层的思路,并使用sign函数减少了异常情况。这里的异常情况是,左黑右白的图减去blur之后的diff是左负右正,直接加diff的话会导致灰度饱和,所以就把diff大于0的地方的权重降低,把黑色更黑?
64. 符号函数的实现,就是判断正负。可以借鉴补码的首位就表示了正负,对int型向右移动31位。
基于bit操作https://www.cnblogs.com/xyd-/p/14641948.html
65. 使用docker训练的时候,报错bus error,out of shared memory。使用shm查看容器共享内存的大小,docker run的时候加上--shm-size 8G或者--ipc=host,后者是让容器与主机之间共享内存
66. docker: Error response from daemon: No command specified.解决docker run报错:Error response from daemon: No command specified.-CSDN博客
67.可以在行尾使用反斜杠 \
来表示代码行的延续
68. docker run /bin/bash -c "echo Hello && echo world"可以在创建容器后进入执行指令。-c是把多个指令合并成一个字符串,使用bash来执行。
69.
clang-format 可用于格式化(排版)多种不同语言的代码, 其自带的排版格式主要有: LLVM, Google, Chromium, Mozilla, WebKit。但是自带的这几种排版格式可能并不满足个人编码习惯的全部要求, clang-format 也提供了使用自定义排版格式的功能。Ubuntu 上可以使用以下命令导出上述 5 种自带的排版格式, 并且可以对其中某一个导出的文件进行修改, 实现自定义格式化:
clang-format -style=格式名 -dump-config > 文件名
其中, 格式名的取值可以为 llvm, google, chromium, mozilla, webkit 中的任一种; 文件名可以取任何名字, 一般取.clang-format 或_clang-format, 因为自定义的排版格式文件只有取这两种名字之一, 才能被 clang-format 识别。
70.
clang --version版本对应不上,则会报错,unknown key 'AfterCasesLabel'
sudo apt-get install clang-10遇到了依赖问题,the following packages have unmet dependencies
ubuntu - How do I resolve `The following packages have unmet dependencies` - Stack Overflow
install的时候直接-f也无法解决,尝试apt-get替换为aptitude,没有这个指令,先apt-get install aptitude,aptitude会自动解决依赖问题,只要输入y确认就可以了。
成功安装clang-format-10,位置在/usr/bin/下
为了直接在命令行中使用clang-format,使用软连接替换:
link /usr/bin/clang-format-13 /usr/bin/clang-format
71.torch.save(module.state_dict(),"./state_dict.pkl")
torch.save:将一个序列化的对象保存到磁盘; torch.load:将 pickled 对象文件反序列化到内存
state_dict 是一个 Python dictionary object
注意:只有含有可学习参数的层 (convolutional layers, linear layers),或者含有 registered buffers 的层 (batchnorm's running_mean) 才有 state_dict。
PyTorch 83. PyTorch 模型的保存与加载 - 知乎
72. __dict__是大部分python对象的一种属性,就是一个字典,然后可以继续使用torch.save存储成文件。torch.load再恢复成dict。
73. dict转换为Namespace
import argparse
ns = argparse.Namespace(**dic)
dic = vars(ns) # namespace转换为字典【python】dict 和 Namespace 之间的转换_dict 转namespace-CSDN博客
74. 改写windows中的bat文件,在ubuntu中出错。因为换行符不一样。在vscode中右下角选择LF/CRLF
75.pwd
是“print working directory”的缩写,意为打印当前工作目录。
76.RuntimeError: CUDA out of memory,RuntimeError: CUDA out of memory. Tried to allocate 144.00 MiB (GPU 0; 2.00 GiB total capacity; 1.29 GiB already allocated; 79.00 MiB free; 1.30 GiB reserved in total by PyTorch)
对应方法有清空缓存torch.cuda.empty_cache(),
减小batchsize,
指定其他卡os.environ['CUDA_VISIBLE_DEVICES'] = '1',
修改最大碎片参数os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:256'。
77. 下载人造数据集,Home,Unreal Dataset | ISARLab
ISAR得到的数据集格式是bag文件,包含了IMU,GPS,双目图像等
参考这个脚本进行解析。解析rosbag文件,得到带有时间戳的.jpg图片数据和.pcd点云数据_ros接收点云的时间戳-CSDN博客
安装rosbag,pip3 install bagpy,
注意topic要选对,否则会报错没有encoding。topic通过下面命令得到:
rosbag info *.bag //*为你自己的数据包的名称
78. 计算模型结构参数量,可以使用summarytorch-summary · PyPI
会得到参数个数和对应所需要的内存。因为每个参数是float,需要占4个Byte。
可以得到每一层的参数量。但是有的模型结构不支持,如MIRnet使用了列表而不是concate,这时可以使用
total_params = sum(p.numel() for p in model.parameters())
print("Total parameters:", total_params)
79.使用rename批量修改文件名:Ubuntu中rename命令和批量重命名_ubuntu rename-CSDN博客
rename -n 's/^/test_/' *.jpg
解释:
s-替换
^-在文件名称开头加字符,“^”开头;以“$”结尾
test_-将名称前面添加上test_
-n表示只是模拟运行,会把重命名前后的文件名都打印出来
rename 's/.png/xx.png/' *.png // 把所有png结尾的替换为xx.png结尾
rename 's/ /_/g' * // 把文件名中的空格替换为下划线
80.
在大多数Unix和Linux shell环境中,可以使用上下箭头键来浏览输入的命令历史记录。此外,还可以使用以下命令查看和搜索历史命令:
-
history
:显示所有历史命令列表。 -
history | grep 搜索词
:搜索与“搜索词”相关的命令历史
81.
random_array = np.random.randn(3, 4) # float64
random_array = torch.randn((3, 4)).numpy() #随机生成0~1浮点数构成的二维数组,float32
82.pycharm或者idea中Run/Debug Python项目报错 Argument for @NotNull parameter ‘module‘ of …
删除项目根目录的 idea 文件夹
随后重启,重新配置即可
83. rgb向raw退化,可以提前用list存好一个uint的通道情况,如[0,0,1,1, 0,0,1,1, 1,1,2,2,1,1,2,2,]
遍历时使用enumrate,返回是i取整除结果和余数得到坐标,返回的x表示通道情况:
for i,x in enumrate(list):
raw[j//4::4,j%4::4]=rgb[j//4::4,j%4::4,x]
84. export ANDROID_NDK_PATH=""
printenv查看所有环境变量
echo $ANDROID_NDK_PATH查看指定环境变量
85. linux中运行bash脚本,报错$'\r':command not found. 这是因为此脚本直接复制自windows下的脚本,换行有差异。可以使用命令去除\r:
sed -i 's/\r//' one-more.sh
86. 打开开发者选项,可以继续打开显示触控和坐标位置,或者使用命令:
adb shell getevent -p | grep -e "0035" -e "0036" #获取事件体系里的宽高
adb shell getevent | grep -e "0035" -e "0036" #获取点击的事件坐标
adb shell input tap <x> <y> # 点击屏幕指定位置
adb shell input keyevent 26 # 按电源键锁屏/解锁
adb shell input swipe 500 1500 500 500 500 # 滑动解锁
但adb模拟点击的坐标貌似adb捕捉不到坐标。截图后图像的坐标和点击的坐标是一致的。
要获得adb shell的返回值,可以使用subprocess来替换os.system()
command=adb shell sumpsys window policy |grep screenState
subprocess.sun(command,shell=True,capture_output=True,Text=True)
87. 给定一个有序lits和一个数字,找到离这个数最近的索引:
np.argmin(np.ads(list-a))
88.对list排序,list.sort(),没有返回值
89. 除了mipi格式都是unpacked的格式,但是要区分大端还是小端。大端(Big-Endian)和小端(Little-Endian)是以字节为单位。这时有可能要交换位置。
使用Image.frombytes按字节读取数据。切片得到奇数和偶数位置的数据。对second_byte左移之后和first_byte按位求或。
raw16bit = np.zeros([height, width])
file = open(rawPath, 'rb')
rawdata = file.read()
file.close()
stride = (width * 2)
rawdata = Image.frombytes('L', (int(stride), int(height)), rawdata)
rawdata = np.asarray(rawdata)
data_per_line = int(width * 2)
first_byte = np.uint16(rawdata[:, 0:data_per_line:2])
second_byte = np.uint16(rawdata[:, 1:data_per_line:2])
# get 10bit data
first_pixel = np.bitwise_or(np.left_shift(second_byte, 8), first_byte)
raw16bit[:, 0:width] = first_pixel
90.cv2 interpolate插值-align_corners
91. windows下cd到其他盘符:
直接cd d:不管用,应该cd /d d:或者直接d:
92.Windows Docker 安装 | 菜鸟教程
Docker 并非是一个通用的容器工具,它依赖于已存在并运行的 Linux 内核环境。
Docker Desktop 是 Docker 在 Windows 10 和 macOS 操作系统上的官方安装方式,这个方法依然属于先在虚拟机中安装 Linux 然后再安装 Docker 的方法。
93.
Shell 既是一种命令语言,又是一种程序设计语言。
sh(Bourne Shell)是一个早期的重要shell,1978年由史蒂夫·伯恩编写,并同Version 7 Unix一起发布。
bash(Bourne-Again Shell)是一个为GNU计划编写的Unix shell。1987年由布莱恩·福克斯创造。主要目标是与POSIX标准保持一致,同时兼顾对sh的兼容,是各种Linux发行版标准配置的Shell,在Linux系统上/bin/sh往往是指向/bin/bash的符号链接。
#!是一个特殊标记,说明这是一个可执行的脚本。除了第一行,其他以#开头的都不再生效,为注释。
94.linux上执行os.system(bash ./a.sh),但是在windows上会出错,因为没有bash。可以统一使用sub。subprocess,把.sh改成.bat文件。
95. c语言中没有string,所以通过string的成员函数.c_str()转换为char*,这样就能使用c函数如sprintf,否则报错cannot pass object non-trivial type
96.vim中复制
-
按
v
进入 Visual Mode (按V
为 Visual Line Mode,可以选择整行) -
使用方向键选中需要复制的文本
-
按
y
复制选中的文本。
97. python中的list完全可以直接充当stack。由list可以再包装成stack类:
python 栈的简单实现-阿里云开发者社区
98.Python的`heapq`模块提供了优先队列(Priority Queue)的实现。这个模块使用堆(一种特殊的完全二叉树)来实现优先队列,因此它也被称为堆队列。在`heapq`模块中,最小的元素具有最高的优先级,最小的元素始终位于根节点 heap[0]
。https://docs.python.org/zh-cn/3/library/heapq.html
那么堆排序就是heappush把所有数字放到优先级队列之后,再不停地heappop
入队的对象还可以是可以为元组。元组的第一个元素用于排序。
99。字典用来计数。Counter 是 dict 的子类
cnt = Counter() >>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']: ... cnt[word] += 1
100.Deque 队列是对栈或 queue 队列的泛化(该名称的发音为 "deck",是 "double-ended queue" 的简写形式)。一旦限定长度的deque满了,当新项加入时,同样数量的项就从另一端弹出。
appendleft(x) #添加 x 到左端。 popleft()
append(x) #添加 x 到右端。 pop()