DNA结合之Motif_1:CNN

news2025/1/23 8:24:59

1,首先可以识别在KO前后的motif——》由CNN模型做出识别,看看这个有没有什么灵感

2,ZNF143等都可以使用来识别

3,暂时只使用单个peak文件,后期可以使用ENCODE中所有的对应的TF的peak文件

===========

1,文件解压之后:共53583个peak

narrowpeak的格式是:

参考:

https://mp.weixin.qq.com/s/Zz0DvH2-kgoU7xexfSZiQg

事实上,我可以使用ATAC-seq来作为对照,获取开放以及封闭区域的peak分别来训练CNN

当然,实际上是能够用各种组学数据来进一步缩短范围来训练的(实际上被排除掉的peak甚至可以作为负样本)

——》甚至是可以用在LL那篇文章中,看在NCR突变前后

https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE261213(但是只有bw,当然可以直接由上游数据获得)

首先这个peak大致在100 bp左右

然后可以获取hg19的参考基因组

原先的peak有53583行

但是在中心flank之后都会丢失掉一些数据,可能是在chr特殊区域或者是末端

应该抽取出fa文件的第2行作为字符串存入列表中

第一性原理:对团块做个分类器(或者是聚类)

分类的解释性:——》从结果上去看有没有什么灵感

是交叉的

还是团块:clique

——》再分别对这些团块蛋白做个motif机制(有motif的)

2,整理输入数据的x:(x,y)中的x

其中fit_transform 方法是依据所有因子的字母顺序进行标记,而不是依据 Unicode 码。具体来说,它会将输入的类别按字母顺序排序,然后依次分配整数标签。

from sklearn.preprocessing import LabelEncoder, OneHotEncoder #导入 LabelEncoder 和 OneHotEncoder,用于将序列编码为整数和 one-hot 编码

# 创建一个 LabelEncoder 实例,用于将碱基序列编码为整数
integer_encoder = LabelEncoder() 
integer_encoder.fit_transform(list('CTAG'))

#fit_transform 方法是依据所有因子的字母顺序进行标记,而不是依据 Unicode 码。具体来说,它会将输入的类别按字母顺序排序,然后依次分配整数标签。
#例如,对于 DNA 序列中的碱基 'A', 'C', 'G', 'T',LabelEncoder 会按字母顺序将它们编码为 0, 1, 2, 3。

所以fit_transform正好对sequences中所有序列之和的factor的水平levels(其实就是4种),按照字母顺序(其实就是unicode、ASCII)进行编码0123,不是按照出现前后顺序

参考:
https://scikit-learn.org/1.4/modules/generated/sklearn.preprocessing.LabelEncoder.html#sklearn.preprocessing.LabelEncoder.fit_transform

我们随便以1条序列作为例子:

以最后一条序列作为例子

总之对于sequences列表中的每一个peak sequence,我们已经获取了其对应的整数编码数组(序列)

然后实际上reshape是将原来的序列字符串的编码之后的一维数组,转换为2维的列向量

from sklearn.preprocessing import LabelEncoder, OneHotEncoder #导入 LabelEncoder 和 OneHotEncoder,用于将序列编码为整数和 one-hot 编码
integer_encoder = LabelEncoder() 

# 创建一个 OneHotEncoder 实例,用于将整数编码转换为 one-hot 编码
one_hot_encoder = OneHotEncoder(categories='auto')   
input_features = [] #初始化一个空列表,用于存储每个序列的 one-hot 编码
integer_encoded =integer_encoder.fit_transform(list('CGGGGACACGGCCCCGCCATGCGGGGGCGCTCAGGCGCGGGGCGCCGCGC'))
np.array(integer_encoded)
integer_encoded = np.array(integer_encoded).reshape(-1, 1)

然后进行独热编码:

https://scikit-learn.org/1.4/modules/generated/sklearn.preprocessing.OneHotEncoder.html

我们其实可以看到:

整数编码其实就是独热编码那个不为0而为1的位置编号

但是独热编码结果如果不转换为数组其实就很难理解

from sklearn.preprocessing import LabelEncoder, OneHotEncoder #导入 LabelEncoder 和 OneHotEncoder,用于将序列编码为整数和 one-hot 编码
integer_encoder = LabelEncoder() 

# 创建一个 OneHotEncoder 实例,用于将整数编码转换为 one-hot 编码
one_hot_encoder = OneHotEncoder(categories='auto')   
input_features = [] #初始化一个空列表,用于存储每个序列的 one-hot 编码
integer_encoded =integer_encoder.fit_transform(list('CGGGGACACGGCCCCGCCATGCGGGGGCGCTCAGGCGCGGGGCGCCGCGC'))
np.array(integer_encoded)
integer_encoded = np.array(integer_encoded).reshape(-1, 1)
print(integer_encoded)
one_hot_encoded = one_hot_encoder.fit_transform(integer_encoded) 
one_hot_encoded 
print(one_hot_encoded)

只有在toarray转换为数组之后,展现的形式我们才熟悉:

from sklearn.preprocessing import LabelEncoder, OneHotEncoder
import numpy as np

# 示例序列
sequence = 'GCTAG'

# 创建 LabelEncoder 实例
integer_encoder = LabelEncoder()

# 将序列中的每个碱基转换为整数编码
integer_encoded = integer_encoder.fit_transform(list(sequence))

# 将整数编码转换为列向量
integer_encoded = np.array(integer_encoded).reshape(-1, 1)

# 打印整数编码结果
print("整数编码结果:\n", integer_encoded)

# 创建 OneHotEncoder 实例
one_hot_encoder = OneHotEncoder(categories='auto')

# 将整数编码转换为独热编码
one_hot_encoded = one_hot_encoder.fit_transform(integer_encoded)

# 将独热编码转换为数组并打印
one_hot_encoded_array = one_hot_encoded.toarray()
print("独热编码结果:\n", one_hot_encoded_array)

所以input_features里面其实就相当于获取了很多sequence peak序列的独热编码的array,是高维array(tensor)

接下来就是对序列的拼接堆叠:也就是np.stack这一步

import numpy as np

# 示例独热编码数组
one_hot_encoded_1 = np.array([[0, 0, 1, 0],
                              [0, 1, 0, 0],
                              [0, 0, 0, 1],
                              [1, 0, 0, 0],
                              [0, 0, 1, 0]])

one_hot_encoded_2 = np.array([[1, 0, 0, 0],
                              [0, 1, 0, 0],
                              [0, 0, 1, 0],
                              [0, 0, 0, 1],
                              [1, 0, 0, 0]])

one_hot_encoded_3 = np.array([[0, 1, 0, 0],
                              [1, 0, 0, 0],
                              [0, 0, 1, 0],
                              [0, 0, 0, 1],
                              [0, 1, 0, 0]])

# 将这些独热编码数组放入一个列表
input_features = [one_hot_encoded_1, one_hot_encoded_2, one_hot_encoded_3]

# 使用 np.stack 将这些数组堆叠成一个三维数组
stacked_features = np.stack(input_features)

# 打印堆叠后的数组及其形状
print("堆叠后的数组:\n", stacked_features)
print("堆叠后的数组形状:", stacked_features.shape)

但是实际运行的时候报错了:

from sklearn.preprocessing import LabelEncoder, OneHotEncoder #导入 LabelEncoder 和 OneHotEncoder,用于将序列编码为整数和 one-hot 编码


# 创建一个 LabelEncoder 实例,用于将碱基序列编码为整数
integer_encoder = LabelEncoder()  

# 创建一个 OneHotEncoder 实例,用于将整数编码转换为 one-hot 编码
one_hot_encoder = OneHotEncoder(categories='auto')   
input_features = [] #初始化一个空列表,用于存储每个序列的 one-hot 编码

for sequence in sequences: 
  integer_encoded = integer_encoder.fit_transform(list(sequence)) 
  #将序列中的每个碱基转换为整数编码,Fit label encoder and return encoded labels
  #对每条序列sequence拆分为单碱基字符列表后,先fit,再transform,就是对ATCG进行数字编码,就是从0开始
  
  integer_encoded = np.array(integer_encoded).reshape(-1, 1) #将整数编码转换为列向量,reshape(-1, 1)中的-1表示自动计算行数
  one_hot_encoded = one_hot_encoder.fit_transform(integer_encoded) #将整数编码转换为 one-hot 编码
  input_features.append(one_hot_encoded.toarray()) #将 one-hot 编码转换为数组并添加到 input_features 列表中
print(input_features)
print(input_features[0])
print(input_features[0][0])
print(input_features[0][0][0])


np.set_printoptions(threshold=40) #设置打印选项,限制输出的元素数量
input_features = np.stack(input_features) #将 input_features 列表转换为 numpy 数组
print("Example sequence\n-----------------------")  #打印示例序列的标题
print('DNA Sequence #1:\n',sequences[0][:10],'...',sequences[0][-10:]) #打印第一个 DNA 序列的前 10 个和后 10 个碱基
print('One hot encoding of Sequence #1:\n',input_features[0]) #打印第一个 DNA 序列的 one-hot 编码

在stack的时候array维度不一致

要调试并找出哪些数组的形状不同,可以遍历 input_features 列表并打印每个数组的形状。

# 打印每个数组的形状
for i, feature in enumerate(input_features):
# enumerate返回index、value的元组
    print(f"Array {i} shape: {feature.shape}")

# 检查是否所有数组形状一致
shapes = [feature.shape for feature in input_features]
if len(set(shapes)) != 1:
    print("存在形状不一致的数组")
else:
    print("所有数组形状一致")

# 如果所有数组形状一致,则进行堆叠
if len(set(shapes)) == 1:
    input_features = np.stack(input_features)
    print("堆叠后的数组形状:", input_features.shape)
else:
    print("无法堆叠数组,因为它们的形状不一致")

其实按理来说,所有array的shape应该都是(50,4),因为有50bp长,每个bp都是一个长度为4的独热编码

问题:每个sequence只依据自身信息进行编码,是否有些序列50bp内只出现3字符,比如说某个序列只有ACG,那么独热编码的时候实际上class只有3类,所以只能3维的独热——》需要在全局统一一下

array是从0开始的,0-index,peak是1-index的

以array 53233为例,实际上就是peak 53234

果然检查了一下是只有3字母的:A/T/G

所以问题其实就出在了原本脚本中的auto这一个参数

one_hot_encoder = OneHotEncoder(categories='auto')  

实际上参考scikit-learn中的

https://scikit-learn.org/1.4/modules/generated/sklearn.preprocessing.OneHotEncoder.html

所以应该指定category:

one_hot_encoder = OneHotEncoder(categories=[['A', 'C', 'G', 'T']])   

当然,如果前面指定了category,后面其实就不用整数编码了,可以直接使用独热编码

from sklearn.preprocessing import LabelEncoder, OneHotEncoder #导入 LabelEncoder 和 OneHotEncoder,用于将序列编码为整数和 one-hot 编码

# 创建一个 OneHotEncoder 实例,用于将整数编码转换为 one-hot 编码
one_hot_encoder = OneHotEncoder(categories=[['A', 'C', 'G', 'T']])   
input_features = [] #初始化一个空列表,用于存储每个序列的 one-hot 编码

for sequence in sequences: 
    # 将序列中的每个碱基转换为列向量
    sequence_array = np.array(list(sequence)).reshape(-1, 1)
    
    # 将字符序列转换为 one-hot 编码
    one_hot_encoded = one_hot_encoder.fit_transform(sequence_array)
    
    # 将 one-hot 编码转换为数组并添加到 input_features 列表中
    input_features.append(one_hot_encoded.toarray())

for i, feature in enumerate(input_features):
# enumerate返回index、value的元组
    print(f"Array {i} shape: {feature.shape},has value {feature}")

np.set_printoptions(threshold=40) #设置打印选项,限制输出的元素数量,表示在打印时如果数组超过40个元素,则只打印开头和结尾的部分
input_features = np.stack(input_features) #将 input_features 列表转换为 numpy 数组
print("Example sequence\n-----------------------")  #打印示例序列的标题
print('DNA Sequence #1:\n',sequences[0][:10],'...',sequences[0][-10:]) #打印第一个 DNA 序列的前 10 个和后 10 个碱基
print('One hot encoding of Sequence #1:\n',input_features[0]) #打印第一个 DNA 序列的 one-hot 编码

那再测试一下:

同样,list获取字符串列表,np.array获取数组(一维)

reshape之后转换为二维向量

然后这个二维向量直接使用one-hot编码处理

在toarray之直观观察one-hot的稀疏矩阵,所以没有toarray之前其实是稀疏矩阵值为1的坐标标记

from sklearn.preprocessing import OneHotEncoder #导入OneHotEncoder即可,用于将序列编码为one-hot 编码;无需导入LabelEncoder,用于将序列编码为整数


# 创建一个 OneHotEncoder 实例,用于将整数编码转换为 one-hot 编码
one_hot_encoder = OneHotEncoder(categories=[['A', 'C', 'G', 'T']])   
input_features = [] #初始化一个空列表,用于存储每个序列的 one-hot 编码


# 将序列中的每个碱基转换为列向量
sequence_array = np.array(list('CGGGGACACGGCCCCGCCATGCGGGGGCGCTCAGGCGCGGGGCGCCGCGC')).reshape(-1, 1)
print(list('CGGGGACACGGCCCCGCCATGCGGGGGCGCTCAGGCGCGGGGCGCCGCGC'))
print(np.array(list('CGGGGACACGGCCCCGCCATGCGGGGGCGCTCAGGCGCGGGGCGCCGCGC')))
print(sequence_array)

# 将字符序列转换为 one-hot 编码
one_hot_encoded = one_hot_encoder.fit_transform(sequence_array)
print(one_hot_encoded)
print(one_hot_encoded.toarray())
    
# 将 one-hot 编码转换为数组并添加到 input_features 列表中
input_features.append(one_hot_encoded.toarray())

for i, feature in enumerate(input_features):
# enumerate返回index、value的元组
    print(f"Array {i} shape: {feature.shape},has value {feature}")

np.set_printoptions(threshold=40) #设置打印选项,限制输出的元素数量,表示在打印时如果数组超过40个元素,则只打印开头和结尾的部分
input_features = np.stack(input_features) #将 input_features 列表转换为 numpy 数组
print("Example sequence\n-----------------------")  #打印示例序列的标题
print('DNA Sequence #1:\n',sequences[0][:10],'...',sequences[0][-10:]) #打印第一个 DNA 序列的前 10 个和后 10 个碱基
print('One hot encoding of Sequence #1:\n',input_features[0]) #打印第一个 DNA 序列的 one-hot 编码

验证之后没有问题:

3,整理输入数据中的y:(x,y)中的y

首先原始数据中给出的label标签其实正样本以及负样本都包括的,所以对应的标签也就有1以及0;

然后前面我所处理的数据中其实给出的基本上都是CTCF chip-nexus的peak的序列信息,可以说都是正样本positive sample。

但是实际上我是需要提供对应的负样本的,所以如何产生以及评估负样本negative sample呢?
目前有2种策略:
①1种就是将上面的CTCF chip实验中获取的peak的序列信息,再使用ATAC-seq的数据筛选一遍样本,然后将在开放区域内的peak作为正样本,反之则作为负样本;

当然事实上我们前面获取的数据中其实都没有input的数据

②第2种方法就是随机生成序列,或者是随机从CTCF peak对应的周边序列的fa文件中抽取长度为50的窗口的序列

——》现在采取方法①:

但是我们手头上只有那个数据集ATAC的bw文件,如果还是走前面bed提取序列的方法的话,我们需要将bw文件转换为bed文件,

发现peak太多了,而且peak大小其实数量级和CTCF peak差不多

在用bedtools取交集之后,其实数量级没有变化,还是10M级的

这里其实还需要注意一下narrowpeak的index是不是0-index的

因为ATAC获取的交集peak是0-index的。

——》其实我们的条件更严苛,因为我使用的是CTCF peak中心的50bp窗口,拿这个去筛选开放区域交集的窗口,但是实际上并不一定,

比如说某个CTCF的peak和开放性区域有交集,但是实际上的CTCF中心的50bp(看前面分布,实际上50bp的窗口是占少数的peak,所以说大多数CTCF 50bp都能捕获到peak的区域,至少大多数是被包含在peak内的)不一定和开放性区域有交集。

总之2种策略:
①用原始的narrowpeak去和ATAC开放性区域筛选交集,有交集的区域,重新计算中心,再flank上下游25bp,然后作为正样本,其余narrowpeak作为负样本

②直接计算narrowpeak中心区域flank的50bp窗口,再看这个窗口和ATAC开放性区域有交集的作为正样本,其余作为负样本

下面以策略②为准:
但是实际处理之后发现样本基本上都在开放性区域内

=======》

总之我现在的想法就是:
只使用现有的CTCF peak样本数据,从中使用其他组学数据进一步区分正样本以及负样本,

前面使用的是开放性区域,但是发现基本上都是重合的,

所以还是使用其他的组学数据:
比如说使用Cohesin chip-seq数据,训练Cohesin和CTCF peak重合下(共定位下)的CTCF motif,然后不重合的作为负样本。

还是参考:

https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE236632

还是上面的那样:
①获取CTCF和Cohesinn共定位(有overlap)的CTCF peak,然后取这个peak的中心,做50bp窗口,其余没有overlap的就作为负样本

单看区域的数目有40965个正样本,但是有些这些区域可能是对CTCF的1个peak的多个划分交集

解释略:

总之就是使用大概4:1的正:负样本去训练

然后如果我只是使用这中心的50bp的窗口去取交集的话,实际上也差不多,

大概在40825的正样本,和前面只看中心的40921的正样本差不了多少

现在可以从头构建(x,y)数据:
对于CTCF的原始peak,和cohesin共定位的(用bedtools识别出来),可以在该peak的bed文件新增的label一列中记录1,没有bedtools intersect识别出来的,就标记为0;

然后对于CTCF在处理之后的peak的bed数据,只保留chr+start+end+label,

然后再和之前一样使用取start、end中心再上下延伸50bp窗口。

#!/bin/bash

# 定义文件路径
CTCF_FILE="/mnt/sdb/zht/project/uniprot/dl_model/Deep_Learning_in_Genomics_Primer/GSM7566824_WT_ChIP-nexus_CTCF_peaks.narrowPeak"
RAD21_FILE="/mnt/sdb/zht/project/uniprot/dl_model/Deep_Learning_in_Genomics_Primer/GSM7566825_WT_ChIP-nexus_RAD21_peaks.narrowPeak"
OUTPUT_FILE="/mnt/sdb/zht/project/uniprot/dl_model/Deep_Learning_in_Genomics_Primer/GSM7566824_WT_ChIP-nexus_CTCF_peaks_with_labels.narrowPeak"

# 使用 bedtools intersect 找出共定位的峰值,并将结果保存到临时文件
bedtools intersect -a "$CTCF_FILE" -b "$RAD21_FILE" -wa | cut -f 1-3 | sort | uniq > intersected_peaks.tmp

# 使用 awk 处理 CTCF 文件,新增标签列
awk 'BEGIN {FS=OFS="\t"} 
    NR==FNR {intersect[$1,$2,$3]=1; next} 
    {label = ($1,$2,$3) in intersect ? 1 : 0; print $0, label}' intersected_peaks.tmp "$CTCF_FILE" > "$OUTPUT_FILE"

# 删除临时文件
rm intersected_peaks.tmp

echo "处理完成,结果已保存到 $OUTPUT_FILE"




使用 bedtools intersect 找出 CTCF 和 Rad21 共定位的峰值,并将结果保存到临时文件 intersected_peaks.tmp。
使用 awk 处理 CTCF 文件,检查每个峰值是否在临时文件中出现。如果出现,则新增标签列为 1,否则为 0。
将处理后的结果保存到新的文件 GSM7566824_WT_ChIP-nexus_CTCF_peaks_with_labels.narrowPeak。
删除临时文件。

获得的新文件:

行数peak不变,我们只看前面3行+最后1行。

因为我知道前面粗略取50bp窗口的话有些peak取不到,所以这次我会一起保留label列

#这是对原始的没有label的文件
awk '{mid=int(($2+$3)/2); start=mid-25; end=mid+25; print $1"\t"start"\t"end}' GSM7566824_WT_ChIP-nexus_CTCF_peaks.narrowPeak > GSM7566824_WT_ChIP-nexus_CTCF_peaks_50.bed

bedtools getfasta -fi /mnt/sdb/zht/ref_genome/ucsc_hg19/hg19_new.fa -bed /mnt/sdb/zht/project/uniprot/dl_model/Deep_Learning_in_Genomics_Primer/GSM7566824_WT_ChIP-nexus_CTCF_peaks_50.bed -fo /mnt/sdb/zht/project/uniprot/dl_model/Deep_Learning_in_Genomics_Primer/GSM7566824_WT_ChIP-nexus_CTCF_peaks_50.fa

#这是对原始的没有label的文件
awk '{mid=int(($2+$3)/2); start=mid-25; end=mid+25; print $1"\t"start"\t"end"\t"$11}' /mnt/sdb/zht/project/uniprot/dl_model/Deep_Learning_in_Genomics_Primer/GSM7566824_WT_ChIP-nexus_CTCF_peaks_with_labels.narrowPeak > GSM7566824_WT_ChIP-nexus_CTCF_peaks_with_labels_50.bed

bedtools getfasta -fi /mnt/sdb/zht/ref_genome/ucsc_hg19/hg19_new.fa -bed /mnt/sdb/zht/project/uniprot/dl_model/Deep_Learning_in_Genomics_Primer/GSM7566824_WT_ChIP-nexus_CTCF_peaks_50.bed -fo /mnt/sdb/zht/project/uniprot/dl_model/Deep_Learning_in_Genomics_Primer/GSM7566824_WT_ChIP-nexus_CTCF_peaks_50.fa

正好是第11列

正好发现了原始数据并没有处理好数据,简直是mess:

因为我的参考fa文件是没有异常染色体的,我一般都会在处理前将这些异常染色体除去

修改之后为:

awk '{mid=int(($2+$3)/2); start=mid-25; end=mid+25; print $1"\t"start"\t"end"\t"$11}' /mnt/sdb/zht/project/uniprot/dl_model/Deep_Learning_in_Genomics_Primer/GSM7566824_WT_ChIP-nexus_CTCF_peaks_with_labels.narrowPeak | awk 'BEGIN {FS=OFS="\t"} $1 ~ /^chr([1-9]$|1[0-9]$|2[0-2]$|X$|Y$)/' > GSM7566824_WT_ChIP-nexus_CTCF_peaks_with_labels_50.bed

bedtools getfasta -fi /mnt/sdb/zht/ref_genome/ucsc_hg19/hg19_new.fa -bed GSM7566824_WT_ChIP-nexus_CTCF_peaks_with_labels_50.bed -fo /mnt/sdb/zht/project/uniprot/dl_model/Deep_Learning_in_Genomics_Primer/GSM7566824_WT_ChIP-nexus_CTCF_peaks_with_labels_50.fa

这会发现才应该是没有缺失的,所以理论上每个peak中获取中心50bp窗口,前面缺失了一部分不是因为有些peak短于50bp且在末端chr上,而是因为index中有异常chr在

所以正负样本混合之后是53234个peak。

总之目前统一一下我们的数据

awk 'BEGIN {positive=0; negative=0} {if ($4==1) positive++; else negative++} END {print "Positive:", positive; print "Negative:", negative}' /mnt/sdb/zht/project/uniprot/dl_model/Deep_Learning_in_Genomics_Primer/GSM7566824_WT_ChIP-nexus_CTCF_peaks_with_labels_50.bed


Positive: 40657

Negative: 12577

同样获得的fa文件作为数据集,和前面一样处理:

import numpy as np #导入 NumPy 库,并将其命名为 np。NumPy 是一个用于科学计算的库,提供了支持多维数组和矩阵运算的功能
import pandas as pd #导入 Pandas 库,并将其命名为 pd。Pandas 是一个用于数据操作和分析的库,提供了高效的数据结构和数据分析工具
import matplotlib.pyplot as plt #导入 Matplotlib 库中的 pyplot 模块,并将其命名为 plt。Matplotlib 是一个用于数据可视化的库,pyplot 模块提供了类似于 MATLAB 的绘图 API
import requests #导入 Requests 库。Requests 是一个用于发送 HTTP 请求的库,简化了与 Web 服务的交互。


sequences = []
with open('/mnt/sdb/zht/project/uniprot/dl_model/Deep_Learning_in_Genomics_Primer/GSM7566824_WT_ChIP-nexus_CTCF_peaks_with_labels_50.fa', 'r') as file:
    sequence = ''
    for line in file:
        if line.startswith('>'):
            continue  # 跳过以'>'开头的行
        sequence = line.strip().upper()  # 去除两端空白并转换为大写
        sequences.append(sequence)
sequences = list(filter(None, sequences)) # 过滤掉空序列,最外层还是要套一层list,否则不是list类型,是map类型


# 先看看数据集长什么样
pd.DataFrame(sequences, index=np.arange(1, len(sequences)+1), 
             columns=['Sequences']).head()

然后关键就是处理数据中的label,

# 提取最后一列并保存到 labels.txt 文件中
# cut -f 4 GSM7566824_WT_ChIP-nexus_CTCF_peaks_with_labels_50.bed > labels.txt

# 读取 labels.txt 文件
with open('/mnt/sdb/zht/project/uniprot/dl_model/Deep_Learning_in_Genomics_Primer/labels.txt', 'r') as file:
    labels = file.read().splitlines()
#读取文件的所有内容,并按行分割成一个列表。splitlines() 方法会移除每行末尾的换行符

# 移除空行
labels = list(filter(None, labels))

# 创建一个 OneHotEncoder 实例
one_hot_encoder = OneHotEncoder(categories='auto')

# 将标签转换为 NumPy 数组并进行独热编码
labels = np.array(labels).reshape(-1, 1)
input_labels = one_hot_encoder.fit_transform(labels).toarray()

print('Labels:\n', labels)
print('One-hot encoded labels:\n', input_labels)

对应上:

然后是过滤空行:

可以看到,这里其实就是一个array,并不是前面sequences中对于sequence的循环。所以这里的category指定是auto,而不是手动选择0-1。

其实我们可以发现,最终的label中编码,其实就是针对levels水平而言的,然后因子的水平levels之间需要注意顺序,一般的顺序就是按照ASCII码,所以是0-1,当然这里也可以在前面指定类别是:

one_hot_encoder = OneHotEncoder(categories=[['A', 'C', 'G', 'T']]) 

one_hot_encoder = OneHotEncoder(categories=[['0', '1']]) 

这个时候我们注意到,确实在指定了category为0-1的独热编码之后,0就被指定为二维向量第一位的独热编码了;1被指定为二维向量第2位的独热编码。

哪怕后面array给出1-0,实际上也是看水平的顺序的。

然后在处理好了训练集以及测试集之后,再对其进行划分

首先这里的*array允许多个输入,

test/train_size是互补的,然后random_state实际上就是随机数的种子

from sklearn.model_selection import train_test_split #将数据集拆分为训练集和测试集

train_features, test_features, train_labels, test_labels = train_test_split(
    input_features, input_labels, test_size=0.25, random_state=2025)
#(features,labels)实际上就是(x,y)

print ("x_train shape: ", train_features.shape)
#print(train_features)
print ("y_train shape: ", train_labels.shape)
#print(train_labels)
print("x_test shape: ", test_features.shape)
#print(test_features)
print ("y_test shape: ", test_labels.shape)
#

几维度的tensor,多少样本,多少feature(sequence字符),每个feature的表征。

53234——》可以发现即使我当初划分训练集与测试集的比例是4:1,也就是给出0.25的比例,虽然不能整除,但是实际上train和test还是int划分出来了。

4,选择网络架构并进行训练:如果是使用tensorflow框架

# 导入所需的层和模型
# 导入了TensorFlow Keras库中的一些常用层和模型类:
# Conv1D:一维卷积层,用于处理序列数据。
# Dense:全连接层。
# MaxPooling1D:一维最大池化层。
# Flatten:将多维输入展平为一维。
# Sequential:线性堆叠模型,每一层的输出自动成为下一层的输入
from tensorflow.keras.layers import Conv1D, Dense, MaxPooling1D, Flatten
from tensorflow.keras.models import Sequential

# 初始化Sequential模型,这是一种线性堆叠的模型,每一层的输出自动成为下一层的输入
# 初始化了一个Sequential模型对象,表示模型的层是按顺序堆叠的
model = Sequential()

# 添加一维卷积层Conv1D
# filters=32表示有32个卷积核,积核的数量决定了输出特征图的深度。32个卷积核可以提取32种不同的特征。kernel_size=12表示每个卷积核的大小是12,卷积核的大小决定了每次卷积操作覆盖的输入区域。12个碱基的窗口大小可以捕捉到局部的模式。
# input_shape=(train_features.shape[1], 4)指定输入数据的形状。每个输入序列是50个碱基,4维度的one-hot编码(A, C, G, T)
# 其中train_features.shape[1]是特征的数量,4是特征的维度
model.add(Conv1D(filters=32, kernel_size=12, 
                 input_shape=(train_features.shape[1], 4)))

# 添加一维最大池化层MaxPooling1D
# pool_size=4表示池化窗口的大小是4,即每次取4个连续的数据进行池化操作。池化操作通过取窗口内的最大值来减少特征图的尺寸,从而减少计算量和防止过拟合。
model.add(MaxPooling1D(pool_size=4))

# 添加Flatten层,将多维的输出展平成一维,以便输入到全连接层。卷积层和池化层的输出是多维的,需要展平成一维才能输入到全连接层。
model.add(Flatten())

# 添加全连接层Dense
# units=16表示该层有16个神经元,全连接层的神经元数量决定了该层的输出维度。16个神经元可以学习到16种不同的特征组合。activation='relu'表示使用ReLU激活函数,ReLU激活函数可以引入非线性,使模型能够学习到更复杂的模式。
model.add(Dense(16, activation='relu'))

# 添加输出层,也是一个全连接层
# units=2表示该层有2个神经元,输出层的神经元数量决定了模型的输出维度,2个神经元对应二分类问题的两个类别。activation='softmax'表示使用softmax激活函数,用于二分类问题,softmax激活函数将输出转换为概率分布,用于多分类或二分类问题。
model.add(Dense(2, activation='softmax'))

# 编译模型,配置其学习过程
# loss='binary_crossentropy'指定损失函数为二元交叉熵,适用于二分类问题
# optimizer='adam'指定优化器为Adam,这是一种自适应学习率的优化算法
# metrics=['binary_accuracy']指定评估模型性能的指标为二元准确率
model.compile(loss='binary_crossentropy', optimizer='adam', 
              metrics=['binary_accuracy'])

# 打印模型的概述,包括每层的输出形状和参数数量
model.summary()

模型框架如下:

然后是增加早停机制:

from tensorflow.keras.callbacks import EarlyStopping #Keras中的一个模块,包含了许多回调函数,用于在训练过程中执行特定的操作。EarlyStopping是一个回调函数,用于在监控的指标不再改善时提前停止训练,以防止过拟合
early_stop = EarlyStopping(monitor='val_loss',patience=10)
# monitor='val_loss':monitor:指定要监控的指标。在这里,'val_loss'表示监控验证集的损失。'val_loss':验证集的损失值。选择监控验证集的损失是因为它可以反映模型在未见过的数据上的表现,从而帮助防止过拟合。
# patience=10:patience:指定在监控的指标不再改善时,允许训练继续的轮数。在这里,10表示如果验证集的损失在连续2个训练轮次中没有改善,则停止训练。2:具体的耐心值,表示在验证集损失不再改善的情况下,允许训练继续的最大

import numpy as np
from tensorflow.keras.layers import Conv1D
from tensorflow.keras.models import Sequential

# 创建一个简单的序列数据
data = np.random.rand(10, 100, 4)  # 10个样本,每个样本100个时间步,每个时间步4个特征

# 创建模型
model = Sequential()
model.add(Conv1D(filters=32, kernel_size=3, input_shape=(100, 4)))

# 查看模型结构
model.summary()

from tensorflow.keras.layers import Dense
from tensorflow.keras.models import Sequential

# 创建模型
model = Sequential()
model.add(Dense(16, activation='relu', input_shape=(10,)))

# 查看模型结构
model.summary()

import torch # torch:PyTorch 主库
import torch.nn as nn # torch.nn:包含神经网络模块和层
import torch.optim as optim # torch.optim:包含优化器模块 
from torch.utils.data import DataLoader, TensorDataset # torch.utils.data:包含数据加载和处理模块
import numpy as np # numpy:用于科学计算的库

# train_features, train_labels, test_features, test_labels就是前面已经定义的(x_train, y_train, x_test, y_test)
# 将训练和测试数据转换为 PyTorch 张量,并指定数据类型为 float32
train_features_tensor = torch.tensor(train_features, dtype=torch.float32) #x_train
train_labels_tensor = torch.tensor(train_labels, dtype=torch.float32) #y_train
test_features_tensor = torch.tensor(test_features, dtype=torch.float32) #x_test
test_labels_tensor = torch.tensor(test_labels, dtype=torch.float32) #y_test

首先是将前面划分处理好的训练集以及测试集中的array数据转换为tensor数据

将numpy中的ndarray转换为pytorch支持的tensor数据类型函数,可以参考:
https://pytorch.org/docs/stable/generated/torch.tensor.html#torch-tensor

然后就是

# 创建数据加载器
train_dataset = TensorDataset(train_features_tensor, train_labels_tensor) #创建 TensorDataset,将特征和标签组合在一起,主要作用:将特征张量和标签张量组合成一个数据集对象
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True) #作用:将数据集分成小批量(batch),并支持随机打乱(shuffle)和多线程加载

test_dataset = TensorDataset(test_features_tensor, test_labels_tensor)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)
# 创建 DataLoader,用于批量加载数据:
# batch_size=32:每个批次包含 32 个样本。
# shuffle=True:在每个 epoch 开始时打乱训练数据,适用于train训练集,test测试集不需要打乱

主要涉及到两个函数:

前者组合feature以及label数据成为一个数据集,后者主要是用于batch获取

https://pytorch.org/docs/stable/data.html#torch.utils.data.TensorDataset

https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader

主要关注的参数就是batch_size,以及shuffle

因为我的样本数据维度是:

train的dataset大概在4w左右,test的dataset大概在1w左右;

然后一个epoch,如果batch_size大概在32左右的话,那么大概会有1w(数量级)/30≈300个batch左右。

问题就在于这里的batch size,如果数据量不能被batch size整除,最后一个批次的数据可能会导致形状不匹配问题,也就是train以及test中最后一个batch的数据会不足。

为解决此问题,可以在PyTorch的DataLoader中设置drop_last=True,这样可以自动丢弃最后一个不足batch size的数据,确保每个epoch内数据的完整性和批处理的一致性。这是一个简单而有效的方法,尤其适用于大量数据的情况

可以看到,其实就是数据类型变化了而已,int变为float,整体是tensor

#示例
features = torch.tensor([[1, 2], [3, 4], [5, 6], [7, 8]],dtype=torch.float32)
labels = torch.tensor([0, 1, 0, 1],dtype=torch.float32)
print(features,labels)
dataset = TensorDataset(features, labels)
print(dataset)
loader = DataLoader(dataset, batch_size=2, shuffle=True)
print(loader)

for batch_features, batch_labels in loader:
    print(batch_features, batch_labels)

然后就是合并tensor,再按照两个sample为一个batch进行划分。

确实最后一个batch需要注意一下,所以需要将batch size相关的参数修改一下。

修改之后为:

# 创建数据加载器
train_dataset = TensorDataset(train_features_tensor, train_labels_tensor) #创建 TensorDataset,将特征和标签组合在一起,主要作用:将特征张量和标签张量组合成一个数据集对象
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True,drop_last=True) #作用:将数据集分成小批量(batch),并支持随机打乱(shuffle)和多线程加载,batch_size如果无法整除整个数据集,可以丢弃最后一个batch

test_dataset = TensorDataset(test_features_tensor, test_labels_tensor)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False,drop_last=True)

还是测验:

如果设置了drop_last之后:

效果非常差:

from tensorflow.keras.layers import Conv1D, Dense, MaxPooling1D, Flatten, Dropout

model = Sequential()
model.add(Conv1D(filters=32, kernel_size=12, input_shape=(train_features.shape[1], 4)))
model.add(MaxPooling1D(pool_size=4))
model.add(Dropout(0.5))  # 添加Dropout层,丢弃50%的神经元
model.add(Flatten())
model.add(Dense(16, activation='relu'))
model.add(Dropout(0.5))  # 添加Dropout层,丢弃50%的神经元
model.add(Dense(2, activation='softmax'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['binary_accuracy'])
model.summary()

from tensorflow.keras.layers import Conv1D, Dense, MaxPooling1D, Flatten
from tensorflow.keras.regularizers import l2

model = Sequential()
model.add(Conv1D(filters=32, kernel_size=12, input_shape=(train_features.shape[1], 4), kernel_regularizer=l2(0.01)))
model.add(MaxPooling1D(pool_size=4))
model.add(Flatten())
model.add(Dense(16, activation='relu', kernel_regularizer=l2(0.01)))
model.add(Dense(2, activation='softmax'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['binary_accuracy'])
model.summary()
model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), metrics=['binary_accuracy'])
history = model.fit(train_features, train_labels, epochs=50, batch_size=64, verbose=1, validation_split=0.25)

再改:

# 导入所需的层和模型
# 导入了TensorFlow Keras库中的一些常用层和模型类:
# Conv1D:一维卷积层,用于处理序列数据。
# Dense:全连接层。
# MaxPooling1D:一维最大池化层。
# Flatten:将多维输入展平为一维。
# Sequential:线性堆叠模型,每一层的输出自动成为下一层的输入
from tensorflow.keras.layers import Conv1D, Dense, MaxPooling1D, Flatten, Dropout, Input
from tensorflow.keras.models import Sequential
from tensorflow.keras.regularizers import l2

# 初始化Sequential模型,这是一种线性堆叠的模型,每一层的输出自动成为下一层的输入
# 初始化了一个Sequential模型对象,表示模型的层是按顺序堆叠的
model = Sequential()

# 添加一维卷积层Conv1D
# filters=32表示有32个卷积核,积核的数量决定了输出特征图的深度。32个卷积核可以提取32种不同的特征。kernel_size=12表示每个卷积核的大小是12,卷积核的大小决定了每次卷积操作覆盖的输入区域。12个碱基的窗口大小可以捕捉到局部的模式。
# input_shape=(train_features.shape[1], 4)指定输入数据的形状。每个输入序列是50个碱基,4维度的one-hot编码(A, C, G, T)
# 其中train_features.shape[1]是特征的数量,4是特征的维度
model.add(Input(shape=(train_features.shape[1], 4)))
model.add(Conv1D(filters=32, kernel_size=12, kernel_regularizer=l2(0.01)))

# 添加一维最大池化层MaxPooling1D
# pool_size=4表示池化窗口的大小是4,即每次取4个连续的数据进行池化操作。池化操作通过取窗口内的最大值来减少特征图的尺寸,从而减少计算量和防止过拟合。
model.add(MaxPooling1D(pool_size=4))

model.add(Dropout(0.5))  # 添加Dropout层,丢弃50%的神经元!!!!


# 再加一层
model.add(Conv1D(filters=64, kernel_size=6, kernel_regularizer=l2(0.01)))  # 增加一层卷积层
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))


# 添加Flatten层,将多维的输出展平成一维,以便输入到全连接层。卷积层和池化层的输出是多维的,需要展平成一维才能输入到全连接层。
model.add(Flatten())

# 添加全连接层Dense
# units=16表示该层有16个神经元,全连接层的神经元数量决定了该层的输出维度。16个神经元可以学习到16种不同的特征组合。activation='relu'表示使用ReLU激活函数,ReLU激活函数可以引入非线性,使模型能够学习到更复杂的模式。
model.add(Dense(32, activation='relu', kernel_regularizer=l2(0.01)))

# 添加输出层,也是一个全连接层
# units=2表示该层有2个神经元,输出层的神经元数量决定了模型的输出维度,2个神经元对应二分类问题的两个类别。activation='softmax'表示使用softmax激活函数,用于二分类问题,softmax激活函数将输出转换为概率分布,用于多分类或二分类问题。
model.add(Dense(2, activation='softmax'))

# 编译模型,配置其学习过程
# loss='binary_crossentropy'指定损失函数为二元交叉熵,适用于二分类问题
# optimizer='adam'指定优化器为Adam,这是一种自适应学习率的优化算法
# metrics=['binary_accuracy']指定评估模型性能的指标为二元准确率

model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(learning_rate=0.005), metrics=['binary_accuracy']) #learning_rate=0.001

# 打印模型的概述,包括每层的输出形状和参数数量
model.summary()
from tensorflow.keras.callbacks import EarlyStopping #Keras中的一个模块,包含了许多回调函数,用于在训练过程中执行特定的操作。EarlyStopping是一个回调函数,用于在监控的指标不再改善时提前停止训练,以防止过拟合
early_stop = EarlyStopping(monitor='val_loss',patience=5)
# monitor='val_loss':monitor:指定要监控的指标。在这里,'val_loss'表示监控验证集的损失。'val_loss':验证集的损失值。选择监控验证集的损失是因为它可以反映模型在未见过的数据上的表现,从而帮助防止过拟合。
# patience=10:patience:指定在监控的指标不再改善时,允许训练继续的轮数。在这里,10表示如果验证集的损失在连续2个训练轮次中没有改善,则停止训练。2:具体的耐心值,表示在验证集损失不再改善的情况下,允许训练继续的最大
history = model.fit(train_features, train_labels, 
                    epochs=100, batch_size=32,verbose=1, validation_split=0.25) #原先epochs是50
# model.fit:Keras中用于训练模型的函数
# epochs=50:指定训练的轮次数。每个轮次表示整个训练数据集被用来更新模型参数一次。建议:可以根据模型的训练情况调整这个值。如果模型在较少的轮次内就达到了较好的性能,可以减少这个值,例如 epochs=30
# verbose=0:指定训练过程的日志显示模式。0表示不输出训练过程的日志,1表示输出进度条,2表示每个轮次输出一行日志。建议:在调试和观察训练过程时,可以设置为 verbose=1,以便查看训练进度

再练:

再练:崩了

7,模型优化:

调整类权重

在model.fit中添加class_weight参数:

class_weight = {0: 1., 1: 3.}  # 根据数据集的类别分布调整权重

history = model.fit(train_features, train_labels, epochs=50, batch_size=64, verbose=1, validation_split=0.25, class_weight=class_weight)

增加模型复杂度

增加卷积层和全连接层的数量:

from tensorflow.keras.layers import Conv1D, Dense, MaxPooling1D, Flatten, Dropout, Input
from tensorflow.keras.models import Sequential
from tensorflow.keras.regularizers import l2

model = Sequential()
model.add(Input(shape=(train_features.shape[1], 4)))
model.add(Conv1D(filters=32, kernel_size=12, kernel_regularizer=l2(0.01)))
model.add(MaxPooling1D(pool_size=4))
model.add(Dropout(0.5))
model.add(Conv1D(filters=64, kernel_size=6, kernel_regularizer=l2(0.01)))  # 增加一层卷积层
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))
model.add(Flatten())
model.add(Dense(32, activation='relu', kernel_regularizer=l2(0.01)))  # 增加全连接层的神经元数量
model.add(Dropout(0.5))
model.add(Dense(2, activation='softmax'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['binary_accuracy'])
model.summary()

使用学习率调度器

使用学习率调度器动态调整学习率:

from tensorflow.keras.callbacks import ReduceLROnPlateau

reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=5, min_lr=0.001)

history = model.fit(train_features, train_labels, epochs=50, batch_size=64, verbose=1, validation_split=0.25, class_weight=class_weight, callbacks=[reduce_lr])

调整之后大致:

from tensorflow.keras.layers import Conv1D, Dense, MaxPooling1D, Flatten, Dropout, Input, BatchNormalization
from tensorflow.keras.models import Sequential
from tensorflow.keras.regularizers import l2
from tensorflow.keras.callbacks import ReduceLROnPlateau

# 初始化Sequential模型
model = Sequential()

# 添加输入层和第一层卷积层
model.add(Input(shape=(train_features.shape[1], 4)))
model.add(Conv1D(filters=64, kernel_size=5, kernel_regularizer=l2(0.01), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))

# 添加第二层卷积层
model.add(Conv1D(filters=128, kernel_size=3, kernel_regularizer=l2(0.01), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))

# 添加第三层卷积层
model.add(Conv1D(filters=256, kernel_size=3, kernel_regularizer=l2(0.01), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))

# 添加Flatten层
model.add(Flatten())

# 添加全连接层
model.add(Dense(128, activation='relu', kernel_regularizer=l2(0.01)))
model.add(Dropout(0.5))

# 添加输出层
model.add(Dense(2, activation='softmax'))

# 编译模型
model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), metrics=['binary_accuracy'])

# 打印模型的概述
model.summary()

# 使用学习率调度器
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=5, min_lr=0.0001)

# 训练模型
history = model.fit(train_features, train_labels, epochs=500, batch_size=64, verbose=1, validation_split=0.25, callbacks=[reduce_lr])

调整类权重

在model.fit中添加class_weight参数:

class_weight = {0: 1., 1: 3.}  # 根据数据集的类别分布调整权重

history = model.fit(train_features, train_labels, epochs=500, batch_size=64, verbose=1, validation_split=0.25, class_weight=class_weight, callbacks=[reduce_lr])

增加数据增强

使用数据增强技术生成更多的训练数据:

from tensorflow.keras.preprocessing.image import ImageDataGenerator

datagen = ImageDataGenerator(
    rotation_range=20,
    width_shift_range=0.2,
    height_shift_range=0.2,
    horizontal_flip=True)

# 使用数据增强生成训练数据
history = model.fit(datagen.flow(train_features, train_labels, batch_size=64),
                    epochs=500, verbose=1, validation_split=0.25, class_weight=class_weight, callbacks=[reduce_lr])

使用更复杂的模型架构

尝试使用更复杂的模型架构,如ResNet:

from tensorflow.keras.applications import ResNet50

base_model = ResNet50(weights=None, include_top=False, input_shape=(train_features.shape[1], 4, 1))
model = Sequential()
model.add(base_model)
model.add(Flatten())
model.add(Dense(128, activation='relu', kernel_regularizer=l2(0.01)))
model.add(Dropout(0.5))
model.add(Dense(2, activation='softmax'))

model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), metrics=['binary_accuracy'])
model.summary()

history = model.fit(train_features, train_labels, epochs=500, batch_size=64, verbose=1, validation_split=0.25, class_weight=class_weight, callbacks=[reduce_lr])

调整超参数

尝试不同的学习率、批量大小和正则化参数:

model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(learning_rate=0.0005), metrics=['binary_accuracy'])

history = model.fit(train_features, train_labels, epochs=500, batch_size=32, verbose=1, validation_split=0.25, class_weight=class_weight, callbacks=[reduce_lr])

使用交叉验证

使用交叉验证来评估模型的性能:

from sklearn.model_selection import KFold

kf = KFold(n_splits=5)
for train_index, val_index in kf.split(train_features):
    X_train, X_val = train_features[train_index], train_features[val_index]
    y_train, y_val = train_labels[train_index], train_labels[val_index]
    
    history = model.fit(X_train, y_train, epochs=500, batch_size=64, verbose=1, validation_data=(X_val, y_val), class_weight=class_weight, callbacks=[reduce_lr])

learning_rate参数用于设置初始学习率,而ReduceLROnPlateau回调函数用于在训练过程中动态调整学习率。

具体来说,learning_rate=0.001设置了优化器的初始学习率为0.001,而ReduceLROnPlateau回调函数会在验证损失不再改善时,将学习率乘以factor(即0.2),直到达到min_lr(即0.0001)。——》两者不会冲突

修改后如下:
···

from tensorflow.keras.layers import Conv1D, Dense, MaxPooling1D, Flatten, Dropout, Input, BatchNormalization
from tensorflow.keras.models import Sequential
from tensorflow.keras.regularizers import l2
from tensorflow.keras.callbacks import ReduceLROnPlateau

# 初始化Sequential模型
model = Sequential()

# 添加输入层和第一层卷积层
model.add(Input(shape=(train_features.shape[1], 4)))
model.add(Conv1D(filters=64, kernel_size=5, kernel_regularizer=l2(0.01), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))

# 添加第二层卷积层
model.add(Conv1D(filters=128, kernel_size=3, kernel_regularizer=l2(0.01), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))

# 添加第三层卷积层
model.add(Conv1D(filters=256, kernel_size=3, kernel_regularizer=l2(0.01), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))

# 添加Flatten层
model.add(Flatten())

# 添加全连接层
model.add(Dense(128, activation='relu', kernel_regularizer=l2(0.01)))
model.add(Dropout(0.5))

# 添加输出层
model.add(Dense(2, activation='softmax'))

# 编译模型
model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), metrics=['binary_accuracy'])

# 打印模型的概述
model.summary()

# 使用学习率调度器
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=5, min_lr=0.0001)

# 训练模型
history = model.fit(train_features, train_labels, epochs=500, batch_size=64, verbose=1, validation_split=0.25, callbacks=[reduce_lr])

初始学习率设置为0.001,当验证损失在连续5个epoch中没有改善时,学习率将乘以0.2,直到达到最小学习率0.0001。这样可以确保模型在训练过程中动态调整学习率,以便更好地收敛。

from tensorflow.keras.layers import Conv1D, Dense, MaxPooling1D, Flatten, BatchNormalization, Dropout, Input
from tensorflow.keras.models import Sequential
from tensorflow.keras.regularizers import l2
from tensorflow.keras.callbacks import ReduceLROnPlateau

# 初始化Sequential模型
model = Sequential()

# 添加输入层和第一层卷积层
model.add(Input(shape=(train_features.shape[1], 4)))
model.add(Conv1D(filters=64, kernel_size=5, kernel_regularizer=l2(0.01), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))  # 增加Dropout率

# 添加第二层卷积层
model.add(Conv1D(filters=128, kernel_size=3, kernel_regularizer=l2(0.01), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))  # 增加Dropout率

# 添加第三层卷积层
model.add(Conv1D(filters=256, kernel_size=3, kernel_regularizer=l2(0.01), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))  # 增加Dropout率

# 添加Flatten层
model.add(Flatten())

# 添加全连接层
model.add(Dense(128, activation='relu', kernel_regularizer=l2(0.01)))
model.add(Dropout(0.5))  # 增加Dropout率

# 添加输出层
model.add(Dense(2, activation='softmax'))

# 编译模型
model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), metrics=['binary_accuracy'])

# 打印模型的概述
model.summary()

# 使用学习率调度器
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=5, min_lr=0.0001)  # 调整min_lr

# 训练模型
history = model.fit(train_features, train_labels, epochs=100, batch_size=32, verbose=1, validation_split=0.25, callbacks=[reduce_lr])

使用类权重

如果数据集不平衡,可以通过调整类权重来平衡损失函数对不同类别的关注:

class_weight = {0: 1., 1: 3.}  # 根据数据集的类别分布调整权重

history = model.fit(train_features, train_labels, epochs=100, batch_size=32, verbose=1, validation_split=0.25, class_weight=class_weight, callbacks=[reduce_lr])

class_weight = {0: 1., 1: 3.23}  # 根据类别比例调整权重

history = model.fit(train_features, train_labels, epochs=100, batch_size=32, verbose=1, validation_split=0.25, class_weight=class_weight, callbacks=[reduce_lr])

实际上class weight需要修改如下;

https://stackoverflow.com/questions/44716150/how-can-i-assign-a-class-weight-in-keras-in-a-simple-way

修改权重之后的class是:
但是需要修改初始学习率:

从原来的0.001,感觉loss下降得有点慢,还是提高初始的学习率

其中调参比较好的一次代码保存在:

大致效果如下:

8,正常分析:
前面都是入学者的瞎炼,现在进行正规分析:

from tensorflow.keras.layers import Conv1D, Dense, MaxPooling1D, Flatten, BatchNormalization, Input
from tensorflow.keras.models import Sequential
from tensorflow.keras.regularizers import l2


model = Sequential()

model.add(Input(shape=(train_features.shape[1], 4)))
model.add(Conv1D(filters=32, kernel_size=20, kernel_regularizer=l2(0.01),activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.1))  

model.add(Conv1D(filters=128, kernel_size=3, kernel_regularizer=l2(0.01),activation='relu')) 
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.1))

model.add(Conv1D(filters=256, kernel_size=3, kernel_regularizer=l2(0.01), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.1))


model.add(Flatten())
model.add(Dense(128, activation='relu', kernel_regularizer=l2(0.01)))
model.add(Dropout(0.5))

model.add(Dense(2, activation='softmax'))

model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), metrics=['binary_accuracy']) #learning_rate=0.001,初始学习率,因为后期的loss下降的太慢

model.summary()

首先我的数据是获取cohesin共定位(overlap)附近的CTCF peak中心然后窗口大小为100bp的序列;

为什么又改为100bp了呢?
之前使用的不是50bp吗?
之前设置的50bp大概是考虑motif大小大概不会超过

(1)首先我们的每一个样本都是50bp的DNA序列(实际上是特定蛋白共定位下的CTCF的peak),然后每个字符都用ACGT的one-hot编码,

所以实际上我们输入的是一维的序列数据,而且channel是4(对应ACGT)

所以我们import的模块都是1D的卷积模块

(2)输入层只给出每个样本的shape,无需整个数据集

model.add(Input(shape=(train_features.shape[1], 4)))

(3)对于一维卷积层:
我们需要注意的架构超参数就那么几个:

参考:

https://keras.io/api/layers/convolution_layers/convolution1d/

https://www.tensorflow.org/api_docs/python/tf/keras/layers/Conv1D

kernel_size:卷积核filter的大小(2D一般是方块nxn,1D就是窗口大小)————然后一般的motif长度大概在10~30bp左右范围内,我们取20bp,卷积核可以直接覆盖一个完整的motif,从而有效地提取与该motif相关的特征;

但是保险起见为了看到更大的范围以及规律,我这里使用40bp,后面还是改回了20bp。

filter卷积核的数目:我们输入的DNA序列是4 channel的,而依据CNN架构,不同的卷积核会专注于处理不同的特征信息,1个卷积核对应1个feature map,卷积核的数目对应了这一层输出数据的channel数目————个人建议是随着网络逐层加深之后数目也加深,2的幂次为好——暂时取32

步长stride:控制卷积核移动的步长,1D默认为1

填充padding:1D不需要,为valid

卷积核正则化器:l2正则化,防止过拟合

参考:https://keras-zh.readthedocs.io/regularizers/

model.add(Conv1D(filters=32, kernel_size=20, kernel_regularizer=l2(0.01),activation='relu'))

(5)卷积之后是批归一化:

但是问题就在于批归一化放在激活函数前还是后,最原始的论文是放在前面,但是现在反正没有定数,

按照实际效果好坏来处理前后位置

参考:https://www.tensorflow.org/api_docs/python/tf/keras/layers/BatchNormalization

https://keras-cn.readthedocs.io/en/latest/legacy/layers/normalization_layer/

BatchNormalization:添加批量归一化层,用于加速训练和稳定模型。

model.add(BatchNormalization())   #暂时放在激活函数后面

至于batch normalization与pooling的位置,以pooling为先

https://stackoverflow.com/questions/42015156/the-order-of-pooling-and-normalization-layer-in-convnet

(4)卷积之后就是池化pooling:

池化窗口的大小是4,即每次取4个连续的数据进行池化操作

model.add(MaxPooling1D(pool_size=2)) #原先是4,后面该为2

然后注意这里的strides是none,默认是pool_size,意味着每个pool其实分析的元素不会有重叠

所以如果我pool size设置为4的话,那么最后获得的pooling之后的shape为11-4+1/4=2,也就是只有2个长度的feature map,这简直就没法接着往下卷积了,因为序列数据已经被霍霍完了,没必要将网络做深了。

(其实当我不知道网络结构变成什么样,下一步要提供什么样的参数的时候,可以用model_summary看一下网络输出结构是怎么样的,再然后考虑是否要进一步再叠一层CNN)

(6)随机丢弃一些神经元,防止过拟合:

model.add(Dropout(0.3))  

反正放在哪里还是没有定论,放在全连接层前面比较多,但是因为是随机正则化机制,所以放在哪里都行。

卷积层也可以放,一般建议是卷积层丢drop少一点,在全连接层丢多一点。

总之input——》conv——》激活——》pooling——》batchnorm——》dropout——》全连接层(激活——》dropout)

只要是绘制logomarker:

详细解释其中的每一行代码、函数、参数,并对所使用的函数举1-2个简单例子来解释说明

训练:

对于非常小或非常不平衡的数据集,随机分割很有可能完全从其中一个分割中消除一个类。

——》

另外有:

https://stackoverflow.com/questions/34842405/parameter-stratify-from-method-train-test-split-scikit-learn

# 将数据集拆分为训练集和测试集,使用 stratify 参数保持比例不变
train_features, test_features, train_labels, test_labels = train_test_split(
    input_features, input_labels, test_size=0.25, random_state=2025, stratify=input_labels)

——》
2025.1.22更新:
上述代码实际上使用的是tensorflow框架,我本来是想用pytorch的,所以暂时先做个新手测验,所以代码写的就这样;
另外前面写的只是草稿部分,现在来看有很多缺漏以及说错了的地方:
比如说weight就是train以及test的怎么平衡,其实不是一行代码就行的;
另外网络架构上稍微变了变,然后数据上也稍微变化了一下,就是一些超参数;
输入数据的编码也变化了;
然后对于卷积核的理解我是这样理解的:
虽然本质上说我们用ChIP的数据来训练,按理来说有了这个数据我们早就知道哪里是蛋白质结合哪里不是了,那为什么我们还要训练1个CNN呢,当然也可以使用其他架构,我认为原因在于:
1,首先是能运用到train之外的数据集上:
同个细胞系就算了,因为正如前面所言有了数据;
不同细胞系呢,可以参考,当然现在学了生物不敢说那么绝对,但是可以迁移学习;
当然理论上来说不同细胞系基因组难道不是一样吗,所以如果只是给个DNA序列,我们又不做DNA-seq也就是WGS、WES,会有什么区别吗,值得思考
2,我们可以调整前提条件:
比如说多个组学数据筛选下的正负样本,这样学到的东西就不是简单的TFBS二分类问题了,
比如说我用ATAC数据去筛选,我用TAD边界去筛选,我考虑链的正反方向性等等,或者共定位,或者多个motif异二聚体异多聚体等等,当然这就不是模型的作用了,重点应该是上游数据的处理上
3,理论上,训练完毕之后的CNN,我提取出第一层卷积层(这里我捉摸着可能是因为第一层可视化之后最有生物学意义,当然大家都可以自己去可视化不同的卷积层),然后选取权重最大的那几个kernel卷积核,就是best kernel,然后我再对这个核在正样本上做一个cross correlation,然后相关性最高的位点,取这些正样本的子序列做一个平均,类似于motif效果;但是我的窗口可以开很大,所以40bp我就看40bp motif,多少就多少。
整理修改之后的CNN在PR之后仓库

在这里插入图片描述

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

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

相关文章

【unity游戏开发之InputSystem——02】InputAction的使用介绍(基于unity6开发介绍)

文章目录 一、InputAction简介1、InputAction是什么?2、示例 二、InputAction参数相关1、点击齿轮1.1 Actions 动作(1)动作类型(Action Type)(2)初始状态检查(Initial State Check&a…

机器学习 vs 深度学习

目录 一、机器学习 1、实现原理 2、实施方法 二、深度学习 1、与机器学习的联系与区别 2、神经网络的历史发展 3、神经网络的基本概念 一、机器学习 1、实现原理 训练(归纳)和预测(演绎) 归纳: 从具体案例中抽象一般规律…

Docker核心命令与Yocto项目的高效应用

随着软件开发逐渐向分布式和容器化方向演进,Docker 已成为主流的容器化技术之一。它通过标准化的环境配置、资源隔离和高效的部署流程,大幅提高了开发和构建效率。Yocto 项目作为嵌入式 Linux 系统构建工具,与 Docker 的结合进一步增强了开发…

Linux通过docker部署京东矩阵容器服务

获取激活码 将京东无线宝app升级到最新版,然后打开首页,点击号 选择添加容器矩阵,然后获取激活码 运行容器 read -p "请输入你的激活码: " ACTIVECODE;read -p "请输入宿主机的缓存路径: " src;docker rm -f cmatrix;docker run -d -it --name cmatrix …

vue视频流播放,支持多种视频格式,如rmvb、mkv

先将视频转码为ts ffmpeg -i C:\test\3.rmvb -codec: copy -start_number 0 -hls_time 10 -hls_list_size 0 -f hls C:\test\a\output.m3u8 后端配置接口 import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.spring…

【Solr分词器】

Solr分词器 一,什么是solr分词器? 前面已经提到过,Solr是一个高性能的全文检索服务,基于Apache Lucene的,Lucene是一个用Java编写的开源的信息检索库,为全文索引和搜索提供了基础功能。 在Solr中&#xf…

OS2.【Linux】基本命令入门(1)

目录 1.操作系统是什么? 2.好操作系统的衡量标准 3.操作系统的核心工作 4.在计算机上所有行为都会被转换为硬件行为 5.文件 6.简单介绍一些基本命令 1.clear 2.pwd 3.ls 1.ls -l 2.隐藏文件的创建 3.ls -al 4.ls -ld 5.ls -F(注意是大写) 4.cd 1.cd .. "…

LabVIEW处理复杂系统和数据处理

LabVIEW 是一个图形化编程平台,广泛应用于自动化控制、数据采集、信号处理、仪器控制等复杂系统的开发。它的图形化界面使得开发人员能够直观地设计系统和算法,尤其适合处理需要实时数据分析、高精度控制和复杂硬件集成的应用场景。LabVIEW 提供丰富的库…

激光雷达和相机早期融合

通过外参和内参的标定将激光雷达的点云投影到图像上。 • 传感器标定 首先需要对激光雷达和相机(用于获取 2D 图像)进行外参和内参标定。这是为了确定激光雷达坐标系和相机坐标系之间的转换关系,包括旋转和平移。通常采用棋盘格等标定工具&…

C++----STL(vector)

vector的介绍 vector的文档介绍:cplusplus.com/reference/vector/vector/ 1.基本概念 简单来说,vector是表示可以改变大小的数组的顺序容器。使用连续的存储位置来存储元素,因此可以通过常规指针的偏移量来高效访问。 2.内部机制 vector…

Airflow:BranchOperator实现动态分支控制流程

Airflow是用于编排复杂工作流的开源平台,支持在有向无环图(dag)中定义、调度和监控任务。其中一个关键特性是能够使用BranchOperator创建动态的、有条件的工作流。在这篇博文中,我们将探索BranchOperator,讨论它是如何…

rocketmq-MQClientInstance-单进程多生产者组多消费者组的实例模型

多生产者组多消费者组的思考 思考下。当一个client,订阅多个consumergroup、多个productgroup时。此时进程的线程模型是如何的? 之前文章有分析到。消费者组,是有多个线程去共同协作的。 假设订阅2个consumergroup, 线程数量是2倍…

nuxt3项目打包部署到服务器后配置端口号和开启https

nuxt3打包后的项目部署相对于一般vite打包的静态文件部署要稍微麻烦一些,还有一个主要的问题是开发环境配置的.env环境变量在打包后部署时获取不到,具体的解决方案可以参考我之前文章 nuxt3项目打包后获取.env设置的环境变量无效的解决办法。 这里使用的…

Class ‘com.xxx.xxx‘ not found in module ‘xxxx‘ 解决方法

目录 前言1. 问题所示2. 原理分析3. 解决方法前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 1. 问题所示 启动项目的时候,出现如下Bug: Class ‘com.xxx.xxx‘ not found in module ‘xxxx‘截图如下: 2. 原理分析 Java 项目中引用的类未能被正…

ngrok同时配置多个内网穿透方法

一、概要 ngrok可以用来配置免费的内网穿透,启动后就可以用外网ip:端口访问到自己计算机的某个端口了。 可以用来从外网访问自己的测试页面(80、8080)、ftp文件传输(21)、远程桌面(3389)等。 …

OGG 19C 集成模式启用DDL复制

接Oracle19C PDB 环境下 OGG 搭建(PDB to PDB)_cdb架构 配置ogg-CSDN博客,给 pdb 环境 ogg 配置 DDL 功能。 一个报错 SYShfdb1> ddl_setup.sqlOracle GoldenGate DDL Replication setup scriptVerifying that current user has privile…

【计算机网络】- 应用层HTTP协议

目录 初识HTTP 什么是HTTP 版本 HTTPS 模型 HTTP抓包工具 为什么使用 抓包工具的下载 下载后的重要操作 Fiddler的使用 HTTP请求与响应的基本格式 HTTP请求基本格式​编辑 HTTP响应基本格式 协议格式总结❗️❗️❗️​编辑 HTTP 详解 认识 URL URL基本格式 …

基于SpringBoot+Vue的旅游管理系统【源码+文档+部署讲解】

系统介绍 基于SpringBootVue实现的旅游管理系统采用前后端分离架构方式,系统设计了管理员、用户两种角色,系统实现了用户登录与注册、个人中心、用户管理、景点信息管理、订票信息管理、用户评价管理、景点咨询、轮播图管理等功能。 技术选型 开发工具…

Agent群舞,在亚马逊云科技搭建数字营销多代理(Multi-Agent)(下篇)

在本系列的上篇中,小李哥为大家介绍了如何在亚马逊云科技上给社交数字营销场景创建AI代理的方案,用于社交动态的生成和对文章进行推广曝光。在本篇中小李哥将继续本系列的介绍,为大家介绍如何创建主代理,将多个子代理挂载到主代理…

【Ubuntu】安装SSH启用远程连接

【Ubuntu】安装OpenSSH启用远程连接 零、安装软件 使用如下代码安装OpenSSH服务端: sudo apt install openssh-server壹、启动服务 使用如下代码启动OpenSSH服务端: sudo systemctl start ssh贰、配置SSH(可跳过) 配置文件 …