商汤科技实习生准备@TOC
面试准备
- 技术一面(1小时):自我介绍(关于项目)+关于项目的细节+技术概念+手撕代码
- 技术二面(1小时):自我介绍(关于项目)+介绍一下发表的论文所做的工作+技术概念+深度学习基础+手撕代码+问数理基础
- 技术三面(1小时):自我介绍,论文和项目
- 四面(45分钟)介绍实习项目,介绍论文相关,
简历准备
- 中文
- 荣誉/奖项
- 研究经历
- 技能
- 其他
- 自我介绍准备
面试准备
- Python中tuple,list,dict的介绍,hash的介绍,局部变量和全局变量,Python中list的内存超出了是怎么做的?
- TensorFlow 中转置函数的实现是怎么做的,是否用了新的内存空间?
- Git版本回退命令
- C++ 的map
- 写一个 Person 类,再继承出男人类和女人类
- 手写kmeans
- 手写个简单的resnet
- Batch Normalization 怎么实现?
- 关于BN层。可学习参数,BN层的作用,在训练阶段和预测阶段的有什么不同,了解GN吗?
- 定图像尺寸,卷积size,stride,padding,求卷积后的size;1x1卷积核的作用
- 最大子数组和
- Top K (2) 2n+1个数 找1;2n+2个数,找2
- 给定两个字符串str1和str2,再给定三个整数ic,dc,rc,分别代表插入、删除、替换一个字符的代价,返回将str1编辑成str2的最小代价。
- 图的遍历(DFS BFS自选)
- 编程: 对一个灰度图进行均值滤波(卷积)操作,可以使用numpy,要求输入输出的图片和输入的图片的shape保持一致。
滤波的目的:消除噪声,提取特征
均值滤波:平滑,模糊
中值滤波:消除椒盐噪声
高斯滤波:消除高斯噪声
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
kenel=np.array([
[1,0,1],
[0,1,0],
[1,0,1]
])
# img=Image.open('C:/Users/Kaiser/Desktop/wlop1.jpg')
# plt.imshow(img)
# plt.show()
# img=np.array(img)
img=np.array([
[1,1,1,0,0],
[0,1,1,1,0],
[0,0,1,1,1],
[0,0,1,1,0],
[0,1,1,0,0]
])
def conv(data,kenel):
h,w=data.shape
k=kenel.shape[0]
conv_height=h-k+1
conv_weight=w-k+1
conv=np.zeros((conv_height,conv_weight),dtype=np.float64)
for i in range(conv_height):
for j in range(conv_weight):
conv[i][j]=vaildpixel(np.sum(data[i:i+k,j:j+k]*kenel)/kenel.size)
return conv
img_new=conv(img,kenel)
print(img_new)
#plt.imshow(img_new)
#plt.show()
- 编程: 写一个链表的快速排序,自定义结点。
class NodeList:
def __init__(self,val):
self.val=val
self.next=None
def sort_nodeList_quicksort(head):
'''sort a nodelist by quicksort method
Args:
head: the head node of the nodelist
return:
the head node of the sorted nodelist
'''
if not head:
return None
newhead = NodeList(-1)
newhead.next=head
return quicksort_fornodelist(newhead,None).next
def quicksort_fornodelist(head,end):
'''用于链表快速排序的递归函数
参数:
head:排序列表头节点
end:排序列表尾节点
返回值:
排序完成链表头节点
'''
if head == end or head.next == end or head.next.next == end:
return head
temp_node_head = NodeList(-1)
temp_node=temp_node_head
current_node = head.next
partition_node = head.next
#小于轴值节点的节点接在temp_node后
while current_node.next != end:
next_node=current_node.next
if next_node.val < partition_node.val:
current_node.next = next_node.next
temp_node.next = next_node
temp_node = temp_node.next
else:
current_node = current_node.next
#print_nodelist(partition_node)
#print_nodelist(temp_node_head)
#合并temp链表和原链表
temp_node.next = head.next
head.next = temp_node_head.next
quicksort_fornodelist(head,partition_node)
quicksort_fornodelist(partition_node,end)
return head
def build_nodelist(num_list):
'''创建一个链表,值为num_list中元素值
参数:
num_list:新链表中的元素值列表
返回:
新建链表的头节点
'''
if not num_list:
return None
head = NodeList(num_list[0])
current_node = head
for i in range(1,len(num_list)):
temp_node = NodeList(num_list[i])
current_node.next = temp_node
current_node=current_node.next
return head
def print_nodelist(head):
'''从头到尾打印链表
参数:
head:链表的头节点
返回:
无返回值,直接调用print函数输出
'''
res = []
current_node = head
while current_node:
res.append(current_node.val)
current_node = current_node.next
print(res)
return
num_list = [5,4,3,2,1]
head = build_nodelist(num_list)
print_nodelist(head)
print_nodelist(sort_nodeList_quicksort(head))
- 单链表的翻转
def reverse_nodelist(head):
'''翻转输入链表
参数:
要翻转链表的头节点
返回:
翻转完成链表的头节点
'''
if not head:
return None
pre_node = None
current_node = head
while current_node:
next_node=current_node.next
current_node.next=pre_node
pre_node=current_node
current_node=next_node
return pre_node
- 之字形打印二叉树
class TreeNode:
def __init__(self,val):
self.val=val
self.left=None
self.right=None
def print_binarytree_zhizixing(root):
'''之字形打印二叉树
参数:
树的根节点
返回值:
按之字形顺序排列的节点值数组
'''
if not root:
return None
is_odd = False
queue = [root]
result_ary = []
while queue:
layer_output = []
next_queue = []
if is_odd:
for i in range(len(queue)):
layer_output.append(queue[i].val)
else:
for i in range(len(queue)-1,-1,-1):
layer_output.append(queue[i].val)
for node in queue:
if node.left:
next_queue.append(node.left)
if node.right:
next_queue.append(node.right)
is_odd= not is_odd
queue = next_queue
result_ary.append(layer_output)
return result_ary
def build_binarytree(num_list,treenode_index):
'''按照num_list中的值建立新的二叉树
输入:
num_list:按照层序遍历的次序,存放二叉树节点的值,空节点使用None代替
treenode_index:建立树节点所对应的值在数组中的索引
返回值:
root,新建树的根节点
'''
if not num_list:
return None
if treenode_index >= len(num_list):
return
if not num_list[treenode_index]:
return
else:
root=TreeNode(num_list[treenode_index])
root.left = build_binarytree(num_list,2 * treenode_index + 1)
root.right = build_binarytree(num_list,2 * treenode_index + 2)
return root
num_list=list(range(1,16))
root=build_binarytree(num_list,0)
print(print_binarytree_zhizixing(root))
- 快速排序
void quick_sort(int s[], int l, int r)
{
if (l < r)
{
//Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1
int i = l, j = r, x = s[l];
while (i < j)
{
while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
j--;
s[i] = s[j];
while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
i++;
s[j] = s[i];
}
s[i] = x;
quick_sort(s, l, i - 1); // 递归调用
quick_sort(s, i + 1, r);
}
}
- 将字符串转换成整数
#include<iostream>
#include<string>
#include<limits>
using namespace std;
Bool Input = FALSE;
/************************************************
/* 将数字字符串转换成对应的整数
/************************************************/
int strToInt(const char* str)
{
Input = FALSE;
Bool IsMinus = FALSE;
const char* digit = str;
long result = 0;
if(NULL == str)
{
std::cerr<<"str is NULL"<<std::endl;
}
else if(NULL != str)
{
//判断首字符是不是正负号
if(*digit == '+')
{
digit++;
}
else if(*digit == '-')
{
IsMinus = TRUE;
digit++;
}
//判断剩下的字符
while(*digit != '\0')
{
//字符处于0-9之间的有效字符
if(*digit >= '0' && *digit <= '9')
{
result = result * 10 + (*digit - '0');
//溢出,即大于最大的正数,小于最小的负数
if((result > numeric_limits<int>::max() && !IsMinus) || (-result < numeric_limits<int>::min() && IsMinus))
{
result = 0;
break;
}
digit++;
}
//其他在0-9之外的非法字符
else
{
result = 0;
break;
}
}
//遍历到最后一个字符,说明是有效输入;检查正负号
if(*digit == '\0')
{
Input = TRUE;
if(IsMinus)
{
result = 0 - result;
}
}
}
return static_cast<int>(result);
}
int main()
{
cout<<"请输入你的字符串"<<endl;
char *mystring = new char[1024];
cin>>mystring;
cout<<"您所输入的字符串转换成整数为:"<<endl;
cout<<strToInt(mystring)<<endl;
cout<<"您的输入转换状态是:"<<endl;
cout<<Input<<endl;
delete[] mystring;
return 0;
}
- 给一个现成的有序矩阵,判断一个数是否在这个矩阵中
//给一个现成的有序矩阵,判断一个数是否在这个矩阵中
templat <class T>
bool find(T target ,vector<vector<T>>&array,int x,int y)
{
if(x==array.size()||y<0)
return false;
if(array[x][y]==target)
return true;
if(array[x][y]>target)
return find(target,array,x,y-1);//如果当前数字比target大,往回找
return find(target,array,x+1,y);//如果当前数字比target小,往下找
}
bool FIND(T target,vector<vector<T>>&array)
{
return find(target,array,0, array[0].size()-1));//从右上角开始找
}
-判断二叉树是否包含另一二叉树
class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
if (t == null) return true; // t 为 null 一定都是 true
if (s == null) return false; // 这里 t 一定不为 null, 只要 s 为 null,肯定是 false
return isSubtree(s.left, t) || isSubtree(s.right, t) || isSameTree(s,t);
}
/**
* 判断两棵树是否相同
*/
public boolean isSameTree(TreeNode s, TreeNode t){
if (s == null && t == null) return true;
if (s == null || t == null) return false;
if (s.val != t.val) return false;
return isSameTree(s.left, t.left) && isSameTree(s.right, t.right);
}
}
- 斐波那契数列的O(logn)解法
def fibona(n):
'''计算 f(n)的值 f(n)=f(n-1)+f(n-2)
输入:
n:数列索引,从0开始
返回:
f(n)的值
'''
if n == 0:
return 0
if n == 1:
return 1
f1_f0 = [[1,0]]
matrix_xishu = [
[1,1],
[1,0]
]
fn_fn_1 = matrix_mutilply(f1_f0,matrix_mi(matrix_xishu,n-1)) #[[f_n,f_n_1]]
return fn_fn_1[0][0]
def matrix_mi(matrix,mi):
'''求矩阵的幂
输入:
matrix:底数矩阵
mi:幂次
返回:
res:matrix的mi次结果
'''
if mi == 1:
return matrix
residual = mi%2
oushu_mi = mi - residual
matrix_dishu = matrix
while oushu_mi>1:
matrix_dishu = matrix_mutilply(matrix_dishu,matrix_dishu)
oushu_mi = oushu_mi/2
return matrix_dishu if residual == 0 else matrix_mutilply(matrix_dishu,matrix)
def matrix_mutilply(matrix_a,matrix_b):
'''矩阵乘积运算
输入:
matrix_a:n*m矩阵
matrix_b: m*k矩阵
返回:
res:n*k矩阵
'''
n,m,k = len(matrix_a),len(matrix_b),len(matrix_b[0])
res=[[0] * k for i in range(n)]
for i in range(n):
for j in range(m):
for z in range(k):
res[i][z]+=matrix_a[i][j]*matrix_b[j][z]
return res
print(fibona(100))
- 如何解决车道线分割有车阻拦视线导致分割结果断裂的问题
class Solution:
def singleNumber(self, nums: List[int]) -> List[int]:
# xor 为特殊两个数的异或
xor = 0
for num in nums:
xor = xor ^ num
# bit 为xor 第一个为1的位
bit = 1
while xor & bit == 0:
bit <<= 1
# 通过和bit异或的结果,把数分为两组,两个数肯定在不同组,两个组异或出的结果就是两个数
a = 0
b = 0
for num in nums:
if num & bit == 0:
a ^= num
else:
b ^= num
return [b,a]
- 给了一个一维数组,给了一个行数和列数,要求我转换成二维数组
- numpy写个batch norm 层
- 输入 3x100x100的feature map 卷积是5x5 输出是100x100x15,问计算量的数目?
- SGD 使用mini batch优化和使用所有优化样本优化哪个更好,为什么?
- 如何处理工作中困难?
- 经典网络模型,inception,resnet等
- 正则化方法
L1、L2 正则化,是通过在损失函数中添加一项对权重的约束来实现正则化的,L1 是求权重的绝对值之和,L2 是求权重的平方和;
dropout 是通过设置 keep_prob
参数随机让部分神经元在训练过程中不起作用来实现正则化的;早停是通过比较验证集损失和测试集损失在适当时候提前停止训练神经网络来实现正则化的;数据扩增是在图像处理领域通过对输入图片进行裁剪、旋转等方法增大训练数据集来实现正则化的。
- 哪些激活函数?
sigmoid, tanh, relu, leaky relu。
- Momentum 优化算法原理,为什么要用?
Momentum 实际上就是指数加权平均,每次更新参数值时通过对最新的数值和以往的数值分配一定权重来实现更新过程,可以让参数平滑更新,某些情况下能够加快训练速度。
- 读过那些论文,了解过计算机视觉哪些领域哪些方法?
- 反卷积、group convolution、dilated convolution?
反卷积可以理解为卷积的逆过程,最开始是为了可视化卷积过程,现在多用来生成图片;dilated convolution 叫做空洞卷积,是在卷积过程中引入空洞,可以增大感受野;说 group convolution 的时候当时我有点懵,还以为是一种新的卷积方式,后来一查才发现原来是将卷积过程分散到多个 GPU 上去运算,最后再把计算结果进行合并。
- PCA分解是怎么做的?
- 为什么用 Linux,Linux 命令:wc、grep、cat、重定向?
Ubuntu 下方便配置深度学习框架的环境,又问了我使用的编程工具,我说用 PyCharm、Clion 和 Code:Blocks。
wc: 统计指定文件中的字节数、字数、行数,并将统计结果显示输出(这个没用过,当时没答上来)
grep: 正则化匹配
cat: 输出文件内容
重定向:默认的标准输入设备是键盘,输出设备是终端,重定向就是在输入输出的时候重新配置输入设备和输出设备,比如输入时从文件读入和输出时写入文件。
- max pooling 反向传播
max pooling也要满足梯度之和不变的原则 ,max pooling的前向传播是把patch中最大的值传递给后一层,而其他像素的值直接被舍弃掉。那么反向传播也就是把梯度直接传给前一层某一个像素,而其他像素不接受梯度,也就是为0
。所以max pooling操作和mean pooling操作不同点在于需要记录下池化操作时到底哪个像素的值是最大,也就是max id ,这个变量就是记录最大值所在位置的,因为在反向传播中要用到
- 懂马尔科夫吗?
- 如何解决multiscale问题
- sqrt(), log()如何求
- 求数组中出现次数超过一半的数字
- GAN的理解和不易训练的解决办法
- C++面向对象介绍下:
面向对象的三大特性:
1、封装
隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性。
2、继承
提高代码复用性;继承是多态的前提。
3、多态
父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高了程序的拓展性。
-介绍知识蒸馏是怎么做的
知识蒸馏(KD)是想将复杂模型(teacher)中的dark knowledge迁移到简单模型(student)中去,一般来说,teacher具有强大的能力和表现,而student则更为紧凑。通过知识蒸馏,希望student能尽可能逼近亦或是超过teacher,从而用更少的复杂度来获得类似的预测效果。Hinton在Distilling the Knowledge in a Neural Network中首次提出了知识蒸馏的概念,通过引入teacher的软目标(soft targets)以诱导学生网络的训练。
- 接下来面试官问我有什么问题,我问如果能去实习的话会主要负责什么?
面试官说主要应该是搜集计算机视觉领域相关的论文然后用商汤自己的框架来复现这些方法;另一方面就是研究现有框架的底层。
Key
- 遇到不会的问题还是主动说自己没有深入研究过这方面,未来继续加强学习
- 论文细节
- 商汤这里确实更加注重项目经历和论文竞赛,默认已经掌握常见基本概念