【实习】商汤科技实习生准备

本文分享了商汤科技实习生面试的经历与准备建议,包括技术面试流程、常见问题及解答技巧,适合准备应聘该公司的同学参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

商汤科技实习生准备@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

  1. 遇到不会的问题还是主动说自己没有深入研究过这方面,未来继续加强学习
  2. 论文细节
  3. 商汤这里确实更加注重项目经历和论文竞赛,默认已经掌握常见基本概念

参考博客链接:
1
2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值